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
↳ in “Optimizers: SGD, Adam, Adafactor, Lion, Muon & Shampoo”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
↳ in “The Anatomy of LLM Inference: Prefill, Decode & The KV Cache”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
↳ in “PEFT I: LoRA, QLoRA, DoRA & The Adapter Family”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
↳ in “The Roofline Model & Performance Engineering”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
↳ in “The Transformer Block: Norms, Residuals, MLPs & Activations”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
↳ in “The Roofline Model & Performance Engineering”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
↳ in “The Attention Mechanism From Scratch”Attention mask visualizer
allowed
masked
Backprop on a computation graph
↳ in “Automatic Differentiation & PyTorch Internals”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
| leaf | closed form | value | backprop | finite 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.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)
-
Per-step decision
Surviving beams, ranked by cumulative logP
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.
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
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 κ = λ2/λ1 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| = κ·|θ2/θ1| 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.
KV-cache & context memory budgeter
↳ in “Evaluation & Serving: Honest Benchmarks, int4 Quantization, and Running on a Laptop”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
↳ in “The Anatomy of LLM Inference: Prefill, Decode & The KV Cache”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.
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 ½(ηmax+ηmin), 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).
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
LayerNorm vs RMSNorm, live
↳ in “The Transformer Block: Norms, Residuals, MLPs & Activations”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.
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))
—
—
—
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.
Parameter & FLOP counter
↳ in “The Capstone: Building Stack-100M, and the 2026 Small-Model Landscape”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:
| Property | selected | fp32 |
|---|
Quantization explorer: FP16 → 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
RoPE frequency ladder visualizer
↳ in “Positional Encodings: Sinusoidal, Learned, RoPE & ALiBi”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.
Chinchilla compute-optimal allocator
↳ in “Mini Scaling Laws: Fit Your Own Law Before Spending the Budget”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.
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.
Softmax & 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.
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
↳ in “Speculative Decoding: Draft Models, Medusa, EAGLE & Lookahead”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.
Tokenizer playground: train a real byte-level BPE
↳ in “A Byte-Level BPE Tokenizer From Scratch (and Why Vocab Size Is a Design Lever at 100M)”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
| rank | pair | new token | pair 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
↳ in “The Capstone: Building Stack-100M, and the 2026 Small-Model Landscape”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.