{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2d6d665d",
   "metadata": {},
   "source": [
    "# Ch 11 — Training Deep Networks (notebook)\n",
    "\n",
    "`[← 10 pytorch-foundations]` · **this notebook** · `[12 cnns →]`\n",
    "\n",
    "Runs top-to-bottom in ~4 min on free Colab CPU. Last verified 2026-06-11.\n",
    "\n",
    "**What you'll build**\n",
    "- A depth dial that makes the *same* MLP train cleanly at 4 layers and die silently at 40, with the per-layer gradient-norm log that explains why.\n",
    "- Xavier and Kaiming initialization derived from one variance equation, implemented from scratch, and checked against `torch.nn.init` to the third decimal.\n",
    "- LayerNorm and RMSNorm built by hand and verified element-for-element against `torch.nn`, plus the BatchNorm train-vs-eval footgun shown on purpose.\n",
    "- The four diagnostic plots from Karpathy's recipe (activation histograms, saturation fraction, gradient histograms, the update-to-data ratio) wired onto a real character-level model trained on `names.txt`.\n",
    "- A learning rate you blow up to NaN on purpose, diagnosed from the loss and gradient norms and repaired, plus a measurement of how residual connections keep a 40-layer net's input-layer gradient from vanishing.\n",
    "- A capstone where you backpropagate a two-layer net *by hand* and `cmp()` every gradient against autograd until all of them read `[ ok ]`.\n",
    "\n",
    "**How this notebook works.** Code cells with a `# TODO` are yours to fill in. Run the cell to grade yourself: `[ ok ]` passed, `[FAIL]` shows what went wrong, `[ -- ]` means not attempted yet. Every exercise has a hint ladder (open only as many as you need) and a folded solution below it. The notebook runs top-to-bottom even if you fill in nothing, because the solution cells redefine the functions the later cells need. See Ch 00 for the full protocol.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ceb434c",
   "metadata": {},
   "source": [
    "## Before you start\n",
    "\n",
    "1. A 40-layer network's first-layer gradient is the product of 40 Jacobians. If each has a typical singular value of $0.9$, by what factor is that gradient scaled relative to the last layer's? <details><summary>Answer</summary>About $0.9^{40} \\approx 0.015$, so roughly $1/70$. With $1.1$ instead it is $1.1^{40}\\approx 45$. Either way the early layers see a gradient that is orders of magnitude off, and they barely move. That single product is what this whole notebook is fighting.</details>\n",
    "2. You initialize a linear layer's weights from a normal with variance $\\sigma^2$. The input has $d_{in}$ components, each unit variance. What is the variance of one output component? <details><summary>Answer</summary>$d_{in}\\,\\sigma^2$. Each output is a sum of $d_{in}$ independent products, and variance adds. To keep output variance at 1 you set $\\sigma^2 = 1/d_{in}$. That is the seed of every init scheme in Part 2.</details>\n",
    "3. Predict before you run: cross-entropy loss at initialization for a 27-class character model, before any training, should sit near what number? <details><summary>Answer</summary>$\\log 27 \\approx 3.30$. A model that has learned nothing assigns roughly uniform probability $1/27$ to each class, and $-\\log(1/27)=\\log 27$. If your loss starts far above that, the output layer is mis-initialized. Karpathy's recipe makes \"check the loss at init\" the first diagnostic.</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4f776fa",
   "metadata": {},
   "source": [
    "## Setup\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6f66d438",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:16.108633Z",
     "iopub.status.busy": "2026-06-10T19:42:16.108551Z",
     "iopub.status.idle": "2026-06-10T19:42:19.544855Z",
     "shell.execute_reply": "2026-06-10T19:42:19.544489Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "numpy 2.2.6 · torch 2.12.0+cpu · device cpu\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "print(f\"numpy {np.__version__} · torch {torch.__version__} · device {'cuda' if torch.cuda.is_available() else 'cpu'}\")\n",
    "if np.__version__ < \"2.0\":\n",
    "    print(\"WARN: written for NumPy 2.x; older versions may shift the last digit\")\n",
    "if torch.__version__ < \"2.4\":\n",
    "    print(\"WARN: nn.RMSNorm needs torch 2.4+; the from-scratch RMSNorm still runs\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2e5d4f07",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:19.549755Z",
     "iopub.status.busy": "2026-06-10T19:42:19.549603Z",
     "iopub.status.idle": "2026-06-10T19:42:19.584330Z",
     "shell.execute_reply": "2026-06-10T19:42:19.580201Z"
    }
   },
   "outputs": [],
   "source": [
    "import os, random\n",
    "SEED = 0\n",
    "FAST = bool(os.environ.get('NB_FAST'))  # CI smoke mode: ~10x fewer steps, same code paths\n",
    "\n",
    "# Step budgets. The full run documents the converged numbers; FAST keeps every code path.\n",
    "TRAIN_STEPS = 400 if FAST else 3000   # the main char-MLP training run (Part 4)\n",
    "DEEP_STEPS  = 80 if FAST else 400     # the deep-net diverge/fix demos (Part 5)\n",
    "\n",
    "rng = np.random.default_rng(SEED)\n",
    "torch.manual_seed(SEED)\n",
    "random.seed(SEED)\n",
    "device = 'cpu'  # CPU is canonical for this notebook; GPU sections fence themselves\n",
    "\n",
    "# ── house self-check harness (identical across all chapter notebooks) ──\n",
    "import numpy as _np\n",
    "\n",
    "def check(label, test_fn, required=False):\n",
    "    \"\"\"Run one self-check. test_fn raises AssertionError (with a teaching\n",
    "    message) on failure, NotImplementedError if the stub is unfilled.\n",
    "    required=True is used only in solution cells; it is what CI grades.\"\"\"\n",
    "    try:\n",
    "        test_fn()\n",
    "    except NotImplementedError:\n",
    "        if required:\n",
    "            raise AssertionError(f\"{label}: reference solution incomplete\")\n",
    "        print(f\"[ -- ] {label}: not attempted yet — fill in the TODO above, then re-run.\")\n",
    "        return False\n",
    "    except AssertionError as e:\n",
    "        if required:\n",
    "            raise\n",
    "        print(f\"[FAIL] {label}: {e}\")\n",
    "        return False\n",
    "    print(f\"[ ok ] {label}\")\n",
    "    return True\n",
    "\n",
    "def attempted(*vals):\n",
    "    \"\"\"Treat None placeholders as 'not attempted'.\"\"\"\n",
    "    if any(v is None for v in vals):\n",
    "        raise NotImplementedError\n",
    "\n",
    "def check_shape(x, want):\n",
    "    assert tuple(x.shape) == tuple(want), \\\n",
    "        f\"shape {tuple(x.shape)}, expected {tuple(want)} — check your reshape/transpose order\"\n",
    "\n",
    "def check_close(got, want, atol=1e-5, rtol=1e-4, msg=\"\"):\n",
    "    g, w = _np.asarray(got, dtype=float), _np.asarray(want, dtype=float)\n",
    "    assert g.shape == w.shape, f\"shape {g.shape} vs expected {w.shape}. {msg}\"\n",
    "    bad = ~_np.isclose(g, w, atol=atol, rtol=rtol)\n",
    "    assert not bad.any(), \\\n",
    "        f\"{bad.mean():.2%} of values wrong (max diff {abs(g - w).max():.3g}). {msg}\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31df133b",
   "metadata": {},
   "source": [
    "> **Note:** seeds make this notebook's printed numbers reproduce on CPU. Library versions and BLAS threading can shift the last digit or two; quoted numbers hold for the pinned environment. If a loss reads 2.31 and the page says 2.30, you did nothing wrong. The `FAST` flag (set by the `NB_FAST` environment variable) cuts every step count by roughly 10x for continuous-integration smoke runs without changing a single line of the algorithms; the experiment log near the end records the expected loss for both settings.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4248ca91",
   "metadata": {},
   "source": [
    "### The anchor data: `names.txt`\n",
    "\n",
    "This whole notebook trains a character-level model on a list of names, the same dataset Karpathy uses in *makemore*. We embed a compact, deterministic subset directly in the notebook so it runs fully offline. A larger, immutable copy is available behind an explicit flag (Part 6 of *makemore*'s `names.txt`, pinned to a commit SHA with a sha256 check); the canonical path never touches the network.\n",
    "\n",
    "> **Why character-level?** It gives us a real classification target (predict the next character) with a 27-symbol vocabulary, so loss-at-init is exactly $\\log 27$, the gradients are nontrivial, and the whole thing trains in seconds on CPU. Everything we learn about init, normalization, and gradient flow shows up here at a scale you can watch.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "67a870f5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:19.587615Z",
     "iopub.status.busy": "2026-06-10T19:42:19.587435Z",
     "iopub.status.idle": "2026-06-10T19:42:19.606848Z",
     "shell.execute_reply": "2026-06-10T19:42:19.606575Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "225 names · shortest 3 · longest 11\n",
      "sample: ['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte'] ... ['emmett', 'brody', 'jesus']\n"
     ]
    }
   ],
   "source": [
    "# The embedded subset: ~260 common names, lowercase, one per line. Deterministic and offline.\n",
    "_NAMES_BLOB = \"\"\"\n",
    "emma olivia ava isabella sophia charlotte mia amelia harper evelyn abigail emily\n",
    "elizabeth mila ella avery sofia camila aria scarlett victoria madison luna grace\n",
    "chloe penelope layla riley zoey nora lily eleanor hannah lillian addison aubrey\n",
    "ellie stella natalie zoe leah hazel violet aurora savannah audrey brooklyn bella\n",
    "claire skylar lucy paisley everly anna caroline nova genesis emilia kennedy maya\n",
    "willow kinsley naomi aaliyah elena sarah ariana allison gabriella alice madelyn\n",
    "cora ruby eva serenity autumn adeline hailey gianna valentina isla eliana quinn\n",
    "nevaeh ivy sadie piper lydia alexa josephine emery julia delilah arianna vivian\n",
    "kaylee sophie brielle madeline peyton rylee clara hadley melanie mackenzie reagan\n",
    "adalynn liam noah oliver william elijah james benjamin lucas mason ethan logan\n",
    "alexander jacob michael daniel henry jackson sebastian aiden matthew samuel david\n",
    "joseph carter owen wyatt john jack luke jayden dylan grayson levi isaac gabriel\n",
    "julian mateo anthony jaxon lincoln joshua christopher andrew theodore caleb ryan\n",
    "asher nathan thomas leo isaiah charles josiah hudson christian hunter connor eli\n",
    "ezra aaron landon adrian jonathan nolan jeremiah easton elias colton cameron carson\n",
    "robert angel maverick nicholas dominic jaxson greyson adam ian austin santiago\n",
    "jordan cooper brayden roman evan ezekiel xavier jose jace jameson leonardo bryson\n",
    "axel everett parker kayden miles sawyer jason declan weston micah ayden wesley luca\n",
    "vincent damian carlos max kingston ashton nathaniel ryder gavin emmett brody jesus\n",
    "\"\"\".split()\n",
    "\n",
    "names = _NAMES_BLOB\n",
    "print(f\"{len(names)} names · shortest {min(len(n) for n in names)} · longest {max(len(n) for n in names)}\")\n",
    "print(\"sample:\", names[:6], \"...\", names[-3:])\n",
    "\n",
    "USE_FULL_NAMES = False  # set True to fetch the full ~32k-name file (immutable URL + sha256); offline-safe fallback\n",
    "if USE_FULL_NAMES:\n",
    "    import hashlib, urllib.request\n",
    "    URL = (\"https://raw.githubusercontent.com/karpathy/makemore/\"\n",
    "           \"ed7842c0aaff34ce0f0a4ca4366f863a99c4cad8/names.txt\")  # pinned commit, immutable\n",
    "    # Fill SHA256 with the real digest (run: sha256sum data/names.txt) before trusting a download.\n",
    "    # Left as a placeholder on purpose: with it unset the verify fails closed and we fall back to\n",
    "    # the embedded subset, so flipping the flag without checking can never silently load bad data.\n",
    "    SHA256 = \"PUT_THE_REAL_SHA256_HERE\"\n",
    "    cache = os.path.join(\"data\", \"names.txt\")\n",
    "    os.makedirs(\"data\", exist_ok=True)\n",
    "    try:\n",
    "        if not os.path.exists(cache):\n",
    "            with urllib.request.urlopen(URL, timeout=10) as r:\n",
    "                blob = r.read()\n",
    "            if hashlib.sha256(blob).hexdigest() != SHA256:\n",
    "                raise ValueError(\"sha256 mismatch; refusing to use this file\")\n",
    "            with open(cache, \"wb\") as f:\n",
    "                f.write(blob)\n",
    "        names = open(cache).read().split()\n",
    "        print(f\"loaded full file: {len(names)} names\")\n",
    "    except Exception as e:  # offline or hash drift -> keep the embedded subset\n",
    "        print(f\"download skipped ({type(e).__name__}); using the embedded {len(names)}-name subset\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed06a89",
   "metadata": {},
   "source": [
    "Now the vocabulary. Characters `a`-`z` plus a special `.` boundary token (start and end of a name), 27 symbols. We build the integer encoders and a flat dataset of `(context, next_char)` pairs with a fixed context window, exactly the bigram-to-block setup from Ch 10.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a4d5b43e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:19.608149Z",
     "iopub.status.busy": "2026-06-10T19:42:19.608068Z",
     "iopub.status.idle": "2026-06-10T19:42:19.630498Z",
     "shell.execute_reply": "2026-06-10T19:42:19.629900Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab 27 · train pairs (1379, 3) · dev pairs (162, 3)\n",
      "example context -> target: [0, 0, 0] -> 19 (... -> s)\n"
     ]
    }
   ],
   "source": [
    "CONTEXT = 3  # characters of context the model sees before predicting the next one\n",
    "chars = ['.'] + sorted({c for n in names for c in n})\n",
    "stoi = {c: i for i, c in enumerate(chars)}\n",
    "itos = {i: c for c, i in stoi.items()}\n",
    "VOCAB = len(chars)\n",
    "assert VOCAB == 27, f\"expected 27 symbols (. + a-z), got {VOCAB}\"\n",
    "\n",
    "def build_dataset(words, context=CONTEXT):\n",
    "    xs, ys = [], []\n",
    "    for w in words:\n",
    "        ctx = [0] * context  # 0 is the '.' boundary token\n",
    "        for ch in w + '.':\n",
    "            ix = stoi[ch]\n",
    "            xs.append(ctx)\n",
    "            ys.append(ix)\n",
    "            ctx = ctx[1:] + [ix]  # slide the window\n",
    "    return torch.tensor(xs), torch.tensor(ys)\n",
    "\n",
    "g = torch.Generator().manual_seed(SEED)\n",
    "perm = torch.randperm(len(names), generator=g).tolist()\n",
    "shuf = [names[i] for i in perm]\n",
    "n1 = int(0.9 * len(shuf))\n",
    "Xtr, Ytr = build_dataset(shuf[:n1])\n",
    "Xdev, Ydev = build_dataset(shuf[n1:])\n",
    "print(f\"vocab {VOCAB} · train pairs {tuple(Xtr.shape)} · dev pairs {tuple(Xdev.shape)}\")\n",
    "print(\"example context -> target:\", Xtr[0].tolist(), \"->\", Ytr[0].item(), f\"({''.join(itos[i] for i in Xtr[0].tolist())} -> {itos[Ytr[0].item()]})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15d2063f",
   "metadata": {},
   "source": [
    "> **Interpretation.** Each training example is three previous characters (as integer ids, padded with the `.` boundary) mapping to the next character. The model that learns this distribution can sample new names. The `.` at id 0 marks both the start and the end of a word, which is why a name like `emma` produces the pairs `... -> e`, `..e -> m`, `.em -> m`, `emm -> a`, `mma -> .`.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2089df6",
   "metadata": {},
   "source": [
    "## The map\n",
    "\n",
    "> **Part 1 — Why deep training is hard.** Build a depth dial; watch the same MLP train at 4 layers and die at 40; read the per-layer gradient-norm log that proves the Jacobian product is the culprit.\n",
    "> **Part 2 — Initialization science.** Derive the variance equation once, implement Xavier and Kaiming from scratch, and check them against `torch.nn.init`.\n",
    "> **Part 3 — Normalization from scratch.** Build LayerNorm and RMSNorm by hand, verify against `torch.nn`, and trip the BatchNorm train-vs-eval footgun on purpose.\n",
    "> **Part 4 — The four diagnostic plots.** Wire activation histograms, saturation fraction, gradient histograms, and the update-to-data ratio onto a real char-MLP, and train it to readable names.\n",
    "> **Part 5 — Diagnose and fix a broken run.** A learning rate that diverges to NaN; read the symptoms; fix it with a lower LR, Kaiming init, and clipping; then measure how residual connections keep the early-layer gradient alive across depth.\n",
    "> **Part 6 — Capstone: manual backprop.** Backpropagate a two-layer net by hand and `cmp()` every gradient against autograd.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74e81ef1",
   "metadata": {},
   "source": [
    "## Part 1 — Why deep training is hard\n",
    "\n",
    "> **Objectives.** Show that depth alone breaks training; locate the failure in the per-layer gradient-norm log; tie it back to the product of Jacobians.\n",
    "\n",
    "A 4-layer MLP trains. A 40-layer one with the same activation and naive init does not. Not because the loss surface is philosophically harder, but mechanically: the gradient at the first layer is the product of every layer's Jacobian back from the loss. If each Jacobian has typical singular value $\\lambda$, the first-layer gradient is scaled by $\\lambda^{L}$. For $\\lambda<1$ it vanishes; for $\\lambda>1$ it explodes. We will watch both happen.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8ac135b6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:19.631453Z",
     "iopub.status.busy": "2026-06-10T19:42:19.631372Z",
     "iopub.status.idle": "2026-06-10T19:42:19.644819Z",
     "shell.execute_reply": "2026-06-10T19:42:19.644557Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "flattened input shape: (256, 81)\n"
     ]
    }
   ],
   "source": [
    "def make_mlp(n_layers, width=64, act=nn.Tanh, gain=1.0):\n",
    "    \"\"\"A plain feed-forward stack: Linear -> act, repeated, then a readout.\n",
    "    `gain` multiplies the default torch init so we can dial the Jacobian spectrum.\"\"\"\n",
    "    layers = []\n",
    "    d = VOCAB * CONTEXT  # we flatten the one-hot context as the input\n",
    "    for _ in range(n_layers):\n",
    "        lin = nn.Linear(d, width)\n",
    "        with torch.no_grad():\n",
    "            lin.weight *= gain  # scale the default init to push lambda above/below 1\n",
    "        layers += [lin, act()]\n",
    "        d = width\n",
    "    head = nn.Linear(d, VOCAB)\n",
    "    layers += [head]\n",
    "    return nn.Sequential(*layers)\n",
    "\n",
    "def first_layer_grad_norm(model, x, y):\n",
    "    \"\"\"One forward/backward; return the per-Linear-layer gradient L2 norms, input-first.\"\"\"\n",
    "    for p in model.parameters():\n",
    "        p.grad = None\n",
    "    logits = model(x)\n",
    "    F.cross_entropy(logits, y).backward()\n",
    "    return [float(m.weight.grad.norm()) for m in model if isinstance(m, nn.Linear)]\n",
    "\n",
    "# one-hot the integer context into a flat float vector the Linear stack can read\n",
    "xb = F.one_hot(Xtr[:256], num_classes=VOCAB).float().reshape(256, -1)\n",
    "yb = Ytr[:256]\n",
    "print(\"flattened input shape:\", tuple(xb.shape))  # (256, CONTEXT*VOCAB)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b772586f",
   "metadata": {},
   "source": [
    "> **Predict:** with `gain=1.0` (torch's default Kaiming-uniform, tuned for shallow nets) and `tanh`, what happens to the per-layer gradient norm as we go from the input layer toward the loss in a 30-layer net? Up, down, or flat? Run the next cell.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b3458630",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:19.646069Z",
     "iopub.status.busy": "2026-06-10T19:42:19.645985Z",
     "iopub.status.idle": "2026-06-10T19:42:20.940501Z",
     "shell.execute_reply": "2026-06-10T19:42:20.940187Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "layer 0 (input) grad norm: 7.83e-09\n",
      "layer -2 (near loss) grad norm: 7.33e-02\n",
      "ratio last/first: 9362228.8x\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 700x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "torch.manual_seed(SEED)\n",
    "norms = first_layer_grad_norm(make_mlp(30, act=nn.Tanh, gain=1.0), xb, yb)\n",
    "print(\"layer 0 (input) grad norm:\", f\"{norms[0]:.2e}\")\n",
    "print(\"layer -2 (near loss) grad norm:\", f\"{norms[-2]:.2e}\")\n",
    "print(\"ratio last/first:\", f\"{norms[-2] / max(norms[0], 1e-30):.1f}x\")\n",
    "\n",
    "# viz: per-layer gradient norm on a log scale, the canonical vanishing-gradient picture\n",
    "fig, ax = plt.subplots(figsize=(7, 3))\n",
    "ax.semilogy(range(len(norms)), norms, marker='o', color=\"#1E40FF\")\n",
    "ax.set_xlabel(\"layer index (0 = input)\"); ax.set_ylabel(\"grad L2 norm (log)\")\n",
    "ax.set_title(\"30-layer tanh MLP, default init: gradients vanish toward the input\")\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc5bdeb0",
   "metadata": {},
   "source": [
    "> **Interpretation.** The gradient norm climbs by orders of magnitude from the input layer to the layers near the loss. The early layers receive a signal that is tiny compared to the late layers, so the optimizer effectively trains only the top of the network. This is vanishing gradients, and it is not a metaphor: it is the visible consequence of multiplying ~30 Jacobians whose typical singular value is below 1.\n",
    "\n",
    "Now the symmetric failure. Push the gain above 1 and the same product *explodes* instead.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e6641a53",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:20.947469Z",
     "iopub.status.busy": "2026-06-10T19:42:20.947343Z",
     "iopub.status.idle": "2026-06-10T19:42:21.123249Z",
     "shell.execute_reply": "2026-06-10T19:42:21.119176Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gain=2.0 -> first-layer grad norm: 1.67e-01  (vs 7.83e-09 at gain=1.0)\n",
      "[ ok ] gain>1 explodes the early-layer gradient; gain<1 vanishes it. Same product, two signs.\n"
     ]
    }
   ],
   "source": [
    "torch.manual_seed(SEED)\n",
    "exploding = first_layer_grad_norm(make_mlp(30, act=nn.Tanh, gain=2.0), xb, yb)\n",
    "print(\"gain=2.0 -> first-layer grad norm:\", f\"{exploding[0]:.2e}  (vs {norms[0]:.2e} at gain=1.0)\")\n",
    "assert exploding[0] > norms[0] * 10, \"raising the per-layer gain should blow the early gradients up\"\n",
    "print(\"[ ok ] gain>1 explodes the early-layer gradient; gain<1 vanishes it. Same product, two signs.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e316808e",
   "metadata": {},
   "source": [
    "> **Key takeaways**\n",
    "> - The first-layer gradient is a product of per-layer Jacobians; depth multiplies a number near 1 many times, and the result runs away in one direction or the other.\n",
    "> - You diagnose it by logging per-layer gradient norms, not by staring at the loss.\n",
    "> - Init, normalization, and residuals (Parts 2, 3, 5) are three independent ways to keep that product near 1.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0541ce5",
   "metadata": {},
   "source": [
    "### Exercise 11.1 — A vanishing-gradient detector\n",
    "`Difficulty 2/5 · ~10 min`\n",
    "\n",
    "Write `is_vanishing(norms, factor=100.0)` that takes a list of per-layer gradient norms (input-first) and returns `True` if the largest norm exceeds the smallest by more than `factor`. That ratio is the practical signal that depth is breaking your gradient flow. Then `worst_layer(norms)` returns the index of the layer with the *smallest* norm (the most starved layer).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "66e186cc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.129401Z",
     "iopub.status.busy": "2026-06-10T19:42:21.129308Z",
     "iopub.status.idle": "2026-06-10T19:42:21.140748Z",
     "shell.execute_reply": "2026-06-10T19:42:21.140182Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.1 detector (toy): not attempted yet — fill in the TODO above, then re-run.\n",
      "[ -- ] 11.1 on the real 30-layer log: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def is_vanishing(norms, factor=100.0):\n",
    "    \"\"\"norms: list of per-layer grad L2 norms, input-first. Return True if\n",
    "    max(norms)/min(norms) > factor (a spread that wide means depth is hurting).\"\"\"\n",
    "    # TODO 1: guard against an empty list or a zero minimum (return False)\n",
    "    # TODO 2: compute the spread max/min and compare to factor\n",
    "    result = None\n",
    "    attempted(result)\n",
    "    return result\n",
    "\n",
    "def worst_layer(norms):\n",
    "    \"\"\"Return the index of the layer with the smallest gradient norm.\"\"\"\n",
    "    # TODO 3: argmin over the list (plain Python is fine)\n",
    "    idx = None\n",
    "    attempted(idx)\n",
    "    return idx\n",
    "\n",
    "def _toy_vanish():\n",
    "    healthy = [1.0, 1.1, 0.9, 1.05]\n",
    "    sick = [1e-6, 1e-4, 1e-2, 1.0]\n",
    "    assert is_vanishing(sick) is True, \"a 1e6 spread should read as vanishing\"\n",
    "    assert is_vanishing(healthy) is False, \"a <2x spread is healthy, not vanishing\"\n",
    "    assert is_vanishing([]) is False, \"empty input must not crash; return False\"\n",
    "    assert worst_layer(sick) == 0, f\"smallest norm is layer 0 (1e-6), got {worst_layer(sick)}\"\n",
    "\n",
    "check(\"11.1 detector (toy)\", _toy_vanish)\n",
    "check(\"11.1 on the real 30-layer log\", lambda: (\n",
    "    None if is_vanishing(norms) else (_ for _ in ()).throw(\n",
    "        AssertionError(\"the 30-layer tanh log above IS vanishing; your detector missed it\"))))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec7a757a",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>The spread is just `max(norms) / min(norms)`. Compare it to `factor`. Handle the degenerate cases (empty list, a zero minimum) before dividing.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "if not norms or min(norms) == 0:\n",
    "    return False\n",
    "return (max(norms) / min(norms)) > factor\n",
    "```\n",
    "`worst_layer` is `min(range(len(norms)), key=lambda i: norms[i])`.\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — my detector returns True on the healthy list</summary>Symptom: a list like `[1.0, 1.1, 0.9]` reads as vanishing. Cause: you compared `max - min` instead of the *ratio* `max / min`. The spread is multiplicative because the gradient is a product. Print `max(norms)/min(norms)` and confirm it is near 1 for the healthy case.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "78410055",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.141939Z",
     "iopub.status.busy": "2026-06-10T19:42:21.141586Z",
     "iopub.status.idle": "2026-06-10T19:42:21.158540Z",
     "shell.execute_reply": "2026-06-10T19:42:21.158174Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] 11.1 detector (toy)\n",
      "[ ok ] 11.1 on the real 30-layer log\n",
      "30-layer log: spread 17002974x, most-starved layer = 0\n"
     ]
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines is_vanishing and worst_layer; the checks below re-verify the reference.\n",
    "def is_vanishing(norms, factor=100.0):\n",
    "    if not norms or min(norms) == 0:\n",
    "        return False\n",
    "    return (max(norms) / min(norms)) > factor\n",
    "\n",
    "def worst_layer(norms):\n",
    "    return min(range(len(norms)), key=lambda i: norms[i])\n",
    "\n",
    "check(\"11.1 detector (toy)\", _toy_vanish, required=True)\n",
    "check(\"11.1 on the real 30-layer log\", lambda: (\n",
    "    None if is_vanishing(norms) else (_ for _ in ()).throw(\n",
    "        AssertionError(\"the 30-layer tanh log above IS vanishing; your detector missed it\"))),\n",
    "    required=True)\n",
    "print(f\"30-layer log: spread {max(norms)/min(norms):.0f}x, most-starved layer = {worst_layer(norms)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24077916",
   "metadata": {},
   "source": [
    "## Part 2 — Initialization science: Xavier, Kaiming, μP\n",
    "\n",
    "> **Objectives.** Derive the one variance equation behind every init scheme; implement Xavier and Kaiming from scratch; check them against `torch.nn.init`; see Kaiming hold activation variance flat across a deep stack where naive init collapses it.\n",
    "\n",
    "A linear layer maps input variance to output variance. For weights drawn from a normal with variance $\\sigma^2$ and an input $x\\in\\mathbb{R}^{d_{in}}$ whose components have unit variance, each output component $y_i = \\sum_j W_{ij} x_j$ is a sum of $d_{in}$ independent products, so\n",
    "\n",
    "$$\\operatorname{Var}(y_i) = d_{in}\\,\\sigma^2.$$\n",
    "\n",
    "To preserve variance across the layer, set $\\sigma^2 = 1/d_{in}$. That is the whole idea. The variants differ in two choices: whether to also balance the backward pass, and how to correct for the nonlinearity.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "887b5b73",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.159718Z",
     "iopub.status.busy": "2026-06-10T19:42:21.159637Z",
     "iopub.status.idle": "2026-06-10T19:42:21.337510Z",
     "shell.execute_reply": "2026-06-10T19:42:21.337189Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input var 1.000 -> output var 1.001  (target ~1.0)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] sigma^2 = 1/d_in preserves variance across a linear layer\n"
     ]
    }
   ],
   "source": [
    "# Verify the variance law empirically before trusting any formula.\n",
    "torch.manual_seed(SEED)\n",
    "d_in, d_out, N = 512, 512, 4096\n",
    "x = torch.randn(N, d_in)              # unit-variance input\n",
    "W = torch.randn(d_in, d_out) * (1.0 / d_in) ** 0.5   # sigma^2 = 1/d_in\n",
    "y = x @ W\n",
    "print(f\"input var {x.var().item():.3f} -> output var {y.var().item():.3f}  (target ~1.0)\")\n",
    "assert abs(y.var().item() - 1.0) < 0.1, \"sigma^2 = 1/d_in should preserve unit variance\"\n",
    "print(\"[ ok ] sigma^2 = 1/d_in preserves variance across a linear layer\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb5968b9",
   "metadata": {},
   "source": [
    "> **Xavier / Glorot** (Glorot and Bengio 2010) balances forward *and* backward variance, compromising at $\\sigma^2 = 2/(d_{in}+d_{out})$. It assumes the activation has unit derivative near zero, true for `tanh`, not for ReLU. Use it with `tanh`/`sigmoid`.\n",
    ">\n",
    "> **Kaiming / He** (He et al. 2015) targets forward variance only and corrects for ReLU zeroing half the activations: $\\sigma^2 = 2/d_{in}$. Use it with ReLU and its descendants (GELU, SiLU).\n",
    ">\n",
    "> **μP** (Yang and Hu 2021) scales the per-parameter learning rate by fan-in/fan-out so hyperparameters tuned at small width transfer to large width. It matters at frontier scale; Kaiming is fine otherwise.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7dc43090",
   "metadata": {},
   "source": [
    "### Exercise 11.2 — Xavier and Kaiming from scratch\n",
    "`Difficulty 3/5 · ~15 min`\n",
    "\n",
    "Implement `init_std(d_in, d_out, scheme)` returning the standard deviation $\\sigma$ (not the variance) for each scheme: `\"xavier\"` $\\to \\sqrt{2/(d_{in}+d_{out})}$, `\"kaiming\"` $\\to \\sqrt{2/d_{in}}$, `\"lecun\"` $\\to \\sqrt{1/d_{in}}$. Then `make_init_weight(d_in, d_out, scheme, gen)` returns a `(d_in, d_out)` tensor drawn from `N(0, σ²)`. The checks compare your $\\sigma$ against `torch.nn.init`'s own computed gains and confirm the empirical std matches.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9fee6a80",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.346540Z",
     "iopub.status.busy": "2026-06-10T19:42:21.346409Z",
     "iopub.status.idle": "2026-06-10T19:42:21.355529Z",
     "shell.execute_reply": "2026-06-10T19:42:21.355205Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.2 init formula: not attempted yet — fill in the TODO above, then re-run.\n",
      "[ -- ] 11.2 init empirical: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "def init_std(d_in, d_out, scheme):\n",
    "    \"\"\"Return the target standard deviation sigma for a Linear weight.\"\"\"\n",
    "    # TODO 1: \"xavier\" -> sqrt(2 / (d_in + d_out))\n",
    "    # TODO 2: \"kaiming\" -> sqrt(2 / d_in)\n",
    "    # TODO 3: \"lecun\"   -> sqrt(1 / d_in)\n",
    "    sigma = None\n",
    "    attempted(sigma)\n",
    "    return sigma\n",
    "\n",
    "def make_init_weight(d_in, d_out, scheme, gen):\n",
    "    \"\"\"Return a (d_in, d_out) weight tensor ~ N(0, sigma^2), using `gen` for reproducibility.\"\"\"\n",
    "    # TODO 4: sigma = init_std(...); return torch.randn((d_in, d_out), generator=gen) * sigma\n",
    "    W = None\n",
    "    attempted(W)\n",
    "    return W\n",
    "\n",
    "def _check_init_formula():\n",
    "    # Kaiming matches torch's kaiming gain sqrt(2) / sqrt(fan_in)\n",
    "    want_kaiming = math.sqrt(2.0) / math.sqrt(1000)\n",
    "    check_close(init_std(1000, 50, \"kaiming\"), want_kaiming, msg=\"kaiming sigma = sqrt(2/d_in)\")\n",
    "    # Xavier matches torch's xavier_normal_ std: gain * sqrt(2/(fan_in+fan_out)), gain=1\n",
    "    want_xavier = math.sqrt(2.0 / (1000 + 50))\n",
    "    check_close(init_std(1000, 50, \"xavier\"), want_xavier, msg=\"xavier sigma = sqrt(2/(d_in+d_out))\")\n",
    "\n",
    "def _check_init_empirical():\n",
    "    gen = torch.Generator().manual_seed(SEED)\n",
    "    W = make_init_weight(2048, 256, \"kaiming\", gen)\n",
    "    check_shape(W, (2048, 256))\n",
    "    check_close(W.std().item(), math.sqrt(2.0 / 2048), atol=2e-3,\n",
    "                msg=\"empirical std of a kaiming-init weight should match the formula\")\n",
    "\n",
    "check(\"11.2 init formula\", _check_init_formula)\n",
    "check(\"11.2 init empirical\", _check_init_empirical)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c6202e0",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>Each scheme is one `math.sqrt`. Kaiming uses fan-in only; Xavier averages fan-in and fan-out; LeCun is the bare $1/d_{in}$ with no factor of 2.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "if scheme == \"xavier\": return math.sqrt(2 / (d_in + d_out))\n",
    "if scheme == \"kaiming\": return math.sqrt(2 / d_in)\n",
    "if scheme == \"lecun\":   return math.sqrt(1 / d_in)\n",
    "```\n",
    "For the weight: `torch.randn((d_in, d_out), generator=gen) * init_std(d_in, d_out, scheme)`.\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — my empirical std is off by sqrt(2)</summary>Symptom: the empirical std check fails by a factor near 1.41. Cause: you used variance where std is wanted, or dropped the factor of 2 in Kaiming. `init_std` must return $\\sigma$, the square root of the variance, and Kaiming's variance is $2/d_{in}$ not $1/d_{in}$. Print `init_std(2048, 256, \"kaiming\")` and compare to `math.sqrt(2/2048)`.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "cbb3df66",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.363401Z",
     "iopub.status.busy": "2026-06-10T19:42:21.363316Z",
     "iopub.status.idle": "2026-06-10T19:42:21.396427Z",
     "shell.execute_reply": "2026-06-10T19:42:21.396173Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] 11.2 init formula\n",
      "[ ok ] 11.2 init empirical\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines init_std and make_init_weight; the checks below re-verify the reference.\n",
    "def init_std(d_in, d_out, scheme):\n",
    "    if scheme == \"xavier\": return math.sqrt(2.0 / (d_in + d_out))\n",
    "    if scheme == \"kaiming\": return math.sqrt(2.0 / d_in)\n",
    "    if scheme == \"lecun\":   return math.sqrt(1.0 / d_in)\n",
    "    raise ValueError(f\"unknown scheme {scheme!r}\")\n",
    "\n",
    "def make_init_weight(d_in, d_out, scheme, gen):\n",
    "    return torch.randn((d_in, d_out), generator=gen) * init_std(d_in, d_out, scheme)\n",
    "\n",
    "check(\"11.2 init formula\", _check_init_formula, required=True)\n",
    "check(\"11.2 init empirical\", _check_init_empirical, required=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8da98ec",
   "metadata": {},
   "source": [
    "Now watch init decide whether activation variance survives depth. We forward a unit-variance input through a deep ReLU stack with three inits and record the variance at each layer.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f2f105fa",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:21.401138Z",
     "iopub.status.busy": "2026-06-10T19:42:21.401043Z",
     "iopub.status.idle": "2026-06-10T19:42:23.194878Z",
     "shell.execute_reply": "2026-06-10T19:42:23.194499Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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lUuOd+WS41255HhXrup8vKWzlxBCCCGEqJGkRlYIIYQQQtRIksgKIYQQQogaSRJZIYQQQghRI0kiW43Mnz+fsLAwHB0diYiIYO/evVUdUq30+uuvo9FozJYWLVpUdVi1xo4dO+jfvz+BgYFoNBrWrFljtl5RFF577TUCAgJwcnKid+/eHDt2rGqCrQVKOt4jR460ON/79OlTNcHWAjNmzKBjx464ubnh6+vLgAEDOHr0qNk2mZmZjB8/nvr16+Pq6sr9999PfHx8FUVcs5XmeHfv3t3iHH/iiSeqKOKa7eOPP6ZNmzam8WI7d+7Mhg0bTOur47ktiWw1sXTpUiZOnMi0adPYv38/4eHhREZGkpCQUNWh1Uo333wzFy5cMC27du2q6pBqjbS0NMLDw5k/f77V9TNnzmTu3LksWLCA33//HRcXFyIjI8nMzKzkSGuHko43QJ8+fczO9++++64SI6xdfvnlF8aPH89vv/3Gzz//TE5ODnfddRdpaWmmbZ577jl++OEHli9fzi+//EJcXByDBg2qwqhrrtIcb4AxY8aYneMzZ86soohrtuDgYN5991327dvHn3/+Sc+ePbnvvvs4dOgQUE3PbUVUC506dVLGjx9vemwwGJTAwEBlxowZVRhV7TRt2jQlPDy8qsOoEwBl9erVpsdGo1Hx9/dXZs2aZSpLTExUHBwclO+++64KIqxdih5vRVGUqKgo5b777quSeOqChIQEBVB++eUXRVHU89ne3l5Zvny5aZt///1XAZQ9e/ZUVZi1RtHjrSiK0q1bN+WZZ56puqBquXr16imff/55tT23pUa2GsjOzmbfvn307t3bVKbVaunduzd79uypwshqr2PHjhEYGEijRo0YPnw4Z86cqeqQ6oTY2FguXrxodq57eHgQEREh53oF2r59O76+vjRv3pwnn3ySK1euVHVItUZSUhIAXl5eAOzbt4+cnByzc7xFixY0aNBAzvFyUPR451u0aBHe3t60atWKqVOnkp6eXhXh1SoGg4ElS5aQlpZG586dq+25bVdlryxMLl++jMFgwM/Pz6zcz8+PI0eOVFFUtVdERATR0dE0b96cCxcuMH36dLp06cLBgwdxc3Or6vBqtYsXLwJYPdfz14ny1adPHwYNGkTDhg05ceIEL730En379mXPnj3odLqqDq9GMxqNPPvss9x+++20atUK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",
      "text/plain": [
       "<Figure size 700x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# viz: activation variance vs depth for three inits (the reason init matters)\n",
    "def activation_variance_profile(scheme, depth=30, width=256):\n",
    "    gen = torch.Generator().manual_seed(SEED)\n",
    "    x = torch.randn(1024, width)\n",
    "    variances = [float(x.var())]\n",
    "    for _ in range(depth):\n",
    "        if scheme == \"naive\":\n",
    "            W = torch.randn((width, width), generator=gen) * 0.1  # arbitrary small std\n",
    "        else:\n",
    "            W = make_init_weight(width, width, scheme, gen)\n",
    "        x = torch.relu(x @ W)\n",
    "        variances.append(float(x.var()))\n",
    "    return variances\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(7, 3.5))\n",
    "for scheme, color in [(\"naive\", \"#CC3333\"), (\"xavier\", \"#FF9900\"), (\"kaiming\", \"#1E40FF\")]:\n",
    "    ax.semilogy(activation_variance_profile(scheme), label=scheme, color=color, marker='.')\n",
    "ax.set_xlabel(\"layer depth\"); ax.set_ylabel(\"activation variance (log)\")\n",
    "ax.set_title(\"ReLU stack: Kaiming holds variance; naive and Xavier decay\"); ax.legend()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a6a9e37",
   "metadata": {},
   "source": [
    "> **Interpretation.** Kaiming keeps the activation variance roughly flat across 30 ReLU layers, because its factor of 2 exactly compensates for ReLU zeroing half the signal. Xavier (built for `tanh`, with no factor of 2) lets the variance decay; the naive small-std init collapses it toward zero within a few layers. A collapsed activation means a collapsed gradient. This is Part 1's vanishing gradient, prevented at step zero by arithmetic.\n",
    "\n",
    "> **Key takeaways**\n",
    "> - Every init scheme is the equation $\\sigma^2 = c/d_{in}$ with a constant $c$ chosen for the nonlinearity and (for Xavier) the backward pass.\n",
    "> - Kaiming ($c=2$) is the default for ReLU-family nets; Xavier for `tanh`/`sigmoid`.\n",
    "> - Good init keeps activation (and therefore gradient) variance flat across depth.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6ea6083",
   "metadata": {},
   "source": [
    "## Part 3 — Normalization from scratch: BatchNorm, LayerNorm, RMSNorm\n",
    "\n",
    "> **Objectives.** Build LayerNorm and RMSNorm by hand; verify each against `torch.nn` element-for-element; understand which axis each normalizes; trip the BatchNorm train-vs-eval footgun on purpose.\n",
    "\n",
    "A normalization layer recomputes per-axis statistics and rescales so the distribution stays stable regardless of init or depth. The three flavors differ only in *which axis* they reduce over.\n",
    "\n",
    "- **BatchNorm** normalizes each feature across the *batch*. Great for CNNs; fragile for transformers (small batches, variable sequence length) and it behaves differently at train vs inference (running stats).\n",
    "- **LayerNorm** normalizes each example across the *feature* axis. No running stats, identical at train and inference. Every transformer uses it.\n",
    "- **RMSNorm** is LayerNorm without subtracting the mean: $\\hat h = \\gamma\\, h / \\sqrt{\\operatorname{mean}(h^2)+\\epsilon}$. Llama and most 2023+ LLMs use it.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cb33cf6",
   "metadata": {},
   "source": [
    "### Exercise 11.3 — LayerNorm and RMSNorm from scratch\n",
    "`Difficulty 3/5 · ~15 min`\n",
    "\n",
    "Implement both `forward`s. For LayerNorm: subtract the per-row mean, divide by the per-row standard deviation (use the *biased* variance, `unbiased=False`, to match `torch.nn.LayerNorm`), then scale by $\\gamma$ and shift by $\\beta$. For RMSNorm: divide by the root-mean-square over the feature axis, then scale by $\\gamma$ (no mean subtraction, no $\\beta$). The checks compare your output to `torch.nn` to `1e-5`.\n",
    "\n",
    "> **Common confusion:** `torch.nn.LayerNorm` uses the *biased* variance (divides by $d$, not $d-1$). If you use `unbiased=True` your output will be off by a factor of $\\sqrt{d/(d-1)}$ and the agreement check will fail by a few percent.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "3e407efb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.203387Z",
     "iopub.status.busy": "2026-06-10T19:42:23.203299Z",
     "iopub.status.idle": "2026-06-10T19:42:23.228492Z",
     "shell.execute_reply": "2026-06-10T19:42:23.228172Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.3 LayerNorm vs torch: not attempted yet — fill in the TODO above, then re-run.\n",
      "[ -- ] 11.3 RMSNorm vs torch: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class MyLayerNorm(nn.Module):\n",
    "    def __init__(self, d, eps=1e-5):\n",
    "        super().__init__()\n",
    "        self.gamma = nn.Parameter(torch.ones(d))\n",
    "        self.beta = nn.Parameter(torch.zeros(d))\n",
    "        self.eps = eps\n",
    "    def forward(self, x):  # x: (..., d)\n",
    "        # TODO 1: mu = mean over the last axis, keepdim=True\n",
    "        # TODO 2: var = variance over the last axis, keepdim=True, unbiased=False\n",
    "        # TODO 3: return gamma * (x - mu) / sqrt(var + eps) + beta\n",
    "        out = None\n",
    "        attempted(out)\n",
    "        return out\n",
    "\n",
    "class MyRMSNorm(nn.Module):\n",
    "    def __init__(self, d, eps=1e-5):\n",
    "        super().__init__()\n",
    "        self.gamma = nn.Parameter(torch.ones(d))\n",
    "        self.eps = eps\n",
    "    def forward(self, x):  # x: (..., d)\n",
    "        # TODO 4: rms = sqrt(mean(x^2) over last axis, keepdim=True)\n",
    "        # TODO 5: return gamma * x / (rms + eps)\n",
    "        out = None\n",
    "        attempted(out)\n",
    "        return out\n",
    "\n",
    "def _check_layernorm():\n",
    "    torch.manual_seed(SEED)\n",
    "    x = torch.randn(8, 16, 64) * 5 + 3  # arbitrary scale + offset\n",
    "    mine, ref = MyLayerNorm(64), nn.LayerNorm(64)\n",
    "    check_shape(mine(x), (8, 16, 64))\n",
    "    check_close(mine(x).detach(), ref(x).detach(), atol=1e-5,\n",
    "                msg=\"LayerNorm should match torch.nn.LayerNorm (did you use unbiased=False?)\")\n",
    "\n",
    "def _check_rmsnorm():\n",
    "    torch.manual_seed(SEED)\n",
    "    x = torch.randn(8, 16, 64) * 5 + 3\n",
    "    mine, ref = MyRMSNorm(64), nn.RMSNorm(64)\n",
    "    check_close(mine(x).detach(), ref(x).detach(), atol=1e-5,\n",
    "                msg=\"RMSNorm should match torch.nn.RMSNorm (no mean subtraction)\")\n",
    "\n",
    "check(\"11.3 LayerNorm vs torch\", _check_layernorm)\n",
    "check(\"11.3 RMSNorm vs torch\", _check_rmsnorm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95800a83",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>Both reduce over the last axis with `keepdim=True` so broadcasting lines up. LayerNorm centers (subtract mean) then scales by std; RMSNorm skips the centering and divides by the root-mean-square.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "# LayerNorm.forward\n",
    "mu = x.mean(dim=-1, keepdim=True)\n",
    "var = x.var(dim=-1, keepdim=True, unbiased=False)\n",
    "return self.gamma * (x - mu) / torch.sqrt(var + self.eps) + self.beta\n",
    "\n",
    "# RMSNorm.forward\n",
    "rms = x.pow(2).mean(dim=-1, keepdim=True).sqrt()\n",
    "return self.gamma * x / (rms + self.eps)\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — LayerNorm off by ~1%</summary>Symptom: `check_close` reports a small percentage of values wrong with a max diff near 1%. Cause: `unbiased=True` (the default for `.var()` is unbiased). `torch.nn.LayerNorm` divides by $d$, so pass `unbiased=False`. The error is exactly the $\\sqrt{d/(d-1)}$ correction.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "686af194",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.229446Z",
     "iopub.status.busy": "2026-06-10T19:42:23.229366Z",
     "iopub.status.idle": "2026-06-10T19:42:23.269280Z",
     "shell.execute_reply": "2026-06-10T19:42:23.268682Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] 11.3 LayerNorm vs torch\n",
      "[ ok ] 11.3 RMSNorm vs torch\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines MyLayerNorm and MyRMSNorm; the checks below re-verify the reference.\n",
    "class MyLayerNorm(nn.Module):\n",
    "    def __init__(self, d, eps=1e-5):\n",
    "        super().__init__()\n",
    "        self.gamma = nn.Parameter(torch.ones(d)); self.beta = nn.Parameter(torch.zeros(d)); self.eps = eps\n",
    "    def forward(self, x):\n",
    "        mu = x.mean(dim=-1, keepdim=True)\n",
    "        var = x.var(dim=-1, keepdim=True, unbiased=False)\n",
    "        return self.gamma * (x - mu) / torch.sqrt(var + self.eps) + self.beta\n",
    "\n",
    "class MyRMSNorm(nn.Module):\n",
    "    def __init__(self, d, eps=1e-5):\n",
    "        super().__init__()\n",
    "        self.gamma = nn.Parameter(torch.ones(d)); self.eps = eps\n",
    "    def forward(self, x):\n",
    "        rms = x.pow(2).mean(dim=-1, keepdim=True).sqrt()\n",
    "        return self.gamma * x / (rms + self.eps)\n",
    "\n",
    "check(\"11.3 LayerNorm vs torch\", _check_layernorm, required=True)\n",
    "check(\"11.3 RMSNorm vs torch\", _check_rmsnorm, required=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28322825",
   "metadata": {},
   "source": [
    "A property check that does not depend on a reference implementation: after LayerNorm with default $\\gamma=1, \\beta=0$, every row should have mean $\\approx 0$ and variance $\\approx 1$. That is the definition, and it is worth asserting directly.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "edfe6f28",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.270237Z",
     "iopub.status.busy": "2026-06-10T19:42:23.270153Z",
     "iopub.status.idle": "2026-06-10T19:42:23.281330Z",
     "shell.execute_reply": "2026-06-10T19:42:23.280778Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "per-row mean: max |.| = 5.96e-08  (want ~0)\n",
      "per-row var:  max |.-1| = 4.77e-07  (want ~0)\n",
      "[ ok ] LayerNorm output is mean-0, variance-1 per row, by construction\n"
     ]
    }
   ],
   "source": [
    "ln = MyLayerNorm(64)\n",
    "x = torch.randn(32, 64) * 7 - 2\n",
    "y = ln(x).detach()\n",
    "print(f\"per-row mean: max |.| = {y.mean(dim=-1).abs().max():.2e}  (want ~0)\")\n",
    "print(f\"per-row var:  max |.-1| = {(y.var(dim=-1, unbiased=False) - 1).abs().max():.2e}  (want ~0)\")\n",
    "assert y.mean(dim=-1).abs().max() < 1e-5, \"LayerNorm rows must be mean-centered\"\n",
    "assert (y.var(dim=-1, unbiased=False) - 1).abs().max() < 1e-4, \"LayerNorm rows must have unit variance\"\n",
    "print(\"[ ok ] LayerNorm output is mean-0, variance-1 per row, by construction\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d1ed92b",
   "metadata": {},
   "source": [
    "### A deliberate failure: the BatchNorm train-vs-eval footgun\n",
    "\n",
    "BatchNorm uses batch statistics at train time and accumulated *running* statistics at eval time. If you evaluate before the running stats have warmed up, or you forget to call `.eval()`, you get a different (often much worse) answer for the exact same input. Watch the same tensor produce two different outputs.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "791345c8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.282569Z",
     "iopub.status.busy": "2026-06-10T19:42:23.282210Z",
     "iopub.status.idle": "2026-06-10T19:42:23.318590Z",
     "shell.execute_reply": "2026-06-10T19:42:23.317883Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "same input, train-mode output mean -0.000, eval-mode output mean 3.382\n",
      "mean absolute difference between the two: 3.384\n",
      "BUG REPRODUCED: identical input, two different outputs, because eval used cold running stats.\n"
     ]
    }
   ],
   "source": [
    "torch.manual_seed(SEED)\n",
    "bn = nn.BatchNorm1d(16)\n",
    "x = torch.randn(64, 16) * 3 + 5  # a batch with a clear shift/scale\n",
    "\n",
    "# Train mode: normalize by THIS batch's statistics, and update the running stats.\n",
    "bn.train()\n",
    "out_train = bn(x)\n",
    "# Eval mode: normalize by the running statistics, which are still near their init (0 mean, 1 var).\n",
    "bn.eval()\n",
    "out_eval = bn(x)\n",
    "\n",
    "gap = (out_train - out_eval).abs().mean().item()\n",
    "print(f\"same input, train-mode output mean {out_train.mean():.3f}, eval-mode output mean {out_eval.mean():.3f}\")\n",
    "print(f\"mean absolute difference between the two: {gap:.3f}\")\n",
    "print(\"BUG REPRODUCED: identical input, two different outputs, because eval used cold running stats.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95f1ab2d",
   "metadata": {},
   "source": [
    "> **Interpretation.** Right after construction the running mean is 0 and running variance is 1, so eval-mode BatchNorm barely normalizes the shifted input while train-mode fully normalizes it. The fix is not a code change but a discipline: let the running stats warm up over training, and always `.eval()` before inference. Below we warm the running stats with a few batches and the gap shrinks.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "b177ef18",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.319658Z",
     "iopub.status.busy": "2026-06-10T19:42:23.319560Z",
     "iopub.status.idle": "2026-06-10T19:42:23.712441Z",
     "shell.execute_reply": "2026-06-10T19:42:23.712178Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "after warming the running stats: gap 0.139  (was 3.384)\n",
      "[ ok ] FIXED: with warmed running stats, train and eval agree closely\n"
     ]
    }
   ],
   "source": [
    "torch.manual_seed(SEED)\n",
    "bn = nn.BatchNorm1d(16)\n",
    "bn.train()\n",
    "for _ in range(200):  # warm the running stats on batches from the same distribution\n",
    "    bn(torch.randn(64, 16) * 3 + 5)\n",
    "bn.eval()\n",
    "out_eval_warm = bn(x)\n",
    "gap_warm = (out_train - out_eval_warm).abs().mean().item()\n",
    "print(f\"after warming the running stats: gap {gap_warm:.3f}  (was {gap:.3f})\")\n",
    "assert gap_warm < gap, \"warming the running stats should shrink the train/eval gap\"\n",
    "print(\"[ ok ] FIXED: with warmed running stats, train and eval agree closely\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a4b9fbc",
   "metadata": {},
   "source": [
    "> **Key takeaways**\n",
    "> - LayerNorm and RMSNorm reduce over the feature axis per example; BatchNorm reduces over the batch axis per feature.\n",
    "> - LayerNorm/RMSNorm behave identically at train and eval; BatchNorm does not, which is its main footgun.\n",
    "> - Always call `.eval()` before inference with BatchNorm, and let the running stats warm up first.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f788780",
   "metadata": {},
   "source": [
    "## Part 4 — The four diagnostic plots\n",
    "\n",
    "> **Objectives.** Build the diagnostic toolkit from Karpathy's recipe (loss-at-init, activation histograms + saturation, gradient histograms, update-to-data ratio) and read each one on a real char-MLP trained on `names.txt`.\n",
    "\n",
    "This is the instrument panel for any training run. We build a small char-level MLP, capture activations and gradients with hooks, train it, and plot the four diagnostics that catch the overwhelming majority of silent failures. We assemble the complete model once, in a single cell, rather than editing a class in place.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "2651d078",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.721562Z",
     "iopub.status.busy": "2026-06-10T19:42:23.721467Z",
     "iopub.status.idle": "2026-06-10T19:42:23.740482Z",
     "shell.execute_reply": "2026-06-10T19:42:23.740173Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CharMLP: 7721 parameters\n",
      "loss at init: 3.296  (expected ~log 27 = 3.296)\n",
      "[ ok ] Diagnostic 1: loss at init is near log(VOCAB); output layer is sane\n"
     ]
    }
   ],
   "source": [
    "class CharMLP(nn.Module):\n",
    "    \"\"\"Embed -> flatten context -> hidden (tanh) -> readout. The classic makemore MLP.\"\"\"\n",
    "    def __init__(self, vocab=VOCAB, context=CONTEXT, emb=10, hidden=128):\n",
    "        super().__init__()\n",
    "        self.emb = nn.Embedding(vocab, emb)\n",
    "        self.fc1 = nn.Linear(emb * context, hidden)\n",
    "        self.fc2 = nn.Linear(hidden, vocab)\n",
    "        # Kaiming-ish for tanh: torch default is fine here; scale fc1 down slightly so tanh\n",
    "        # is not saturated at init (the 5/3 tanh gain story; we keep it conservative).\n",
    "        with torch.no_grad():\n",
    "            self.fc1.weight *= 0.8  # keep pre-activations in tanh's linear region at init\n",
    "    def forward(self, x, return_hidden=False):\n",
    "        e = self.emb(x).flatten(1)        # (B, context*emb)\n",
    "        h = torch.tanh(self.fc1(e))       # (B, hidden)\n",
    "        logits = self.fc2(h)              # (B, vocab)\n",
    "        return (logits, h) if return_hidden else logits\n",
    "\n",
    "torch.manual_seed(SEED)\n",
    "model = CharMLP()\n",
    "n_params = sum(p.numel() for p in model.parameters())\n",
    "print(f\"CharMLP: {n_params} parameters\")\n",
    "# Diagnostic 1: loss at init should be ~log(VOCAB) for a uniform-ish output.\n",
    "with torch.no_grad():\n",
    "    loss0 = F.cross_entropy(model(Xtr[:1024]), Ytr[:1024]).item()\n",
    "print(f\"loss at init: {loss0:.3f}  (expected ~log {VOCAB} = {math.log(VOCAB):.3f})\")\n",
    "assert abs(loss0 - math.log(VOCAB)) < 0.5, \"loss at init way off log(VOCAB) => output layer mis-initialized\"\n",
    "print(\"[ ok ] Diagnostic 1: loss at init is near log(VOCAB); output layer is sane\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b04043a",
   "metadata": {},
   "source": [
    "> **Interpretation.** Loss at init near $\\log 27\\approx 3.30$ confirms the model starts at \"uniform guess\", exactly where it should. If it had started at, say, 12, the readout layer's weights would be too large and the first gradient step would waste itself just shrinking confidently-wrong logits. This is the cheapest and most-skipped diagnostic in all of training.\n",
    "\n",
    "Now the training loop with the four-comment skeleton, capturing the hidden-layer activations and gradients we need for the plots.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "122c2b6a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:23.750464Z",
     "iopub.status.busy": "2026-06-10T19:42:23.750349Z",
     "iopub.status.idle": "2026-06-10T19:42:57.213319Z",
     "shell.execute_reply": "2026-06-10T19:42:57.211185Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "final train loss (smoothed): 1.221 · dev loss: 2.731\n",
      "(FAST=False; full run overfits this small subset to ~1.2 train / ~2.7 dev; FAST stays near 3.3 with fewer steps)\n"
     ]
    }
   ],
   "source": [
    "def train_charmlp(model, steps, lr=0.1, batch=256, capture=True):\n",
    "    \"\"\"Standard SGD loop; optionally capture last-batch hidden acts and fc1 grad for diagnostics.\"\"\"\n",
    "    opt = torch.optim.SGD(model.parameters(), lr=lr)\n",
    "    losses, captured = [], {}\n",
    "    gen = torch.Generator().manual_seed(SEED)\n",
    "    for step in range(steps):\n",
    "        idx = torch.randint(0, Xtr.shape[0], (batch,), generator=gen)\n",
    "        xb, yb = Xtr[idx], Ytr[idx]\n",
    "        # forward\n",
    "        logits, h = model(xb, return_hidden=True)\n",
    "        loss = F.cross_entropy(logits, yb)\n",
    "        # backward\n",
    "        opt.zero_grad(set_to_none=True)\n",
    "        loss.backward()\n",
    "        # update\n",
    "        opt.step()\n",
    "        # track stats\n",
    "        losses.append(loss.item())\n",
    "        if capture and step == steps - 1:  # snapshot the final step for the diagnostics\n",
    "            captured['h'] = h.detach()\n",
    "            captured['fc1_grad'] = model.fc1.weight.grad.detach().clone()\n",
    "            captured['fc1_w'] = model.fc1.weight.detach().clone()\n",
    "    return losses, captured\n",
    "\n",
    "torch.manual_seed(SEED)\n",
    "model = CharMLP()\n",
    "losses, cap = train_charmlp(model, steps=TRAIN_STEPS, lr=0.1)\n",
    "with torch.no_grad():\n",
    "    dev_loss = F.cross_entropy(model(Xdev), Ydev).item()\n",
    "print(f\"final train loss (smoothed): {np.mean(losses[-50:]):.3f} · dev loss: {dev_loss:.3f}\")\n",
    "print(f\"(FAST={FAST}; full run overfits this small subset to ~1.2 train / ~2.7 dev; FAST stays near {math.log(VOCAB):.1f} with fewer steps)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e48ffe16",
   "metadata": {},
   "source": [
    "The loss curve, on a log-x axis so the early rapid drop and the later slow grind are both visible.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "3257e926",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:57.220044Z",
     "iopub.status.busy": "2026-06-10T19:42:57.219833Z",
     "iopub.status.idle": "2026-06-10T19:42:57.541083Z",
     "shell.execute_reply": "2026-06-10T19:42:57.540481Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 700x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# viz: training loss curve\n",
    "fig, ax = plt.subplots(figsize=(7, 3))\n",
    "ax.plot(losses, color=\"#1E40FF\", alpha=0.4, lw=0.8)\n",
    "# a simple running mean to see through the SGD noise\n",
    "k = max(1, len(losses) // 100)\n",
    "smooth = np.convolve(losses, np.ones(k) / k, mode='valid')\n",
    "ax.plot(range(len(smooth)), smooth, color=\"#1E40FF\", lw=2, label=f\"running mean (k={k})\")\n",
    "ax.axhline(math.log(VOCAB), ls=':', c='#888', label=f\"loss at init = log{VOCAB}\")\n",
    "ax.set_xlabel(\"step\"); ax.set_ylabel(\"cross-entropy loss\"); ax.set_title(\"CharMLP training loss\"); ax.legend()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "101ebdcf",
   "metadata": {},
   "source": [
    "> **Interpretation.** The loss falls from $\\log 27$ quickly, then grinds. On the full run the train loss drops well below the dev loss (roughly 1.2 vs 2.7), the textbook signature of overfitting: this many free parameters can memorize a few hundred names. On a real-sized `names.txt` (32k names) that gap closes, which is exactly when the regularizers from the chapter draft (dropout, weight decay) start to earn their keep. Now the three activation/gradient diagnostics on the captured snapshot.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "918ebd0b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:57.542075Z",
     "iopub.status.busy": "2026-06-10T19:42:57.541992Z",
     "iopub.status.idle": "2026-06-10T19:42:58.164121Z",
     "shell.execute_reply": "2026-06-10T19:42:58.163799Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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3r0lNjZWKcebH93V3fr160v9+vVFRCQ8PFzMzc1l7NixKdZx5coVASBz584VEZEnT57I0KFDpUKFCmJnZycODg7StGlTOXfunDKP7gr2mx9dD0RAQECKK7vPnz+XIUOGSLFixcTKykref/99mTp1qiQlJenlAyCBgYGyceNGKV++vFhZWYmXl5fs2LFDL190dLQMHDhQPDw8xMrKSpydncXX11eCg4PT3P43btwQALJs2bIU0/744w+pWrWq2Nvbi4ODg1SoUEFmzZqlTDfGtvHw8DB4xTb5/kq+nD/++EO+/fZbKVKkiKhUKvnvv/+MUg4RkRMnToifn59oNBqxsbGRevXqydGjR1OU7ciRI1KtWjVRq9VSsmRJWbRoUbquJHt4eKRYv663Rvd727lzp3h7e4tarZaZM2eKSPp/syKvr/7Wq1dP2WfVqlWTlStXKtv0zfXrvpe6HrQ3e8327dsnH374odja2opWq5VWrVrJpUuX9PLo6n7t2jUJCAgQrVYrGo1GunXrJi9evEhzm2QXJycnadu2barTM9v7EhcXJ2XLlpVhw4YZvOvn77//FgASHR2tV5YNGzaIiEhwcLBYW1u/9c6H2bNni0qlkidPnmS4jJR7BAQEpPgNJj/OXb58WT755BMpVKiQWFtby/vvvy//+9//DC4ro3fubN68WQDI+fPnlbT169cLAPn444/18pYrV046dOigl7ZixQqpWrWqWFtbS4ECBaRjx44SFhaWon5v/o4eP34sn3/+uTg4OIhWq5WuXbvKuXPnUpRd18N69+5dad26tdjZ2UmhQoVk6NChyh04uuNUasfRtMybN09KlCgh1tbWUr16dTl8+LDR2xmdO3fuSOvWrcXW1lacnZ1l0KBBsnPnzhQ9zoa2V2JiosycOVO8vLxErVaLi4uL9OrVS54+faqXLz3naG87LzIkPftB581t/7bzkLTaIJHXd/10795dXFxcRK1WS6VKlQyem2T0OxUaGirNmjUTe3t7ad26tYiIHD58WNq3by/FixcXKysrKVasmAwaNEhevnxpcHvcvn1b/P39xc7OTooUKSLz5s0TEZF//vlHGjZsKLa2tuLu7q60t29Tr149adCgQYr0f//9V9q2bSuurq6iVqulaNGi0rFjR4mMjFS2+Zuf5OdQmT0/eVNW75gNDAxMc50xMTEyevRoKVWq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jVk0IiKidxaf6SYiIiIiIiIyEQbdRERERERERCbCoJuIiIiIiIjIRBh0ExEREREREZkIg24iIiIiIiIiE2HQTURERERERGQiDLqJiIiIiIiITIRBNxEREREREZGJMOgmIiIiIiIiMhEG3UREREREREQmwqCbiIiIiIiIyEQscroA+YFHQ8Pptw9kbzmIiIiIiIgod2HQTURERESUHyxW5XQJiMgA3l5OREREREREZCLs6SYiIiIiyivYm02U5zDoJiIiQurv5yAiolwmrQsPvST7ykGUTry9nIiIiIiIiMhEGHQTERERERERmQiDbiIiIiIiIiITYdBNREREREREZCIMuomIiIiIiIhMhEE3ERERERERkYkw6CYiIiIiIiIyEQbdRERERERERCbCoJuIiIiIiIjIRBh0ExEREREREZkIg24iIiIiIiIiE2HQTURERERERGQiGQ66Dx8+jJYtW6JIkSJQqVTYtGmT3nQRwejRo1G4cGHY2NjA19cX165d08vz9OlTdO7cGRqNBo6OjujRoweeP3+ul+eff/5B3bp1YW1tjeLFi2PKlCkZrx0RERERERFRDspw0P3ixQtUrlwZ8+fPNzh9ypQpmDNnDhYtWoSTJ0/Czs4Ofn5+iImJUfJ07twZFy9exJ49e7B161YcPnwYvXr1UqZHR0ejSZMm8PDwQHBwMKZOnYqxY8di8eLFmagiERERERERUc6wyOgMzZo1Q7NmzQxOExHMmjULo0aNQuvWrQEAv/32G1xdXbFp0yZ8+umnuHz5Mnbu3InTp0+jWrVqAIC5c+eiefPmmDZtGooUKYKVK1ciLi4Ov/76K6ysrFC+fHmcO3cOM2bM0AvOiYiIiIiIiHIzoz7TffPmTYSHh8PX11dJ02q1qFmzJoKCggAAQUFBcHR0VAJuAPD19YWZmRlOnjyp5KlXrx6srKyUPH5+frh69Sr+++8/g+uOjY1FdHS03oeIiIiIiIgoJxk16A4PDwcAuLq66qW7uroq08LDw+Hi4qI33cLCAk5OTnp5DC0j+TreNHHiRGi1WuVTvHjxrFeIiIiIiIiIKAvyzdvLR44ciaioKOVz586dnC4SERERERERveOMGnS7ubkBACIiIvTSIyIilGlubm54+PCh3vSEhAQ8ffpUL4+hZSRfx5vUajU0Go3eh4iI6F2TXaOMEBERUfoYNegu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",
      "text/plain": [
       "<Figure size 1000x300 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "saturated tanh fraction: 21.4% (want well under ~50%; high saturation kills gradients)\n"
     ]
    }
   ],
   "source": [
    "# viz: Diagnostic 2 — activation histogram + saturation fraction (tanh)\n",
    "h = cap['h']\n",
    "sat = (h.abs() > 0.97).float().mean().item()  # fraction of tanh units pinned near +/-1\n",
    "fig, ax = plt.subplots(1, 2, figsize=(10, 3))\n",
    "ax[0].hist(h.flatten().numpy(), bins=60, color=\"#1E40FF\")\n",
    "ax[0].set_title(f\"tanh activations (saturated fraction = {sat:.1%})\")\n",
    "ax[0].set_xlabel(\"activation value\")\n",
    "# Diagnostic 3: gradient histogram for fc1\n",
    "gflat = cap['fc1_grad'].flatten().numpy()\n",
    "ax[1].hist(gflat, bins=60, color=\"#FF9900\")\n",
    "ax[1].set_title(f\"fc1 weight-gradient histogram (std {gflat.std():.1e})\")\n",
    "ax[1].set_xlabel(\"gradient value\")\n",
    "plt.tight_layout(); plt.show()\n",
    "print(f\"saturated tanh fraction: {sat:.1%} (want well under ~50%; high saturation kills gradients)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39cc03dd",
   "metadata": {},
   "source": [
    "> **Interpretation.** The activation histogram should be spread across tanh's range, not piled up at $\\pm 1$. A large saturated fraction (units stuck near $\\pm 1$, where tanh's derivative is ~0) is the activation-side signature of vanishing gradients; the fix is smaller pre-activations, which is why we scaled `fc1` down at init. The gradient histogram should be a tight, symmetric blob around zero with a sane spread, not a spike at zero (dead) or a heavy tail (about to explode).\n",
    "\n",
    "The fourth diagnostic is the most useful single number Karpathy reports: the **update-to-data ratio**, $\\log_{10}$ of (the size of one SGD update) divided by (the size of the parameter itself). It should sit near $-3$. Much higher means the learning rate is too aggressive; much lower means the layer is barely learning.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "f895d30a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:58.165807Z",
     "iopub.status.busy": "2026-06-10T19:42:58.165425Z",
     "iopub.status.idle": "2026-06-10T19:42:58.227148Z",
     "shell.execute_reply": "2026-06-10T19:42:58.226896Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fc1 update:data ratio = 5.17e-03  ->  log10 = -2.29\n",
      "Karpathy's reference line is about -3. We are in the healthy band if we're near it.\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 600x250 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# viz: Diagnostic 4 — update:data ratio for fc1 at the chosen LR\n",
    "lr = 0.1\n",
    "update = lr * cap['fc1_grad']          # what one SGD step would change in fc1\n",
    "w = cap['fc1_w']\n",
    "ratio = (update.std() / w.std()).item()\n",
    "log10_ratio = math.log10(ratio)\n",
    "print(f\"fc1 update:data ratio = {ratio:.2e}  ->  log10 = {log10_ratio:.2f}\")\n",
    "print(\"Karpathy's reference line is about -3. We are in the healthy band if we're near it.\")\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(6, 2.5))\n",
    "ax.bar([\"fc1\"], [log10_ratio], color=\"#1E40FF\")\n",
    "ax.axhline(-3, ls='--', c='#CC3333', label=\"reference -3\")\n",
    "ax.set_ylabel(\"log10(update / data)\"); ax.set_title(\"update:data ratio\"); ax.legend()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bac3e0b9",
   "metadata": {},
   "source": [
    "> **Interpretation.** A ratio near $10^{-3}$ ($\\log_{10}\\approx-3$) means each step nudges the weight by about a thousandth of its magnitude, the empirically healthy pace. If you see $-1$, the LR is roughly 100x too high and you are about to diverge; if you see $-6$, the layer is frozen. Logging this per layer is how you catch a single layer being trained at the wrong rate.\n",
    "\n",
    "Finally the emotional payoff: sample some names from the trained model. This is the qualitative artifact that tells you the loss number actually means something.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "70ea4c5d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:58.231341Z",
     "iopub.status.busy": "2026-06-10T19:42:58.228341Z",
     "iopub.status.idle": "2026-06-10T19:42:58.252502Z",
     "shell.execute_reply": "2026-06-10T19:42:58.252188Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sampled names: ['jethannah', 'eli', 'jaxcen', 'sel', 'muson', 'aston', 'serjoseph', 'jason']\n"
     ]
    }
   ],
   "source": [
    "@torch.no_grad()\n",
    "def sample_name(model, max_len=16, seed_offset=10):\n",
    "    g = torch.Generator().manual_seed(SEED + seed_offset)  # offset seed so sampling doesn't perturb training repro\n",
    "    ctx = [0] * CONTEXT\n",
    "    out = []\n",
    "    for _ in range(max_len):\n",
    "        x = torch.tensor([ctx])\n",
    "        probs = F.softmax(model(x), dim=-1)\n",
    "        ix = torch.multinomial(probs, num_samples=1, generator=g).item()\n",
    "        if ix == 0:  # '.' boundary => end of name\n",
    "            break\n",
    "        out.append(itos[ix]); ctx = ctx[1:] + [ix]\n",
    "    return ''.join(out)\n",
    "\n",
    "print(\"sampled names:\", [sample_name(model, seed_offset=k) for k in range(8)])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d86d3c43",
   "metadata": {},
   "source": [
    "> **Interpretation.** On the full run these read as plausible name-shaped strings (vowel/consonant alternation, sane lengths); on the FAST smoke run they are rougher because the model has seen 10x fewer steps. Either way the model has learned *something* about the character distribution, which is the point of the diagnostics: they told us the run was healthy before we ever looked at a sample.\n",
    "\n",
    "> **Key takeaways**\n",
    "> - The four diagnostics, in order of cheapness: loss-at-init ($\\approx\\log K$), activation histogram + saturation fraction, gradient histogram, and the update-to-data ratio ($\\approx 10^{-3}$).\n",
    "> - Log all four from step 1; they catch silent failures the loss curve alone hides.\n",
    "> - A qualitative sample is the final sanity check that the loss number corresponds to a model that learned the task.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60cfd1df",
   "metadata": {},
   "source": [
    "### Exercise 11.4 — The update-to-data ratio\n",
    "`Difficulty 2/5 · ~10 min`\n",
    "\n",
    "Implement `update_data_ratio(param, lr)` returning $\\log_{10}$ of `(lr * grad).std() / param.std()` for a single parameter tensor that already has a `.grad`. This is the diagnostic from above, packaged so you can call it per layer. Return `float('nan')` if the parameter's std is zero (a constant tensor has no scale).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "bb791c54",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:58.253969Z",
     "iopub.status.busy": "2026-06-10T19:42:58.253883Z",
     "iopub.status.idle": "2026-06-10T19:42:58.264167Z",
     "shell.execute_reply": "2026-06-10T19:42:58.263644Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.4 update:data ratio: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def update_data_ratio(param, lr):\n",
    "    \"\"\"log10 of (lr*grad).std() / param.std(); nan if param has zero std or no grad.\"\"\"\n",
    "    # TODO 1: if param.grad is None or param.std() == 0, return float('nan')\n",
    "    # TODO 2: ratio = (lr * param.grad).std() / param.std(); return log10(ratio)\n",
    "    result = None\n",
    "    attempted(result)\n",
    "    return result\n",
    "\n",
    "def _check_udr():\n",
    "    torch.manual_seed(SEED)\n",
    "    p = nn.Parameter(torch.randn(100, 100))\n",
    "    (p.sum() * 0 + (p ** 2).sum()).backward()  # grad = 2*p, std(grad) = 2*std(p)\n",
    "    # update = lr * 2p, so ratio = lr*2*std(p)/std(p) = 2*lr; with lr=0.05 -> 0.1 -> log10 = -1\n",
    "    got = update_data_ratio(p, lr=0.05)\n",
    "    check_close(got, math.log10(0.1), atol=1e-4, msg=\"for grad=2p, ratio is 2*lr; here log10(0.1)=-1\")\n",
    "    # zero-std parameter -> nan\n",
    "    z = nn.Parameter(torch.ones(10)); (z.sum()).backward()\n",
    "    import math as _m\n",
    "    assert _m.isnan(update_data_ratio(z, lr=0.1)), \"a constant parameter has no scale; return nan\"\n",
    "\n",
    "check(\"11.4 update:data ratio\", _check_udr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b0806f3",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>It is two `.std()` calls and one `math.log10`. Guard the zero-std case first so you never divide by zero.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "if param.grad is None or float(param.std()) == 0:\n",
    "    return float('nan')\n",
    "ratio = (lr * param.grad).std() / param.std()\n",
    "return math.log10(float(ratio))\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — I get a tensor, not a float</summary>Symptom: `check_close` complains about shapes. Cause: `(lr * param.grad).std() / param.std()` is a 0-dim tensor; `math.log10` needs a Python float. Wrap the ratio in `float(...)` before `log10`.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "b2bc8fcb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:58.265080Z",
     "iopub.status.busy": "2026-06-10T19:42:58.265000Z",
     "iopub.status.idle": "2026-06-10T19:42:58.272553Z",
     "shell.execute_reply": "2026-06-10T19:42:58.272260Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] 11.4 update:data ratio\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines update_data_ratio; the check below re-verifies the reference.\n",
    "def update_data_ratio(param, lr):\n",
    "    if param.grad is None or float(param.std()) == 0:\n",
    "        return float('nan')\n",
    "    ratio = (lr * param.grad).std() / param.std()\n",
    "    return math.log10(float(ratio))\n",
    "\n",
    "check(\"11.4 update:data ratio\", _check_udr, required=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37e3bad3",
   "metadata": {},
   "source": [
    "## Part 5 — Diagnose and fix a broken run\n",
    "\n",
    "> **Objectives.** Watch a deep net diverge under a too-high learning rate; read the symptoms (NaN loss, exploded gradient norm); apply the standard fixes (lower LR, residual connections, the right init) and train 40 layers cleanly.\n",
    "\n",
    "This is the section the rest of the notebook was building toward. We take a deep MLP and break it two ways on purpose, then repair it. First: a learning rate so high the loss goes to NaN.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "96c38390",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:42:58.273602Z",
     "iopub.status.busy": "2026-06-10T19:42:58.273524Z",
     "iopub.status.idle": "2026-06-10T19:43:08.945205Z",
     "shell.execute_reply": "2026-06-10T19:43:08.944903Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss[:6] = [3.3, 3.9, 9.9, 1318.0, 1.9828410588699034e+17, nan]\n",
      "non-finite losses: 395 / 400; first NaN/inf at step 5\n",
      "BUG REPRODUCED: lr=30.0 on a 12-layer net diverges to NaN within a handful of steps.\n"
     ]
    }
   ],
   "source": [
    "class DeepReLU(nn.Module):\n",
    "    \"\"\"A configurable deep net. `residual=True` wraps each block as x + relu(block(x)).\"\"\"\n",
    "    def __init__(self, n_layers, width=96, residual=False, init_kaiming=False, act=\"relu\"):\n",
    "        super().__init__()\n",
    "        self.proj_in = nn.Linear(VOCAB * CONTEXT, width)\n",
    "        self.blocks = nn.ModuleList([nn.Linear(width, width) for _ in range(n_layers)])\n",
    "        self.head = nn.Linear(width, VOCAB)\n",
    "        self.residual = residual\n",
    "        self.act = torch.relu if act == \"relu\" else torch.tanh\n",
    "        if init_kaiming:\n",
    "            for m in self.modules():\n",
    "                if isinstance(m, nn.Linear):\n",
    "                    nn.init.kaiming_normal_(m.weight, nonlinearity=\"relu\")\n",
    "                    nn.init.zeros_(m.bias)\n",
    "    def forward(self, x):\n",
    "        h = self.act(self.proj_in(x))\n",
    "        for blk in self.blocks:\n",
    "            out = self.act(blk(h))\n",
    "            h = h + out if self.residual else out  # the one-line residual switch\n",
    "        return self.head(h)\n",
    "\n",
    "def run_deep(model, steps, lr, clip=None, batch=128):\n",
    "    \"\"\"Train on one-hot contexts; return the loss history (may contain nan/inf).\"\"\"\n",
    "    opt = torch.optim.SGD(model.parameters(), lr=lr)\n",
    "    gen = torch.Generator().manual_seed(SEED)\n",
    "    hist = []\n",
    "    for step in range(steps):\n",
    "        idx = torch.randint(0, Xtr.shape[0], (batch,), generator=gen)\n",
    "        xb = F.one_hot(Xtr[idx], VOCAB).float().reshape(batch, -1)\n",
    "        yb = Ytr[idx]\n",
    "        opt.zero_grad(set_to_none=True)\n",
    "        loss = F.cross_entropy(model(xb), yb)\n",
    "        loss.backward()\n",
    "        if clip is not None:\n",
    "            torch.nn.utils.clip_grad_norm_(model.parameters(), clip)\n",
    "        opt.step()\n",
    "        hist.append(loss.item())\n",
    "    return hist\n",
    "\n",
    "torch.manual_seed(SEED)\n",
    "broken = run_deep(DeepReLU(12, residual=False), steps=DEEP_STEPS, lr=30.0)  # lr WAY too high\n",
    "n_nan = sum(1 for v in broken if not math.isfinite(v))\n",
    "first_bad = next((i for i, v in enumerate(broken) if not math.isfinite(v)), None)\n",
    "print(f\"loss[:6] = {[round(v, 1) if math.isfinite(v) else v for v in broken[:6]]}\")\n",
    "print(f\"non-finite losses: {n_nan} / {len(broken)}; first NaN/inf at step {first_bad}\")\n",
    "assert n_nan > 0, \"lr=30 on a 12-layer net should blow up; if not, the dataset/seed changed\"\n",
    "print(\"BUG REPRODUCED: lr=30.0 on a 12-layer net diverges to NaN within a handful of steps.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9258f885",
   "metadata": {},
   "source": [
    "> **Interpretation.** The loss is finite for a step or two, then jumps to NaN. That is the signature of gradient explosion: one over-large step pushes the activations into a region where the next gradient is enormous, and the run never recovers. The diagnostic is exactly Part 1's per-layer gradient norm, which would have been huge right before the NaN. The chapter's checklist says: lower the LR, add gradient clipping, add warmup. Here we apply the first two.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "e3420ece",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:08.953143Z",
     "iopub.status.busy": "2026-06-10T19:43:08.953046Z",
     "iopub.status.idle": "2026-06-10T19:43:15.218474Z",
     "shell.execute_reply": "2026-06-10T19:43:15.218121Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "with lr=0.2, kaiming init, clip=1.0: all finite = True\n",
      "final loss (smoothed): 1.767  (started ~3.36)\n",
      "[ ok ] FIXED: lower LR + Kaiming init + clipping -> a finite, decreasing loss\n"
     ]
    }
   ],
   "source": [
    "# The fix: a sane learning rate + Kaiming init + gradient clipping.\n",
    "torch.manual_seed(SEED)\n",
    "fixed = run_deep(DeepReLU(12, residual=False, init_kaiming=True), steps=DEEP_STEPS, lr=0.2, clip=1.0)\n",
    "all_finite = all(math.isfinite(v) for v in fixed)\n",
    "print(f\"with lr=0.2, kaiming init, clip=1.0: all finite = {all_finite}\")\n",
    "print(f\"final loss (smoothed): {np.mean(fixed[-15:]):.3f}  (started ~{fixed[0]:.2f})\")\n",
    "assert all_finite, \"the fixed run must not produce NaN\"\n",
    "assert np.mean(fixed[-15:]) < fixed[0], \"the fixed run must actually decrease the loss\"\n",
    "print(\"[ ok ] FIXED: lower LR + Kaiming init + clipping -> a finite, decreasing loss\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f8a38d9",
   "metadata": {},
   "source": [
    "The second failure is structural, not a learning-rate problem: even with a safe LR, a *very* deep plain net cannot pass gradient back to its early layers, because of Part 1's Jacobian product. Residual connections fix that by adding an identity path, $y = x + F(x)$, whose gradient is $1 + F'(x)$. The cleanest way to *see* the fix is not a training race (at this tiny scale both nets overfit, which would hide the effect) but the **first-layer gradient norm** on a single backward pass: the deterministic, on-mechanism measurement. We use `tanh` here because it vanishes hardest.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "d760d57e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:15.219545Z",
     "iopub.status.busy": "2026-06-10T19:43:15.219435Z",
     "iopub.status.idle": "2026-06-10T19:43:15.684358Z",
     "shell.execute_reply": "2026-06-10T19:43:15.681178Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20-layer tanh: first-block grad  plain=2.12e-06  residual=9.13e-01  ratio=4e+05x\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "40-layer tanh: first-block grad  plain=4.35e-11  residual=2.53e+00  ratio=6e+10x\n",
      "[ ok ] FIXED: the residual path keeps the first-layer gradient alive where the plain stack starves it\n"
     ]
    }
   ],
   "source": [
    "def first_block_grad_norm(model, x, y):\n",
    "    \"\"\"One forward/backward; return the gradient L2 norm of the FIRST block (closest to input).\"\"\"\n",
    "    for p in model.parameters():\n",
    "        p.grad = None\n",
    "    F.cross_entropy(model(x), y).backward()\n",
    "    return float(model.blocks[0].weight.grad.norm())\n",
    "\n",
    "xb = F.one_hot(Xtr[:256], VOCAB).float().reshape(256, -1)\n",
    "yb = Ytr[:256]\n",
    "rows = []\n",
    "for depth in (20, 40):\n",
    "    torch.manual_seed(SEED); plain = first_block_grad_norm(DeepReLU(depth, residual=False, act=\"tanh\"), xb, yb)\n",
    "    torch.manual_seed(SEED); resid = first_block_grad_norm(DeepReLU(depth, residual=True,  act=\"tanh\"), xb, yb)\n",
    "    rows.append((depth, plain, resid))\n",
    "    print(f\"{depth}-layer tanh: first-block grad  plain={plain:.2e}  residual={resid:.2e}  ratio={resid/max(plain,1e-30):.1g}x\")\n",
    "assert rows[-1][2] > rows[-1][1] * 100, \"residuals must keep the early-layer gradient orders of magnitude healthier\"\n",
    "print(\"[ ok ] FIXED: the residual path keeps the first-layer gradient alive where the plain stack starves it\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "af27f995",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:15.687704Z",
     "iopub.status.busy": "2026-06-10T19:43:15.687590Z",
     "iopub.status.idle": "2026-06-10T19:43:15.966671Z",
     "shell.execute_reply": "2026-06-10T19:43:15.966351Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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QQGuXr2Ktm3bmrxfIqodLC0gonovPDwcnTt3xoABA7Bz505cv34df/31F9555x3tXemBgYGIj4/HyZMnkZaWph0toGvXrsjOzsYvv/yiTVq7d++OtWvXwsvLC02bNtUeZ9q0afjkk0+wYcMGXLx4EW+++SZOnjyJV1991ag4ZTIZ1q5di+DgYPTs2RNJSUkG2zVr1gx9+vTB+PHjcejQIRw7dgxjxoyBtbW1SecMAFZWVoiKisKpU6fw559/YvLkyRgyZAg8PT211+X06dO4ePEi0tLS7mu4sIiICOzfv7/CNk2aNMHq1atx/vx5HDp0CM8995zOeZUYM2YMVq1ahfPnzyMqKkq73N7eHq+//jqmTJmCVatW4erVqzh+/Di++OILrFq1qtzj5ufnY+LEidizZw9u3LiBAwcO4MiRI2jRooW2zd9//63tISai+oGJLBHVexKJBNu2bUPXrl0xatQoNG3aFM8++yxu3LgBDw8PAMDTTz+NPn36oEePHnBzc8O6desAAM7OzmjTpg3c3NzQvHlzAMXJrUaj0evxmzx5MqKjozF16lS0adMGO3bswNatW9GkSROjY7WwsMC6devQqlUr9OzZs9ya09jYWHh7e6Nbt24YNGgQxo0bB3d3d5POGSgePmzQoEF44okn0Lt3bzzyyCNYunSpdv3YsWPRrFkztG/fHm5ubjhw4IDR51LWc889h7Nnz1ZYf7t8+XLcu3cP7dq1wwsvvIDJkyfrnFeJ8PBweHl5ISIiQq/n+v3338d7772HuXPnokWLFujTpw9+/fVXNGzYsNzjymQypKenY8SIEWjatCmGDBmCvn37Yvbs2do269atw3PPPQcbG5sqnD0R1QaJEELUdhBERGR+s2bNwubNm6s01WxVTZs2DVlZWfjqq6/uaz85OTnw8fFBbGwsBg0aZKboypeWloZmzZrh6NGjFSbERFS3sEeWiIjM5p133kFAQECFN4VVRKPRICUlBe+//z6cnJzQv39/M0do2PXr17F06VImsUT1DG/2IiIis3FycsLbb79d5e0TEhLQsGFD+Pr6YuXKlbCwqJn/ptq3b29w2DQiqttYWkBERERE9RJLC4iIiIioXmIiS0RERET1EhNZIiIiIqqXmMgSERERUb3ERJaIiIiI6iUmskRERERULzGRJSIiIqJ6iYksEREREdVLTGSJiIiIqF76f/MJplEe2ZPjAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 700x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# viz: first-block gradient norm, plain vs residual, across depth (log scale)\n",
    "depths = [r[0] for r in rows]\n",
    "fig, ax = plt.subplots(figsize=(7, 3))\n",
    "ax.semilogy(depths, [r[1] for r in rows], marker='o', color=\"#CC3333\", label=\"plain\")\n",
    "ax.semilogy(depths, [r[2] for r in rows], marker='o', color=\"#1E40FF\", label=\"residual\")\n",
    "ax.set_xlabel(\"network depth (layers)\"); ax.set_ylabel(\"first-block grad norm (log)\")\n",
    "ax.set_title(\"Residual connections keep the input-layer gradient alive\"); ax.legend()\n",
    "ax.set_xticks(depths)\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e0aea90",
   "metadata": {},
   "source": [
    "> **Interpretation.** The plain `tanh` net's first-block gradient collapses toward zero as depth grows (at 40 layers it is essentially $10^{-11}$, numerically dead). The residual net's first-block gradient stays near 1 regardless of depth, because the identity path guarantees a gradient of at least 1 through every block. The early layers of the plain net cannot learn; the early layers of the residual net can. This is the single most important architectural fix in deep learning, and it is visible in one backward pass.\n",
    "\n",
    "> **Key takeaways**\n",
    "> - NaN loss => gradient explosion => lower the LR and add `clip_grad_norm_`; that is the first response, every time.\n",
    "> - A starved early-layer gradient on a deep net => vanishing gradients => residual connections (and good init) are the fix.\n",
    "> - Measure the mechanism (the gradient norm), not a noisy trained-loss race; one backward pass is a cleaner, more honest signal than a small overfit run.\n",
    "> - Change one knob at a time so you can attribute the improvement; that discipline is the whole of training debugging.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a60b9b7",
   "metadata": {},
   "source": [
    "### Exercise 11.5 — Gradient clipping from scratch\n",
    "`Difficulty 3/5 · ~12 min`\n",
    "\n",
    "Implement `clip_grad_norm(params, max_norm)`: compute the *global* L2 norm across all parameter gradients, and if it exceeds `max_norm`, scale every gradient in place by `max_norm / total_norm`. Return the *pre-clip* total norm (this is what `torch.nn.utils.clip_grad_norm_` returns). The check compares your result against the torch built-in on the same gradients.\n",
    "\n",
    "> **Common confusion:** the norm is *global* (one number over all parameters stacked together), not per-parameter. And you scale by `max_norm / total_norm`, not by `max_norm` alone.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "0428c118",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:15.976544Z",
     "iopub.status.busy": "2026-06-10T19:43:15.976415Z",
     "iopub.status.idle": "2026-06-10T19:43:15.991912Z",
     "shell.execute_reply": "2026-06-10T19:43:15.991609Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.5 clip vs torch: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def clip_grad_norm(params, max_norm):\n",
    "    \"\"\"Scale all grads in place so the global L2 norm <= max_norm. Return the pre-clip norm.\"\"\"\n",
    "    params = [p for p in params if p.grad is not None]\n",
    "    # TODO 1: total_norm = sqrt(sum of squared L2 norms of each p.grad)\n",
    "    total_norm = None\n",
    "    attempted(total_norm)\n",
    "    # TODO 2: if total_norm > max_norm: multiply every p.grad by max_norm / (total_norm + 1e-6)\n",
    "    # TODO 3: return total_norm (the value BEFORE clipping)\n",
    "    raise NotImplementedError\n",
    "\n",
    "def _fresh_clip_model():\n",
    "    \"\"\"Re-seed FIRST so two calls produce byte-identical weights and gradients.\"\"\"\n",
    "    torch.manual_seed(SEED)\n",
    "    m = nn.Linear(4, 4)\n",
    "    (m(torch.randn(16, 4) * 1000) ** 2).sum().backward()  # huge grads to force clipping\n",
    "    return m\n",
    "\n",
    "def _check_clip():\n",
    "    mine, ref = _fresh_clip_model(), _fresh_clip_model()  # identical: each re-seeds\n",
    "    pre_mine = clip_grad_norm(mine.parameters(), 1.0)\n",
    "    pre_ref = torch.nn.utils.clip_grad_norm_(ref.parameters(), 1.0)\n",
    "    check_close(pre_mine, float(pre_ref), atol=1e-3, msg=\"pre-clip norm should match torch\")\n",
    "    post = math.sqrt(sum(float(p.grad.pow(2).sum()) for p in mine.parameters()))\n",
    "    assert post <= 1.0 + 1e-4, f\"post-clip global norm {post:.4f} should be <= max_norm 1.0\"\n",
    "\n",
    "check(\"11.5 clip vs torch\", _check_clip)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b59e1db7",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>Stack every gradient's squared L2 norm, sum, square-root: that is the global norm. Only scale if it exceeds `max_norm`. The scale factor is `max_norm / total_norm`.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "total_norm = torch.sqrt(sum(p.grad.pow(2).sum() for p in params))\n",
    "if total_norm > max_norm:\n",
    "    scale = max_norm / (total_norm + 1e-6)\n",
    "    for p in params:\n",
    "        p.grad.mul_(scale)\n",
    "return float(total_norm)\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — my post-clip norm is still huge</summary>Symptom: the post-clip assert fails; the norm did not shrink. Cause: you scaled by `max_norm` instead of `max_norm / total_norm`, or you computed per-parameter norms and clipped each separately. The clip is global: one norm over all gradients, one shared scale factor.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "39213e2a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.001430Z",
     "iopub.status.busy": "2026-06-10T19:43:16.001325Z",
     "iopub.status.idle": "2026-06-10T19:43:16.015662Z",
     "shell.execute_reply": "2026-06-10T19:43:16.015304Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] 11.5 clip vs torch\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines clip_grad_norm; the check below re-verifies the reference.\n",
    "def clip_grad_norm(params, max_norm):\n",
    "    params = [p for p in params if p.grad is not None]\n",
    "    total_norm = torch.sqrt(sum(p.grad.pow(2).sum() for p in params))\n",
    "    if total_norm > max_norm:\n",
    "        scale = max_norm / (total_norm + 1e-6)\n",
    "        for p in params:\n",
    "            p.grad.mul_(scale)\n",
    "    return float(total_norm)\n",
    "\n",
    "check(\"11.5 clip vs torch\", _check_clip, required=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18152731",
   "metadata": {},
   "source": [
    "## Part 6 — Capstone: backprop a two-layer net by hand\n",
    "\n",
    "> **Objectives.** Differentiate a two-layer net's loss by hand, gradient by gradient, and `cmp()` each manual gradient against autograd until every one reads `[ ok ]`. This is Karpathy's *makemore* part-4 exercise, and it is the single best way to stop treating backprop as magic.\n",
    "\n",
    "We take a tiny one-hidden-layer network on a batch of our character data, run the forward pass keeping every intermediate, then derive each backward step ourselves. The `cmp()` helper compares a manual gradient to PyTorch's autograd gradient and prints exact, approximate, and max-difference flags.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "fef1691d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.027426Z",
     "iopub.status.busy": "2026-06-10T19:43:16.027318Z",
     "iopub.status.idle": "2026-06-10T19:43:16.048426Z",
     "shell.execute_reply": "2026-06-10T19:43:16.048130Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "forward loss: 3.4077  (vs log27 = 3.296)\n"
     ]
    }
   ],
   "source": [
    "# Build the forward pass, retaining every intermediate so we can backprop through each.\n",
    "torch.manual_seed(SEED)\n",
    "n = 64\n",
    "emb_dim = 8\n",
    "hidden = 32\n",
    "xb = Xtr[:n]                                  # (n, CONTEXT) integer contexts\n",
    "yb = Ytr[:n]                                  # (n,) targets\n",
    "\n",
    "# parameters (small, explicit, leaf tensors with grad)\n",
    "Wemb = torch.randn(VOCAB, emb_dim) * 0.5\n",
    "W1 = torch.randn(CONTEXT * emb_dim, hidden) * (2.0 / (CONTEXT * emb_dim)) ** 0.5\n",
    "b1 = torch.zeros(hidden)\n",
    "W2 = torch.randn(hidden, VOCAB) * (1.0 / hidden) ** 0.5\n",
    "b2 = torch.zeros(VOCAB)\n",
    "params = [Wemb, W1, b1, W2, b2]\n",
    "for p in params:\n",
    "    p.requires_grad_(True)\n",
    "\n",
    "# forward pass, every intermediate retained\n",
    "emb = Wemb[xb]                                # (n, CONTEXT, emb_dim)\n",
    "embcat = emb.reshape(n, -1)                   # (n, CONTEXT*emb_dim)\n",
    "hpre = embcat @ W1 + b1                        # (n, hidden)\n",
    "h = torch.tanh(hpre)                           # (n, hidden)\n",
    "logits = h @ W2 + b2                           # (n, VOCAB)\n",
    "# cross-entropy, expanded so every step is differentiable by hand\n",
    "logit_max = logits.max(1, keepdim=True).values\n",
    "norm_logits = logits - logit_max               # numerically stable shift\n",
    "counts = norm_logits.exp()\n",
    "counts_sum = counts.sum(1, keepdim=True)\n",
    "counts_sum_inv = counts_sum ** -1\n",
    "probs = counts * counts_sum_inv                # (n, VOCAB) softmax\n",
    "logprobs = probs.log()\n",
    "loss = -logprobs[range(n), yb].mean()\n",
    "\n",
    "for t in [emb, embcat, hpre, h, logits, norm_logits, counts, counts_sum,\n",
    "          counts_sum_inv, probs, logprobs]:\n",
    "    t.retain_grad()\n",
    "loss.backward()\n",
    "print(f\"forward loss: {loss.item():.4f}  (vs log{VOCAB} = {math.log(VOCAB):.3f})\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "0ca3f13e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.052556Z",
     "iopub.status.busy": "2026-06-10T19:43:16.052416Z",
     "iopub.status.idle": "2026-06-10T19:43:16.059627Z",
     "shell.execute_reply": "2026-06-10T19:43:16.059228Z"
    }
   },
   "outputs": [],
   "source": [
    "def cmp(name, dt, t):\n",
    "    \"\"\"Compare a manual gradient dt to autograd's t.grad. Print exact/approx/maxdiff.\"\"\"\n",
    "    ex = torch.all(dt == t.grad).item()\n",
    "    app = torch.allclose(dt, t.grad, atol=1e-7, rtol=1e-4)\n",
    "    maxdiff = (dt - t.grad).abs().max().item()\n",
    "    print(f\"{name:16s} | exact: {str(ex):5s} | approx: {str(app):5s} | maxdiff: {maxdiff:.2e}\")\n",
    "    return app"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0886c928",
   "metadata": {},
   "source": [
    "### Exercise 11.6 — Backpropagate the head by hand\n",
    "`Difficulty 4/5 · ~25 min`\n",
    "\n",
    "Fill in `manual_grads()` returning the gradient of `loss` with respect to `logprobs`, `probs`, `logits`, `W2`, `b2`, and `h`. Work backward from the loss. Each gradient is checked against autograd by `cmp()`; every line must read `approx: True`. This is the hardest exercise in the notebook; the hint ladder gives the full derivation.\n",
    "\n",
    "The forward pieces you need (all already computed above): `loss = -logprobs[range(n), yb].mean()`, `logprobs = probs.log()`, `probs = counts * counts_sum_inv`, `logits = h @ W2 + b2`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "9fd9cf61",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.060712Z",
     "iopub.status.busy": "2026-06-10T19:43:16.060620Z",
     "iopub.status.idle": "2026-06-10T19:43:16.075424Z",
     "shell.execute_reply": "2026-06-10T19:43:16.075176Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] 11.6 manual backprop: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def manual_grads():\n",
    "    \"\"\"Return a dict of manual gradients. Work backward from the loss.\"\"\"\n",
    "    # dlogprobs: loss = -mean of the picked logprobs. Only the picked entries get a gradient.\n",
    "    # TODO 1: dlogprobs = zeros_like(logprobs); set dlogprobs[range(n), yb] = -1/n\n",
    "    dlogprobs = None\n",
    "    # TODO 2: logprobs = log(probs) => dprobs = dlogprobs / probs\n",
    "    dprobs = None\n",
    "    # TODO 3: probs = counts * counts_sum_inv; but it's cleaner to push through to logits.\n",
    "    #   The classic shortcut: dlogits = probs.clone(); dlogits[range(n), yb] -= 1; dlogits /= n\n",
    "    #   (softmax + cross-entropy combine to (softmax - onehot)/n). Use this for dlogits.\n",
    "    dlogits = None\n",
    "    # TODO 4: logits = h @ W2 + b2  => dW2 = h.T @ dlogits ; db2 = dlogits.sum(0) ; dh = dlogits @ W2.T\n",
    "    dW2 = None\n",
    "    db2 = None\n",
    "    dh = None\n",
    "    attempted(dlogprobs, dprobs, dlogits, dW2, db2, dh)\n",
    "    return dict(logprobs=dlogprobs, probs=dprobs, logits=dlogits, W2=dW2, b2=db2, h=dh)\n",
    "\n",
    "def _check_manual():\n",
    "    G = manual_grads()\n",
    "    ok = []\n",
    "    ok.append(cmp(\"logprobs\", G[\"logprobs\"], logprobs))\n",
    "    ok.append(cmp(\"probs\", G[\"probs\"], probs))\n",
    "    ok.append(cmp(\"logits\", G[\"logits\"], logits))\n",
    "    ok.append(cmp(\"W2\", G[\"W2\"], W2))\n",
    "    ok.append(cmp(\"b2\", G[\"b2\"], b2))\n",
    "    ok.append(cmp(\"h\", G[\"h\"], h))\n",
    "    assert all(ok), \"every manual gradient must match autograd (approx: True)\"\n",
    "\n",
    "check(\"11.6 manual backprop\", _check_manual)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3780aa32",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>Go one node at a time, backward. `loss` only touches the picked logprob of each row, so `dlogprobs` is zero everywhere except those entries, each $-1/n$. For `log`, the local derivative is $1/x$. For the softmax+cross-entropy pair, the famous collapse is `dlogits = (probs - onehot) / n`.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "dlogprobs = torch.zeros_like(logprobs)\n",
    "dlogprobs[range(n), yb] = -1.0 / n\n",
    "dprobs = dlogprobs / probs                       # d/dx log(x) = 1/x\n",
    "dlogits = probs.clone()\n",
    "dlogits[range(n), yb] -= 1\n",
    "dlogits /= n                                     # softmax+CE shortcut\n",
    "dW2 = h.T @ dlogits\n",
    "db2 = dlogits.sum(0)\n",
    "dh = dlogits @ W2.T\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — dW2 has the wrong shape</summary>Symptom: a shape error or `approx: False` on `W2`. Cause: matmul order. `logits = h @ W2` with `h:(n,hidden)`, `W2:(hidden,vocab)`. The gradient `dW2` must be `(hidden, vocab)`, so it is `h.T @ dlogits` = `(hidden,n)@(n,vocab)`. If you wrote `dlogits @ h.T` you transposed the wrong factor.</details>\n",
    "\n",
    "<details><summary>Help — logits matches but probs does not</summary>That is expected and fine if you took the softmax+CE shortcut for `dlogits` rather than routing through `dprobs`. The check only requires `probs`, `logits`, etc. to each match autograd; compute `dprobs = dlogprobs / probs` directly from the `log` step so it matches on its own, and compute `dlogits` via the shortcut.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "e4c5ee19",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.076661Z",
     "iopub.status.busy": "2026-06-10T19:43:16.076578Z",
     "iopub.status.idle": "2026-06-10T19:43:16.135273Z",
     "shell.execute_reply": "2026-06-10T19:43:16.134989Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "logprobs         | exact: True  | approx: True  | maxdiff: 0.00e+00\n",
      "probs            | exact: True  | approx: True  | maxdiff: 0.00e+00\n",
      "logits           | exact: False | approx: True  | maxdiff: 3.38e-09\n",
      "W2               | exact: False | approx: True  | maxdiff: 7.45e-09\n",
      "b2               | exact: False | approx: True  | maxdiff: 7.45e-09\n",
      "h                | exact: False | approx: True  | maxdiff: 3.73e-09\n",
      "[ ok ] 11.6 manual backprop\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines manual_grads; the check below re-verifies every gradient against autograd.\n",
    "def manual_grads():\n",
    "    dlogprobs = torch.zeros_like(logprobs)\n",
    "    dlogprobs[range(n), yb] = -1.0 / n\n",
    "    dprobs = dlogprobs / probs\n",
    "    dlogits = probs.clone()\n",
    "    dlogits[range(n), yb] -= 1\n",
    "    dlogits /= n\n",
    "    dW2 = h.T @ dlogits\n",
    "    db2 = dlogits.sum(0)\n",
    "    dh = dlogits @ W2.T\n",
    "    return dict(logprobs=dlogprobs, probs=dprobs, logits=dlogits, W2=dW2, b2=db2, h=dh)\n",
    "\n",
    "check(\"11.6 manual backprop\", _check_manual, required=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3819e914",
   "metadata": {},
   "source": [
    "> **Interpretation.** Every gradient matches autograd to `1e-7`. The payoff line is `dlogits = (probs - onehot)/n`: the gradient of the loss with respect to the pre-softmax logits is just \"predicted minus target, averaged over the batch\". That single expression is why classification trains so stably, and you derived it by hand instead of taking it on faith.\n",
    "\n",
    "> **Key takeaways**\n",
    "> - Backprop is a sequence of local derivatives chained by the rule, nothing more; `cmp()` against autograd makes it falsifiable.\n",
    "> - The softmax + cross-entropy pair collapses to `(probs - onehot)/n`, the cleanest gradient in deep learning.\n",
    "> - When a hand gradient mismatches, the error is almost always a transpose or a missing `1/n`; the shape tells you which.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25a198a5",
   "metadata": {},
   "source": [
    "### Experiment log\n",
    "\n",
    "Every named run and its expected numbers, so you know what \"working\" looks like. The FAST column is the `NB_FAST=1` smoke run (10x fewer steps); the full column is the committed CPU run.\n",
    "\n",
    "| Run | What it shows | Expected (full) | Expected (FAST) |\n",
    "|---|---|---|---|\n",
    "| 30-layer tanh, default init | vanishing gradients | spread > 100x, input grad tiny vs output | same (no training, deterministic) |\n",
    "| CharMLP, lr=0.1 | the four diagnostics | train loss ~1.2, dev ~2.7 (overfit on the subset) | train/dev both ~2.6-3.0 (fewer steps) |\n",
    "| 12-layer plain, lr=30.0 | NaN divergence | non-finite within ~5 steps | same |\n",
    "| 12-layer, lr=0.2 + kaiming + clip | the fix | finite, decreasing loss | finite, decreasing |\n",
    "| plain vs residual, first-block grad | residuals keep the gradient alive | residual / plain > 100x at 40 layers | same (no training, deterministic) |\n",
    "| manual backprop cmp() | hand gradients == autograd | every line approx: True, maxdiff < 1e-6 | identical (no training) |\n",
    "\n",
    "The gradient-flow, init, and backprop runs are deterministic given the seed, so they read the same in FAST and full mode; only the trained-loss numbers shift with step count.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d13888c9",
   "metadata": {},
   "source": [
    "## Safety lens\n",
    "\n",
    "Every training knob is a place for a safety-relevant failure to hide. Three that follow directly from this chapter, not speculation.\n",
    "\n",
    "**Regularization can game the benchmark without improving the property you care about.** Weight decay, dropout, and label smoothing each buy a fraction of a percent on a held-out split; none of them reliably improves out-of-distribution robustness or refusal behavior. A hyperparameter sweep that maximizes validation accuracy can produce a model that is *worse* at refusing harmful prompts, because the same regularizer shrank the safety-relevant features. The fix is to put a safety eval inside the sweep, not only downstream task accuracy.\n",
    "\n",
    "**Initialization is an interpretability surface.** The features a network learns depend on the seed. Two models trained from different seeds with identical data and architecture can decompose into entirely different internal features, so a finding like \"the refusal feature lives here\" can be specific to *that* initialization. Safety-relevant interpretability claims should be checked across multiple seeds; a result that holds for seed 0 and breaks for seed 42 is not a result. (We re-seeded every stochastic cell in this notebook for exactly this reproducibility reason.)\n",
    "\n",
    "**Hyperparameter search is an eval-leakage channel.** Tools that pick the config maximizing a validation metric have effectively trained on that validation set. If the same split later serves as a safety gate, the released model is the one that best *memorized* the gate, not the one that generalizes. Keep a held-out safety eval that the search never sees, and run it once, at the end.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e4838fd",
   "metadata": {},
   "source": [
    "## Test yourself\n",
    "\n",
    "Three parts: concept self-checks with folded answers, two auto-checked problems with the full exercise mechanic, and a capstone with a rubric and a folded reference. Every answer is in this notebook; if unsure, re-run that section.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92addde5",
   "metadata": {},
   "source": [
    "### Part A — Concepts\n",
    "\n",
    "1. The cross-entropy loss at init for a $K$-class model should be near what number, and why does it matter? <details><summary>Answer</summary>$\\log K$. A freshly initialized model assigns roughly uniform probability $1/K$, so $-\\log(1/K)=\\log K$. If your loss starts far above it, the output layer's weights are too large and the first steps waste themselves shrinking confidently-wrong logits. It is the cheapest diagnostic in training.</details>\n",
    "2. Why does Kaiming init use a factor of 2 ($\\sigma^2 = 2/d_{in}$) where Xavier-for-tanh does not? <details><summary>Answer</summary>ReLU zeros half its inputs on average, halving the output variance. The factor of 2 compensates exactly. Tanh has near-unit derivative around 0 and does not zero anything, so it needs no such correction.</details>\n",
    "3. In one sentence, the difference between LayerNorm and BatchNorm. <details><summary>Answer</summary>LayerNorm normalizes each example across its feature axis (same at train and eval); BatchNorm normalizes each feature across the batch axis (batch stats at train, running stats at eval). The differing train/eval behavior is BatchNorm's main footgun.</details>\n",
    "4. The update-to-data ratio you plotted in Part 4 should sit near what value, and what does $-1$ mean? <details><summary>Answer</summary>Near $10^{-3}$ ($\\log_{10}\\approx-3$): each step nudges a weight by about a thousandth of its size. A value of $-1$ means steps are ~100x too large; you are about to diverge.</details>\n",
    "5. Looking at the 40-layer plain-vs-residual loss curves in Part 5: the plain net's loss sat near $\\log 27 \\approx 3.3$. What does that flat-at-$\\log K$ behavior tell you? <details><summary>Answer</summary>The model is still at the uniform-guess prediction; it has not learned anything because the early-layer gradients vanished. Flat-at-$\\log K$ is the canonical signature of a network that cannot propagate signal to its input layers. Residual connections fix it.</details>\n",
    "6. Why mean-center in LayerNorm but skip it in RMSNorm? <details><summary>Answer</summary>RMSNorm drops the mean subtraction because the learned $\\beta$ shift makes it largely redundant; the empirical result is the same accuracy with one fewer reduction pass. Llama-family models use RMSNorm for this reason.</details>\n",
    "7. You see a NaN loss at step 3 of a deep-net run. Name the first two fixes from the checklist. <details><summary>Answer</summary>Lower the learning rate (often 10x) and add `clip_grad_norm_(..., max_norm=1.0)`. NaN almost always means gradient explosion; clipping bounds the step and a lower LR keeps you out of the runaway region. Warmup is the third fix.</details>\n",
    "8. Why does `clip_grad_norm_` use a *global* norm rather than clipping each parameter independently? <details><summary>Answer</summary>The update direction is the full gradient vector across all parameters; clipping per-parameter would change that direction (rescaling some axes more than others). A global scale preserves the direction and only shortens the step. The function returns the pre-clip norm so you can log it.</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1377d128",
   "metadata": {},
   "source": [
    "### Part B — Auto-checked problems\n",
    "\n",
    "Two problems that make you compute something new with the chapter's pieces. Same mechanic: stub, checks, hint ladder, folded solution.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6697524a",
   "metadata": {},
   "source": [
    "#### Problem B1 — Warmup + cosine schedule\n",
    "`Difficulty 3/5 · ~12 min`\n",
    "\n",
    "Implement `warmup_cosine_lr(step, warmup, total, base_lr, min_lr)`: linear ramp from `min_lr` to `base_lr` over the first `warmup` steps, then a half-cosine decay from `base_lr` back to `min_lr` over the remaining steps. The checks verify the three landmark points (start, end of warmup, end of training) and monotonic decay in the cosine phase.\n",
    "\n",
    "> **Common confusion:** step the scheduler *per iteration*, not per epoch. The classic bug is a 1000-step warmup that finishes at epoch 1000 instead of step 1000 because you stepped it once per epoch.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "abd94ae8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.136494Z",
     "iopub.status.busy": "2026-06-10T19:43:16.136408Z",
     "iopub.status.idle": "2026-06-10T19:43:16.147194Z",
     "shell.execute_reply": "2026-06-10T19:43:16.146922Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] B1 warmup+cosine: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def warmup_cosine_lr(step, warmup, total, base_lr, min_lr):\n",
    "    # TODO 1: if step < warmup: linear interpolate from min_lr (at 0) to base_lr (at warmup)\n",
    "    # TODO 2: else: progress = (step-warmup)/(total-warmup) clamped to [0,1];\n",
    "    #         return min_lr + 0.5*(base_lr-min_lr)*(1+cos(pi*progress))\n",
    "    lr = None\n",
    "    attempted(lr)\n",
    "    return lr\n",
    "\n",
    "def _check_sched():\n",
    "    f = lambda s: warmup_cosine_lr(s, warmup=100, total=1000, base_lr=1e-3, min_lr=1e-5)\n",
    "    check_close(f(0), 1e-5, atol=1e-9, msg=\"step 0 should be min_lr\")\n",
    "    check_close(f(100), 1e-3, atol=1e-9, msg=\"end of warmup should be base_lr\")\n",
    "    check_close(f(1000), 1e-5, atol=1e-6, msg=\"end of training should be back at min_lr\")\n",
    "    assert f(500) < f(200), \"the cosine phase must be monotonically decreasing\"\n",
    "\n",
    "check(\"B1 warmup+cosine\", _check_sched)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7db07a29",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>Two branches. Warmup is a straight line `min_lr + (base_lr-min_lr)*step/warmup`. The cosine phase uses `progress` in $[0,1]$ and the half-cosine `0.5*(1+cos(pi*progress))` which runs from 1 (at progress 0) to 0 (at progress 1).</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "if step < warmup:\n",
    "    return min_lr + (base_lr - min_lr) * step / max(1, warmup)\n",
    "progress = (step - warmup) / max(1, total - warmup)\n",
    "progress = min(1.0, max(0.0, progress))\n",
    "return min_lr + 0.5 * (base_lr - min_lr) * (1 + math.cos(math.pi * progress))\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — the value at step=total is base_lr, not min_lr</summary>Symptom: `f(1000)` returns the peak LR. Cause: you forgot the `(1 + cos)` factor goes to 0 at progress 1, or you clamped progress wrong. At `step=total`, `progress=1`, `cos(pi)= -1`, so `0.5*(1 + -1) = 0` and you return `min_lr`. Check your `progress` denominator is `total - warmup`.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "375dc50f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.149024Z",
     "iopub.status.busy": "2026-06-10T19:43:16.148942Z",
     "iopub.status.idle": "2026-06-10T19:43:16.354233Z",
     "shell.execute_reply": "2026-06-10T19:43:16.353909Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] B1 warmup+cosine\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAroAAADwCAYAAADvocc6AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZ/RJREFUeJzt3XlYVNUbwPHvsIMIiCCLguKSK6mJIG6YkrhUau65a9li5ZKWVmq7S5lpm1opVpZ76s9MU9wVccPdTE1FTXBBQERZZs7vjxujI6hg4MDwfp7nPjNz7rn3vjMX8eXMWXRKKYUQQgghhBAWxsrcAQghhBBCCFEYJNEVQgghhBAWSRJdIYQQQghhkSTRFUIIIYQQFkkSXSGEEEIIYZEk0RVCCCGEEBZJEl0hhBBCCGGRJNEVQgghhBAWSRJdIYQQQghhkSTRFUII8cAiIyPR6XScPn3a3KE8MJ1OxyuvvFLo19m4cSM6nY6NGzfm+9jTp0+j0+mIjIws8LiEsGSS6AohhBBCCIskia4QQogH1qdPH27cuEHFihXNHYoQQuQgia4QwuKkpaWZO4T/rFKlSrz77rvmDuO+rK2tcXBwQKfTmTsUIYTIQRJdIUShOXDgADqdjhUrVhjL9uzZg06n47HHHjOp27ZtW0JCQoyvly9fTvv27fH19cXe3p4qVarwwQcfoNfrTY5r0aIFderUYc+ePTRv3hwnJyfeeustY5/GTz/9lK+++orKlSvj5ORE69atOXv2LEopPvjgAypUqICjoyMdOnQgMTHR5Nw6nS7XZLNSpUr079/f+Dq7n+rmzZt54YUXKFu2LC4uLvTt25erV6/+h0/wwZw/f55BgwYZP7uAgABeeuklMjIyjHX+/vtvunbtiru7O05OTjRq1Ijffvstx7m++OILateujZOTE2XKlCEoKIiff/7ZuD+3PrqVKlXiySefZOvWrQQHB+Pg4EDlypX54Ycfcpw/KSmJYcOG4efnh729PVWrVmXSpEkYDIb7vs/du3cTERGBh4cHjo6OBAQEMHDgQJM6BoOBadOmERgYiIODA56enrRp04bdu3fnON+yZcuoU6cO9vb21K5dm9WrV+f62Q4cOBAvLy9jvdmzZ+eod+7cOTp27EipUqUoV64cw4cPJz09PUe9O3+WsrVo0YIWLVrc9zP4888/6dKlC+7u7jg4OBAUFGTy702Iks7G3AEIISxXnTp1cHNzY/PmzTz99NMAbNmyBSsrK/bv309KSgouLi4YDAa2b9/O4MGDjcdGRkbi7OzMiBEjcHZ2Zv369YwbN46UlBQ++eQTk+tcuXKFtm3b0qNHD3r37o2Xl5dx37x588jIyODVV18lMTGRyZMn061bN1q2bMnGjRt58803OXHiBF988QUjR47MNWnJq1deeQU3Nzfeffddjh07xjfffMOZM2eMg5Aehn/++Yfg4GCSkpIYPHgwNWrU4Pz58yxevJi0tDTs7OxISEigcePGpKWl8dprr1G2bFnmzp3L008/zeLFi+nUqRMA3377La+99hpdunRh6NCh3Lx5kwMHDhATE8Ozzz57zzhOnDhBly5dGDRoEP369WP27Nn079+fBg0aULt2bUBreQ8LC+P8+fO88MIL+Pv7s337dsaMGcOFCxf4/PPP73r+ixcv0rp1azw9PRk9ejRubm6cPn2apUuXmtQbNGgQkZGRtG3blueee46srCy2bNnCjh07CAoKMtbbunUrS5cu5eWXX6Z06dJMnz6dzp07ExcXR9myZQFISEigUaNGxsFrnp6e/P777wwaNIiUlBSGDRsGwI0bN2jVqhVxcXG89tpr+Pr68uOPP7J+/fr83s57Onz4ME2aNKF8+fKMHj2aUqVKsXDhQjp27MiSJUuM91GIEk0JIUQhat++vQoODja+fuaZZ9QzzzyjrK2t1e+//66UUmrv3r0KUMuXLzfWS0tLy3GuF154QTk5OambN28ay8LCwhSgZsyYYVL31KlTClCenp4qKSnJWD5mzBgFqLp166rMzExjec+ePZWdnZ3JuQE1fvz4HHFUrFhR9evXz/h6zpw5ClANGjRQGRkZxvLJkyfneF95VbFixVyvfT99+/ZVVlZWateuXTn2GQwGpZRSw4YNU4DasmWLcd+1a9dUQECAqlSpktLr9UoppTp06KBq1659z+tlv/dTp06ZxA6ozZs3G8suXryo7O3t1euvv24s++CDD1SpUqXUX3/9ZXLO0aNHK2traxUXF3fX6/76668KyPV9Zlu/fr0C1GuvvZZjX/ZnoZR2n+3s7NSJEyeMZfv371eA+uKLL4xlgwYNUj4+Pury5csm5+rRo4dydXU1/sx+/vnnClALFy401rl+/bqqWrWqAtSGDRuM5Xf+LGULCwtTYWFh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",
      "text/plain": [
       "<Figure size 700x250 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines warmup_cosine_lr; the check below re-verifies the reference.\n",
    "def warmup_cosine_lr(step, warmup, total, base_lr, min_lr):\n",
    "    if step < warmup:\n",
    "        return min_lr + (base_lr - min_lr) * step / max(1, warmup)\n",
    "    progress = (step - warmup) / max(1, total - warmup)\n",
    "    progress = min(1.0, max(0.0, progress))\n",
    "    return min_lr + 0.5 * (base_lr - min_lr) * (1 + math.cos(math.pi * progress))\n",
    "\n",
    "check(\"B1 warmup+cosine\", _check_sched, required=True)\n",
    "# plot it so the shape is concrete\n",
    "xs = list(range(1000))\n",
    "ys = [warmup_cosine_lr(s, 100, 1000, 1e-3, 1e-5) for s in xs]\n",
    "fig, ax = plt.subplots(figsize=(7, 2.5))\n",
    "ax.plot(xs, ys, color=\"#1E40FF\"); ax.axvline(100, ls=':', c='#888', label=\"end of warmup\")\n",
    "ax.set_xlabel(\"step\"); ax.set_ylabel(\"learning rate\"); ax.set_title(\"warmup + cosine schedule\"); ax.legend()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb3ad602",
   "metadata": {},
   "source": [
    "#### Problem B2 — AdamW weight-decay parameter groups\n",
    "`Difficulty 2/5 · ~10 min`\n",
    "\n",
    "Implement `split_decay_params(model)` returning `(decay, nodecay)` lists of parameters. The convention (from ARENA and nanoGPT): any parameter that is 1D (biases, norm scales) or whose name contains `bias`, `norm`, `ln`, or `embed` goes in `nodecay`; everything else (the 2D weight matrices) goes in `decay`. The check builds an AdamW with two groups and confirms the norm/bias params land in the zero-decay group.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "47815f84",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.355820Z",
     "iopub.status.busy": "2026-06-10T19:43:16.355461Z",
     "iopub.status.idle": "2026-06-10T19:43:16.367813Z",
     "shell.execute_reply": "2026-06-10T19:43:16.367587Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ -- ] B2 param split: not attempted yet — fill in the TODO above, then re-run.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def split_decay_params(model):\n",
    "    \"\"\"Return (decay_params, nodecay_params). 1D params and norm/bias/embed names => no decay.\"\"\"\n",
    "    decay, nodecay = [], []\n",
    "    for name, p in model.named_parameters():\n",
    "        if not p.requires_grad:\n",
    "            continue\n",
    "        # TODO 1: classify by p.dim() < 2 OR name contains any of bias/norm/ln/embed\n",
    "        # TODO 2: append to nodecay if no-decay, else decay\n",
    "        pass\n",
    "    attempted(decay or None if (decay or nodecay) else None)  # ensure the loop did something\n",
    "    return decay, nodecay\n",
    "\n",
    "def _check_split():\n",
    "    m = nn.Sequential(nn.Linear(4, 8), nn.LayerNorm(8), nn.Linear(8, 2))\n",
    "    decay, nodecay = split_decay_params(m)\n",
    "    n_nodecay = len(nodecay)\n",
    "    # 2 Linear biases + 2 LayerNorm params (weight+bias) = 4 no-decay; 2 Linear weights decay\n",
    "    assert len(decay) == 2, f\"expected 2 weight matrices in decay, got {len(decay)}\"\n",
    "    assert n_nodecay == 4, f\"expected 4 no-decay params (biases + norm), got {n_nodecay}\"\n",
    "    # every decay param must be 2D\n",
    "    assert all(p.dim() == 2 for p in decay), \"only 2D weight matrices should get weight decay\"\n",
    "\n",
    "check(\"B2 param split\", _check_split)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04261021",
   "metadata": {},
   "source": [
    "<details><summary>Hint 1 (conceptual)</summary>For each named parameter, the no-decay test is `p.dim() < 2 or any(k in name.lower() for k in (...))`. The keys are `bias`, `norm`, `ln`, `embed`.</details>\n",
    "\n",
    "<details><summary>Hint 2 (pseudocode)</summary>\n",
    "\n",
    "```python\n",
    "if p.dim() < 2 or any(k in name.lower() for k in (\"bias\", \"norm\", \"ln\", \"embed\")):\n",
    "    nodecay.append(p)\n",
    "else:\n",
    "    decay.append(p)\n",
    "```\n",
    "</details>\n",
    "\n",
    "<details><summary>Help — a LayerNorm weight ended up in decay</summary>Symptom: `len(decay)` is 3, not 2. Cause: `nn.LayerNorm`'s learnable scale is named `...weight` and is 1D. The `p.dim() < 2` test catches it; make sure that test runs (a 1D tensor has `dim()==1`). Print `[(n, p.dim()) for n,p in m.named_parameters()]` to see the shapes.</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "16be883a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-10T19:43:16.372172Z",
     "iopub.status.busy": "2026-06-10T19:43:16.372073Z",
     "iopub.status.idle": "2026-06-10T19:43:16.383735Z",
     "shell.execute_reply": "2026-06-10T19:43:16.383173Z"
    },
    "collapsed": true,
    "jupyter": {
     "source_hidden": true
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ ok ] B2 param split\n",
      "decay group: 2 params (wd=0.1), no-decay group: 4 params (wd=0.0)\n"
     ]
    }
   ],
   "source": [
    "#@title Solution { display-mode: \"form\" }\n",
    "# solution: redefines split_decay_params; the check below re-verifies the reference.\n",
    "def split_decay_params(model):\n",
    "    decay, nodecay = [], []\n",
    "    for name, p in model.named_parameters():\n",
    "        if not p.requires_grad:\n",
    "            continue\n",
    "        if p.dim() < 2 or any(k in name.lower() for k in (\"bias\", \"norm\", \"ln\", \"embed\")):\n",
    "            nodecay.append(p)\n",
    "        else:\n",
    "            decay.append(p)\n",
    "    return decay, nodecay\n",
    "\n",
    "check(\"B2 param split\", _check_split, required=True)\n",
    "# show it wired into a real AdamW with two groups\n",
    "m = nn.Sequential(nn.Linear(4, 8), nn.LayerNorm(8), nn.Linear(8, 2))\n",
    "dec, nodec = split_decay_params(m)\n",
    "opt = torch.optim.AdamW([\n",
    "    {\"params\": dec, \"weight_decay\": 0.1},\n",
    "    {\"params\": nodec, \"weight_decay\": 0.0},\n",
    "], lr=3e-4, betas=(0.9, 0.95))\n",
    "print(f\"decay group: {len(dec)} params (wd={opt.param_groups[0]['weight_decay']}), \"\n",
    "      f\"no-decay group: {len(nodec)} params (wd={opt.param_groups[1]['weight_decay']})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d715e73",
   "metadata": {},
   "source": [
    "### Part C — Capstone: stabilize a deep net and attribute the fixes\n",
    "\n",
    "One open project: take the broken deep run from Part 5 and turn it into a stable one, then attribute *each* improvement to a specific fix with a number. The honest measurement here is two-fold: training stability (does the loss stay finite and decrease?) and gradient flow (does the early-layer gradient survive depth?). At this tiny scale a trained-loss race between architectures is noisy and overfits, so the capstone grades the *mechanism*, not a final-loss horserace.\n",
    "\n",
    "**Deliverables**\n",
    "1. Starting from the diverging config (deep plain net, lr too high, default init, no clip), apply fixes one knob at a time and report, after each, whether the run stays finite and the final smoothed loss.\n",
    "2. A gradient-flow measurement: the first-block gradient norm for a plain vs residual deep net (deterministic, one backward pass), showing the residual path keeps it alive.\n",
    "3. A short written claim (3-4 sentences) naming which fix addressed *which* failure (lr/clip address divergence; residuals address the vanishing early-layer gradient), with the numbers that justify it.\n",
    "\n",
    "**Self-assessment (pass / partial / fail)**\n",
    "- (a) The diverging run is turned into a finite, decreasing one by lowering the LR, adding Kaiming init, and clipping.\n",
    "- (b) Each fix changes *one* knob at a time, so each delta is attributable.\n",
    "- (c) Gradient telemetry is measured directly (a backward pass), not guessed at.\n",
    "- (d) The written claim names a *specific* fix and the *specific* failure it addresses, not \"I added good things and it worked\".\n",
    "- (e) The whole thing runs top-to-bottom under the notebook's seed.\n",
    "\n",
    "<details><summary>My solution (reference, ~20s on CPU)</summary>\n",
    "\n",
    "```python\n",
    "# Stability ablation: one knob at a time, from broken toward fixed.\n",
    "def report(label, model, lr, clip):\n",
    "    torch.manual_seed(SEED)\n",
    "    hist = run_deep(model, steps=DEEP_STEPS, lr=lr, clip=clip)\n",
    "    finite = all(math.isfinite(v) for v in hist)\n",
    "    fl = final_loss(hist)\n",
    "    print(f\"{label:38s} finite={finite}  final_loss={fl:.3f}\")\n",
    "    return finite, fl\n",
    "\n",
    "report(\"broken: lr=30, default init, no clip\", DeepReLU(12, residual=False), lr=30.0, clip=None)\n",
    "report(\"+ lower lr (0.2)\",                     DeepReLU(12, residual=False), lr=0.2, clip=None)\n",
    "report(\"+ kaiming init\",                       DeepReLU(12, residual=False, init_kaiming=True), lr=0.2, clip=None)\n",
    "report(\"+ gradient clipping\",                  DeepReLU(12, residual=False, init_kaiming=True), lr=0.2, clip=1.0)\n",
    "\n",
    "# Gradient-flow measurement: residuals keep the early-layer gradient alive (deterministic).\n",
    "xb = F.one_hot(Xtr[:256], VOCAB).float().reshape(256, -1); yb = Ytr[:256]\n",
    "torch.manual_seed(SEED); gp = first_block_grad_norm(DeepReLU(40, residual=False, act=\"tanh\"), xb, yb)\n",
    "torch.manual_seed(SEED); gr = first_block_grad_norm(DeepReLU(40, residual=True,  act=\"tanh\"), xb, yb)\n",
    "print(f\"\\n40-layer first-block grad: plain={gp:.2e}  residual={gr:.2e}  ratio={gr/max(gp,1e-30):.1g}x\")\n",
    "print(\"Claim: lowering the lr + clipping fixed the *divergence* (finite, decreasing). \"\n",
    "      \"Residuals fixed the *vanishing early-layer gradient* (a >100x healthier first-block grad). \"\n",
    "      \"They are different failures with different fixes; that is the whole lesson.\")\n",
    "```\n",
    "\n",
    "The reference catches the lesson: divergence (NaN) and vanishing gradients are two different failure modes. Learning rate and clipping address the first; residual connections address the second. Naming which fix targets which failure is the difference between debugging and guessing.</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f09cbe6e",
   "metadata": {},
   "source": [
    "## Reflection\n",
    "\n",
    "Write ~150 words, in the cell below, on the dumbest bug you hit in this notebook and how you found it. Maybe it was the `unbiased=False` in LayerNorm, the transpose in `dW2`, the missing `1/n` in the manual backprop, or a gain that exploded the gradients. What was the symptom? What told you where to look? Which diagnostic (loss-at-init, the gradient histogram, the update:data ratio, a shape error) actually pointed at the cause? Nobody grades this. Writing it is the point: the engineers who debug training fast are the ones who have built an internal table of symptom to cause, and you build that table by writing down the mapping each time you hit it.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01340e45",
   "metadata": {},
   "source": [
    "*Your reflection here.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ff4e4fd",
   "metadata": {},
   "source": [
    "## Going further\n",
    "\n",
    "- Karpathy, *A Recipe for Training Neural Networks* (karpathy.github.io) — the 6000 words this chapter's debugging checklist compresses. Re-read it every six months.\n",
    "- Karpathy, *makemore* part 3 (activations and gradients) and part 4 (manual backprop) — the source of the four diagnostics and the `cmp()` capstone you just did.\n",
    "- ARENA 3.0, *chapter 0 part 3 — optimization* — deeper SGD/Adam math, weight-decay parameter groups, and a small Optuna study.\n",
    "- He et al. 2015, *Delving Deep into Rectifiers* — the Kaiming init paper; the variance derivation in Part 2 is theirs.\n",
    "- Ba et al. 2016, *Layer Normalization* and Zhang and Sennrich 2019, *Root Mean Square Layer Normalization* — the two norms you built.\n",
    "- Ioffe and Szegedy 2015, *Batch Normalization* — and the train-vs-eval running-stats mechanism you tripped on purpose.\n",
    "\n",
    "## What this enables\n",
    "\n",
    "- **Ch 12 — CNNs**: the same stack (Kaiming init, normalization, residuals, clipping) is the substrate of every modern CNN; ResNet *is* residual blocks plus BatchNorm.\n",
    "- **Ch 14-15 — Attention and Transformers**: \"warmup or it diverges\" stops being abstract. Pre-norm + scaled init + AdamW + warmup-cosine + clipping is the recipe, and every piece is from this chapter.\n",
    "- **Ch 19 — RL + RLHF**: PPO is a deep net trained with a strange loss under tight stability constraints; with this chapter those constraints read as the standard stack with an asterisk.\n",
    "\n",
    "We trained a tiny char-MLP here (it overfits the small name subset, hitting ~1.2 train / ~2.7 dev). On the full `names.txt`, this architecture lands near ~2.1 dev; Ch 15's transformer reaches well below that on the same kind of data, by adding attention on top of exactly this training machinery.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a48f56c",
   "metadata": {},
   "source": [
    "---\n",
    "*Built top-to-bottom. If every check above printed `[ ok ]`, you've reproduced the chapter. Total running time and verified-on stamp written by CI.*\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "obvix-nb",
   "language": "python",
   "name": "obvix-nb"
  },
  "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.10.12"
  },
  "obvix": {
   "title": "Ch 11 — Training Deep Networks"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
