The LLM StackFrom Silicon to Agents
Interactive tools

Tools & calculators

Live calculators and visualizers from across the book — FLOP/memory/cost budgets, the Chinchilla-optimal split, KV-cache sizing, and more. Each also appears inline in the chapter that teaches it.

Self-check: Adam optimizer-state memory
Mixed-precision Adam keeps an fp32 master copy + two fp32 moments (m, v) = 12 bytes/parameter of optimizer state. For a 1.0-billion-parameter model, how much? (answer in GB, 1 GB = 109 bytes)
GB
Show working
12 bytes × 109 params = 1.2×1010 bytes = 12 GB — on top of the weights and gradients, which is why optimizer state dominates training memory and motivates 8-bit optimizers / sharding.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.

Self-check: Chinchilla-optimal tokens

↳ in “Scaling Laws: Kaplan, Chinchilla & Beyond”
Self-check: Chinchilla-optimal tokens
The Chinchilla compute-optimal recipe uses roughly 20 tokens per parameter. About how many training tokens should a 1.4-billion-parameter model see? (answer in billions of tokens)
B tokens
Show working
20 tokens/param × 1.4×109 params = 2.8×1010 = 28 billion tokens. (Modern small models deliberately over-train far past this — see the capstone.)
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.

Self-check: embedding-table size

↳ in “Embeddings & The Input Pipeline”
Self-check: embedding-table size
The token-embedding matrix is vocab × d_model. How many parameters for vocab = 128000, d_model = 4096? (answer in millions)
M params
Show working
128000 × 4096 = 524,288,000 ≈ 524M parameters — untied, the model pays for this table twice (input + output), which is why small models tie the embeddings.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.

Self-check: the 6ND training-FLOP rule

