{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "686375ac",
   "metadata": {},
   "source": [
    "# Day 5 — Superpowers: PyTorch + GPU ⚡\n",
    "\n",
    "> **Mission briefing:** Yesterday you hand-built a neural network's engine in pure NumPy — and felt the pain: roughly **130 forward passes** just to nudge the weights *once*. Today the Lab issues you power tools. A library called **PyTorch** computes those nudges automatically and exactly, and a **GPU** runs them at ludicrous speed. Your mission: teach a network to read handwriting — then point it at *your own*.\n",
    "\n",
    "**Previously in the Lab:** on Day 4 you built a neural net by hand and earned a deep respect for backpropagation (and no module — just calluses). Today you get the tools the pros actually use.\n",
    "\n",
    "**Today you will:**\n",
    "- Meet **tensors** — NumPy arrays that can live on a GPU and remember their own history\n",
    "- Let **autograd** compute gradients for you (no more finite-difference nudging)\n",
    "- Train a real network on **MNIST** and read thousands of strangers' handwritten digits\n",
    "- Watch a **GPU** demolish a CPU at the one operation that matters: matrix multiply\n",
    "- Upgrade to a **convolutional** network, then draw your own digit for it to read\n",
    "\n",
    "**Today you unlock:** 🔓 **Digit Reader** — draw a digit on screen and the network *you trained* names it.\n",
    "\n",
    "<details data-resource-precision=\"5\"><summary>A precise reading</summary><p>MNIST has 60,000 training and 10,000 test images; the test images do not train the model. Autograd applies the chain rule to recorded tensor operations, subject to floating-point precision. GPU speed depends on matrix size, hardware and transfer overhead; small workloads can be faster on the CPU.</p></details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6baee145",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:54:59.354926Z",
     "iopub.status.busy": "2026-08-17T12:54:59.354796Z",
     "iopub.status.idle": "2026-08-17T12:54:59.463389Z",
     "shell.execute_reply": "2026-08-17T12:54:59.462312Z"
    }
   },
   "outputs": [],
   "source": [
    "# ╔═══════════════════════════════════════════════════════════════════╗\n",
    "# ║ SETUP — run me first! (identical in every Lab notebook)           ║\n",
    "# ╚═══════════════════════════════════════════════════════════════════╝\n",
    "import os, sys, pathlib\n",
    "\n",
    "IN_COLAB = \"google.colab\" in sys.modules\n",
    "SMOKE = os.environ.get(\"SMOKE_TEST\", \"0\") == \"1\"   # tiny fast run for automated tests\n",
    "\n",
    "REPO_URL = \"https://github.com/A-Halimi/ai-studio-internship.git\"  # instructor: set once\n",
    "\n",
    "if IN_COLAB:\n",
    "    if not pathlib.Path(\"/content/ai-studio-internship\").exists():\n",
    "        !git clone -q {REPO_URL} /content/ai-studio-internship\n",
    "    %cd /content/ai-studio-internship/notebooks/day05                    # ← ADAPT day folder\n",
    "    %pip -q install gradio==6.19.0                                       # ← ADAPT per-day installs (see spec; delete line if none)\n",
    "\n",
    "HERE = pathlib.Path.cwd()\n",
    "REPO = HERE.parents[1]                       # .../notebooks/dayNN -> repo root\n",
    "DATA_DIR = pathlib.Path(os.environ.get(\"COURSE_DATA_DIR\", REPO / \"data\"))\n",
    "SAMPLES_DIR = REPO / \"data_samples\"          # small datasets shipped with the repo\n",
    "MODELS_DIR = REPO / \"ai_studio\" / \"models\"   # where your Studio modules unlock\n",
    "for p in (REPO / \"data\", MODELS_DIR):\n",
    "    p.mkdir(parents=True, exist_ok=True)\n",
    "\n",
    "import random\n",
    "import numpy as np\n",
    "SEED = 42\n",
    "random.seed(SEED); np.random.seed(SEED)\n",
    "print(f\"✅ Setup done | Colab: {IN_COLAB} | Smoke test: {SMOKE}\")\n",
    "print(f\"   data: {DATA_DIR}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d0ff20b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:54:59.465068Z",
     "iopub.status.busy": "2026-08-17T12:54:59.464878Z",
     "iopub.status.idle": "2026-08-17T12:55:03.374238Z",
     "shell.execute_reply": "2026-08-17T12:55:03.373170Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "torch.manual_seed(SEED)\n",
    "DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "if DEVICE.type == \"cuda\":\n",
    "    print(f\"⚡ Running on GPU: {torch.cuda.get_device_name(0)}\")\n",
    "else:\n",
    "    print(\"🐢 No GPU found — running on CPU. Everything still works, just slower.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05cd5cb3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.376044Z",
     "iopub.status.busy": "2026-08-17T12:55:03.375807Z",
     "iopub.status.idle": "2026-08-17T12:55:03.379982Z",
     "shell.execute_reply": "2026-08-17T12:55:03.378884Z"
    }
   },
   "outputs": [],
   "source": [
    "# ── CONFIG — the Lab's control panel. Tweak me later! ──────────────\n",
    "EPOCHS        = 1 if SMOKE else 3\n",
    "TRAIN_SUBSET  = 2_000 if SMOKE else 60_000    # how many training images to use\n",
    "BATCH_SIZE    = 128\n",
    "LEARNING_RATE = 1e-3\n",
    "print(f\"EPOCHS={EPOCHS}  TRAIN_SUBSET={TRAIN_SUBSET:,}  BATCH_SIZE={BATCH_SIZE}  LR={LEARNING_RATE}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06cfe173",
   "metadata": {},
   "source": [
    "## 1. Tensors — NumPy arrays with two superpowers\n",
    "\n",
    "A **tensor** is PyTorch's version of a NumPy array: an n-dimensional grid of numbers. You already know how to work with these from Days 2–4. Tensors add two things a plain NumPy array can't do:\n",
    "\n",
    "1. **They can live on a GPU** — thousands of numbers multiplied in parallel.\n",
    "2. **They remember their own history** — so PyTorch can compute gradients automatically (that's the next section).\n",
    "\n",
    "The bridge between the two worlds is one function each way: `torch.from_numpy(array)` and `tensor.numpy()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca35e1ee",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.381543Z",
     "iopub.status.busy": "2026-08-17T12:55:03.381403Z",
     "iopub.status.idle": "2026-08-17T12:55:03.465985Z",
     "shell.execute_reply": "2026-08-17T12:55:03.464944Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import torch\n",
    "\n",
    "a = np.array([[1., 2.], [3., 4.]])\n",
    "t = torch.from_numpy(a)          # numpy -> torch\n",
    "back = t.numpy()                 # torch -> numpy\n",
    "\n",
    "print(\"numpy array:\\n\", a)\n",
    "print(\"torch tensor:\\n\", t)\n",
    "print(\"shape:\", t.shape, \"| dtype:\", t.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82dc7ccb",
   "metadata": {},
   "source": [
    "### 🧪 Exercise 1 — your first tensors\n",
    "\n",
    "- `A` is 3×4 and `B` is 4×2. Matrix-multiply them with the `@` operator into `C`.\n",
    "- Print `C`'s shape and dtype.\n",
    "- Expected: `C` is **3×2** — the shared inner dimension (4) cancels, exactly like the matmuls you traced by hand on Day 4."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b254193f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.467583Z",
     "iopub.status.busy": "2026-08-17T12:55:03.467439Z",
     "iopub.status.idle": "2026-08-17T12:55:03.533283Z",
     "shell.execute_reply": "2026-08-17T12:55:03.532276Z"
    }
   },
   "outputs": [],
   "source": [
    "A = torch.rand(3, 4)\n",
    "B = torch.rand(4, 2)\n",
    "\n",
    "# ==================== YOUR CODE HERE ====================\n",
    "### HINT: '@' is matrix multiply; the inner dims (4) must match and cancel\n",
    "...\n",
    "\n",
    "print(\"C shape:\", C.shape, \"| dtype:\", C.dtype)\n",
    "assert C.shape == (3, 2), \"C should be 3x2 — check which dimensions cancel\"\n",
    "print(\"✅ nice — that's a matrix multiply, the heartbeat of every network\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50ec1055",
   "metadata": {},
   "source": [
    "## 2. Autograd — yesterday's nudge, automatic and exact\n",
    "\n",
    "On Day 4 you found gradients by **nudging**: bump a weight a tiny bit, re-run the network, see how the loss changed. It worked, but it was slow and only approximate.\n",
    "\n",
    "PyTorch does it exactly. Mark a tensor with `requires_grad=True` and it silently records every operation you do to it. Call `.backward()` and PyTorch replays those operations in reverse — the **chain rule**, done for you — leaving the gradient in `.grad`.\n",
    "\n",
    "<details><summary>🔢 <b>Math Corner</b> — what <code>backward()</code> actually did (optional)</summary>\n",
    "\n",
    "`y = x**2` builds a tiny computation graph: `x → square → y`. `y.backward()` walks that graph in reverse, applying the chain rule at each step, and drops the result into `x.grad`. For `y = x²` the derivative is `2x`, so at `x = 2` you get `4`. The magic: the same reverse walk works for a graph with a *million* nodes — which is exactly a neural network.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5138cd9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.534892Z",
     "iopub.status.busy": "2026-08-17T12:55:03.534752Z",
     "iopub.status.idle": "2026-08-17T12:55:03.539527Z",
     "shell.execute_reply": "2026-08-17T12:55:03.538569Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor(2.0, requires_grad=True)\n",
    "y = x ** 2\n",
    "y.backward()                     # fill in dy/dx\n",
    "\n",
    "print(\"y =\", y.item())\n",
    "print(\"dy/dx at x=2 =\", x.grad.item(), \" (calculus says 2x = 4 ✓)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "446bd54b",
   "metadata": {},
   "source": [
    "### 🧪 Exercise 2 — differentiate by yourself (well, by PyTorch)\n",
    "\n",
    "- Build `y = x**3 + 2*x` at `x = 3.0`, then call `y.backward()`.\n",
    "- Expected: `x.grad` is **29**. (By hand: d/dx(x³ + 2x) = 3x² + 2 = 3·9 + 2 = 29.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06d05bf8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.541110Z",
     "iopub.status.busy": "2026-08-17T12:55:03.540952Z",
     "iopub.status.idle": "2026-08-17T12:55:03.554318Z",
     "shell.execute_reply": "2026-08-17T12:55:03.553298Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor(3.0, requires_grad=True)\n",
    "\n",
    "# ==================== YOUR CODE HERE ====================\n",
    "### HINT: write the formula for y, then call y.backward()\n",
    "...\n",
    "\n",
    "print(\"dy/dx at x=3:\", x.grad.item())\n",
    "assert abs(x.grad.item() - 29.0) < 1e-5, \"d/dx (x^3 + 2x) = 3x^2 + 2 = 29 at x=3\"\n",
    "print(\"✅ exact gradient, zero calculus homework\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64ac4928",
   "metadata": {},
   "source": [
    "### 🎯 Two passes, not 130\n",
    "\n",
    "On **Day 4** you estimated each gradient by nudging one weight, running the whole network **forward**, and measuring the change — one forward pass **per weight**. With ~130 weights that was ~130 passes for a *single* training step.\n",
    "\n",
    "`loss.backward()` just did that same job for **all** the weights at once, with **one** forward pass and **one** backward pass. That is the entire reason modern AI is possible: autograd records every operation on the way *in*, then replays the chain rule on the way *out*.\n",
    "\n",
    "> Under the hood it's all matrix multiplication — the same `@` you just used. Making those multiplications scream on real hardware is a whole field of its own — high-performance computing."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4d0b464",
   "metadata": {},
   "source": [
    "## 3. The mission: read handwriting (MNIST)\n",
    "\n",
    "**MNIST** is the \"hello world\" of computer vision: 60,000 training and 10,000 test handwritten digits (0–9), each a 28×28 grayscale image. 60,000 to train on, 10,000 strangers' digits to test on.\n",
    "\n",
    "One rule matters for later: we apply **only** `ToTensor()`, which scales pixels to the range 0–1 and gives us **white digits on a black background**. We do **not** normalize — your Digit Reader Studio module expects exactly this format, so keep it in mind for the unlock."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24393b73",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:03.555777Z",
     "iopub.status.busy": "2026-08-17T12:55:03.555628Z",
     "iopub.status.idle": "2026-08-17T12:55:05.878788Z",
     "shell.execute_reply": "2026-08-17T12:55:05.877756Z"
    }
   },
   "outputs": [],
   "source": [
    "from torchvision import datasets, transforms\n",
    "from torch.utils.data import DataLoader, Subset\n",
    "\n",
    "tfm = transforms.ToTensor()          # pixels -> [0, 1]; no normalization (module contract!)\n",
    "train_full = datasets.MNIST(DATA_DIR, train=True, download=True, transform=tfm)\n",
    "test_set   = datasets.MNIST(DATA_DIR, train=False, download=True, transform=tfm)\n",
    "train_set  = Subset(train_full, range(TRAIN_SUBSET))\n",
    "\n",
    "# num_workers=0: MNIST is tiny, so one process is plenty, and it avoids the\n",
    "# harmless-but-noisy DataLoader worker-shutdown warnings you can hit in Jupyter.\n",
    "train_loader = DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True, num_workers=0)\n",
    "test_loader  = DataLoader(test_set, batch_size=256, shuffle=False, num_workers=0)\n",
    "print(f\"train images: {len(train_set):,}   test images: {len(test_set):,}\")\n",
    "\n",
    "img0, label0 = train_full[0]\n",
    "print(\"one image:\", tuple(img0.shape), \"| pixel range:\", float(img0.min()), \"to\", float(img0.max()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a0c2f3b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:05.880774Z",
     "iopub.status.busy": "2026-08-17T12:55:05.880560Z",
     "iopub.status.idle": "2026-08-17T12:55:06.538146Z",
     "shell.execute_reply": "2026-08-17T12:55:06.537064Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "fig, axes = plt.subplots(4, 8, figsize=(10, 5))\n",
    "for ax, idx in zip(axes.ravel(), range(32)):\n",
    "    img, label = train_full[idx]\n",
    "    ax.imshow(img.squeeze(), cmap=\"gray\")\n",
    "    ax.set_title(str(label), fontsize=9)\n",
    "    ax.axis(\"off\")\n",
    "fig.suptitle(\"MNIST — handwritten digits (white ink on black, values 0–1)\")\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a6643bd",
   "metadata": {},
   "source": [
    "## 4. Build the network (a multi-layer perceptron)\n",
    "\n",
    "Time to assemble the same kind of network you hand-wired on Day 4 — but in three lines. `nn.Sequential` just stacks layers front to back:\n",
    "\n",
    "- `nn.Flatten()` — unroll each 28×28 image into a flat vector of 784 numbers\n",
    "- `nn.Linear(784, 128)` — the big matmul: `(batch, 784) @ (784, 128)`\n",
    "- `nn.ReLU()` — the non-linearity that lets it learn curves, not just lines\n",
    "- `nn.Linear(128, 10)` — ten output scores, one per digit\n",
    "\n",
    "### 🧪 Exercise 3 — assemble the MLP\n",
    "\n",
    "Build the four-layer `nn.Sequential` above and move it onto `DEVICE`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01dfd2a6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:06.539927Z",
     "iopub.status.busy": "2026-08-17T12:55:06.539695Z",
     "iopub.status.idle": "2026-08-17T12:55:06.711114Z",
     "shell.execute_reply": "2026-08-17T12:55:06.709991Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "# ==================== YOUR CODE HERE ====================\n",
    "...\n",
    "\n",
    "print(mlp)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b29076b3",
   "metadata": {},
   "source": [
    "That `nn.Linear(784, 128)` is exactly the `(batch, 784) @ (784, 128)` matrix multiply you discovered on Day 4 — the network is *made* of matmuls. Let's count how many numbers it's juggling.\n",
    "\n",
    "### 🧪 Exercise 4 — count the parameters\n",
    "\n",
    "Sum `p.numel()` over `mlp.parameters()`. Expected: **101,770** (that's 784·128 + 128 weights and biases in the first layer, plus 128·10 + 10 in the second)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "254698de",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:06.712936Z",
     "iopub.status.busy": "2026-08-17T12:55:06.712787Z",
     "iopub.status.idle": "2026-08-17T12:55:06.716953Z",
     "shell.execute_reply": "2026-08-17T12:55:06.715950Z"
    }
   },
   "outputs": [],
   "source": [
    "# ==================== YOUR CODE HERE ====================\n",
    "### HINT: p.numel() counts the entries in one weight tensor; sum over all params\n",
    "...\n",
    "\n",
    "print(f\"The MLP has {n_params:,} parameters.\")\n",
    "assert n_params == 101_770, \"Expected 100,480 + 1,290 = 101,770 — check your layer sizes\"\n",
    "print(\"✅ 101,770 knobs, all tuned by autograd\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4148dab",
   "metadata": {},
   "source": [
    "## 5. The training loop — the pattern that runs all of AI\n",
    "\n",
    "Almost every neural network in the world learns with the **same five-step loop**. Burn it into memory:\n",
    "\n",
    "1. **`optimizer.zero_grad()`** — forget the last step's gradients\n",
    "2. **`logits = model(x)`** — forward pass (a stack of matrix multiplies)\n",
    "3. **`loss = loss_fn(logits, y)`** — how wrong were we?\n",
    "4. **`loss.backward()`** — autograd fills in every gradient\n",
    "5. **`optimizer.step()`** — nudge all the weights a little downhill\n",
    "\n",
    "That's it. Even GPT-scale models train with this exact skeleton — just bigger.\n",
    "\n",
    "### 🧪 Exercise 5 — complete the training loop\n",
    "\n",
    "Fill the **five ★ gaps** in `train_model` below — one line each, in order. This is the single most important code you'll write all course.\n",
    "\n",
    "- Loss function: `nn.CrossEntropyLoss()` (already wired up).\n",
    "- Optimizer: `Adam` at `LEARNING_RATE` (already wired up).\n",
    "- Expected: the printed **loss goes down** each epoch. If it stays flat or explodes, re-check the order of the five steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19f29641",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:06.718720Z",
     "iopub.status.busy": "2026-08-17T12:55:06.718573Z",
     "iopub.status.idle": "2026-08-17T12:55:06.767043Z",
     "shell.execute_reply": "2026-08-17T12:55:06.766054Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "from tqdm.auto import tqdm\n",
    "\n",
    "def train_model(model, epochs):\n",
    "    'Run the canonical training loop; reused for every model today.'\n",
    "    loss_fn = nn.CrossEntropyLoss()\n",
    "    optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n",
    "\n",
    "    for epoch in range(1, epochs + 1):\n",
    "        model.train()\n",
    "        running = 0.0\n",
    "        for images, labels in tqdm(train_loader, desc=f\"epoch {epoch}/{epochs}\"):\n",
    "            images, labels = images.to(DEVICE), labels.to(DEVICE)\n",
    "\n",
    "            # ==================== YOUR CODE HERE ====================\n",
    "            ### HINT: ★1 — clear the gradients left over from the previous step\n",
    "            ...\n",
    "\n",
    "            # ==================== YOUR CODE HERE ====================\n",
    "            ### HINT: ★2 — forward pass: feed the images through the model to get logits\n",
    "            ...\n",
    "\n",
    "            # ==================== YOUR CODE HERE ====================\n",
    "            ### HINT: ★3 — compare logits to the true labels to get the loss\n",
    "            ...\n",
    "\n",
    "            # ==================== YOUR CODE HERE ====================\n",
    "            ### HINT: ★4 — backpropagate: fill in a gradient for every weight\n",
    "            ...\n",
    "\n",
    "            # ==================== YOUR CODE HERE ====================\n",
    "            ### HINT: ★5 — take one step downhill with the optimizer\n",
    "            ...\n",
    "\n",
    "            running += loss.item() * images.size(0)\n",
    "        print(f\"epoch {epoch}: train loss {running / len(train_loader.dataset):.3f}\")\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "434984ca",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:06.769076Z",
     "iopub.status.busy": "2026-08-17T12:55:06.768931Z",
     "iopub.status.idle": "2026-08-17T12:55:22.301301Z",
     "shell.execute_reply": "2026-08-17T12:55:22.300231Z"
    }
   },
   "outputs": [],
   "source": [
    "train_model(mlp, EPOCHS)\n",
    "print(\"✅ MLP trained.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60ca5e9a",
   "metadata": {},
   "source": [
    "## 6. Grade it on strangers' handwriting\n",
    "\n",
    "Training loss is nice, but the real question is: how well does it read digits it has **never seen**? We loop over the 10,000-image test set and count how many it gets right — all inside `torch.no_grad()`, because we're only *reading* the network now, not training it (no gradients needed, so it runs faster and uses less memory).\n",
    "\n",
    "### 🧪 Exercise 6 — write `evaluate`\n",
    "\n",
    "Fill in the no-grad loop that counts correct predictions. The class the network picks is the index of the largest logit: `logits.argmax(dim=1)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c32e4d76",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:22.303014Z",
     "iopub.status.busy": "2026-08-17T12:55:22.302869Z",
     "iopub.status.idle": "2026-08-17T12:55:23.043508Z",
     "shell.execute_reply": "2026-08-17T12:55:23.042311Z"
    }
   },
   "outputs": [],
   "source": [
    "def evaluate(model, loader):\n",
    "    'Return accuracy (fraction correct) over a data loader.'\n",
    "    model.eval()\n",
    "    correct = total = 0\n",
    "    # ==================== YOUR CODE HERE ====================\n",
    "    ### HINT: wrap the loop in `with torch.no_grad():` and compare argmax to labels\n",
    "    ...\n",
    "    return correct / total\n",
    "\n",
    "mlp_acc = evaluate(mlp, test_loader)\n",
    "print(f\"MLP test accuracy: {mlp_acc:.4f}\")\n",
    "if not SMOKE:\n",
    "    assert mlp_acc >= 0.95, \"Expected >= 0.95 on the full run — check your training loop\"\n",
    "    print(f\"🎉 Your network just read {round(mlp_acc * 10000):,} of 10,000 strangers' digits!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2ffc7c1",
   "metadata": {},
   "source": [
    "## 7. Why a GPU? Let's measure it ⚡\n",
    "\n",
    "Everything above is matrix multiplication. A **GPU** does thousands of multiplies at once, which is why it obliterates a CPU on big matrices. Let's prove it: time `A @ B` for growing matrix sizes on the CPU, and (if you have one) on the GPU.\n",
    "\n",
    "One catch: a GPU runs **asynchronously**. When you write `A @ B`, the CPU fires off the work and moves on *before* the GPU is done. So to time it honestly you must call `torch.cuda.synchronize()` — \"wait until the GPU actually finishes\" — before stopping the clock.\n",
    "\n",
    "### 🧪 Exercise 7 — time the matmul\n",
    "\n",
    "Fill in the two timed blocks (CPU and GPU). The warm-up call and the `synchronize()` calls are given — you write the timing with `time.perf_counter()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0cfdc65",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:23.045204Z",
     "iopub.status.busy": "2026-08-17T12:55:23.045058Z",
     "iopub.status.idle": "2026-08-17T12:55:23.545159Z",
     "shell.execute_reply": "2026-08-17T12:55:23.544036Z"
    }
   },
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "sizes = [256, 512, 1024] if SMOKE else [512, 1024, 2048, 4096]\n",
    "cpu_times, gpu_times = [], []\n",
    "\n",
    "for n in sizes:\n",
    "    A = torch.randn(n, n)\n",
    "    B = torch.randn(n, n)\n",
    "\n",
    "    # --- CPU timing ---\n",
    "    # ==================== YOUR CODE HERE ====================\n",
    "    ### HINT: read time.perf_counter() before and after C = A @ B, store the difference\n",
    "    ...\n",
    "\n",
    "    # --- GPU timing (only if we actually have a CUDA device) ---\n",
    "    if DEVICE.type == \"cuda\":\n",
    "        Ag, Bg = A.to(DEVICE), B.to(DEVICE)\n",
    "        _ = Ag @ Bg                 # warm-up: the very first CUDA call is slow\n",
    "        torch.cuda.synchronize()    # wait for the warm-up to finish\n",
    "        # ==================== YOUR CODE HERE ====================\n",
    "        ### HINT: same idea, but call torch.cuda.synchronize() before stopping the clock\n",
    "        ...\n",
    "\n",
    "    line = f\"n={n:5d}   CPU {cpu_times[-1] * 1e3:8.1f} ms\"\n",
    "    if DEVICE.type == \"cuda\":\n",
    "        line += f\"   GPU {gpu_times[-1] * 1e3:8.3f} ms\"\n",
    "    print(line)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "babe0f49",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:23.546710Z",
     "iopub.status.busy": "2026-08-17T12:55:23.546553Z",
     "iopub.status.idle": "2026-08-17T12:55:23.644933Z",
     "shell.execute_reply": "2026-08-17T12:55:23.643692Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "x = np.arange(len(sizes))\n",
    "plt.figure(figsize=(8, 5))\n",
    "plt.bar(x - 0.2, np.array(cpu_times) * 1e3, width=0.4, label=\"CPU\")\n",
    "if DEVICE.type == \"cuda\":\n",
    "    plt.bar(x + 0.2, np.array(gpu_times) * 1e3, width=0.4, label=\"GPU\")\n",
    "plt.yscale(\"log\")\n",
    "plt.xticks(x, [str(n) for n in sizes])\n",
    "plt.xlabel(\"matrix size n (an n × n matrix)\")\n",
    "plt.ylabel(\"time per matmul (ms, log scale)\")\n",
    "plt.title(\"Matrix multiply: CPU vs GPU\")\n",
    "plt.legend(); plt.tight_layout(); plt.show()\n",
    "\n",
    "if DEVICE.type == \"cuda\":\n",
    "    for n, ct, gt in zip(sizes, cpu_times, gpu_times):\n",
    "        print(f\"n={n:5d}:  GPU is {ct / gt:6.1f}× faster\")\n",
    "else:\n",
    "    print(\"🐢 CPU only — no GPU to compare against right now.\")\n",
    "    print(\"   On the Lab's GPU, the GPU bars would sit far below the CPU bars.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67d974b3",
   "metadata": {},
   "source": [
    "### The numbers behind the numbers\n",
    "\n",
    "Multiplying two n×n matrices costs about **2·n³** floating-point operations. Divide that by the time you measured and you get **FLOP/s** — operations per second. A workstation CPU manages a few billion (**GFLOP/s**); a modern GPU does **tens of trillions** (**TFLOP/s**). Same math, wildly different speed — because the GPU does thousands of multiplies in parallel.\n",
    "\n",
    "> Squeezing these numbers out of the hardware is a profession of its own — high-performance computing. Today you *use* the speed; one day you could *build* it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc0ea026",
   "metadata": {},
   "source": [
    "## 8. A sharper tool: the convolutional network 🔬\n",
    "\n",
    "The MLP flattens each image into 784 numbers on the very first line — throwing away the fact that nearby pixels belong together. A **convolutional** network keeps the 2-D shape and slides small pattern-detectors across it. (Day 6 goes deep on *why* this works — today, just feel the accuracy jump.)\n",
    "\n",
    "Here's a small CNN. Don't worry about every line yet; notice it ends in the same two `Linear` layers, just fed by smarter features."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b51b3506",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:23.646618Z",
     "iopub.status.busy": "2026-08-17T12:55:23.646469Z",
     "iopub.status.idle": "2026-08-17T12:55:23.653404Z",
     "shell.execute_reply": "2026-08-17T12:55:23.652466Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class SmallCNN(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.features = nn.Sequential(\n",
    "            nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),   # 28 -> 14\n",
    "            nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),  # 14 -> 7\n",
    "        )\n",
    "        self.classifier = nn.Sequential(\n",
    "            nn.Flatten(),\n",
    "            nn.Linear(32 * 7 * 7, 128), nn.ReLU(),\n",
    "            nn.Linear(128, 10),\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        return self.classifier(self.features(x))\n",
    "\n",
    "print(SmallCNN())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bfe1325",
   "metadata": {},
   "source": [
    "### 🧪 Exercise 8 — train the CNN by reusing your loop\n",
    "\n",
    "You already wrote `train_model`. That's the point of a reusable function: the CNN trains with the **exact same call**. Instantiate `SmallCNN`, move it to `DEVICE`, and train it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "708515f7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:23.654970Z",
     "iopub.status.busy": "2026-08-17T12:55:23.654839Z",
     "iopub.status.idle": "2026-08-17T12:55:45.090046Z",
     "shell.execute_reply": "2026-08-17T12:55:45.088860Z"
    }
   },
   "outputs": [],
   "source": [
    "cnn = SmallCNN().to(DEVICE)\n",
    "\n",
    "# ==================== YOUR CODE HERE ====================\n",
    "### HINT: no new loop needed — call the train_model you already wrote\n",
    "...\n",
    "\n",
    "cnn_acc = evaluate(cnn, test_loader)\n",
    "print(f\"CNN test accuracy: {cnn_acc:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7debe7e4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:45.091719Z",
     "iopub.status.busy": "2026-08-17T12:55:45.091566Z",
     "iopub.status.idle": "2026-08-17T12:55:45.095682Z",
     "shell.execute_reply": "2026-08-17T12:55:45.094435Z"
    }
   },
   "outputs": [],
   "source": [
    "print(f\"MLP accuracy: {mlp_acc:.4f}\")\n",
    "print(f\"CNN accuracy: {cnn_acc:.4f}\")\n",
    "print(f\"The CNN got {(cnn_acc - mlp_acc) * 100:+.2f} percentage points by respecting the image's shape.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e3d0623",
   "metadata": {},
   "source": [
    "## 9. Autopsy: where does it slip?\n",
    "\n",
    "No network is perfect. Let's look at the digits the CNN got **wrong** — true label on the left of the arrow, its guess on the right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee423a4d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:45.097229Z",
     "iopub.status.busy": "2026-08-17T12:55:45.097083Z",
     "iopub.status.idle": "2026-08-17T12:55:45.466651Z",
     "shell.execute_reply": "2026-08-17T12:55:45.465503Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "cnn.eval()\n",
    "wrong = []\n",
    "with torch.no_grad():\n",
    "    for images, labels in test_loader:\n",
    "        preds = cnn(images.to(DEVICE)).argmax(1).cpu()\n",
    "        for img, t_, p_ in zip(images, labels, preds):\n",
    "            if int(t_) != int(p_):\n",
    "                wrong.append((img, int(t_), int(p_)))\n",
    "        if len(wrong) >= 16:\n",
    "            break\n",
    "\n",
    "fig, axes = plt.subplots(2, 8, figsize=(10, 3))\n",
    "for ax, (img, t_, p_) in zip(axes.ravel(), wrong[:16]):\n",
    "    ax.imshow(img.squeeze(), cmap=\"gray\")\n",
    "    ax.set_title(f\"{t_} → {p_}\", fontsize=9, color=\"crimson\")\n",
    "    ax.axis(\"off\")\n",
    "fig.suptitle(\"Where the network slips: true → predicted\")\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16e58050",
   "metadata": {},
   "source": [
    "Be honest with yourself as you look: how many of those would *you* get right, out of context? A lot of them are genuinely mangled — a 4 that's really a 9, a 7 with a slash that looks like a 1. The network isn't dumb; the handwriting is hard."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9500a891",
   "metadata": {},
   "source": [
    "## 10. Read *your* handwriting ✍️\n",
    "\n",
    "Now the fun part. The helper below turns whatever you scribble on the pad into the exact format your network trained on — a 28×28 white-on-black image, centered — then asks the CNN what it sees.\n",
    "\n",
    "**Tip:** draw **big and centered**, filling most of the box. The network learned from centered digits, so a tiny scribble in the corner confuses it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f1a1ffc7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:45.468520Z",
     "iopub.status.busy": "2026-08-17T12:55:45.468380Z",
     "iopub.status.idle": "2026-08-17T12:55:45.475595Z",
     "shell.execute_reply": "2026-08-17T12:55:45.474566Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from PIL import Image\n",
    "\n",
    "def sketch_to_tensor(sketch):\n",
    "    'Sketchpad value -> MNIST-style (1,1,28,28) tensor + a preview, or (None, None).'\n",
    "    img = sketch.get(\"composite\") if isinstance(sketch, dict) else sketch\n",
    "    if img is None:\n",
    "        return None, None\n",
    "    arr = np.asarray(img).astype(np.float32)\n",
    "\n",
    "    if arr.ndim == 3 and arr.shape[2] == 4:        # RGBA -> flatten onto white\n",
    "        rgb, alpha = arr[..., :3], arr[..., 3:4] / 255.0\n",
    "        arr = rgb * alpha + 255.0 * (1.0 - alpha)\n",
    "    if arr.ndim == 3:\n",
    "        arr = arr.mean(axis=2)                     # to grayscale\n",
    "\n",
    "    ink = 255.0 - arr if np.median(arr) > 127 else arr   # want white ink on black\n",
    "    if ink.max() < 10:                             # empty canvas\n",
    "        return None, None\n",
    "    ink = ink / ink.max() * 255.0\n",
    "\n",
    "    ys, xs = np.where(ink > 30)                    # crop tight to the strokes\n",
    "    crop = ink[ys.min():ys.max() + 1, xs.min():xs.max() + 1]\n",
    "    side = max(crop.shape)\n",
    "    sq = np.zeros((side, side), np.float32)        # pad to a square\n",
    "    oy, ox = (side - crop.shape[0]) // 2, (side - crop.shape[1]) // 2\n",
    "    sq[oy:oy + crop.shape[0], ox:ox + crop.shape[1]] = crop\n",
    "\n",
    "    small = Image.fromarray(sq.astype(np.uint8)).resize((20, 20), Image.LANCZOS)\n",
    "    canvas = np.zeros((28, 28), np.float32)\n",
    "    canvas[4:24, 4:24] = np.asarray(small, dtype=np.float32)   # center in 28x28\n",
    "\n",
    "    x = torch.from_numpy(canvas / 255.0).reshape(1, 1, 28, 28)\n",
    "    preview = np.kron(canvas.astype(np.uint8), np.ones((6, 6), np.uint8))  # zoom for display\n",
    "    return x, preview"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16dbd62a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:45.477111Z",
     "iopub.status.busy": "2026-08-17T12:55:45.476977Z",
     "iopub.status.idle": "2026-08-17T12:55:48.032939Z",
     "shell.execute_reply": "2026-08-17T12:55:48.031500Z"
    }
   },
   "outputs": [],
   "source": [
    "import gradio as gr\n",
    "\n",
    "cnn.eval()\n",
    "\n",
    "def read_digit(sketch):\n",
    "    x, preview = sketch_to_tensor(sketch)\n",
    "    if x is None:\n",
    "        return {}, None\n",
    "    dev = next(cnn.parameters()).device      # follow the model onto whatever device it is on\n",
    "    with torch.no_grad():\n",
    "        probs = torch.softmax(cnn(x.to(dev))[0], dim=0)\n",
    "    return {str(i): float(p) for i, p in enumerate(probs)}, preview\n",
    "\n",
    "with gr.Blocks() as demo:\n",
    "    gr.Markdown(\"## ✍️ Draw a digit (0–9) — big and centered!\")\n",
    "    with gr.Row():\n",
    "        pad = gr.Sketchpad(label=\"Draw here\", canvas_size=(280, 280))\n",
    "        with gr.Column():\n",
    "            out = gr.Label(num_top_classes=3, label=\"I think it's a ...\")\n",
    "            seen = gr.Image(label=\"What the network sees (28×28)\", height=180)\n",
    "    pad.change(read_digit, pad, [out, seen])\n",
    "\n",
    "if not SMOKE:\n",
    "    demo.launch(share=IN_COLAB)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4feff02",
   "metadata": {},
   "source": [
    "## 🔓 Unlock your Studio module\n",
    "\n",
    "Time to ship. We export the trained CNN as a **TorchScript** file — a self-contained, frozen copy the AI Studio can load without your training code. The input contract must match exactly what your Studio module expects: a `(1, 1, 28, 28)` tensor with values 0–1, white-on-black."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0631c43",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-17T12:55:48.036041Z",
     "iopub.status.busy": "2026-08-17T12:55:48.035808Z",
     "iopub.status.idle": "2026-08-17T12:55:48.345847Z",
     "shell.execute_reply": "2026-08-17T12:55:48.344586Z"
    }
   },
   "outputs": [],
   "source": [
    "# Export the trained CNN so the Studio's Digit Reader tab can load it.\n",
    "REQUIRED_FILES = [\"digit_reader.pt\"]\n",
    "\n",
    "cnn.eval()\n",
    "example = torch.zeros(1, 1, 28, 28)                # input contract: (1,1,28,28), values 0–1\n",
    "scripted = torch.jit.trace(cnn.cpu(), example)     # trace on CPU; the Studio runs on CPU\n",
    "scripted.save(str(MODELS_DIR / \"digit_reader.pt\"))\n",
    "cnn.to(DEVICE)                                     # put the live model back on its device\n",
    "\n",
    "missing = [f for f in REQUIRED_FILES if not (MODELS_DIR / f).exists()]\n",
    "assert not missing, f\"Something didn't save: {missing}\"\n",
    "print(\"🔓 UNLOCKED: Digit Reader! Run the Studio to see it live:\")\n",
    "print(\"   python ai_studio/app.py   (from the repo root)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95d20a82",
   "metadata": {},
   "source": [
    "## 🏁 Checkpoint\n",
    "\n",
    "Look what you built today:\n",
    "\n",
    "- ✅ Traded hand-rolled gradients for **autograd** — two passes instead of 130\n",
    "- ✅ Trained an **MLP** to ~97% on MNIST, then a **CNN** to even higher\n",
    "- ✅ Measured a **GPU** demolishing a CPU at matrix multiply\n",
    "- ✅ Drew your own digit and watched *your* network read it\n",
    "- 🔓 Unlocked **Digit Reader** in your AI Studio\n",
    "\n",
    "You fed this network tiny 28×28 gray digits and it learned to read. But the world isn't gray, and it isn't 28×28.\n",
    "\n",
    "**Tomorrow — Day 6, \"Machines That See\":** real color photos, ten kinds of objects, and convolutions used in earnest. Your network is about to grow eyes. 👁️"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19ea38b9",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "<div align=\"center\" style=\"background:#14161E;border-radius:12px;padding:18px 12px 14px;margin-top:6px\">\n",
    "  <a href=\"https://ahalimi.com\" target=\"_blank\">\n",
    "    <img src=\"data:image/webp;base64,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\" alt=\"Dr. Abdelghafour HALIMI\" width=\"260\"/>\n",
    "  </a>\n",
    "</div>\n",
    "\n",
    "<div align=\"center\" style=\"text-align:center;margin-top:8px\"><sub>The Lab · AI Studio — Intro to AI · © 2026 <b>Dr. Abdelghafour HALIMI</b> · <a href=\"https://ahalimi.com\">ahalimi.com</a> · All rights reserved.</sub></div>"
   ]
  }
 ],
 "metadata": {
  "authors": [
   {
    "name": "Dr. Abdelghafour HALIMI"
   }
  ],
  "copyright": "© 2026 Dr. Abdelghafour HALIMI · ahalimi.com · All rights reserved",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {
     "001d0581b87346788b82597bb1e6c2e2": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "017ad9fc538a40cdb09f5f1b92892f2d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_b2f7deec288b410b8206862d44d93800",
        "IPY_MODEL_61990dc58c3849fe9e8ec3ab5f7b1fbf",
        "IPY_MODEL_8c445f848db64da797e340039a06f131"
       ],
       "layout": "IPY_MODEL_994ba061a9f04b2cb4a5affe86127bd7",
       "tabbable": null,
       "tooltip": null
      }
     },
     "098fc4a610014204b7df79e4eca25208": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "0beb75d922304e01b215485807d1edc8": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_5f8c7e7b864e4e199d4e1d95a0243d5c",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_93c6b44faf3a47d2af9518c5585abfa1",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "0e8c56edeed045b081813f87326ee229": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "13dce749aa89400385a9a9a5c5c7cce5": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "1556dc47a86e412ca91eb59fc8046aaa": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_894c3d0dd19d412ba38db287f98aaebe",
       "placeholder": "​",
       "style": "IPY_MODEL_8755b25e813e48fe9f32718e01937e12",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:05&lt;00:00, 88.95it/s]"
      }
     },
     "17c4fde2d79b4705b19ddb765b3edba0": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "1af5f92c9fe044358944cf9896cb1ea3": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "1bcf9f159acd4ca58d96f7cd865b8b0b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_cafc079581c043f0bf63b281a60aeb28",
        "IPY_MODEL_40feaa95d40740e2863b9651603bd3bc",
        "IPY_MODEL_ddf04f419ceb4466b5f71da5715827f3"
       ],
       "layout": "IPY_MODEL_8b0f2240008544bf9df75a3c701dbf01",
       "tabbable": null,
       "tooltip": null
      }
     },
     "1c5c834b6c4e4e0f9188fae3814f7b2f": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "296b795a77c9484d96242b16b75ce5fe": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "32c88b55137144178cdc9d8faf4d7cee": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_c0f07becfefb47739853b34a87f0f17c",
       "placeholder": "​",
       "style": "IPY_MODEL_fc6f0367973e42bfb75f43519d749f56",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 1/3: 100%"
      }
     },
     "34d9f3afdd96400393bbd6e284707173": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "3fab8a150b3f40c0967a6854f886ae78": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_dbc7aa5b74094b1a958d8a5114e28624",
        "IPY_MODEL_b9aa13ce67de4bdcb776fe0381408b4b",
        "IPY_MODEL_1556dc47a86e412ca91eb59fc8046aaa"
       ],
       "layout": "IPY_MODEL_0e8c56edeed045b081813f87326ee229",
       "tabbable": null,
       "tooltip": null
      }
     },
     "3fdd18f71b2d4bc89be7e3997c8f4783": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "40feaa95d40740e2863b9651603bd3bc": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_13dce749aa89400385a9a9a5c5c7cce5",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_9b882a71b7c041c08811d02ac38c980b",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "426831d80cdb4527965670a63f9ec052": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "45e45b8080e44f4388259032ede68c3a": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "49245545961a42c281de03636182bcf7": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_1c5c834b6c4e4e0f9188fae3814f7b2f",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_d721b4a23c8e4b9eb6ad793ac92c90ea",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "531c0aa72bd046f0818f7be118b51a84": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "5603a9e164cd4466baa0a2d0deca2780": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "5f8c7e7b864e4e199d4e1d95a0243d5c": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "618521eb655a4e6ba114b695f3eb9fe0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_e59d644a6fc34c3db40c296c1ad76066",
       "placeholder": "​",
       "style": "IPY_MODEL_426831d80cdb4527965670a63f9ec052",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:05&lt;00:00, 92.43it/s]"
      }
     },
     "61990dc58c3849fe9e8ec3ab5f7b1fbf": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_d3dee055362641328229178aa20fa9c6",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_bf54b39ae7214faea9e7b447687150ab",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "63642ef08a1b4fe9ba14b8f040ee16f4": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_32c88b55137144178cdc9d8faf4d7cee",
        "IPY_MODEL_0beb75d922304e01b215485807d1edc8",
        "IPY_MODEL_b7407b797cc94544991ea2ffd2892aa0"
       ],
       "layout": "IPY_MODEL_1af5f92c9fe044358944cf9896cb1ea3",
       "tabbable": null,
       "tooltip": null
      }
     },
     "666c4d3c585442038fe9f7f58fe7a139": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "748b4f229f3347e0bbf33998d7b54021": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_296b795a77c9484d96242b16b75ce5fe",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_45e45b8080e44f4388259032ede68c3a",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "8039ae418cb342a08d9bd4f859501d51": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "84bbd8e125ab438a852130e56efd6359": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "85ca5fb4b75f42a48c16f8486b9da78f": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_d9363ee23afb41fe815e655dc4aacc54",
        "IPY_MODEL_49245545961a42c281de03636182bcf7",
        "IPY_MODEL_ea7a02563a464e42b87caf2fc708bf84"
       ],
       "layout": "IPY_MODEL_a7059528ec034e0ba0aa4c2f27e99226",
       "tabbable": null,
       "tooltip": null
      }
     },
     "8755b25e813e48fe9f32718e01937e12": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "894c3d0dd19d412ba38db287f98aaebe": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "8a5639aaa8304a7fbff3f9da4c110293": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "8b0f2240008544bf9df75a3c701dbf01": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "8c445f848db64da797e340039a06f131": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_098fc4a610014204b7df79e4eca25208",
       "placeholder": "​",
       "style": "IPY_MODEL_91a2bf75b43945898cb7b4cc70d34c1e",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:08&lt;00:00, 75.95it/s]"
      }
     },
     "91a2bf75b43945898cb7b4cc70d34c1e": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "93c6b44faf3a47d2af9518c5585abfa1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "989cdf29f1224f5396b13f7daeab6ead": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "994ba061a9f04b2cb4a5affe86127bd7": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "9a22e76d395b4e7ebb17e5128383a5a6": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "9b882a71b7c041c08811d02ac38c980b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "a563f37016d94905b15650866504fa04": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "a7059528ec034e0ba0aa4c2f27e99226": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "a9b1e4ba7b164fad87bb86d22a744db5": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HBoxModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HBoxModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HBoxView",
       "box_style": "",
       "children": [
        "IPY_MODEL_ea26c4f1f4734a929f992318210bb977",
        "IPY_MODEL_748b4f229f3347e0bbf33998d7b54021",
        "IPY_MODEL_618521eb655a4e6ba114b695f3eb9fe0"
       ],
       "layout": "IPY_MODEL_a563f37016d94905b15650866504fa04",
       "tabbable": null,
       "tooltip": null
      }
     },
     "b2f7deec288b410b8206862d44d93800": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_9a22e76d395b4e7ebb17e5128383a5a6",
       "placeholder": "​",
       "style": "IPY_MODEL_5603a9e164cd4466baa0a2d0deca2780",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 1/3: 100%"
      }
     },
     "b7407b797cc94544991ea2ffd2892aa0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_fe051d91f72f425989e1a00bee0198b3",
       "placeholder": "​",
       "style": "IPY_MODEL_8a5639aaa8304a7fbff3f9da4c110293",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:05&lt;00:00, 102.15it/s]"
      }
     },
     "b9aa13ce67de4bdcb776fe0381408b4b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "FloatProgressModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "FloatProgressModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "ProgressView",
       "bar_style": "success",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_84bbd8e125ab438a852130e56efd6359",
       "max": 469,
       "min": 0,
       "orientation": "horizontal",
       "style": "IPY_MODEL_531c0aa72bd046f0818f7be118b51a84",
       "tabbable": null,
       "tooltip": null,
       "value": 469
      }
     },
     "be0492ad8c1047d9ac171f7dfb1a6e66": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "be535684745e42979fdc662c10f7c841": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "bf54b39ae7214faea9e7b447687150ab": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "c0f07becfefb47739853b34a87f0f17c": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "cafc079581c043f0bf63b281a60aeb28": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_be535684745e42979fdc662c10f7c841",
       "placeholder": "​",
       "style": "IPY_MODEL_dd187a0c13ec40b9b07af0ecea6f7c9d",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 3/3: 100%"
      }
     },
     "d3dee055362641328229178aa20fa9c6": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "d721b4a23c8e4b9eb6ad793ac92c90ea": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "ProgressStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "ProgressStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "bar_color": null,
       "description_width": ""
      }
     },
     "d9363ee23afb41fe815e655dc4aacc54": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_8039ae418cb342a08d9bd4f859501d51",
       "placeholder": "​",
       "style": "IPY_MODEL_3fdd18f71b2d4bc89be7e3997c8f4783",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 2/3: 100%"
      }
     },
     "dbc7aa5b74094b1a958d8a5114e28624": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_17c4fde2d79b4705b19ddb765b3edba0",
       "placeholder": "​",
       "style": "IPY_MODEL_be0492ad8c1047d9ac171f7dfb1a6e66",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 3/3: 100%"
      }
     },
     "dd187a0c13ec40b9b07af0ecea6f7c9d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "ddf04f419ceb4466b5f71da5715827f3": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_001d0581b87346788b82597bb1e6c2e2",
       "placeholder": "​",
       "style": "IPY_MODEL_eaed33e06a004d79942fbb671350c174",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:05&lt;00:00, 91.40it/s]"
      }
     },
     "e279207276a64e3fa64db22a9ca1fb36": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "e59d644a6fc34c3db40c296c1ad76066": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     },
     "ea26c4f1f4734a929f992318210bb977": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_e279207276a64e3fa64db22a9ca1fb36",
       "placeholder": "​",
       "style": "IPY_MODEL_989cdf29f1224f5396b13f7daeab6ead",
       "tabbable": null,
       "tooltip": null,
       "value": "epoch 2/3: 100%"
      }
     },
     "ea7a02563a464e42b87caf2fc708bf84": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLModel",
      "state": {
       "_dom_classes": [],
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/controls",
       "_view_module_version": "2.0.0",
       "_view_name": "HTMLView",
       "description": "",
       "description_allow_html": false,
       "layout": "IPY_MODEL_34d9f3afdd96400393bbd6e284707173",
       "placeholder": "​",
       "style": "IPY_MODEL_666c4d3c585442038fe9f7f58fe7a139",
       "tabbable": null,
       "tooltip": null,
       "value": " 469/469 [00:06&lt;00:00, 78.12it/s]"
      }
     },
     "eaed33e06a004d79942fbb671350c174": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "fc6f0367973e42bfb75f43519d749f56": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "HTMLStyleModel",
      "state": {
       "_model_module": "@jupyter-widgets/controls",
       "_model_module_version": "2.0.0",
       "_model_name": "HTMLStyleModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "StyleView",
       "background": null,
       "description_width": "",
       "font_size": null,
       "text_color": null
      }
     },
     "fe051d91f72f425989e1a00bee0198b3": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {
       "_model_module": "@jupyter-widgets/base",
       "_model_module_version": "2.0.0",
       "_model_name": "LayoutModel",
       "_view_count": null,
       "_view_module": "@jupyter-widgets/base",
       "_view_module_version": "2.0.0",
       "_view_name": "LayoutView",
       "align_content": null,
       "align_items": null,
       "align_self": null,
       "border_bottom": null,
       "border_left": null,
       "border_right": null,
       "border_top": null,
       "bottom": null,
       "display": null,
       "flex": null,
       "flex_flow": null,
       "grid_area": null,
       "grid_auto_columns": null,
       "grid_auto_flow": null,
       "grid_auto_rows": null,
       "grid_column": null,
       "grid_gap": null,
       "grid_row": null,
       "grid_template_areas": null,
       "grid_template_columns": null,
       "grid_template_rows": null,
       "height": null,
       "justify_content": null,
       "justify_items": null,
       "left": null,
       "margin": null,
       "max_height": null,
       "max_width": null,
       "min_height": null,
       "min_width": null,
       "object_fit": null,
       "object_position": null,
       "order": null,
       "overflow": null,
       "padding": null,
       "right": null,
       "top": null,
       "visibility": null,
       "width": null
      }
     }
    },
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