↳ in “Scaling Laws: Kaplan, Chinchilla & Beyond”
Self-check: the 6ND training-FLOP rule
Using C = 6ND, estimate the training FLOPs to train N = 7×109 parameters on D = 2×1012 tokens. (answer as the coefficient a, where C = a × 1022)
×10^22 FLOPs
Show working
C = 6 × (7×109) × (2×1012) = 6 × 14×1021 = 8.4×1022 FLOPs.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Self-check: KV-cache size
One sequence's KV cache = 2 × L × n_kv × head_dim × seq × bytes (the leading 2 is K and V). For L=32, n_kv=8, head_dim=128, seq=8192, bf16 (2 bytes), how big is it? (answer in GiB, 1 GiB = 230 bytes)
GiB
Show working
2 × 32 × 8 × 128 × 8192 × 2 bytes = 1,073,741,824 bytes = 230 = exactly 1 GiB — and that is for a single sequence, which is why long-context serving is KV-bound.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Self-check: LoRA parameter count
LoRA adds A (r × d) and B (d × r) to a d × d layer, so it trains 2 r d parameters. How many for d = 4096, r = 16? (answer in thousands)
K params
Show working
2 × 16 × 4096 = 131,072 ≈ 131K parameters — versus 16.8M for the full d×d matrix, a ~128× reduction in trainable weights.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Self-check: Model FLOPs Utilization
MFU = 6 × N × (tokens/s) / peak_FLOPs. A 7×109-param model trains at 3000 tokens/s on an A100 (peak 312×1012 bf16 FLOP/s). What MFU? (answer as a percentage)
%
Show working
6 × 7×109 × 3000 = 1.26×1014 FLOP/s of useful work; ÷ 3.12×1014 = ≈40% — a healthy single-node MFU.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Self-check: SwiGLU MLP parameters
A SwiGLU feed-forward block has three weight matrices (gate, up, down), each of size d × intermediate. How many parameters for d = 4096, intermediate = 11008? (answer in millions)
M params
Show working
3 × d × intermediate = 3 × 4096 × 11008 = 135,266,304 ≈ 135M parameters (the MLP is ~2/3 of a transformer block's params).
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.

Self-check: perplexity from cross-entropy

↳ in “Probability, Statistics & Information Theory”
Self-check: perplexity from cross-entropy
A model reaches a cross-entropy loss of 2.0 nats/token on held-out text. Its perplexity = exp(cross-entropy). What is it? (2 decimals)
perplexity
Show working
PPL = e2.0 = 7.39 — the model is about as uncertain as an even choice among ~7.4 tokens at each step. (If the loss were in bits, you would use 2loss instead.)
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Self-check: the roofline ridge point
A kernel becomes compute-bound above the ridge point = peak_FLOPs / memory_bandwidth. For an A100 (312×1012 bf16 FLOP/s, 2.0×1012 B/s), what is it? (answer in FLOP/byte)
FLOP/byte
Show working
312×1012 / 2.0×1012 = 156 FLOP/byte. A GEMM must reuse each loaded byte ~156× to saturate the tensor cores; decode attention reuses each KV byte ~once, so it is permanently memory-bound.
Self-check: type a number and press Check (or Enter). Answers use a small tolerance, so round sensibly.
Attention mask visualizer
allowed masked
Backprop on a computation graph
One neuron, one squared error: p = w·x, s = p + b, h = act(s), e = h − y, L = e². Set the leaves, pick an activation, then walk the gradient backward one edge at a time. Each edge carries a local derivative; the chain rule is just the running product along the path.
Backward trace — one multiply per edge
Gradient check
leafclosed formvaluebackpropfinite diff.
Takeaway. Backprop never writes down a global formula for dL/dw. It seeds dL/dL = 1 at the loss and then, at every edge, multiplies the gradient arriving from downstream by that edge's local derivative — the only thing each node has to know. Because every leaf here is reached by exactly one path, the products collapse to dL/dw = 2e a′(s) x, dL/dx = 2e a′(s) w, dL/db = 2e a′(s), dL/dy = −2e; when a node feeds several consumers the arriving gradients are summed instead, which is exactly why PyTorch accumulates into .grad and why you must call zero_grad(). Choose ReLU and push s below zero to watch the gate close: a′(s) = 0 annihilates every gradient upstream of it, while dL/dy — which never crosses that edge — survives. The finite-difference column is the same check torch.autograd.gradcheck runs.

Constitutional AI / RLAIF: critique and revise

↳ in “Constitutional AI, RLAIF & Self-Improvement”
Constitutional AI / RLAIF: critique and revise
The two-stage CAI recipe, run end to end with no human labels anywhere. Stage 1 (SL-CAI) steps through a scripted red-team transcript: harmful draft → critique against one sampled constitutional principle → revision, repeated for up to 4 rounds. Stage 2 (RLAIF) is live math on whatever two responses you pick: an AI judge emits a verdict distribution over the tokens A/B, which is turned into a soft preference label p by a 2-way softmax with max-subtraction, debiased by scoring both orderings, and then used as the target of the Bradley–Terry reward-model loss. The response text is scripted; every number below is computed from the rules shown in full.
round 0 of 4
The two-stage recipe (stage 2 is live; the highlighted stage-1 box is where the transcript is)
Stage 1 — supervised critique & revision (SL-CAI)
Constitution violation v[p] of the current response
current response initial draft critique threshold 0.20
Stage 2 — the AI judge's verdict distribution (no human ever sees this pair)
Stage 2 — the soft label trains the preference model (Bradley-Terry)

Constrained decoding: FSM-guided JSON generation

↳ in “Structured & Constrained Generation”
Constrained decoding: FSM-guided JSON generation
A schema is compiled into a character-level DFA, then lifted to the token level: for the current state, every vocabulary token is simulated character by character, and any token that would kill the machine is masked to −∞ before the softmax. It opens on a run where the mask vetoes the toy model's top choice at steps 4, 11 and 13 — press Next step to walk into them and watch how little probability mass survives. Then flip to unconstrained to see what the same model, at the same seed, emits with the mask switched off.
Emitted tokens (the token at step t is boxed; later tokens are faded)
Next-token distribution at step t praw = softmax(l/T) pmasked (allowed) vetoed by the FSM
Guarantee check: re-decode 40 seeds at these settings, then parse and validate each output
What is exact here. The schema is compiled into a real character-level DFA (states = start, expect-key, in-key, expect-colon, expect-value, in-value, after-value, done, product-ed with the field index, the position inside the key literal, and the string/number length counters). The token-level mask is the genuine Outlines-style index: for every token the DFA is stepped one character at a time from the current state, and the token is allowed if and only if it never reaches the dead state. Masking is l~v = lv if allowed else −∞, followed by a max-subtracted softmax, so the surviving probabilities are exactly pv = praw,v / Σu allowed praw,u — look at "mass kept" in the readout: that denominator is the whole story. The logits themselves come from a small hand-written scorer plus seeded Gaussian noise (a stand-in for a real network); it is deterministic, and its only job is to be JSON-ish but fallible. The guarantee check enforces exactly the language the DFA accepts, bounds included — key order, value types, string lengths and digit counts — so "40 / 40" is the grammar's guarantee and not a weaker one.

Three things worth pausing on. (1) Tokens, not characters. With name, na and me all in the vocabulary, the legal set changes with every partial match: at the start of the key both name and na are legal and me is not; one token later it is the other way round. That is why the index must simulate multi-character tokens instead of checking single characters. (2) Alive, not accepting. A token is legal if it leaves the machine in any live state; only EOS requires an accepting state, which is exactly why constrained output can never be truncated mid-object. (3) Masking is local, not conditioning. The mask renormalises over legal next tokens, which is not the same as sampling from the model conditioned on the whole string being valid: mass the model wanted to spend on an illegal continuation gets redistributed over its legal siblings, so constrained sampling is a genuine distribution shift, not a filter. Watch the guarantee bar: constrained decoding is valid 40/40 by construction, while the same model at the same seeds, unconstrained, loses runs to an unquoted value ({"name":Oslo,), a hallucinated key ("adok"), a colon swallowed into a key (": null"), a boolean slot filled with a number, or an integer with more digits than the schema allows — and the failure rate climbs with σ and T.

And the catch. Validity is not correctness. Because the mask only ever asks "can the machine survive this token?", the sampler is free to spend the redistributed mass on rubbish that happens to be well-formed: set the seed to 20 and the constrained run happily emits {"name":", ","age": 12, ...} — perfectly schema-valid, perfectly useless. A grammar buys you syntax; it never buys you semantics, and a model that had to be dragged into the format is telling you something about the prompt. In production the index is precomputed once per grammar (|Q| × |V| bits, well under a megabyte), so the per-step cost is a bitmask lookup that XGrammar overlaps with the model forward pass.

Continuous (iteration-level) batching

↳ in “Continuous Batching & Request Scheduling”
Continuous (iteration-level) batching
A real iteration-level scheduler, simulated step by step against the same arrival stream as a static/dynamic (request-level) baseline. Every iteration the scheduler re-derives the running set: it reserves one decode token + KV block per running request (preempting LIFO when blocks run out), backfills the remaining token budget with prefill chunks, runs one fused pass, then retires finished requests immediately so a waiting request takes the freed slot on the very next iteration. Watch a bar end mid-batch and the slot refill one column later — that is the whole idea. Iteration time is the chapter’s model τ = τ0 + β · (tokens in this pass), with τ0 = 20 ms and β = 0.2 ms/token.
prefill tokens decode (1 token) prompt padding (static) dead slot: finished but locked stalled: no token budget left this pass idle slot slot handoff (new occupant) EOS: slot released preemption
Running batch right now (one bar per slot; each iteration advances one token)
KV cache & waiting queue
Scheduler event log (most recent first)
Both systems see the identical seeded arrival stream and the same device model, so every difference is scheduling. Continuous batching re-derives the running set each forward pass, so a slot freed by an EOS is refilled on the next iteration and the active batch stays pinned near max_num_seqs. Static/request-level batching (given the benefit of a 1 s dynamic-batching window to fill its batch) freezes membership: short requests turn into dead slots that still cost a row in every GEMM until the longest request in the batch finishes, and prompts are padded to the batch maximum — the three leaks, visible as hatching. Turn chunked prefill off (try the RAG workload) and a long prompt seizes a whole prefill-only iteration: τ = 20 + 0.2×2400 = 500 ms during which every decode stalls, drawn as warn-hatched cells. Turn it on and the same prompt is spread over ⌈prompt / leftover budget⌉ iterations that ride along with the decodes, and the longest forward pass row collapses to the budget cap τ0 + β · max_num_batched_tokens — a bound the widget holds exactly. That bound is the point: it is a hard ceiling on inter-token latency. Whether it also costs raw throughput depends on the mix (compare the two output tok/s numbers with the box on and off): chopping prefill into more, thinner passes pays τ0 more times, but keeping decodes alive alongside it raises the mean active batch, and on chat-shaped traffic the second effect usually wins. That trade — ITL ceiling versus τ0 amortization — is the knob Sarathi-Serve turns. Shrink KV blocks until the scheduler cannot reserve the next block for a running request: it evicts the newest request (LIFO), whose materialized KV must be recomputed on resume — that redundant work is what the “useful-compute share” row charges continuous batching for, and it is why you keep headroom instead of admitting to the last block. Caveats: τ0 and β are a linear stand-in for a real kernel timing curve, attention cost grows with context length (ignored here), and the mock model emits a fixed number of tokens per request instead of a real EOS.

Data mixing: temperature sampling, domain weights & the epoch budget

↳ in “Data Mixing, Domain Weighting & Curriculum”
Data mixing: temperature sampling, domain weights & the epoch budget
Five cleaned, deduplicated pools (the chapter’s worked example: web 3 T, multilingual 400 B, code 250 B, books 80 B, math 30 B). Temperature sampling sets the mixture from the pool sizes alone: wi ∝ niα with α = 1/T. At α = 1 (T = 1) you sample proportionally — web eats 80% of every batch. Drive α toward 0 (T → ∞) and the mixture flattens toward uniform, which is just another way of saying you start re-reading the small domains. Watch the epochs column: that is the bill temperature sends you. Then switch on the UniMax epoch cap to pay it differently.
Mixture weight vs. natural share  ·  natural share ni/∑n
domain
sampling weight wi
wi / natural
epochs ei
Epochs per domain as α sweeps uniform → proportional
Data-parallel training: ring all-reduce
Topology & the transfer just executed
partially reduced chunk fully summed over all K arrow label = payload (cn = one chunk, S = whole gradient)
Live accounting
Ring vs. naive, one full all-reduce
Chunk ledger — row = GPU, column = chunk, ticks = which ranks' gradients are already summed in
Bytes serialized on the busiest link for one all-reduce, in multiples of the gradient size S

Decoding strategies: greedy, beam, temperature, top-k, top-p

↳ in “Sampling Strategies & Decoding Algorithms”
Decoding strategies: greedy, beam, temperature, top-k, top-p
A toy vocabulary of 5 tokens (A–E). Every context has a fixed, seeded conditional distribution, so the same tree gets walked, pruned and sampled differently by each strategy. Each edge is labelled with the probability that actually governs the decision at that node; grey dashed branches are the ones not taken, cut marks tokens that top-k / top-p forced to exactly zero, and in beam mode Σ is the cumulative logP that beam search prunes on.
draw #0
chosen sequence kept / still alive not taken / pruned
Chosen sequence
-
Total logP (T=1 model)
-
Sequence P
-
Greedy logP (gap)
-

Diffusion language models: iterative denoising

↳ in “Diffusion & Non-Autoregressive Language Models”
Diffusion language models: iterative denoising
An autoregressive model emits one token per serial forward pass, strictly left to right. A masked-diffusion LM starts from an all-[MASK] block, predicts every masked position in parallel each pass, and commits only the most confident ones (low-confidence remasking) — so the sentence fills in out of order and finishes in T passes instead of L. Scrub the serial step to watch both machines run on the same clock, and move T to trade function evaluations against quality.
Autoregressive (causal) 1 token per forward pass
Masked diffusion (bidirectional) many tokens per forward pass
given / clamped [MASK] committed, matches reference committed, differs committed on this pass will be unmasked next pass
Match rate vs serial forward passes (NFE) — the steps/quality knob
confidence random left to right autoregressive reference
The loop is exactly the chapter's sampler: start all-[MASK], run one full bidirectional pass, softmax the logits at every masked position (with max-subtraction), and unmask the top-n_keep by confidence, where n_keep = (#masked) − round(n_gen × (1 − (k+1)/T)) follows the linear schedule. Unmasking is one-way: a committed token is never revised, which is why an early mistake is permanent. Serial depth is what diffusion buys: NFE = T regardless of length, versus L for AR. But each diffusion pass is more expensive — every position is recomputed, so there is no KV cache and attention costs O(L2) per step instead of O(L) per AR decode step; L/T is an upper bound on wall-clock speedup, not the speedup itself. Why the order matters: within one step the model factorizes p(x0 | xt) as a product of independent per-position predictions, which is wrong for language — set T = 1 and watch half the block come out individually plausible but jointly wrong. Committing only the peaked positions and re-running defers the flat ones until their neighbours exist — that is the repair, and it is why the confidence curve dominates random and left-to-right. Push the order to left-to-right at T = n_gen (with nothing clamped on the right) and masked diffusion is an autoregressive model: same match rate, same NFE, no win. Infilling is free here because prefix and suffix are clamped from step 0, so masked positions have context on both sides immediately; AR has to regenerate everything to the right of the edit and nothing constrains it to land on the required suffix.

What is real and what is scripted. The softmax, the linear schedule, the top-n_keep selection, the one-way commit and the NFE accounting are the real algorithm. The denoiser is a seeded toy: each position has 4 candidates, the reference token scores 3.0·a + 3.4·(0.6·left + 0.4·right) where a is its base predictability and left/right are the fractions of its exponentially-weighted neighbourhood already committed, while the three rivals sit on a fixed plateau scaled by (1 − a). That is calibrated on purpose: with no context every wrong prediction has p ≤ 0.39 and every right one p ≥ 0.51, so confidence really does identify the positions worth committing — the assumption the whole remasking rule rests on. It also makes the AR path a perfect oracle by construction, so its reference line sits at 100%; the question this chart answers is how few serial passes diffusion needs to match AR, not whether AR can be beaten.

Knowledge distillation: soft targets, temperature, and the T² factor

↳ in “Distillation, Model Compression & Knowledge Transfer”
Knowledge distillation: soft targets, temperature, and the T² factor
true label confusable ("dark knowledge") label teacher's hard (T=1) probability, for reference uniform 1/K = 0.100 (the T→∞ limit)
Soft targets carry more information than hard labels. A one-hot label says only "class 7, probability 1, everything else 0." The teacher's full distribution instead says "class 7, but also a bit like class 1" — that relative ranking of the wrong classes is Hinton et al.'s "dark knowledge," and it is exactly what a hard label throws away. Raising T divides every logit before the softmax, which shrinks the gaps between logits and pulls probability mass off the top class onto its runners-up: watch the confusable digit's bar grow, and watch the thin reference tick (its T=1 height) get left behind. Push T far enough and every bar collapses onto the dashed 1/K line — at that point the ordering information is gone too, which is why τ ∈ [2, 5] is the usual range. The student is trained on a blend: α weights ordinary cross-entropy against the true label (computed at T=1), (1−α) weights the KD term. Because that term is T² · KL(qT ‖ pT) rather than raw KL, its gradient is T·(pT − qT): the extra T cancels the 1/T that differentiating a T-scaled softmax introduces, and since pT − qT itself shrinks like 1/T, the gradient norm stays roughly flat instead of dying like 1/T². The last stat tile shows both norms — slide T from 1 to 8 and compare. Click "Auto-train" and watch the student's bars migrate onto the teacher's, including onto the small dark-knowledge bars, while the loss curve shrinks. (With α > 0 the loss plateaus above zero: the hard label pulls toward one-hot while the teacher pulls toward its soft target, and the student settles at the compromise. The curve also rescales whenever you move T or α, because that changes the loss function itself.)
Differential privacy: DP-SGD clip and noise
clipped gradient (kept) part removed by clipping rare "canary" example mean of raw gradients mean after clipping Gaussian noise / noisy update
Privacy budget ε — composition over steps, and the σ dial (RDP accountant, Poisson subsampling)
Canary memorization: secret-sharer exposure (of 26.6 bits, one secret in 108)
Two operations, one guarantee. DP-SGD replaces the batch-mean gradient with ĝ = (1/B)(Σii + N(0, σ2C2I)), where i = gi · min(1, C/‖gi2). Clipping is what makes the guarantee possible: after it, adding or removing any single example changes the summed gradient by at most C, so the mechanism's L2 sensitivity is exactly C — a number you control instead of a number the data chooses. Noise is what buys the guarantee: Gaussian noise of standard deviation σC on the sum hides that one-example change. Note what the picture shows when you drag the canary slider with a large C: one weird example visibly drags the mean. Shrink C below its norm and the mean locks in place — that is bounded sensitivity, drawn. The accountant. ε here is not a fudge factor: it is the Renyi-DP bound for the Poisson-subsampled Gaussian mechanism (Mironov, Talwar & Zhang 2019) at integer orders α, εα = (1/(α−1)) log Σk C(α,k)(1−q)α−kqkek(k−1)/2σ2, composed over t steps (RDP adds) and converted to (ε,δ) with the standard bound, minimized over α. It is an upper bound; a PRV accountant is slightly tighter. Sampling rate q = B/N assumes Poisson sampling — shuffle-and-chunk batching silently invalidates it. Three things to discover. (1) Once C is smaller than the gradients, C cancels: signal and noise both scale with C, so C sets the step size, and σ alone sets the privacy. That is why tuning C is a learning-rate problem, not a privacy problem. (2) The noise is isotropic in all d trained parameters, so its norm grows like σC√d/B while the signal is capped at C — switch d from a LoRA adapter to a 7B full fine-tune and watch the signal-to-noise collapse. That single fact is why DP fine-tuning is done on few parameters. (3) Bigger batches at fixed epochs shrink the noise per step but raise q and cut t, so ε goes up: privacy is not free compute. The canary. Exposure here uses an illustrative extraction model, not a simulated attack: over t steps a canary present in r copies is sampled rqt times, so its imprint grows like rqt · min(C,‖gcanary‖) while the injected noise in that direction is a random walk of size σC√t, giving a discrimination score z = rq√t · min(C,‖gcanary‖)/(σC); the canary's rank among 108 candidate secrets is then 108Φ(−z), and exposure is log2(108) − log2(rank), the Secret Sharer definition used earlier in this chapter. Two things fall out. The "clipping only" bar sits at full exposure: bounding sensitivity without noise hides nothing, so clipping alone is not a privacy mechanism. And a secret that appears once is buried at any reasonable σ, while dragging r up to 100 lights it back up — because DP-SGD's guarantee is per example, and r duplicates degrade it to group privacy of about rε. Deduplication is not an alternative to DP; it is what makes the ε you report mean what you think it means.

DPO explorer: four log-probs, one loss, one gradient step

↳ in “Direct Preference Optimization & Its Variants”
DPO explorer: four log-probs, one loss, one gradient step
1. The four numbers. Higher (less negative) = more probable. Dashed line = reference, frozen; solid bar = policy, the only thing that moves.
2. What DPO actually optimizes. The two log-ratios log(πθref) — the implicit rewards up to the factor β. Only their difference m enters the loss.
3. The loss. −log σ(β·m) against the raw margin m. Dashed: β=0.02 (loose trust region) and β=1.0 (tight). Solid: your β. Faded dots are the margins visited by your gradient steps — each step slides the point right, down the curve.
DPO needs exactly these four numbers per pair — two policy log-probs, two frozen reference log-probs — and turns preference learning into ordinary binary classification: is the implicit reward r = β·log(πθref) higher for the chosen response than for the rejected one? Only the difference of the two log-ratios matters, so anything that shifts both equally (a longer prompt, a globally sharper model) cancels out. The gradient's weight σ(−Δ) is large exactly when the model still gets the pair wrong, so training automatically focuses on unsolved pairs and fades out solved ones — run the 10-step button and watch the point slide right while the weight decays: that is the self-curriculum. β is the trust-region dial: push it down and the sigmoid needs a much larger log-ratio swing before the loss (and the gradient weight) relaxes, so the policy is free to drift far from πref — sharper fit, weaker KL control. Push β up and a tiny swing already saturates the curve, so the policy barely has to move — strong KL control, potentially underfit preferences. Two honest caveats about this toy: the step here moves the two log-probs as free scalars, while in a real model they share weights and a softmax normalizer, so raising log πθ(yw) usually drags log πθ(yl) along — which is why real DPO runs often see both log-probs fall while the margin still grows; and nothing here caps the margin, which is exactly the unbounded-drift failure IPO and the margin-based variants were invented to fix. Unlike PPO-RLHF, none of this needs a trained reward model, sampled rollouts, or a value network — just a sigmoid over four log-probs, backed by the same reward-minus-β-KL objective PPO optimizes explicitly.

Embeddings & cosine similarity in 2D

↳ in “Embeddings & The Input Pipeline”
Embeddings & cosine similarity in 2D
Drag a labelled point, or click it and use the arrow keys (Shift = bigger steps).
Each point is a 2-D stand-in for a row of the embedding matrix WE: a learned direction and magnitude, not a lookup key. Cosine similarity is cos θ = (A·B) / (|A|·|B|) – the dot product with both lengths divided out, so it depends only on the angle. Drag the Scale A by k slider: A slides along its own ray, so A·B and |A−B| both change while cos θ does not move at all. That invariance is exactly why embeddings are compared by cosine – a token whose vector happens to have a large norm (frequent tokens usually do) should not automatically look "more similar" to everything. The construction overlay shows the two unit vectors on the circle |v| = 1 (cos θ is literally their dot product) and the projection of B onto A, whose signed length is |B| cos θ, so that A·B = |A| × (|B| cos θ). The analogy button draws king − man + woman as vector arithmetic: the offset from man to king transplanted onto woman, with the nearest stored embedding to the landing point highlighted – and, because the two metrics rank neighbours differently, cosine and Euclidean answers are reported separately.

FlashAttention tiling & online softmax: stream one B

↳ in “FlashAttention I: IO-Awareness & The Online Softmax”
FlashAttention tiling & online softmax: stream one Br×Bc tile at a time
Q (S×d) — outer loop: one Br-row block is held in SRAM
K (S×d) — inner loop: Bc rows streamed in
V (S×d) — same blocking as K
tile just computed next tile already streamed & discarded
Score tile just computed: sij = qi·kj / √d, only for i in the query block and j in the KV block (Br×Bc)
The full S×S score matrix — never materialized; only one tile is resident at a time
The takeaway: FlashAttention never forms the S×S score matrix. It walks tiles of it, and for each tile updates only three per-row statistics that live in registers/SRAM: running max mi, running denominator i, and unnormalized accumulator Oi. Before adding a tile's contribution it rescales the old state by exp(mold − mnew) — that one multiply re-bases every earlier term onto the new max, so the recurrence is exact, not an approximation: watch the max|diff| column fall to ~1e-16 the moment a row's last KV block lands. The single divide by ℓi happens once, at the end. Raise Br/Bc and the SRAM tile grows as BrBc; lower Bc and K/V get re-read more times (more HBM traffic) — that tension is the entire kernel-design tradeoff, and the naive path stays stuck at O(S²) either way.

MHA vs MQA vs GQA vs MLA: KV-head sharing and cache size

↳ in “Multi-Head Attention, MQA, GQA & MLA”
MHA vs MQA vs GQA vs MLA: KV-head sharing and cache size
Presets
KV cache per token, per layer — bar length and ratio vs MHA
GQA interpolates between MHA and MQA. Group size s = 1 gives every query its own KV head (MHA: best quality, biggest cache); s = h collapses all queries onto one shared KV head (MQA: smallest cache among the head-sharing schemes, largest quality risk); any divisor of h in between trades quality for cache linearly in the KV-head count g = h / s, since the cache is 2 · g · d_h elements per token per layer (the 2 is K and V). MLA takes a different axis: instead of sharing whole KV heads it caches one low-rank latent vector per token (d_c numbers, no factor of 2) and reconstructs full per-head keys/values from it on the fly – so it can beat GQA's cache while keeping every head distinct. Real MLA also caches a single shared decoupled-RoPE key of d_r = 64 (toggle it above); the text's simplified forward pass is the content-only version. Note the “total” column: the same number is both the memory one sequence occupies and the bytes that must be streamed from HBM on every decode step – which is why shrinking the cache speeds up decoding, not just fits more of it.

Activation checkpointing: the memory–recompute tradeoff

↳ in “Memory-Efficient Training: Checkpointing, Offloading & LoRA Math”
Activation checkpointing: the memory–recompute tradeoff
stored checkpoint (segment input, 2BTd bytes) recomputed in backward (nothing stored) live segment during backward (peak) whole block kept in HBM (checkpointing off)
model state 16P (weights + grads + Adam) activations fp32 logits B×T×V
Split the stack into k segments, store only each segment's input, and recompute the rest during backward: M(k) ≈ k·(2BTd) + (L/k)·alayer — stored checkpoints grow with k, the one segment brought back to life shrinks with k. (Exactly, since backward pops segments last→first and only checkpoints 1…i survive while segment i is re-materialized, the peak is maxi [ i·2BTd + sizei·alayer ] — which is what the chart plots, hence the sawtooth.) The textbook answer k = √L assumes the two tensors cost the same; in a real block they do not. Per Korthikanti et al. the stored bytes per layer are alayer = 34·B·T·d (+ 5·a·B·T² when the T×T scores are materialized) — 17× the bf16 block input — so the optimum sits at k* ≈ √(17L), far to the right of √L. For any stack shallower than 17 blocks that is past L, i.e. k* = L exactly; at L = 32 it is 24 of 32, and even at L = 80 (≈37) the curve around the minimum is shallow enough that checkpointing every block costs only tens of percent more activation memory than the true optimum. That is why frameworks expose “checkpoint every block” and stop thinking about it. (The 34 counts every tensor a block stashes, dropout masks included; the chapter’s back-of-envelope 12 elements × 2 bytes = 24 B/token/layer counts only the big ones, which is why it says ≈6.4 GB where this widget says 8.5 GiB for the same 7B config — same story, one constant apart.) The compute price is one extra forward pass, i.e. +33% on a fwd+bwd step, for every k > 0 — it does not grow with k, so there is no reason to stop short of the memory optimum. Note also what checkpointing never touches: the 16P of weights, gradients and Adam state, and the B×T×V fp32 logits of the loss head — which is why a small model with a big vocabulary OOMs on the head, not on the blocks. Everything here is closed form: no randomness, same inputs → same numbers.

Gradient descent playground: optimizers on an ill-conditioned bowl

↳ in “Calculus, Optimization & Convexity”
Gradient descent playground: optimizers on an ill-conditioned bowl
idle
loss surface — click or drag the ring to move the start point
θ1 θ2
loss vs. step (log scale)
The surface is the exact convex bowl L(θ) = ½(λ1θ12 + λ2θ22) with λ1 = 1/√κ and λ2 = √κ, so the Hessian is exactly diag(λ1, λ2), the condition number is κ = λ21 by construction, and the eigenvalues keep geometric mean 1 for every κ. Each update is the real rule applied to the analytic gradient ∇L = (λ1θ1, λ2θ2) plus optional noise σ·N(0,1) per coordinate — SGD: θ ← θ − ηĝ; momentum: v ← βv − ηĝ, θ ← θ + v; Adam: bias-corrected first/second moments with β2 = 0.999, ε = 1e−8. Three things worth doing. (1) With σ = 0, GD on a quadratic is a linear recursion, so its per-step contraction is exactly ρ = maxi|1 − ηλi| (heavy ball: the larger root modulus of z2 − (1+β−ηλ)z + β, which is √β whenever the roots are complex). The dashed line on the loss chart is that prediction, L0ρ2t, and the measured curve settles onto its slope (with momentum it oscillates around it, because the roots are complex). Divergence is not at η = 1/λmax — that is the guaranteed-descent bound; the real ceiling is η = 2/λmax (2(1+β)/λmax with momentum), and just below it you get the classic zig-zag rather than an explosion. (2) The gradient ratio |g2/g1| = κ·|θ21| is enormous, and SGD's step ratio equals it exactly (the step is proportional to the gradient) — that is the zig-zag. Switch to Adam and the step ratio sits at ≈1 no matter how large the gradient ratio: that is what "Adam preconditions the ill-conditioned directions" means in numbers. (3) Raise σ: SGD stops converging and hovers at the stationary noise floor E[L] = ½∑i λiη2σ2/(1 − (1−ηλi)2) ≈ ησ2d/4 for small η — a floor proportional to η, which is exactly why the learning rate has to decay. The noise draws come from a seeded PRNG, so Reset replays a bit-identical run.

GRPO: group-relative advantages

↳ in “GRPO, RLOO & Critic-Free RL”
GRPO: group-relative advantages
The group mean is a Monte-Carlo estimate of the same baseline that PPO trains a whole separate critic network to predict — GRPO gets it for free from the G samples you already generated. Subtracting a baseline removes the common-mode reward level, so gradients point toward relatively better responses; dividing by the group's own std (GRPO) further rescales, so an easy prompt with big reward swings and a hard prompt with small ones contribute comparably-sized updates. Note that RLOO is not quite “mean-only”: its leave-one-out baseline gives A_i = (G/(G−1))·(R_i − mean), i.e. Dr. GRPO's advantage rescaled by a constant — switch between them and watch only the axis numbers change, never the shape. Every mean-based option makes ΣAi = 0 exactly; “no baseline” does not, which is why it pushes every response up. The failure mode to remember: when every response in the group earns (nearly) the same reward — all correct, or all wrong — the group carries no information about which completion was better, every advantage collapses toward 0, and the rollout's gradient vanishes. That is the empirical case for dynamic sampling (DAPO): detect zero-variance groups and resample instead of wasting compute on them. Pick the worked example reward source to check the widget against the ±0.992 / −0.811 / −1.172 numbers derived in the text.
↳ in “Vector Databases & Approximate Nearest Neighbor Search”
An induction head, mechanistically
Edit the sequence below (or repeat it, as in the standard [X X] probe) and watch a two-head, two-layer circuit predict the next token by copying from context. Every attention weight is a real softmax(scores) under a causal mask, computed live — nothing is hand-drawn, and nothing is random.
Animation stage
Stage 1 (gray arcs below): at every position i > 0 the previous-token head attends to position i−1 and copies that token into the residual stream at i — it writes “the token before me was X” everywhere. Stage 2 (colored arcs above): the induction head at the final query position reads that written signal as its key, so it lands on the position right after an earlier copy of the current token, and copies that token forward.
prev-token head writes token[i−1] into position i induction-head attention (opacity ∝ softmax weight) induction-matching keys (solid) and the earlier occurrences that produced them (dashed)
Next-token distribution written by the induction head, assuming its OV circuit is a pure copy: p(t) = Σj αqj · 1[tokenj = t]. Attention mass is aggregated by token identity, so two different source positions carrying the same token add up.
Both heads’ full attention patterns (row = query position i, column = key position j; causal mask applied, so the upper triangle is greyed out; opacity = softmax weight). The induction pattern is computed for every query position, not just the final one — that is what makes it a reusable in-context copying circuit rather than a one-off trick.
Previous-token head
Induction head
This is a K-composition circuit, not a Q-composition one: the induction head’s key at position j is built from what the previous-token head wrote there (the identity of token j−1), while its query at the final position just reads the current token directly — no composition needed on the query side. The score spikes exactly where token[j−1] equals the query token, which is precisely “the position right after the earlier occurrence.” Three things to try. (1) Make the last token something that never appeared before: the induction row goes exactly uniform over the causal history — a fan of faint arcs, no confident copy — because the circuit has no signal to key off. (2) Drag sharpness to S = 0: matched and unmatched keys score identically and the head degenerates to uniform averaging; that is the softmax-saturation trade-off from the self-attention explorer, and it is why the QK circuit has to learn a large effective score gap. (3) Hit Repeat as [X X]: every position in the second copy becomes predictable from the first, the prefix-matching score jumps toward 1, and you are looking at exactly the synthetic probe used to find induction heads in a real model in the code below. This deterministic toy is the mechanism real induction heads implement (Olsson et al., 2022); its emergence during training is one of the most reproducible phase changes in mechanistic interpretability, and it is the textbook explanation for why in-context learning works at all.

Knowledge editing: locate the fact with causal tracing, then rewrite it with a rank-one update (ROME)

↳ in “Knowledge Editing & Machine Unlearning”
Knowledge editing: locate the fact with causal tracing, then rewrite it with a rank-one update (ROME)
A hand-built 10-layer toy transformer that stores four (subject, is-in, city) facts in its middle-layer MLPs. Everything below is computed live in your browser: real activation patching, a real Adam solve for v*, a real 10×10 matrix inverse, and the real closed-form ROME update Δ = (v* − W0k*)(C−1k*)T / ((C−1k*)Tk*) spliced into one weight matrix.
Step 1 — LOCATE: causal tracing on "The Space Needle is in downtown ___"
restored P(Seattle): none → full strongest site current edit site
Step 2 — EDIT: one rank-one update to that layer's Wdown
Step 3 — EVALUATE: reliability, generalization, locality
Locate. Corrupting the two subject-token embeddings destroys the fact. Restoring one cached clean activation at a time and re-running tells you where the fact is causally mediated: the MLP trace lights up at the last subject token in the middle layers (the memory band), and the attention trace lights up at the last token in the late layers (the head that copies the retrieved attribute to where it is read out). That two-site pattern is the empirical result ROME is built on, and here it is a genuine consequence of the circuit rather than a drawing. Widening the restore window recovers more of the effect because no single layer holds the whole association — which is exactly why MEMIT spreads its update over a band of layers. Edit. The MLP down-projection is read as a linear associative memory W k = v. We capture the key k* at the edit site, optimize the value v* by Adam so the model emits the new object, and then add the closed-form rank-one Δ = r (C−1k*)T / ((C−1k*)Tk*) with r = v* − W0k*. The grid makes the structure visible: every column of Δ is the same vector r, rescaled — that is what "rank one" means, and it is a consequence of the least-squares objective, not an assumption. The one knob that matters. Because the constraint W k* = v* is enforced exactly, reliability is free at any site with a live key and a causal path: P(o*) stays pinned near 1 for every λ. Generalization is free here too, for a reason worth naming — the paraphrase shares the entire prefix "The Space Needle", so at the last subject token it produces the identical key k* and inherits the edit automatically. (Real ROME is not so lucky: paraphrases that change the subject's surface form produce a different key, which is why generalization is a scored metric and not a theorem.) The only thing you can still lose is locality. Drag λ up (C → a multiple of I — "ROME without statistics") and watch the two numbers move in opposite directions: ‖Δ‖F goes down, because that limit is the minimum-norm solution, while the collateral mean ‖Δki2 on the layer's ordinary keys goes up, and at the default site two of the three unrelated landmarks start answering with the injected city. That is the whole argument for estimating C: the smallest edit is not the least damaging one. The least damaging one is the edit that C−1 steers into the directions the layer's real keys barely use. Site matters as much as λ. Three failures worth reproducing. Keep layer 4 but move the edit to the last token, "downtown": all three unrelated facts flip, because before the mover heads have run that position looks identical in every prompt — you are overwriting a key the whole corpus fires. Move to layer 0 instead: that MLP is not a memory, so its key is generic and whatever it writes has to survive nine more layers — the solve pays for it with a ‖Δ‖F over ten times the norm of the entire matrix it is editing, and a bystander flips anyway. Try layer 8 or 9 with the edit token back on "Needle": the mover heads have already read that position, so nothing downstream reads what this MLP writes, the v* objective is perfectly flat, and Δ = 0 exactly (v* never moves off W0k*, so the residual r is zero). The traced peak is the site where a small, specific update suffices. (The peak button follows whichever trace is on screen, so sending it to the attention peak lands you on the last token in a late layer — the failure above. Both peaks are real; only the MLP one is editable. That asymmetry is why ROME locates with attention and MLP traces but writes only to MLPs.) Two honest caveats. This W0 is only 16×10 and holds four facts, so it is a tiny target: ‖Δ‖F lands around two to three times ‖W0F even at the good site — read the ratio as "this layer is mostly the new fact now", not as a bug. In GPT-J's 4096×16384 down-projection the identical construction is a sub-percent perturbation, because the denominator is 67M real parameters instead of 160. What carries over unchanged is the rank: one, stored as d + dmlp numbers instead of d · dmlp, and trivially undone by subtracting it. And the edit installs an association, not a belief: nothing here propagates to "which country is it in?" — the ripple-effect failure, which no setting of λ or the edit site can fix.
KV-cache & context memory budgeter
KV bytes = 2 × layers × KV-heads × head-dim × tokens × bytes (the 2 is K and V). With grouped-query attention only the KV heads count — that's the whole memory point of GQA/MLA. Total tokens = context × concurrent requests.
KV cache growth during decoding
KV bytes = 2 × L × Hkv × dh × seq_len × bytes/elem — the leading 2 stores both K and V, and batch size is 1 here (one sequence). Only the number of KV heads enters the formula; query heads do not. That is exactly why GQA shrinks the cache: several query heads read the same stored KV head, so fewer bytes per token, at every layer, for the whole sequence. Prefill writes the entire prompt's KV in one parallel pass (the solid block appears at once); each decode step appends exactly one token's worth of KV, so the cache grows linearly forever — which is why a long, many-turn conversation can end up costing more memory than the model's own weights.

Extending context: RoPE frequency scaling (PI, NTK-aware, YaRN)

↳ in “Long-Context Pretraining & Context Extension”
Extending context: RoPE frequency scaling (PI, NTK-aware, YaRN)
Each line is the angle m · theta'_k that the scaled scheme feeds to dimension k, divided by the largest angle that dimension ever saw in training (L_train · theta_k, original base). RoPE's angle is linear in position, so every line is exactly straight, and the shaded band 0–1 is "angles this dimension actually trained on". None: every dimension ends at ratio = s, so nothing past L_train is in distribution. PI: every dimension ends at exactly 1 – safe, but adjacent tokens are now sx closer in angle (watch the local-resolution tile), which is why PI needs finetuning. NTK-aware: raising the base pins only the lowest-frequency dimension at 1 and leaves k=0 untouched, so the middle, under-rotated dimensions still overshoot. YaRN decides per dimension from its wavelength: leave it alone if it completes ≥ beta_fast rotations inside L_train (its angles already wrap the circle many times, so "new" angles are not new), interpolate it fully (theta_k/s) if it completes ≤ beta_slow – so every genuinely novel dimension lands at exactly 1 and local resolution stays 1.00x. The tile to watch is worst ratio among under-rotated dims: 8.00 for None, 1.75 for NTK-aware, 1.00 for PI and YaRN at the default 8x – but only YaRN gets there while keeping local resolution at 1.00x.

LoRA: adapting a weight with a low-rank update

↳ in “PEFT I: LoRA, QLoRA, DoRA & The Adapter Family”
LoRA: adapting a weight with a low-rank update
A frozen d × d projection W is never touched. LoRA trains two thin factors, B (d × r) and A (r × d), and adds their product back in: W' = W + (α/r)·BA. The picture is a fixed 32 × 32 stand-in so individual rows and columns stay visible; the real d you pick drives only the parameter and memory arithmetic, which is reported per adapted d × d projection (a 7B model has ~7 of these per layer × 32 layers). The right-hand square is a synthetic "target update" ΔW* whose singular values decay geometrically – a stylized version of the low effective rank that Aghajanyan et al. and the LoRA paper report for real fine-tuning deltas. The overlay is the exact best rank-r fit to it (truncated SVD, Eckart–Young), scaled by α/r.

Learning-rate schedules: warmup, cosine, WSD, inverse-sqrt

↳ in “Learning Rate Schedules, Warmup, Batch Size & Hyperparameters”
Learning-rate schedules: warmup, cosine, WSD, inverse-sqrt
Schedule A
Compare cosine against WSD at the same T: the mean LR stat shows WSD spends far more of the budget at full speed (cosine's average is only about ½(ηmaxmin), because the cosine is decaying from step Tw onward), yet Hägele et al. (2024) find they reach the same final loss at equal compute. That is the whole argument for WSD: the long stable phase commits to no total step count, and the short decay leg (shaded; typically ~10% of T) is where the characteristic extra loss drop lives. Set the min-LR ratio to 0 to reproduce MiniCPM's practice of decaying all the way down. Because the lowest-LR tokens are also the most influential, the decay window is the natural seam for a data-mixture switch — this is exactly why mid-training/annealing is scheduled inside the WSD decay phase in the capstone build (see Optimizer & Schedule: Muon + MuonClip and WSD and Mid-Training).

Mixed precision: fp16 gradient underflow and loss scaling

↳ in “Mixed Precision, bf16 & FP8 Training”
Mixed precision: fp16 gradient underflow and loss scaling
A model's gradient magnitudes span many powers of two, and every low-precision format can only represent a window of them. Pick a format, then slide the loss scale S = 2k: multiplying the loss by S multiplies every gradient by S, which is exactly a translation of the distribution along the log axis. Slide it too far left and gradients flush to zero; too far right and they overflow to inf and the step is thrown away. Every number below is computed from the real IEEE-style thresholds of the selected format (derived from its exponent/mantissa widths, round-to-nearest-even included) and the exact log-normal tail integrals — nothing is a lookup table.
Try:
Gradient magnitudes vs the format's representable window
One AMP step — where this state hurts
Dynamic loss scaling: the AIMD controller finding the ceiling
Serving MoE: expert parallelism & all-to-all
E experts are sharded over G GPUs (contiguous blocks). Every GPU holds its own decode tokens (attention-DP) at the top and its resident experts at the bottom. A real top-k router fires, then watch the two collectives: dispatch (all-to-all #1) throws each token to whichever GPU owns its chosen expert, the grouped GEMM runs, and combine (all-to-all #2) throws the results home. The step ends only when the slowest rank finishes — so drag routing skew and watch one hot GPU stall every other GPU at the barrier, and drop capacity factor to see tokens thrown away instead. Drag tokens per GPU down and the straggler disappears: below ~148 slots per expert the grouped GEMM is bound by streaming expert weights out of HBM, and imbalance in tokens is free.
seed = 15
1. router (local) 2. dispatch — all-to-all #1 3. expert FFN (grouped GEMM) 4. combine — all-to-all #2
Per-GPU timeline for ONE MoE layer (the barrier)
all-to-all expert FFN straggler idle at barrier
all-to-all-v send counts (rows = source GPU, cols = expert-owner GPU)
Per-GPU expert load Lg (slots to compute)

Mixture-of-Experts routing, capacity & the aux load-balancing loss

↳ in “Mixture-of-Experts (MoE) Architectures”
Mixture-of-Experts routing, capacity & the aux load-balancing loss
Every token gets a router softmax over E experts from fixed (seeded) gate logits, is dispatched to its top-k experts with renormalized combine weights (Mixtral-style), and is dropped if it arrives at a full capacity buffer. Then drag aux steps T: that runs actual gradient descent on the Switch auxiliary loss (with the hard counts detached) and you watch the collapsed router walk back toward uniform load.
seed = 42
Dispatch (token → expert)
routed edge (width/opacity = combine weight g) dropped (buffer full) token with ≥1 dropped edge
Per-expert load vs capacity
Balance index while the aux loss trains the router

Multi-agent orchestration: supervisor, workers, and the price of coordination

↳ in “Multi-Agent Systems & Orchestration”
Multi-agent orchestration: supervisor, workers, and the price of coordination
Scenario
Cost model (tokens per call)
ready
All four topologies at the current settings (same task, same model)
TopologyCallsTotal tokens Cost / runLatencyP(correct) Cost / success
Every arrow in this diagram is an API call, and every API call re-sends its whole context. That is the entire lesson. A call is charged input = S + goal + R·(packets it reads) + (forwarded messages) and output = O for an intermediate call or N·O for a call that writes the whole report, priced at the chapter's illustrative gpt-4o-mini tier (USD 0.15 / 1M input, USD 0.60 / 1M output — the same tier as the 5-agent pipeline worked example); latency is 0.5 s + out/200 tok/s per call, summed over waves (calls in the same parallel wave overlap, so the dispatch toggle changes wall-clock but never the token bill). Watch the meters as you switch topology: the supervisor pays N extra system prompts plus a plan and a synthesis; the pipeline forwards every earlier stage's output into every later stage, so context grows linearly and the bill grows quadratically; the debate is all-to-all — each of N debaters re-reads all N opening statements, which is N2 message-copies before the judge has read a word. The single agent is almost always the cheapest, which is why the chapter tells you to benchmark it first. Press Next message (or Replay) to walk the run one call at a time and watch the token meter climb; Show whole run puts the totals back.

The reliability panel is a toy model, stated exactly so you can check it. A task is systematically hard for this model with probability ρ (then every sample fails); otherwise each subtask-sized operation succeeds independently with q = p/(1-ρ), so the marginal per-subtask rate is the p you set. Chains multiply (q^k), redundancy uses the chapter's 1-(1-x)^N, and everything is capped by 1-ρ. Single: q^N (it must get all N subtasks right). Supervisor: q^N·q^2 — the plan and the synthesis are model calls too, and they can drop or garble a result. Pipeline: q^(2N-1), because each of the N−1 handoffs is one more place an error is inherited as ground truth. Debate: q·[1-(1-q^N)^N] — genuine redundancy, but the judge must still pick the right answer, and no N ever beats the 1-ρ ceiling. Turn the critic gate on: a failed worker is caught with probability 0.7 and retried, so q -> q + (1-q)·0.7·q. That is usually the biggest single win available, and it costs two calls — the MAST result that most multi-agent failures are organisational, not capability, in one number.

The honest verdict is the last column, cost per successful run = cost / P. At the defaults the single agent wins it. Now drag R up: at some point one context can no longer hold all N source packets, the single agent's call turns amber with OVERFLOW, its success probability drops to zero, and decomposition stops being a luxury. That crossing — not the vibe of "more agents, more intelligence" — is the real argument for multi-agent systems.
LayerNorm vs RMSNorm, live
Drag a bar in the top row (or type in a box below, or focus a bar and press ↑/↓) to edit an activation. Switch the normalization type and move gain/bias to see exactly what each one does to the vector.
presets
input x normalized (pre-affine) output y (after gain & bias)
Hover or focus a bar to inspect its exact value in all three rows.

PagedAttention: the KV cache as paged blocks

↳ in “PagedAttention & KV-Cache Memory Management”
PagedAttention: the KV cache as paged blocks
Sequences & block tables
Physical GPU memory pool
written token slots (color = owner) allocated but unwritten (waste) free block shared block (ref_count > 1)
Pool utilization — both schemes measured on the same live trace, same physical pool tokens storedwasted slotsfree pool
Allocator log (allocations, copy-on-write, stalls)

Parallelism & memory planner (ZeRO-3 / FSDP)

↳ in “Megatron-LM, DeepSpeed & Parallelism in Practice”
Parallelism & memory planner (ZeRO-3 / FSDP)
DP is inferred: DP = floor(GPUs / (TP × PP))
0%50% headroom line100%+
Activation memory for one pipeline stage can roughly double the model-state figure at steady state (chapter Worked Example: 11 GB state + 3.2 GB activations + 2 GB buffers). Treat green (<50%) as the safe target; amber means budget activations carefully. This models full ZeRO-3 / FSDP sharding across DP × TP × PP; ZeRO-1 (chapter Worked Example) shards only across TP × PP and gives a larger per-GPU figure.
How a model is split across GPUs: DP, TP, PP, FSDP
Strategy
1 / 5
held / processed by this GPU not held here temporarily all-gathered active this phase
Per-GPU memory (bf16 weights + bf16 grads + fp32 Adam master/m/v = 16 bytes/param)
params (2 B/p) grads (2 B/p) Adam state (12 B/p) activations all-gather buffer
All four strategies, same settings
The takeaway. Training state is 16 bytes/param (2 bf16 weights + 2 bf16 grads + 12 bytes of fp32 Adam master/momentum/variance), and activations are a separate, often larger, pile. DP replicates all 16 bytes on every GPU and splits only the batch — it buys throughput, never memory, and pays one gradient all-reduce per step. FSDP/ZeRO-3 divides those 16 bytes by G and pays ~1.5× DP's volume per microbatch (all-gather forward, all-gather backward, reduce-scatter grads) — cheap memory, expensive wire. TP also divides model state by G and is the only axis that shrinks activations, but it all-reduces an s×b×h activation tensor 4× per layer, so it must stay inside one NVLink node. PP divides state by the number of stages with almost no bandwidth cost (point-to-point activations at stage boundaries only) but pays the bubble, (p−1)/(m+p−1), and under 1F1B stage 0 still holds p microbatches in flight × L/p layers ≈ a full model's worth of activations. That is why real runs compose them: TP inside the node, PP across nodes, FSDP/DP outermost. Memory model: parameter counts are exact for the Llama-shaped presets (GQA-aware; 7B/13B/70B reproduce 6.74B / 13.02B / 68.98B). Activation bytes use the Megatron formula s·b·h(10 + 24/t + 5as/(ht)) with the published sequence-parallel (everything /t) and selective (drop the 5as²b term) / full (2sbh only) recompute variants. The microbatch is fixed at 1 sequence, all-reduce traffic is costed at the ring value 2(G−1)/G × payload, and the output-logit tensor (s·b·V) plus the tiny replicated norm weights are excluded.
Parameter & FLOP counter
Parameter breakdown
FLOPs per token
Approximate: weight matrices only (ignores biases, LayerNorm, tied vs. untied LM head). 1 MAC = 2 FLOPs. FFN assumed d_ff = 4d (vanilla); gated SwiGLU adds ~50% to MLP. Matches the chapter's 24d² + 4sd per-token-per-layer accounting.

Floating-point formats: fp32, tf32, bf16, fp16, fp8, int8

↳ in “Numerical Computing, Floating Point & Precision”
Floating-point formats: fp32, tf32, bf16, fp16, fp8, int8
Pick a format, type any real number, and see its exact bit layout, the nearest value the format can actually represent, the rounding error, and where it lands in the format's dynamic range. Everything below is computed live from the real IEEE-754 (or affine-quantization) rules -- round-to-nearest-even included -- nothing is looked up from a table.
Try:
Selected format compared with fp32
Propertyselectedfp32

Quantization explorer: FP16 &rarr; INT8 / INT4 / NF4, per-tensor vs per-group

↳ in “Quantization I: Post-Training Quantization (GPTQ, AWQ, SmoothQuant)”
Quantization explorer: FP16 → INT8 / INT4 / NF4, per-tensor vs per-group
A synthetic weight tensor of N = 4096 standard-normal values (seeded, so every render is reproducible). Pick a format and a scale granularity, then inject a few outliers. The widget runs the real arithmetic from this chapter — s = max|x| / qmax per group, q = clip(round(x/s), −qmax, qmax), x̂ = s·q — and reports the exact error it produces.
Weight distribution with the quantization grid overlaid
Dequantization error  x̂ − x

RAG pipeline: retrieve &rarr; augment &rarr; generate

↳ in “Retrieval-Augmented Generation Architectures”
RAG pipeline: retrieve → augment → generate
Drag the query diamond — or tab to it and use the arrow keys (hold Shift for big steps) — to move the query embedding and watch every downstream stage update.
2. Augment — the prompt actually sent to the model
3. Generate — grounded, cited answer
Every stage of RAG is a filter, and the answer is only as good as what survives all of them. Retrieve: chunks and query share one embedding space, and cos θ = (q·c) / (|q||c|) ranks by direction alone – drag the query straight out along its own ray and not one number in the table moves, exactly the length-invariance from the embeddings tool. Two independent gates then apply: top-k caps how many candidates the ANN index hands back, and the similarity gate throws away candidates that are not actually close (drag the query into the empty space between clusters and retrieved chunks vanish although k never changed). Rerank: each question has one chunk that genuinely answers it (ringed ◎), and for two of the four questions the bi-encoder does not rank it first – turn on the reranker and Reciprocal Rank Fusion, RRF(d) = Σr 1/(60 + rankr(d)), promotes it, because a chunk both retrievers like beats a chunk only one of them likes; a chunk that shares no query term is simply absent from the lexical list and contributes nothing from that term, and the large constant 60 keeps any single first place from running away with the fusion. On the INT8 question this decides the answer outright: the bi-encoder ranks the right chunk third, the budget then evicts it and the model refuses – switch the reranker on and the same settings produce a correctly cited answer. Augment: the budget is a hard wall – system prompt and question are paid first, chunks are appended in final rank order, and the tail that no longer fits is dropped before the model ever sees it. Generate: the answer cites only chunks that reached the prompt, so when the answering chunk is filtered out the model is forced into "I don't know" – the readout names which gate dropped it. That failure chain (good corpus, bad k / threshold / budget → refusal) is why chunk size, top-k and context budget must be tuned together, not one at a time.

A ReAct agent loop: Thought, Action, Observation &mdash; and the three ways it can stop

↳ in “The Agentic Loop: ReAct, Plan-Execute & Reflection”
A ReAct agent loop: Thought, Action, Observation — and the three ways it can stop
Action execute Observation LLM Thought -> Action Tools parse + dispatch Environment runs it, returns result ready
Context (system + task + scratchpad) 0 tok
guard: context_full() at 85%
Each click reveals one phase — a Thought, an Action, or the Observation the harness injects — and lights up the node doing the work: the LLM thinks and emits Action: tool(args), the tool layer parses and dispatches it, the environment runs it and hands back an observation that is appended to the context for the next Thought. The context bar only ever grows: after k steps it is O(k·s) tokens, estimated the same crude way the chapter does it (total_chars / 4). That gives the loop exactly three exits, and you can trigger all three here: the model calls finish(...); the step counter hits Tmax; or context_full() trips at 85% of the window. On the defaults the agent just finishes — drop Tmax to 3 to make the step ceiling bite, or shrink the window to about 200 tokens to make the token guard fire first. Toggle “something goes wrong” to watch a call fail exactly the way this chapter's own calculator, search and _parse_action really would — a character-class rejection, an empty fake_db hit, and a TypeError from the regex splitting "299,792,458 / 1000" on its thousands separators (the exact bug native tool-calling removes). Each failure costs a full extra iteration, and the next Thought can only recover because the failed observation is sitting in the context: the context window is the agent's only memory of its own mistakes.

Reward modeling from human preferences

↳ in “The RLHF Pipeline & Reward Modeling”
Reward modeling from human preferences
M candidate responses to one prompt each carry a hidden true quality qi. N human labels are collected over a balanced round-robin of the M(M−1)/2 distinct comparisons, each drawn from the real Bradley–Terry model σ(qi−qj) blended with annotator noise η. A scalar reward ri per response starts at 0 and is fit by real gradient descent, one pair per step, on −log σ(rw−rl). Scrub or play step T and watch the two curves separate: accuracy on the N observed labels climbs past the Bayes ceiling, while accuracy on a fresh label cannot. That gap is fitted label noise, not reward-model quality.
seed = 42
Learned reward rᵢ (bars) vs. hidden true quality qᵢ (diamonds, never seen by training)
learned rᵢ + c true qᵢ (hidden from the RM) chosen in step T's pair rejected in step T's pair
Pairwise preference accuracy vs. training step
train: the N observed labels generalization: expected acc. on fresh labels Bayes ceiling (unbeatable at this η)
RLHF with PPO: the full loop
One toy vocabulary of 6 response tokens, seeded so the numbers are exact and reproducible. Each iteration samples 16 independent responses from the policy (a real PPO minibatch, never just one rollout) — response #1 is shown token-by-token below, but the GAE/whitening/clip/gradient math pools all 16×T tokens, exactly like a production trainer. Every slider drag live-recomputes the pending iteration (same sampled rollouts, new β/ε/epochs) so you can feel the tradeoff before committing; press Step to actually apply the PPO update and advance the reward-vs-KL trajectory. Four models are in play: the trainable policy, the frozen reference (SFT) model, the frozen reward model (which over-weights an exploitable "hack" feature — the reward-hacking trap, by construction), and the trainable scalar value/critic.
7 steps: the four models → rollout → reward → GAE → the clip → the KL leash → reward hacking. Every control stays live during the tour.
iteration 0 · seed 7
The loop (numbers below are the pending iteration — drag sliders to preview it)
Response #1 of 16 sampled this iteration, token by token
Reward vs. KL over iterations (the classic tradeoff)
true reward (quality only) reward-model score (quality + hack) pending preview (not yet committed)
RoPE frequency ladder visualizer
fast spin → slow spin
Each vector is one RoPE dimension pair rotating by angle m·θk. Drag m: high-frequency pairs (left) spin many times; low-frequency pairs (right) barely move – the same geometric frequency ladder as sinusoidal encodings. Hover / focus a cell for its exact numbers.

Sampling explorer: temperature, top-k, top-p

↳ in “Sampling Strategies & Decoding Algorithms”
Sampling explorer: temperature, top-k, top-p
Adjust the knobs to see how temperature reshapes the distribution and top-k / top-p truncate the tail.

Fit your own scaling law and extrapolate

↳ in “Scaling Laws: Kaplan, Chinchilla & Beyond”
Fit your own scaling law and extrapolate
fitted power law (solid = supported by data, dashed = extrapolated) true generating law (reference, unknown to the fit) ladder run — click then drag, or focus and press ↑/↓ (Home restores it)
Ladder runs (N = params, D = tokens, C = 6ND FLOPs; residual = L − fitted L̂)
runNDCloss Lfit L̂resid
Fit-then-extrapolate is the whole scaling-law workflow. You cannot afford to train at the target scale, so you train a cheap ladder of small models, fit L(C) = E + A·C−α to their losses, and read the curve forward to a budget you have not paid for yet. With E held fixed the fit is linear in log space — ordinary least squares of ln(L−E) on ln C, closed form, no local minima. The solid segment is the range your ladder actually covers; the dashed segment is extrapolation, and the further right it runs the more you are trusting the shape of the curve rather than data.

Three lessons are built into the controls. (1) The model is misspecified even with zero noise. The ladder is generated by the real Chinchilla surface L = E + A/Nα + B/Dβ, which along a fixed ratio D = rN is a sum of two power laws in C, not one — so a single-term fit can only approximate it. The "α the ladder implies" stat is the exact slope of that true curve across your ladder's compute range: your fitted α should land near it, and how far it drifts is your bias budget. (2) E is barely identifiable. Slide the assumed floor and watch the prediction at the target swing by hundreds of a nat while R² stays above 0.99. "Auto-fit E" scans the floor for maximum R²: at σ = 0 it recovers ≈1.70 against a true 1.69, but at σ = 0.05 it drifts to ≈1.4 and at σ = 0.3 it collapses to 0 — hit Reseed a few times to see the spread. That is the same fragility Epoch AI reported when replicating Chinchilla (Besiroglu et al., 2024). (3) One bad run rotates the line. Drag a point far off the curve and watch the residual column and the extrapolated loss move; this is why real ladders drop under-converged and loss-spiked runs before fitting.

The "exact compute-optimal" stat does not use the fit at all: it minimises the true surface subject to C = 6ND by Lagrange multipliers, giving N* = (αA/βB)1/(α+β)(C/6)β/(α+β) — the same closed form as the book's optimal_alloc. Because α (0.34) ≠ β (0.28), the implied D*/N* is not a constant: these published constants give ≈32 tok/param at 1019 FLOPs, ≈62 at 1022, and ≈122 at 1025. The famous "~20 tokens per parameter" is the ratio Chinchilla reported, and the fact that its own fitted exponents do not reproduce it is precisely the inconsistency the replication flagged — so treat 20 as a regime-local rule of thumb, never a law. Finally, raising "your plan" ratio above the optimum trains a smaller, cheaper-to-serve model for a loss penalty you can now read off exactly — and the valley is flat: at 1024 FLOPs, going from the optimal ≈100:1 out to 500:1 costs under 0.01 nats, which the last stat restates as the honest currency, "the optimal split would have reached this loss on ~19% less compute". A fifth of your compute for a 5× smaller model to serve is exactly the over-training trade every inference-heavy lab now makes deliberately.
Chinchilla compute-optimal allocator
Compute-optimal means splitting a fixed budget C = 6·N·D between parameters N and tokens D. Chinchilla found the split scales as N,D ∝ √C at a ratio of ~20 tokens per parameter (adjust the ratio for over-/under-training, e.g. inference-aware training uses more). GPU-hours convert via C = GPU-hrs × 3600 × effective FLOP/s.

Self-attention explorer: Q, K, V and the attention matrix

↳ in “The Attention Mechanism From Scratch”
Self-attention explorer: Q, K, V and the attention matrix
Type a short sentence, pick a head, and toggle the causal mask / scale. Every number below is computed live from fixed, seeded token embeddings and random Q/K/V projections (dk = 8) — this is the actual softmax(QKT/√dk)V computation, not a mock-up.
Seven steps from one token to one context vector. The tour drives the controls below; you can leave at any point and keep exploring.
Attention matrix A = softmax(QKT/√dk) — rows = query i, columns = key j, softmax taken along each row. Click a row (or use the selector below) to inspect one query; hover or focus a cell for its exact score and weight.
weight = 0 weight = 0.5 weight = 1 masked (excluded, weight = 0)
Hover or focus a cell to see its exact score and softmax weight.
Softmax distribution for the selected query
Resulting context vector (weighted sum of V, one bar per dimension)
The heatmap is the transformer: each row is one query's probability distribution over all keys (random here, learned in a trained model), and the context vector below is literally that distribution used as mixing weights over the value vectors — so the output can never leave the convex hull of V. Watch the row sum stay pinned at 1.000: softmax normalizes over keys, never over queries. Turn the scale off and the score spread jumps by exactly √8 ≈ 2.83×, the rows sharpen toward one-hot, and the effective-key count collapses — that is softmax saturating into its low-gradient regime, which is the whole reason for the 1/√dk factor. This toy has no positional encoding, so two copies of the same word get identical embeddings; only the causal mask makes their rows differ. New to this? Press Start guided tour at the top for a seven-step walkthrough that drives these same controls.

Softmax &amp; temperature: from logits to a distribution

↳ in “Probability, Statistics & Information Theory”
Softmax & temperature: from logits to a distribution
Edit the six logits (or use a preset) and drag the temperature slider. The top chart is the raw logit vector z; the bottom chart is p = softmax(z/T), recomputed live with the max-subtraction trick for numerical stability. Under each probability bar is that token's surprisal −log₂p, the quantity entropy averages.
T→0: one-hot T→∞: uniform
Logits z (editable above) — bar length is relative; the axis gutter prints the scale
Probabilities p = softmax(z/T), true 0–1 scale
Entropy H (nats)
-
Entropy H (bits)
-
Perplexity e^H
-
Top-1/top-2 odds
-
argmax
-
Speculative decoding: draft, verify, accept
A small drafter proposes γ tokens; the big target model verifies all γ+1 positions in one parallel pass; the longest matching prefix is accepted and one bonus/correction token is appended. Move γ, the per-token acceptance rate α (= 1 − TV(p,q)), and the drafter cost ratio c to see the exact geometric-acceptance math, the marginal-value rule that sets γ*, and a seeded simulation that converges to it.
Drafter proposes γ tokens (cheap, but strictly sequential: γ small forward passes)
↓ target verifies ALL γ+1 positions in ONE parallel forward pass ↓
accepted first rejection discarded (chain already stopped) bonus / correction token
Click “Draft & verify one step” to run the first simulated step.
Speedup vs plain decoding as a function of γ, at the current α and c
Verification is exact, not approximate: a drafted token x ~ q is accepted with probability min(1, p(x)/q(x)); on rejection the replacement is resampled from the residual (p−q)⁺. Summed over both paths a token is emitted with probability min(p,q) + (p−q)⁺ = p(x) — the target's own distribution — no matter how bad the drafter q is. α changes how many tokens come out per parallel pass, never which distribution they come from. With i.i.d. acceptance, E[tokens per target call] = (1−αγ+1)/(1−α) and cost = 1+γc target-equivalent passes, so speedup = E/(1+γc). The γ-th extra draft slot buys only αγ expected tokens but always costs c, so once αγ+1/c drops below the current speedup, growing γ hurts — that is the γ* marked on the chart. Caveats this idealization hides: acceptance is not really i.i.d. (it decays along the chain), single-chain drafting is weaker than tree drafting, and at large batch sizes decode is compute-bound so the “free” verification FLOPs stop being free.

State-space models: the selective scan (Mamba), a linear recurrence instead of a quadratic attention matrix

↳ in “Beyond Attention: SSMs, Mamba, RWKV & Linear Attention”
State-space models: the selective scan (Mamba), a linear recurrence instead of a quadratic attention matrix
Token sequence — click a token to change it. Every Δt, Bt, Ct below is a real linear function of that token's embedding; nothing is hand-assigned. The embeddings of <sep> and <pad> point along (and against) the Δ projection — the direction a trained selective layer discovers — and the input projection is built orthogonal to it, so those two tokens carry xt = 0: whatever they do is the gate alone, never the signal. Push the Δ bias up past −0.25 and watch every token start resetting: Δ is a global knob as well as a per-token one, which is why Mamba initialises the Δ bias low (softplus‑inverse of ~0.001–0.1) and lets the projection push it up only where the token warrants it.
ht[n] > 0 ht[n] < 0 keep = Āt[0] = e−Δt (how much of ht-1[0] survives) write = |B̄t[0] xt| (how much of xt enters) yt = Ct·ht
The bottom row is the implicit attention of the inspected step. Unrolling the recurrence gives ht = ∑s≤t (∏r=s+1..tr) B̄sxs, so each bar is how much of token s is still alive in ht. That product is the SSM's answer to an attention row — but it is never materialised, only accumulated. Watch a <sep> flatten everything to its left, and a <pad> leave the profile untouched.
The arithmetic at the inspected step
Readout
The same numbers as a parallel (associative) scan
ht = atht-1 + bt is an affine map, and affine maps compose associatively: (al,bl) ● (ae,be) = (alae, albe+bl). A Hillis–Steele doubling scan therefore produces every ht in ceil(log2T) rounds. Each cell is labelled with the span of time steps it has folded in.
Cost as T grows: O(T²) scores vs O(T) steps with an O(1) state
Attention: every pair (i,j), i ≥ j
SSM: T steps, one fixed-size state
The takeaway: an SSM layer never builds a T×T score matrix. It carries N numbers per channel and folds each token in with one multiply-add: ht = Ātht-1 + B̄txt, yt = Ct·ht, with Āt = exp(ΔtA) on a diagonal, negative-real A. In S4 the parameters are constant, so the layer is a fixed convolution: it decays at one rate forever and cannot be told to drop or hold anything — switch to "Constant" and watch <sep> and <pad> stop resetting and stop coasting, becoming ordinary decay steps. In Mamba, Δt, Bt, Ct are linear functions of the token, and Δt becomes a gate: large Δ drives Ā = exp(ΔA) → 0 and erases the state (a reset at <sep>); Δ → 0 drives Ā → 1 and B̄ → 0, so the token is skipped and the state coasts (<pad>). Sequential order is not a training bottleneck, because the recurrence is an associative scan — the tree above reproduces the loop to the last bit in ceil(log2T) rounds. And the punchline is the memory chart: a transformer's KV cache is O(T) and never stops growing, while the recurrent state is O(1) — a fixed 304 KiB/layer here, past the crossover at only ~38 tokens and ~3450× smaller at 128k. The bill for that constant state is that everything the model still needs must fit in N numbers, which is exactly why selectivity — deciding what to keep — had to be invented.

Test-time compute: best-of-N, majority vote, search

↳ in “Reasoning, Chain-of-Thought & Test-Time Compute”
Test-time compute: best-of-N, majority vote, search
Every rule starts at the same place: at N = 1 best-of-N, majority vote and weighted vote all equal pass@1. Everything above that line is selection, and the dashed pass@N curve is the ceiling selection could ever reach — coverage keeps climbing roughly log-linearly in N (Brown et al., Large Language Monkeys, 2024) while every selection rule plateaus below it. The gap between them is exactly what a better verifier buys you, which is why pass@k and maj@k must be reported separately. The verifier is a real model here, not a fudge factor: a candidate's score is v = ±d/2 + N(0,1) (plus for correct, minus for wrong), so the two score distributions are unit Gaussians separated by d and the verifier's ROC AUC is exactly Φ(d/√2) — the slider inverts that, d = √2·Φ-1(AUC). The calibrated posterior follows from the likelihood ratio: log-odds = logit(p) + d·v, so P(correct | v) = σ(logit(p) + d·v). Best-of-N takes argmax v; weighted vote sums those posteriors per distinct answer. Drag AUC to 0.50: d = 0, the score is pure noise, best-of-N degenerates into picking a random one of the N samples and its curve goes flat at pass@1 no matter how much you spend. That is the generator-vs-verifier split in one drag — the samples contained the answer, nothing could find it. Majority vote needs no verifier but assumes errors are diverse. Push wrong-answer agreement up and the wrong chains pile onto one attractor answer; once p < 0.5 with high agreement, self-consistency converges to the wrong answer and accuracy falls as N grows. Exact check: set agreement = 1.00, σ = 0, p = 0.60 — voting is now binary and maj@15 is exactly the binomial tail P(Bin(15, 0.6) ≥ 8) = 0.787; the widget reproduces that to within a Monte-Carlo standard error or two (2000 seeded problems, s.e. √(.787·.213/2000) ≈ ±0.009 — hit Resample a few times to watch the sampling noise move it around). Note that maj@16 gives the same 0.787: an 8–8 tie is broken by earliest occurrence, which under exchangeability is a fair coin, so an even N buys nothing over the odd N below it. Beam search spends the same budget differently. Both allocations generate exactly N × D step-completions; flat sampling puts all of it in width (N independent chains, never pruned), beam search keeps only w prefixes and prunes with the step verifier at every one of the D depths, respawning N children per depth spread over the w survivors (⌊N/w⌋ each, with the first N mod w getting one extra, so the cost matches flat even when w does not divide N). A weak step verifier applied D times beats a strong one applied once — this is the whole PRM-over-ORM argument, and you can read it straight off the widget. Set N = 32, D = 6, w = 2, p = 0.35, highlight best-of-N, and sweep AUC: at 0.50 beam and flat are both pinned at pass@1 ≈ 34% (uniform pruning keeps live prefixes at exactly rate q per step, so nothing is gained); at 0.52 — a verifier barely distinguishable from a coin — beam already reaches ~43% against best-of-32's ~37%; by 0.60 it is ~68% against ~49%. Mild selection pressure compounds across depth. The bill arrives as coverage: only w prefixes survive, so beam coverage collapses far below flat coverage (99% at N = 32), and the diversity you destroyed is unrecoverable — a large part of why DeepSeek-R1 abandoned PRM-guided search in favour of outcome-only RL, and why narrow beams with dull verifiers (w = 1, AUC near 0.5) fall all the way back to a single random-walk chain. Set w ≥ N (e.g. N = 4, w = 8) and nothing is ever pruned: beam search reduces exactly to flat sampling and the two curves coincide up to seed noise. The reported α is a least-squares fit of log(1 - accuracy) = log ε - α log N over the highlighted curve, i.e. the test-time scaling law acc ≈ 1 - εN. Model caveats: samples are i.i.d. (no self-correction or sequential revision), verifier scores are Gaussian and calibrated against the population prior p rather than each problem's own pi (a real reward model is neither Gaussian nor calibrated, and RL will hack it), and a chain is correct only if all D steps are — no lucky recoveries.
Tokenizer playground: train a real byte-level BPE
This trains an actual GPT-style byte-level byte-pair-encoding tokenizer in your browser — no library, no network. It splits the corpus with the GPT-2 pre-tokenizer regex (so merges never cross word boundaries), starts every piece from its raw UTF-8 bytes, then repeatedly merges the most frequent adjacent pair and records the ordered merge list. Encoding applies those merges greedily in learned-rank order; decoding just concatenates each token's stored bytes.
Learned merges — vocabulary: 256 base bytes + 0 merges
rankpairnew tokenpair count
Encode
Token ids the model actually sees
Step-through: watch merges apply to one word
The takeaway: byte-level BPE guarantees every possible input is encodable — it bottoms out at raw bytes, so there is no <unk> token — while learned merges compress frequent substrings into single tokens. Push the merge slider and watch bytes / token climb: that ratio is your context-window and inference-cost multiplier. Merges are chosen purely by adjacent-pair frequency in the training corpus, so a tokenizer inherits its corpus's biases (English prose compresses well; unseen scripts fall back toward one token per byte). Decoding is just concatenating each token's stored bytes, which is why encode → decode reproduces the input's bytes exactly, even though a single merged token's bytes need not be valid UTF-8 on their own.
Training compute & cost estimator
Training FLOPs use the standard 6ND rule (forward+backward, dense). Memory uses the Adam mixed-precision 16 bytes/parameter optimizer-state budget (fp16 param+grad + fp32 master+m+v), sharded evenly across GPUs (ZeRO-3/FSDP) — activation memory is extra and not shown.

A full transformer forward pass, stage by stage

↳ in “Building a GPT From Scratch (nanoGPT-style)”
A full transformer forward pass, stage by stage
Pick four input tokens, choose how many layers to stack, then step (or Play) through every stage of a real decoder-only GPT forward pass. Every number shown is computed live from fixed, seeded weights (B=1, T=4, d=8, heads=2, head_dim=4, d_ff=16, V=16) — the same RMSNorm, fused QKV projection, causal softmax attention, SwiGLU MLP, and residual adds a real model runs, just tiny enough to see whole.
15 steps, token ids to next-token distribution. The tour drives the controls below and every control stays live; each step re-applies its own settings, so you can poke at anything and still land back on the canonical run.
Stage 1 / 1
Token ids
[B,T] = [1,4]
Live tensor(s) for this stage — hover or focus a cell for the exact number. positive negative causally masked (−∞)
The residual stream, checkpoint by checkpoint (click a thumbnail to jump there)
The residual stream is a width-d highway: attention and the MLP only ever add to it (never overwrite it), so its shape [T,d] is identical at every checkpoint no matter how many layers you stack. Everything you see is computed from genuinely fixed, seeded (untrained) weights — the shapes and the arithmetic at every stage are exactly what a real GPT computes, but since nothing has been trained, the final next-token distribution is linguistically arbitrary, not a meaningful prediction. New to this? Start guided tour at the top walks the whole pass in 15 steps, naming every shape; exit at any point to keep exploring on your own.

Vision Transformer: an image becomes a sequence of tokens

↳ in “Vision Transformers & Image Encoders”
Vision Transformer: an image becomes a sequence of tokens
Image
Steps 1–2 · pixels → patches click / drag the image to paint
Step 5 · attention weights of the query token click a patch to query it
Step 3 · one patch → one token: flatten, then multiply by the shared matrix E tracing the token selected above
Step 4 · the sequence the transformer actually consumes
token value
Where the query looks · top attention weights
Patch size is the compute dial · real ViT configurations
An image becomes a sequence — that is the whole trick. Patchify cuts H×W into N = (H/P) · (W/P) non-overlapping P×P patches; each is flattened to P²C numbers and hit with one shared matrix E to become a D-dimensional token — arithmetically the same thing as a Conv2d(C, D, kernel_size=P, stride=P), up to the order in which each patch's pixels are flattened (this widget uses the chapter's (P, P, C) einops order; Conv2d uses (C, P, P), a column permutation of E). Prepend a learned [CLS] token, add a learned positional embedding to every position, and an unmodified transformer encoder does the rest. E, x_cls and Epos are parameters: they depend on the seed and nothing else, so they hold perfectly still while you repaint — only the patch rows move. Patch size is the compute dial: halving P quadruples N and multiplies attention's (N+1)² score matrix by about 16 — which is why ViT-B/16 (196 patch tokens) is cheap while a 1024 px input at P = 16 (4096 tokens) needs FlashAttention. Positional embeddings are not decoration: switch them off and two identical patches produce byte-identical tokens; self-attention is permutation-invariant over its inputs, so the model then literally cannot tell top-left from bottom-right (watch the duplicate-token counter on the Checker or Stripes preset). Read the attention honestly: these weights are seeded-random, not trained. LayerNorm (Pre-LN, exactly as in the chapter's ViTBlock) puts every token on the sphere of radius √D, so with the content-similarity head (WK = WQ) the score q·k/√D is an unbiased estimate of √D · cos(ẑi, ẑj): a patch attends to patches that look like it, which is the texture/locality behaviour real ViT heads do learn. The weight vs. looks-like-the-query chip measures exactly that, and typically lands around r ≈ 0.6–0.8 for this head (median 0.64 over the first 60 seeds). Untick the box for independent random WQ, WK and that correlation collapses to about zero on average (roll the seed a few times to see past the noise — any single seed can land anywhere): the head still paints a confident-looking map, it just has nothing to do with what the query looks like. The architecture supplies the mechanism; training supplies the meaning. Query the [CLS] token and you see the same lesson from the other side: its row is usually sharper than a patch's (lower entropy on about two seeds in three), yet [CLS] has no pixels at all — it is one learned constant vector, so at initialisation its “favourite” patches are simply whichever ones happen to align with a random direction. Object-centric CLS attention — the DINO maps later in this chapter — is a product of training, not of the architecture.

Green-list watermarking: bias the logits, then count green tokens

↳ in “Watermarking, Provenance & AI-Content Detection”
Green-list watermarking: bias the logits, then count green tokens
green list (gets +δ) red list sampled token left bar = pv before boost right bar = pv after boost
Detector z-score vs. number of scored tokens t
The mechanism. At every step the previous token is hashed (with a secret key) into a pseudo-random split of the vocabulary: a γ-fraction green list gets its logits raised by +δ before the softmax, so the model samples green tokens more often than chance with no change to its weights. The grid shows one step: each cell is a vocabulary entry with its probability before the boost (grey bar) next to its probability after (coloured bar) — δ visibly pushes mass from the red cells onto the green ones. Detection replays the same hash on a candidate text, counts observed green tokens g out of T, and forms z = (g − γT) / √(T γ(1−γ)). Under the null each token is green independently with probability γ, so human z wanders around 0 like a random walk, while watermarked text with green rate p > γ gives z ≈ (p − γ) √T / √(γ(1−γ)) — it grows like √T, not linearly, which is why detection needs a few hundred tokens rather than a few dozen. Three tradeoffs to play with. (1) Raising δ raises p and reaches z* = 4 in fewer tokens, but the KL(boosted ‖ original) statistic below shows exactly how much distribution you paid for it. (2) σ controls how peaked the model's own distribution is: crank it up (low entropy, near-greedy text like code or a quoted fact) and a fixed δ can no longer move the argmax — the green rate collapses toward γ and z stalls below threshold. That entropy ceiling, not the statistics, is the real limit of logit-bias watermarks. (3) γ is not monotone: a small green list makes each green hit more surprising (more z per green token) but also leaves δ fewer good tokens to promote, so for a fixed δ the z-score peaks at an intermediate γ (try γ ≈ 0.25, the KGW paper's recommendation) and collapses as γ → 1, where green is nearly free and the test is powerless. Note that the null variance γ(1−γ) is largest at γ = 0.5, so γ trades surprise-per-token against the width of the null — it is not a "more green is better" dial.