Notes on the pretraining design choices disclosed in the 2025–26 open-weight model reports: scale, mixture of experts, attention, position, the residual stream, multi-token prediction, multimodal input, optimizers, schedules, stability, numerics and data. Each chapter explains the mechanism, works a real example, and gives the reasons the labs themselves stated.
Disclaimer: This blog is compiled and edited with help from LLMs. Every number links to the public document it came from, so please check the source before citing it.
This is Part 1 of Reading Open-Weight Recipes, a series where I review what open-weight model releases have written down about their training recipes between December 2024 and October 2026. The atlas indexes the disclosures lab by lab; these notes go through pretraining one design choice at a time, with the mechanism, a worked example using numbers from a released model, and the reasons the labs themselves gave.
The same caveats as the rest of the series apply. Everything here comes from the public reports of open-weight releases (plus Microsoft AI's reports, whose models are mostly closed-weight), so it describes what those labs chose to disclose. It is not a description of how frontier closed models are trained, and where a report is silent I don't fill the gap. These are personal notes, not written for, reviewed by, or endorsed by any lab covered here.
Why do the 2025–26 open models look so different from the transformer in a textbook, and what decided each difference?
A textbook transformer has one kind of attention, one kind of feed-forward block, a fixed position encoding and an Adam optimizer. Between DeepSeek-V3 in December 2024 and MAI-Base-2 in October 2026, the labs in this review changed almost every one of those parts. Most of the changes have the same root cause: models became cheap enough to train at enormous scale, and expensive enough to serve that serving cost began to shape the architecture. Long agent sessions with hundreds of thousands of tokens of context made that pressure much stronger.
These notes take each design choice in turn. Each chapter has the same parts:
The notes assume you know how a standard transformer works (embeddings, attention, feed-forward blocks, softmax and backpropagation) and are comfortable with matrix shapes. No prior knowledge of the newer designs is needed. Underlined numbers link to their source; hover them to see the exact quote or configuration value.
| Symbol | Meaning | Symbol | Meaning |
|---|---|---|---|
| L | number of layers | N, Na | total and active parameters |
| d | model width (hidden size) | D | training tokens |
| h, hkv | query heads, key/value heads | E, k | routed experts, experts active per token |
| dh | dimension of one attention head | w | sliding-window size |
| n | context length in tokens | dc, dr | MLA latent dimension, decoupled RoPE dimension |
| b | bytes per stored number | m | tokens selected by sparse attention |
| η | learning rate | S | linear-attention state matrix |
Other symbols are local to the chapter that defines them. Layer ratios are written local : global for sliding-window models (5:1 means five sliding-window layers per global layer) and linear : full for linear-attention hybrids.
The notes draw examples from fifteen labs: six that publish open-weight models and detailed reports (DeepSeek, Moonshot's Kimi, Z.ai's GLM, Xiaomi's MiMo, Google's Gemma and Thinking Machines' Inkling), one that publishes detailed reports on mostly closed-weight models (Microsoft AI's MAI), six open-weight labs the atlas added later (Alibaba's Qwen, MiniMax, StepFun, Ant's Ling, NVIDIA's Nemotron and Mistral), and the open-weight models of two more (OpenAI's gpt-oss and Meta's Llama 4 and Muse). "Total / active" gives all parameters and the parameters one token uses; "builds on" says whether the release is a new pretraining run or reuses an earlier one.
| Lab | Model | Released | Total / active | Builds on |
|---|---|---|---|---|
| DeepSeek | DeepSeek-V3 | Dec 2024 | 671B / 37B | new run |
| DeepSeek-V3.2-Exp, V3.2 | Sep, Dec 2025 | 671B / 37B | V3 lineage, sparse attention added by continued training | |
| DeepSeek-V4 (Pro, Flash) | Apr 2026 | 1.6T / 49B; 284B / 13B | new run | |
| DeepSeek-V4.1-Flash | Sep 2026 | 552B + 196B memory / 16B | new run | |
| Kimi (Moonshot) | Moonlight | Feb 2025 | 16B / 3B | new run (research) |
| Kimi K2 | Jul 2025 | 1.04T / 32.6B | new run | |
| Kimi Linear | Oct 2025 | 48B / 3B | new run (research) | |
| Kimi K2.5 | Jan 2026 | 1.04T / 32B + vision encoder | K2, continued with images | |
| Kimi K3 | Jul 2026 | 2.78T / 104B | new run | |
| GLM (Z.ai) | GLM-4.5 | Jul 2025 | 355B / 32B | new run |
| GLM-5, GLM-5.2 | Feb, Jun 2026 | 744B / 40B | new run; 5.2 builds on it | |
| GLM-5.3-Flash | Aug 2026 | 320B / 18B | new run | |
| MiMo (Xiaomi) | MiMo-7B | Apr 2025 | 7B dense | new run |
| MiMo-V2-Flash | Dec 2025 | 309B / 15B | new run | |
| MiMo-V2.6 (Pro, Flash) | Sep 2026 | 1.02T / 42B; 310B / 15B | new runs | |
| Gemma (Google) | Gemma 3 | Mar 2025 | 1B to 27B, dense | new run, distilled from a larger teacher |
| Gemma 4 | Apr 2026 | up to 31B dense; one 26B / 3.8B MoE | new run | |
| Gemma 4 12B | Jun 2026 | 12B dense | new run, no vision encoder | |
| MAI (Microsoft AI) | MAI-Base-1 and MAI-Thinking-1 | Jun 2026 | 962B / 34.7B | Base-1 is the new run; Thinking-1 is its posttrained model |
| MAI-Base-2 | Oct 2026 | 2.8T / 80B | new run, not released | |
| Thinking Machines | Inkling | Jul 2026 | 975B / 41B | new run |
| Inkling-Small | Jul 2026 | 276B / 12B | separate run | |
| Qwen (Alibaba) | Qwen3 | Apr 2025 | 235B / 22B, plus smaller MoE and dense sizes | new run |
| Qwen3-Next | Sep 2025 | 80B / 3B | new run | |
| Qwen3.5, Qwen3.8 | Feb, Aug 2026 | 397B / 17B; 2.4T / 95B | new runs | |
| Qwen3.8-Flash-Next | Aug 2026 | 125B / 6B, plus 51B of n-gram tables | new run, a preview of Qwen's next architecture | |
| MiniMax | MiniMax-Text-01 | Jan 2025 | 456B / 45.9B | new run |
| MiniMax-M2 | Oct 2025 | 229.9B / 9.8B | new run | |
| MiniMax-M3, M3.1-Flash | Jun, Oct 2026 | about 428B / 23B | new run; M3.1-Flash builds on it | |
| StepFun | Step-3 | Jul 2025 | 321B / 38B, incl. a 5B vision encoder | new run |
| Step 3.5 Flash | Feb 2026 | 196B / 11B | new run | |
| Step 5 Preview | Oct 2026 | 600B / 27B | new run | |
| Ling (Ant) | Ling 2.0 (largest: Ling-1T) | Sep, Oct 2025 | 1T / 51B | new runs |
| Ling-2.5-1T, Ling-2.6-1T | Feb, Apr 2026 | 1T / 63B | Ling-1T, attention replaced by continued training | |
| Ling-3.0-flash | Aug 2026 | 124B / 5.1B | new run | |
| Nemotron (NVIDIA) | Nemotron-H | Mar 2025 | 56B and 8B, dense | new runs |
| Nemotron Nano 2 | Aug 2025 | 12B dense | new run | |
| Nemotron 3 Nano, Super, Ultra | Dec 2025; Mar, Jun 2026 | 31.6B / 3.2B; 120.6B / 12.7B; 550B / 55B | new runs | |
| Nemotron 3.5 Lightning | Aug 2026 | 30B / 3B | Nemotron 3 Nano base, distilled from Super's base | |
| Mistral | Mistral Small 3 | Jan 2025 | 24B dense | new run |
| Mistral Large 3, Small 4 | Dec 2025, Mar 2026 | 675B / 41B; 119B / 6B | new runs (Small 4 inferred: no earlier Mistral model has its shape) | |
| Ministral 3 | Dec 2025 | 3B to 14B, dense | pruned from Small 3.1, then distilled from it | |
| OpenAI | gpt-oss-120b, gpt-oss-20b | Aug 2025 | 117B / 5.1B; 21B / 3.6B | new run |
| Meta | Llama 4 Scout, Maverick | Apr 2025 | 109B / 17B; 400B / 17B | new runs; Maverick distilled from Behemoth (about 2T / 288B, previewed only) |
| Muse Glimmer | Aug 2026 | about 30B dense | new run, distilled from Muse Spark (a closed model) | |
| Muse Spark Open | Oct 2026 | 816B / 42B | open version of Muse Spark |
Dates, sizes and lineage come from the labs' own model cards and reports; the atlas links each source.
Chapters 00 and 01 set up the cost model that the rest of the notes rely on, so read them first. After that, the chapters stand on their own. The calculator after chapter 03 lets you compare the memory needs of real models at any context length. For which lab adopted what and when, see the pretraining section of the atlas; for what happens after pretraining, see the midtraining and posttraining notes.
How many floating-point operations did it take to train DeepSeek-V3, how much memory does one 128K-token conversation take on GLM-4.5, and why did the second number start to drive model design in 2025-26?
A model is paid for twice: once to train it, a bill counted in floating-point operations (FLOPs) and GPU-hours, and again for every token it serves. Training cost grows with the parameters each token touches and the number of training tokens. Serving cost at long context grows mostly with the bytes kept in memory and read back for every new token.
How much compute does training take? A good first answer counts how often each weight is used. For every token, each weight matrix takes part in three matrix products: one on the way forward and two on the way back.
| Step | Product | What it is for | FLOPs per parameter |
|---|---|---|---|
| Forward | y = xW | compute the layer's output | 2 |
| Backward: weight gradient | ∂ℒ/∂W = x⊤ · ∂ℒ/∂y | how each weight should change | 2 |
| Backward: input gradient | ∂ℒ/∂x = ∂ℒ/∂y · W⊤ | pass the error signal to the layer below | 2 |
| Total | 6 |
Each product costs 2 FLOPs per weight: one multiply and one add. So every parameter costs about 6 FLOPs per training token, and a model with Na active parameters trained on D tokens costs
The Chinchilla paper uses this formula as its compute budget. Inference runs only the forward pass, so serving a token costs about 2·Na FLOPs, a third of training.
The count that matters is the parameters that actually multiply the token. In a dense model that is all of them. A mixture-of-experts (MoE) model splits each feed-forward block into E experts, and a router sends each token to only k of them (chapter 02). Unchosen experts do no arithmetic for that token, so FLOPs follow the active parameters Na: the attention and output-head weights, any shared experts (experts every token uses; chapter 02) and the k chosen experts. The input embedding is a table lookup that costs no FLOPs, although some labs include it in their reported active counts. The total N matters for memory.
DeepSeek-V3 has 671B total parameters, 37B active per token, pre-trained on 14.8T tokens.
The report gives 180K H800 GPU-hours per trillion tokens, so 180K × 14.8 = 2,664K GPU-hours of pre-training; with 119K for context extension and 5K for post-training the total is 2.788M. Pre-training lasted 2.664 × 106 × 3,600 = 9.59 × 109 GPU-seconds, so each GPU sustained 3.29 × 1024 / 9.59 × 109 ≈ 3.4 × 1014 FLOP/s, about 343 TFLOP/s. Counting all 671B parameters as active would give 5.96 × 1025 FLOPs, 18 times more.
Cross-check: Llama 4 Maverick has 17B active parameters, saw ~22T tokens and used 2.38M H100 GPU-hours: 6 × 17 × 109 × 22 × 1012 = 2.24 × 1024 FLOPs over 8.57 × 109 GPU-seconds, about 262 TFLOP/s per GPU. Two labs and two GPU types give rates of the same size, so 6NaD works as a budget estimate.
What 6ND leaves out. A transformer does two kinds of work for each token:
For a standard dense transformer with L layers of width d, Kaplan et al. write the forward cost per token as two terms, with n the context length:
Their ratio says when attention matters:
When the context is short next to the width, attention is a small add-on. At d = 4,096 and 8K tokens it is about 17% of the weight work, which is why Kaplan et al. drop it when d > n/12. At 128K tokens the same model spends 2.7 times as much on attention as on its weights, and at 1M about 21 times. The parameter count did not change; the context did. So 6ND is a good budget for pretraining at short context and a poor one for long-context training and serving, the subject of chapter 03.
Weights. A standard layer has 12d2 weights: the query, key, value and output projections (4d2) and an MLP that widens d to 4d and back (8d2). So N ≈ 12Ld2, and applying them costs 2N ≈ 24Ld2 FLOPs per token. The 12 comes from this layer shape; models with grouped-query attention, a different MLP width or mixture-of-experts layers have a different constant.
Attention. One query–key dot product costs 2d FLOPs. Done against n keys in each of L layers, that is 2Lnd per token. Kaplan's term counts only these scores; in causal training, mixing the values roughly doubles the work and the mask roughly halves it, so 2Lnd stays a fair estimate.
A model writes one token at a time. In every layer, each new token attends to all the tokens before it: its query is compared with their keys, and their values are mixed according to the scores. Serving runs in two phases, which Pope et al. call prefill and decode:
Both spend about 2·Na FLOPs per token on weights. What separates them is how many bytes each step has to read, and most of those bytes come from the KV cache.
The earlier tokens do not change while the model writes, so neither do their keys and values. Recomputing them at every step would repeat the same work, so servers keep them in memory instead. That store is the KV cache. In each layer, a decode step does three things (Figure 0.1):
The cache trades computation for memory, and the memory has two costs:
Its size follows from what each layer stores: one key and one value for each key/value head.
A sequence of n tokens needs n times that, and a batch of B sequences B times more. With easy numbers, 32 layers, 8 KV heads of 128 numbers and 2-byte bfloat16 give 2 × 32 × 8 × 128 × 2 = 131,072 bytes (128 KiB) per token: 1 GiB at 8,192 tokens and 16 GiB at 131,072.
The formula counts KV heads hkv, not query heads h. The original transformer gave every query head its own key/value head, and for a 500B+ model laid out that way, Pope et al. find that batch 512 at context 2,048 needs a 3 TB cache, three times the parameters. Multi-query attention shares one key/value head across all query heads, and grouped-query attention (GQA) uses an intermediate number of key/value heads, so each cached head serves h/hkv query heads and the cache shrinks by that factor. Chapter 03 covers designs that shrink it further.
def decode_step(x, cache, layers): # x: [d], the newest token's hidden state
for l, layer in enumerate(layers):
u = rmsnorm(x)
q = (u @ layer.W_q).reshape(h, d_h) # [h, d_h] (RoPE omitted)
k = (u @ layer.W_k).reshape(h_kv, d_h) # [h_kv, d_h]
v = (u @ layer.W_v).reshape(h_kv, d_h) # [h_kv, d_h]
cache.K[l].append(k) # cache grows by 2*h_kv*d_h numbers
cache.V[l].append(v)
K = stack(cache.K[l]) # [n, h_kv, d_h]: all n tokens are read
V = stack(cache.V[l]) # [n, h_kv, d_h]
g = h // h_kv # query heads per KV head
heads = []
for i in range(h):
j = i // g # the KV head this query head uses
scores = K[:, j, :] @ q[i] / sqrt(d_h) # [n], one per key position s
heads.append(softmax(scores) @ V[:, j, :]) # [d_h]
x = x + concat(heads) @ layer.W_o # [d]
x = x + layer.ffn(rmsnorm(x)) # dense FFN or MoE
return x
Caching saves recomputation, but every generated token must read the whole accumulated cache. Whether that matters depends on how fast a chip reads memory compared with how fast it does arithmetic. Dividing one by the other gives the FLOPs each byte read from high-bandwidth memory (HBM) must feed to keep the chip busy. A TPU v4 chip does 275 TFLOPS of bfloat16 matrix arithmetic and reads 1,200 GB/s, about 229 FLOPs per byte. An H100 SXM does 989 TFLOPS of dense 16-bit matrix arithmetic and reads 3.35 TB/s, about 295.
So decode is usually memory-bandwidth-bound: the bytes read per step set its speed, and at long context most of those bytes are cache (Figure 0.2). That is the common case rather than a law. A large batch of short sequences can push decode back toward compute, and some attention designs raise the FLOPs per cached byte (chapter 03).
GLM-4.5 uses GQA in all 92 layers, with 96 query heads, 8 KV heads and head dimension 128, in bfloat16 (b = 2):
At its maximum context of 131,072 tokens that is 376,832 × 131,072 ≈ 49.4 GB for one sequence, 62% of an H100's 80 GB of memory. A server splits both the weights and the cache across its GPUs, but the totals still have to fit: the model has 355B parameters, 32B active, 710 GB in bfloat16, of which each step reads at least the 64 GB active.
Batching does not shrink the cache. With eight sequences a step reads 8 × 49.4 = 395 GB of cache, six times the active weights (Figure 0.2). Each cached number serves 96/8 = 12 query heads at 2 FLOPs each: 24 FLOPs per 2-byte number, or 12 per byte, against the ~295 an H100 needs. Without GQA (hkv = 96) the cache would be 12 times larger, 593 GB per sequence.
In bfloat16 the weights alone overflow eight H100s (8 × 80 = 640 GB). GLM-4.5's model card lists eight H200 GPUs for the full 128K context, with weights and cache in FP8 and at most 8 sequences per batch. FP8 means b = 1, which halves every byte count above. An H200 has 141 GB at 4.8 TB/s. Memory: about 355 GB of weights plus 8 × 24.7 = 198 GB of cache is 553 GB of the 8 × 141 = 1,128 GB. Each step reads at least 32 + 198 = 230 GB, 86% of it cache; at 8 × 4.8 = 38.4 TB/s that takes at least 230 / 38,400 ≈ 6.0 ms, so each sequence gets at most about 167 decode steps per second.
Long contexts also raise compute. Attention for one query costs 4·n·h·dh FLOPs per layer: 2 per number for the q·k scores over n keys and 2 for mixing n values (Kaplan's term counts only the scores). At GLM-4.5's 4,096-token pre-training length that is 4 × 4,096 × 96 × 128 × 92 ≈ 1.9 × 1010 FLOPs for the last token, already 29% of the 2 × 32B = 6.4 × 1010 spent on weights (about 15% on average, since the token at position t sees only t keys). At 131,072 tokens it is 5.9 × 1011, nine times the weights. Kaplan's rule calls this term small because it assumes a dense model whose attention width equals its model width. GLM-4.5's attention width, 96 × 128 = 12,288, exceeds its hidden size of 5,120, and as an MoE it has few active weights per layer.
The training bill is paid once, however long users' conversations later get; the serving bill grows with every token of context. An agent keeps its task, opened files, tool outputs and reasoning in context across many steps, and Kimi K3's report describes training for million-token agentic RL (reinforcement learning on multi-step tool-using tasks, where each rollout keeps the whole session in context). Each session owns a cache that grows linearly with n. Capacity caps how many sessions fit on a server, and so the throughput; bandwidth sets the time per token. The 2025-26 reports name this cause and report cache sizes at 1M tokens.
Many of the later chapters shrink the term 2·L·hkv·dh·b·n: compress each layer's entry (multi-head latent attention, MLA), let most layers see only a recent window, replace the growing cache with a fixed-size state (linear attention), read only selected entries (sparse attention), or store fewer bytes per number. DeepSeek-V3's MLA caches 512 + 64 = 576 numbers per layer per token (a 512-number compressed latent plus a 64-number position key; chapter 03) where GLM-4.5 caches 2 × 8 × 128 = 2,048, so its 128K cache is 61 × 576 × 2 × 131,072 ≈ 9.2 GB instead of 49.4 GB. Hybrids shrink L, the number of layers that keep a cache. Qwen3.5 (397B total) has 60 layers, but only its 15 gated-attention layers cache keys and values, with 2 KV heads of dimension 256; the other 45 are Gated DeltaNet linear-attention layers that keep a fixed state (chapter 03). Its 128K cache is 15 × 2 × 2 × 256 × 2 × 131,072 ≈ 4.0 GB, plus about 94 MB of state (45 layers × 64 heads × 128 × 128 numbers at 2 bytes) that does not grow with n.
"Concurrently, the emergence of long-horizon scenarios and tasks — from complex agentic workflows to massive cross-document analysis — has also made efficient support for ultra-long contexts critical for future progress."
"The widespread adoption of long-horizon agents has made model workloads increasingly input-heavy. Although prior work has substantially reduced the cost of long-context computation, prefill remains computationally expensive, and large KV caches continue to strain HBM and SSD capacity and data-transfer bandwidth."
"Kimi Linear outperforms full MLA with a sizeable margin across all evaluated tasks, while reducing KV cache usage by up to 75% and achieving up to 6× decoding throughput for a 1M context."
Per token per layer: 2 × 8 × 64 × 2 = 2,048 bytes. Full layers: 18 × 2,048 × 131,072 ≈ 4.83 GB; windowed layers: 18 × 2,048 × 128 ≈ 4.7 MB; total ≈ 4.84 GB. All-full would be 36 × 2,048 × 131,072 ≈ 9.66 GB. (From the gpt-oss-120b config.json.)
One read of a shared weight matrix serves all B tokens in the step. Each sequence has its own keys and values, so doubling the batch doubles the cache bytes read while the cache's FLOPs per byte stay fixed.
How large are the 2025-26 base models, how much of each one does a single token use, and why were the large MoE bases trained on 20 to 140 times more tokens per active parameter than Chinchilla's compute-optimal ratio for dense models (and small dense models even more)?
In a dense transformer every parameter processes every token, so the total count N equals the active count Na. Gemma 3 27B is dense: of its 25,600M non-embedding, 1,416M embedding and 417M vision-encoder parameters, all the non-embedding ones serve each text token (the embedding table is a lookup and the vision encoder runs only on images).
A mixture-of-experts (MoE) model pulls the two counts apart: each feed-forward block becomes E experts, and a router picks k of them per token (chapter 02), what Shazeer et al. called conditional computation, where parts of the network are active on a per-example basis. More experts raise N; Na depends only on k and the expert size.
A toy with easy numbers shows how far apart they can drift: 4B of shared weights (attention and the other non-expert layers) that every token uses, and 96B of experts, 64 per layer, with k = 4 per layer.
Doubling the experts to 128 per layer, each the same size, makes the total 4B + 192B = 196B, while a token still uses 4B + 192B × 4/128 = 10B.
The ratio of the two counts is the active share:
It is 100% for a dense model and falls from 10% to 10/196 ≈ 5.1% as the toy's experts double. The two counts pay for different things (chapter 00):
Capacity is bought with memory rather than compute, which is why totals can grow much faster than active counts.
def moe_ffn(x, router, experts, k): # x: [d], one token's hidden state
scores = router(x) # [E], one score per expert
top = argsort(scores)[-k:] # the k highest-scoring experts
w = softmax(scores[top]) # [k] mixing weights (one common choice)
y = zeros_like(x)
for wi, e in zip(w, top): # only k of the E experts run, so the
y += wi * experts[e](x) # token pays FLOPs for k experts, not E
return y # but all E experts must stay in memory
Real bases sit near or below the doubled toy's 5.1%. DeepSeek-V3 runs 37B of 671B per token (5.5%), gpt-oss-120b 5.13B of 116.83B (4.4%), Kimi K3 104.2B of 2.78T (3.7%) and Kimi K2 32.6B of 1.04T (3.1%). Kimi K2 in bfloat16 needs 1.04T × 2 bytes ≈ 2.1 TB for its weights while doing the arithmetic of a 32.6B dense model.
In Figure 1.1, read each outline as memory and its filled sliver as compute per token: the totals run from 27B to 2.78T, while the MoE active counts stay between 5B and 41B, Kimi K3's 104.2B aside.
Beyond these nine, every new base above 32B parameters in the grid these notes were first written from uses experts. Of its 31 new pretraining runs, 21 are MoE, 3 mix dense and MoE layers, and the 7 dense ones have at most 32B parameters. Among the labs added later, the one exception is NVIDIA's Nemotron-H-56B (Mar 2025), which has no experts: about 8% of its layers are attention, and the rest alternate Mamba-2 state-space layers (chapter 03) with dense feed-forward layers.
How long should a model train? For dense models the question is how to split a fixed training budget C ≈ 6ND between parameters and tokens.
The Chinchilla paper (Hoffmann et al., 2022) trained over 400 models from 70M to over 16B parameters and concluded that for every doubling of model size the number of training tokens should also be doubled: its three methods give N and D each growing as about C0.5, so doubling the budget scales both by √2 ≈ 1.4 and keeps their ratio. Kaplan et al. had found the optimal model size growing as C0.73 and the tokens as C0.27, which put most extra compute into larger models. The paper's Table 3 puts a 1B model at 20.2B tokens and a 10B model at 205.1B, about 20 tokens per parameter, and Chinchilla itself, 70B parameters on 1.4T tokens, sits at exactly 20.
Compute-optimal answers one question: the lowest training loss for a given training budget. It counts nothing that happens after training, and it was fitted on dense models. The next section adds both.
Sardana et al. add the serving bill. A model trained on Dtrain tokens and then used on Dserve tokens costs
If Dserve = 10Dtrain, for example, serving costs 20 · N · Dtrain, over three times the training bill. When Dserve is large, a smaller N trained on more tokens reaches the same loss for less total compute, even though its training run alone is less efficient. Sardana et al. conclude that labs expecting large demand should train models smaller and longer than Chinchilla-optimal, and their models kept improving up to 10,000 tokens per parameter. LLaMA made the same case in 2023: a smaller model trained longer will ultimately be cheaper at inference.
Experts strengthen the argument. In an MoE model both terms scale with Na, so a lab can keep Na small for cheap tokens, grow the total for capacity, and train on as many tokens as its data supports. The ratio to watch is therefore D/Na, tokens per active parameter. Chinchilla's 20 was fitted on dense models and no comparable value is established for MoE models, so the multiples of 20 below are a rough guide to how far labs go past the dense rule, not an exact measure of over-training.
def lifetime_flops(N_a, D_train, D_serve): # Sardana et al., with N replaced by N_a
return 6 * N_a * D_train + 2 * N_a * D_serve
Each row divides a lab's stated pre-training tokens by its active count and compares the result with Chinchilla's 20.
| Release | D | Na | D / Na | × Chinchilla's 20 |
|---|---|---|---|---|
| DeepSeek-V3 | 14.8T | 37B | 14.8 × 1012 / 37 × 109 = 400 | 20 |
| Kimi K2 | 15.5T | 32.6B | 15.5 × 1012 / 32.6 × 109 ≈ 475 | 24 |
| Llama 4 Maverick | ~22T | 17B | 22 × 1012 / 17 × 109 ≈ 1,294 | 65 |
| DeepSeek-V4.1-Flash | 45T | 16B | 45 × 1012 / 16 × 109 ≈ 2,813 | 141 |
At Chinchilla's dense ratio, DeepSeek-V3 would have stopped at 20 × 37B = 0.74T tokens; it trained on 20 times that. V4.1-Flash's 16B is its decode-time count. Its causal encoder-decoder layout lets it compute only the first half of its layers when it processes a prompt, so it activates 8B during prefill (chapter 13).
The same choice from the other side: V3's budget was 3.29 × 1024 FLOPs (chapter 00). A dense model sized by the Chinchilla rule (D = 20N) for that budget satisfies 6 · N · 20N = C, so N = √(C/120) ≈ 165B parameters on 3.3T tokens. Serving it costs 2 × 165B ≈ 3.3 × 1011 FLOPs per token; V3 costs 2 × 37B = 7.4 × 1010, 4.5 times less, and pays instead by keeping 671B parameters in memory.
Figure 1.2 widens this to twelve releases on a log scale: the MoE bases sit 20 to 140 times past Chinchilla's dashed 20, and the small dense Gemma 3 270M far beyond, at 22,222.
Releases come in two kinds:
Labs therefore train bases rarely and release on top of them often. Of the 68 releases in that grid, 31 are new pretraining runs; 19 change only post-training, 10 continue pretraining an existing base, 2 are speculative-decoding drafters, 1 is a distilled student and 5 do not say. A drafter is a small model that proposes tokens for the large one to check (chapter 06); a distilled student is trained to imitate another model's outputs (chapter 12).
DeepSeek-V3's base, released in December 2024, carried nine more releases up to V3.2 in December 2025. Two of them added continued pretraining: 840B tokens of long-context training for V3.1, 5.7% of the base's 14.8T, and a phase that added sparse attention for V3.2-Exp. A refresh can change data, context length and even attention, but it keeps the base's N and Na.
"In MoE models, a single token activates only a fraction of the total parameters. MoE architectures are more compute efficient for training and inference and, given a fixed training FLOPs budget, delivers higher quality compared to a dense model."
"we observe that — under a fixed number of activated parameters (i.e., constant FLOPs) — increasing the total number of experts (i.e., increasing sparsity) consistently lowers both the training and validation loss, thereby enhancing overall model performance"
"Meanwhile, its small activation footprint yields low inference latency and serving cost."
6 × 32.6 × 109 × 15.5 × 1012 ≈ 3.0 × 1024 FLOPs, close to DeepSeek-V3's 3.3 × 1024. Weights: 1.04 × 1012 × 2 bytes ≈ 2.1 TB, about 1.5 times V3's 1.34 TB, even though its per-token arithmetic is slightly smaller.
Each served token saves 2 × (165 − 37) × 109 ≈ 2.6 × 1011 FLOPs. Dividing the budget, 3.29 × 1024 / 2.6 × 1011 ≈ 1.3 × 1013: about 13T served tokens, less than its own training set.
How can DeepSeek-V3 store 671B parameters but run only 37B of them for each token, and what keeps its 256 experts evenly used?
A transformer block has two parts: attention, where tokens read each other (chapter 03), and a feed-forward network (FFN), which transforms each token on its own. A standard block sends every token through the same FFN, so every FFN weight works on every token. DeepSeek-V3's FFN is a SwiGLU block with three matrices and 3 · d · dff weights, for model width d and inner width dff.
The gate branch decides, channel by channel, how much of the up branch passes through.
A mixture-of-experts (MoE) layer replaces that FFN with E smaller FFNs, called experts, and a router that sends each token to k of them:
That is how DeepSeek-V3 holds 671B parameters in total (N) but runs 37B per token (Na).
The router holds one learned vector ei ∈ ℝd per expert and takes four steps for each token u:
with u, h′ ∈ ℝd, s, b ∈ ℝE and |Top(u)| = k. The gate is what trains the router: each chosen expert's output is scaled by its score, so the loss gradient can raise or lower that score. Figure 2.1 draws the same steps for one token.
A toy layer. Take E = 4 routed experts, k = 2 and four tokens, with no bias yet:
| Token | Scores s, experts 1–4 | Top 2 | Gates |
|---|---|---|---|
| A | 0.9, 0.6, 0.2, 0.1 | 1, 2 | 0.6, 0.4 |
| B | 0.6, 0.1, 0.4, 0.1 | 1, 3 | 0.6, 0.4 |
| C | 0.4, 0.6, 0.1, 0.3 | 2, 1 | 0.6, 0.4 |
| D | 0.3, 0.1, 0.7, 0.2 | 3, 1 | 0.7, 0.3 |
A's gates are 0.9/1.5 and 0.6/1.5. Each token runs two of the four stored experts. Now count the load: expert 1 gets all four tokens and expert 4 none, where a fair share is 4 × 2/4 = 2 each.
Early MoE layers used a few large experts, often 8 or 16. The DeepSeekMoE paper (2024) argued that each such expert must store mixed knowledge, while knowledge that every token needs gets copied into many experts. It proposed two fixes:
DeepSeek-V3 has 256 routed experts of width 2048, 8 active per token, and 1 shared expert.
Nothing in the language-modeling loss spreads tokens out, and imbalance like the toy's costs twice:
An auxiliary loss is the classic fix. Switch Transformer adds
which is smallest under uniform routing, with α = 10−2. Wang et al. (2024) showed the bind: a small α lets routing collapse, and a large α adds gradients that conflict with language modeling and hurt perplexity.
A routing bias, their auxiliary-loss-free method, adds a per-expert bias bi to the scores only when choosing the top k. Gates use the raw scores, so the bias changes which experts run but leaves the output weights and gradients alone. After each step an overloaded expert's bias falls by γ and an underloaded one's rises by γ. The update uses only the finished batch, so no routing decision depends on future tokens.
In the toy, one update with γ = 0.2 gives b = (−0.2, 0, 0, +0.2). C's biased scores become (0.2, 0.6, 0.1, 0.5) and D's (0.1, 0.1, 0.7, 0.4), so both swap expert 1 for expert 4 and every expert gets 2 tokens. C's gates still come from its raw scores, 0.6 and 0.3: 2/3 and 1/3.
At 1B parameters this improved validation perplexity from 9.56 to 9.50 and cut MaxVio, the busiest expert's excess over a balanced load, from 0.72 to 0.04.
def moe_layer(u, W_r, b, experts, shared, k):
# u: [B_tok, d] tokens; W_r: [E, d] expert vectors; b: [E] routing bias
s = sigmoid(u @ W_r.T) # [B_tok, E] affinity scores
top = topk_indices(s + b, k) # [B_tok, k] bias only picks experts
g = take_along_axis(s, top, axis=-1) # [B_tok, k] gates use raw scores
g = g / g.sum(axis=-1, keepdims=True) # normalize over the k chosen
y = shared(u) # [B_tok, d] always-on shared expert
for t in range(len(u)):
for j in range(k):
y[t] += g[t, j] * experts[top[t, j]](u[t])
load = bincount(top.ravel(), minlength=len(b)) # [E] tokens per expert
return u + y, load
def update_bias(b, load, gamma=1e-3):
# after the optimizer step; the new b routes the next batch
return b + gamma * sign(load.mean() - load) # lift idle, damp busy
DeepSeek-V3 uses γ = 0.001 for the first 14.3T tokens and 0 for the last 500B, plus a sequence-wise loss with α = 0.0001 against extreme imbalance inside one sequence. Balancing at batch level leaves each sequence free to favor the experts for its domain. In DeepSeek's 1B tests validation loss was 2.258 with a sequence-wise loss and 2.253 with the bias or a batch-wise loss. Microsoft AI found the same for MAI-Thinking-1: an f·P auxiliary loss in GShard's form, with f and P counted over the whole global batch (all data-parallel workers and micro-batches) instead of per sequence, matched the loss-free method.
Quantile Balancing. Kimi K3 (2026) raised the count to 896 routed experts and found the fixed step too blunt: a small γ adapts slowly and a large one makes loads oscillate. Quantile Balancing sets each bias in one shot, to the value that would have given that expert exactly its fair share of the batch just routed. In K3's toy example, 8 tokens, 4 experts and k = 1 start with loads (4, 3, 1, 0) and end with 2 each. K3 reads the quantile from per-GPU histograms summed in one all-reduce, applies the bias from the next step, and freezes it at inference.
In a batch of Btok tokens, each expert's fair share is q = Btok · k/E tokens, a fraction k/E of the batch. For every token t the router records its (k+1)-th largest biased score as a cutoff ct: the score an expert must beat to enter that token's top k. Holding the cutoffs fixed, token t picks expert j when st,j + b̂j > ct, that is, when the margin st,j − ct exceeds −b̂j. For exactly a fraction k/E of the margins to exceed −b̂j, −b̂j must be their (1 − k/E) quantile, which gives the minus sign:
Subtracting the mean shifts all of a token's scores equally, so no top-k choice changes.
Kimi K2 defines sparsity as E/k; the toy's is 2. More experts at fixed k cost memory but no extra expert compute per token, and the labs found they pay off:
A latent MoE makes each expert cheaper by running the routed experts in a narrower space. NVIDIA's LatentMoE in Nemotron 3 Super (Mar 2026) runs them in a 1,024-wide latent space instead of the 4,096-wide hidden state and spends the savings on 512 experts with 22 active. Kimi K3 did the same four months later: it projects each token from width 7,168 down to a 3,584-wide latent vector once, sends that vector to its routed experts, and projects their combined output back up; shared experts keep the full width. Every copy sent to an expert, and every expert's input and output matrix, is half as large, which lets K3 afford 16 of 896.
| Model (release) | Routed E | Active k | Shared | E/k |
|---|---|---|---|---|
| DeepSeek-V3 (Dec 2024) | 256 | 8 | 1 | 32 |
| Kimi K2 (Jul 2025) | 384 | 8 | 1 | 48 |
| Qwen3-Next (Sep 2025) | 512 | 10 | 1 | 51 |
| Nemotron 3 Super (Mar 2026) | 512 | 22 | 1 | 23 |
| DeepSeek-V4-Pro (Apr 2026) | 384 | 6 | 1 | 64 |
| MAI-Thinking-1 (Jun 2026) | 512 | 8 | 0 | 64 |
| Kimi K3 (Jul 2026) | 896 | 16 | 2 | 56 |
| Ling-3.0-flash (Aug 2026) | 512 | 8 | 1 | 64 |
An MoE buys loss per FLOP with memory. Meta's Muse Glimmer (2026) had to fit a 32GB memory budget, so Meta compared ladders (families of models of increasing size built by one rule) for a dense design and two MoE designs with 1 of every 8 or 16 experts active, holding inference memory fixed. At equal memory an MoE token uses only part of the parameters and so costs less compute; within the same GPU-hours the MoE ladders trained on 2,800 and 6,000 tokens per parameter against 550 for dense, and dense was still 2–2.5× more compute-efficient. Meta chose a dense 30B model.
From config.json: d = 7168, expert width 2048, 256 routed and 1 shared expert, 8 routed per token, 61 layers with the first 3 dense. Each expert has three matrices (gate, up, down).
"To achieve a better trade-off between load balance and model performance, we pioneer an auxiliary-loss-free load balancing strategy (Wang et al., 2024a) to ensure load balance."
"Though increasing sparsity leads to better performance, this gain comes with increased infrastructure complexity. To balance model performance with cost, we adopt a sparsity of 48 for Kimi K2, activating 8 out of 384 experts per forward pass."
"Scaling curves (Fig 1) demonstrated that within constraints of training compute and parameter size, dense configurations yielded a 2×-2.5× increase in compute efficiency compared to the MoE counterparts."
The gate scales the expert's output. A bias inside it would change what the layer computes and push balancing pressure into the gradients, which is the interference the auxiliary loss caused. Outside the gate, the bias only changes which experts run.
FLOPs per token are equal, since both run 8 experts. B stores 6× the expert weights. Sparsity is 8 for A and 48 for B. Kimi K2's law says B's design reaches validation loss 1.5 with 1.69× fewer training FLOPs, paid for in memory and communication.
Why does every token a model has already read cost memory while it generates, and what did the labs change in attention to make 128K to 1M-token contexts affordable?
In multi-head attention (MHA), each of h heads has its own queries, keys and values of size dh:
Every head caches its own keys and values (hkv = h), so chapter 00's formula becomes
Chapter 00's toy model (32 layers, heads of 128 numbers, bfloat16) with 32 such heads needs 2 × 32 × 32 × 128 × 2 = 524,288 bytes (512 KiB) per token, 64 GiB at 131,072 tokens. That cache costs capacity, since it must sit in GPU memory, and bandwidth, since every new token reads all of it. Each design below changes one thing:
Multi-query attention (MQA) keeps h query heads but one key/value head shared by all of them, shrinking the cache h-fold with "only minor quality degradation" in Shazeer's tests. Grouped-query attention (GQA) splits the query heads into hkv groups that each share one key/value head; GQA-1 is MQA and GQA-h is MHA. A new token still reads every cached token, but each entry is h/hkv times smaller: in the toy, 16 GiB at 128K with 8 KV heads and 2 GiB with MQA.
GQA's authors converted MHA checkpoints by mean-pooling each group's heads and training for 5% of the original compute. GLM-4.5 uses 96 query and 8 KV heads; gpt-oss uses 64 and 8. StepFun's Step-3 (Jul 2025, 321B) went to the MQA end: its 64 query heads share one key and one value head of dimension 256, and its queries are factorized through a low-rank bottleneck (MFA). Step 3.5 Flash moved to GQA with 8 KV heads.
Figure 3.1 compares what MHA, GQA and MLA keep per token in one layer.
Multi-head latent attention (MLA), from DeepSeek-V2, keeps h distinct key and value heads but stores none of them. It compresses each hidden state xt into a short latent vector, from which fixed matrices rebuild every head's keys and values:
The cache holds the latent (dc numbers) plus a small position key kR (dr numbers) shared by all heads. A new token still reads every cached token, but each entry shrinks from 2hdh numbers per layer to dc + dr. With DeepSeek-V3's dc = 512 and dr = 64, the toy model would store 576 numbers per layer, 36 KiB per token and 4.5 GiB at 128K, a fourteenth of MHA.
During decoding the keys and values are never rebuilt: the expansion matrices fold into the query and output projections, so all heads read the same cached latents, which DeepSeek calls the MQA mode of MLA. Rotary position embedding (RoPE) would block that folding, hence the separate position key.
Only ct is cached, and during decoding the expansion need not run. Head i's score is qt,i⊤WiUKcs = (WiUK⊤qt,i)⊤cs, so WUK is "absorbed" into the new query. Values work the same way: with attention weights ats,i, head i outputs WiUV∑sats,ics, and the output projection block WiO multiplies that, so
WiOWiUV is one matrix computed once, and attention only mixes cached latents.
RoPE breaks the absorption. In brief (chapter 04 has the details), RoPE rotates a query at position t by a matrix Rt and a key at position s by Rs; since Rt⊤Rs = Rs−t, their dot product is q⊤Rs−tk, which depends only on the offset. For MLA keys that gives q⊤Rs−tWUKcs: a rotation between query and WUK that differs for every cached token, so nothing can be absorbed. DeepSeek's decoupled RoPE splits each head's query and key into an unrotated content part and a small rotated part. The rotated key kRs ∈ ℝdr is shared by all heads; each head has its own rotated query part. The score adds the two dot products:
The content term absorbs WUK as before; the rotary term reads the cached kRs. The cache holds dc + dr numbers per layer.
Absorbed MLA trades memory for arithmetic: every head scores every cached latent over dc + dr dimensions and mixes it over dc, 1,088 multiply-adds per cached token and head in DeepSeek-V3's shape, against 256 for a 128-number MHA head. With 128 heads sharing each 1,152-byte bfloat16 entry, that is about 240 FLOPs per cached byte, against 12 for GLM-4.5 (chapter 00) and near the ~295 an H100 needs, so the head count drives long-context cost.
Kimi K2 kept MLA but used 64 heads instead of DeepSeek-V3's 128: at 128K tokens, doubling heads adds 83% inference FLOPs for only 0.5–1.2% lower validation loss. Mistral Large 3 (Dec 2025) and Small 4 also use MLA-type attention, with latents of 512 and 256 numbers per layer in their configs.
n = 131,072 tokens, b = 2 bytes (BF16 assumed; configs do not fix serving precision); KB, MB, GB are powers of 10.
DeepSeek-V3, MLA: dc = 512, dr = 64, L = 61. (512 + 64) × 61 = 35,136 numbers × 2 B = 70,272 B ≈ 70.3 KB per token; × 131,072 ≈ 9.2 GB.
GLM-4.5, GQA: hkv = 8, dh = 128, L = 92. 2 × 8 × 128 × 92 = 188,416 numbers × 2 B ≈ 376.8 KB per token; ≈ 49.4 GB at 128K, 5.4× DeepSeek-V3.
MHA with DeepSeek-V3's shape: h = dh = 128, 61 layers. 2 × 128 × 128 × 61 = 1,998,848 numbers × 2 B ≈ 4.0 MB per token; ≈ 524 GB at 128K, 57× MLA.
A sliding-window layer keeps fewer tokens instead of smaller entries: it attends only to the last w tokens, so its cache stops growing at w and each new token reads at most w entries. Older tokens are out of its reach, so models interleave window layers with global (full) layers, in a ratio written local : global. In the toy with 8 KV heads, a 3 : 1 mix with w = 1,024 keeps 4 GiB in its 8 global layers and 96 MiB in its 24 window layers, about 4.1 GiB instead of 16.
Small windows expose a quirk of softmax: weights must sum to one, so a head with nothing to read still puts attention somewhere. StreamingLLM found models park it on the first tokens, which every later token sees, and called them attention sinks. Window attention collapses once they leave the window; keeping 4 initial tokens fixes it. gpt-oss instead gives each head a learned bias in the softmax denominator, "which enables the attention mechanism to pay no attention to any tokens". MiMo-V2-Flash copies this on its window layers (config add_swa_attention_sink_bias):
In MiMo's 32B ablation a w = 128 hybrid scored 54.9 on MMLU without sinks, 58.3 with them, and all-global attention 57.3.
Figure 3.2 compares which keys each query reads under each scheme.
A window chooses what to read by position; sparse attention chooses by content. It keeps the whole cache but reads only a few entries per new token. DeepSeek Sparse Attention (DSA), introduced in DeepSeek-V3.2-Exp and kept in V3.2, adds a lightning indexer that scores each earlier token s for the current token t with HI small heads:
Main attention then reads only the m = 2048 highest-scoring entries, so its work per token falls from O(n) to O(m). The indexer still scans all n, but it "has a small number of heads and can be implemented in FP8". DSA uses MLA's MQA mode, so each selected latent serves every head. At 128K that is one cached latent in 64.
def dsa_decode_step(x_t, latents, idx_keys, m=2048):
# latents: [t, d_c + d_r] MLA cache, shared by all heads
# idx_keys: [t, d_I] one small indexer key per past token
q_I, w_I = indexer_queries(x_t) # [H_I, d_I], [H_I]
scores = relu(q_I @ idx_keys.T) # [H_I, t] few heads, FP8
I = (w_I[:, None] * scores).sum(axis=0) # [t] one score per past token
sel = top_k(I, min(m, len(I))) # indices of m tokens
return mla_attention(x_t, latents[sel]) # core attention reads m entries
DSA was added to dense-attention checkpoints by continued training, in two stages from DeepSeek-V3.1-Terminus:
GLM-5 added DSA the same way and reports 1.5–2× less attention compute on long sequences. GLM-5.2 trims the indexer: IndexShare picks top-k once per 4 layers, for 2.9× fewer per-token FLOPs at 1M.
Three more labs select blocks of tokens instead of single tokens, on GQA instead of MLA:
DeepSeek-V4 merges consecutive tokens into single cache entries, with two layer types:
At 128K tokens a V4-Pro CSA layer keeps 32,768 entries and reads 1,024, an HCA layer keeps and reads 1,024, and both read the last 128 tokens exactly. Together with FP8 storage for most KV dimensions (which alone nearly halves the cache), they bring the cache to about 2% of a BF16 GQA baseline (8 KV heads of dimension 128) at 1M tokens.
Linear attention removes the growing cache. Each head keeps one fixed-size matrix, its state S, which every new token updates with its key and value and reads with its query, so memory and work per token are fixed, whatever n is. In the toy, a 128 × 128 state per head is 32 KiB in bfloat16: 1 MiB for a 32-head layer at any length, against 512 MiB at 128K for a GQA layer with 8 KV heads.
The state comes from regrouping attention. Katharopoulos et al. replaced the softmax similarity exp(q·k) with a plain product φ(q)·φ(k). Take φ as the identity and drop the normalizer, as is now common. Because matrix products are associative, the output can be regrouped:
Attention becomes a recurrent network whose state S replaces the cache.
The catch is recall. Reading with a stored key ks returns Stks = ∑s′ vs′(ks′·ks), which equals vs only if ks has unit length and every other key is orthogonal to it. dk-dimensional keys allow that for at most dk tokens, and S can add associations but never remove them, so stored values blur. The DeltaNet paper calls these key "collisions".
Two changes to the write sharpen recall:
Gated DeltaNet uses both, since "gating enables rapid memory erasure while the delta rule facilitates targeted updates". Kimi Delta Attention (KDA), from Kimi Linear, gives each key channel its own decay. Figure 3.3 shows one step.
def gated_delta_step(S, q, k, v, alpha, beta):
# S: [d_v, d_k] state; q, k: [d_k] (k L2-normalized); v: [d_v]
# alpha: scalar (Gated DeltaNet) or [d_k] vector (KDA); beta in (0, 1)
S = S * alpha # 1 forget (per column for KDA)
v_old = S @ k # what memory returns for key k
S = S + beta * outer(v - v_old, k) # 2+3 erase old value, write new
o = S @ q # read with the query
return S, o
A fixed state cannot hold every detail of a long document, so production models keep some full-attention layers, in a ratio written linear : full. A 3 : 1 hybrid of the toy keeps 4 GiB at 128K in its 8 full layers plus 24 MiB of state in the other 24.
Kimi Linear repeats 3 KDA layers and 1 MLA layer (3 : 1); in its ablation 3 : 1 had the lowest validation perplexity, 5.65, ahead of 1 : 1 (5.66, but slower), 7 : 1 (5.70), all-MLA (5.77) and 15 : 1 (5.82). With the same 1.4T-token recipe as a full-MLA baseline, it scored 51.0 vs 47.2 on MMLU-Pro and 84.3 vs 81.3 on RULER at 128K, with up to 75% less KV cache and up to 6× decoding throughput at 1M (per-token time 6.3× faster in its Figure 1).
Other models mix differently:
MiniMax later went the other way. M2 (Oct 2025) returned to full attention in all 62 layers, because hybrids that had matched full attention on standard benchmarks showed deficits in multi-hop reasoning at larger scale.
| Design | Cache per token, per layer | Attention work per new token, per layer | Gives up |
|---|---|---|---|
| MHA | 2hdh | ∝ n·h·2dh | Nothing; largest cache |
| GQA / MQA | 2hkvdh | Same; h/hkv× less to read | Some quality as hkv falls |
| MLA (absorbed) | dc + dr | ∝ n·h·(2dc + dr) | More arithmetic; decoupled RoPE |
| Window + global | Window layers: last w tokens only | ∝ w (window), ∝ n (global) | Old tokens in window layers |
| Sparse top-k (DSA) | As MLA, plus an indexer key | ∝ m, plus a cheap ∝ n indexer | Memory unchanged; may miss tokens |
| Compressed (CSA / HCA) | One entry per 4 or 128 tokens, plus last w | ∝ m + w (CSA), ∝ n/128 + w (HCA) | Detail inside each block |
| Linear (KDA) | None: fixed dv×dk state per head | ∝ dvdk | Exact recall |
Work counts multiply-adds per cached entry and head. MHA needs dh for the score and dh to mix values; absorbed MLA needs dc + dr for the score (latent plus rotary key) and dc to mix latents.
"Given that sparsity 48 already offers strong performance, the marginal gains from doubling attention heads do not justify the inference cost."
"we empirically observed that learnable attention sinks bias dramatically enhance the performance of hybrid SWA models, matching or even surpassing baselines with fully GA layers."
"Long-context retrieval remains the primary bottleneck for pure linear attention, we therefore hybridize KDA with a small number of full global-attention (Full MLA) layers"
"The price paid became obvious at a larger scale: the model had clear deficits in complex, multi-hop reasoning tasks."
The rotation in q⊤Rs−tWUKcs depends on each cached token's position, so WUK could not be absorbed and every key would be re-expanded at every step. Decoupled RoPE moves position into a separate small key (64 dimensions in DeepSeek-V3).
Any past token may be selected later, so all n latents stay in memory. V4's CSA and HCA store one compressed entry per 4 or 128 tokens, plus a short window of exact entries.
How much memory does each attention design need for a long conversation, and how much does it read per generated token?
Pick models and a context length. The calculator uses each model's published configuration (hover a model name for its source) and the formulas from chapters 00 and 03. KV cache is what the model must keep in memory per sequence; reads per token is how many bytes of cache or state one generated token has to read. Numbers assume the cache is stored at the precision stated for the model, or 2 bytes (BF16) when none is stated; linear-attention states are also counted at 2 bytes, and at FP32 they would double.
How does attention know where each token sits, and why do several 2026 models leave position encoding out of their global layers?
An attention score depends only on the contents of a query and a key. Shuffle the earlier tokens and each query returns the same weighted sum, so "dog bites man" and "man bites dog" look alike. As the Transformer paper put it, "Since our model contains no recurrence and no convolution, in order for the model to make use of the order of the sequence, we must inject some information about the relative or absolute position of the tokens in the sequence." It added sinusoidal vectors to the input embeddings. Current models put position inside attention instead.
Draw two numbers of a query as an arrow in the plane. Rotary position embedding (RoPE), from RoFormer, turns that arrow by tθ for a token at position t, and turns a key at position s by sθ. A dot product is the two lengths times the cosine of the angle between the arrows. Turning changes no lengths, so the score changes only through that angle, which shifts by (s − t)θ: the distance between the tokens, not where they sit.
A toy with easy angles: θ = 20° per position, and a query and key that start 10° apart.
Figure 4.1 draws both cases; its right panel shows that a real head turns many pairs, each at its own speed.
A head of dimension dh splits each query and key into dh/2 pairs and turns pair i by tθi, with
RoFormer uses base = 10,000, so pair 0 turns one radian per token and later pairs ever more slowly. Call the whole block-diagonal rotation Rt. Rotations add their angles and Rt⊤ = R−t, so, as in the toy, a query at t and a key at s score
Each vector carries its absolute position, but attention sees only the offset s − t. Values are not rotated.
YaRN calls λi = 2π/θi the wavelength: the number of tokens pair i needs for one full turn. In Figure 4.1's right panel, after 10 tokens pair 0 has turned 213° and pair 3 under 1°. Fast pairs separate neighboring tokens; slow pairs barely move over thousands of tokens. The base sets the slowest wavelength, which is why it is tied to context length.
def rope(x, pos, base=10_000.0, p=1.0):
# x: [n, d_h] queries or keys of one head; pos: [n] positions t
d_h = x.shape[-1]
i = arange(d_h // 2)
theta = base ** (-2 * i / d_h) # [d_h/2], fastest pair first
theta = where(i < int(p * d_h // 2), theta, 0.0) # p-RoPE: drop slow pairs
ang = pos[:, None] * theta[None, :] # [n, d_h/2] angle t * theta_i
x1, x2 = x[:, 0::2], x[:, 1::2] # the d_h/2 pairs
out = empty_like(x)
out[:, 0::2] = x1 * cos(ang) - x2 * sin(ang)
out[:, 1::2] = x1 * sin(ang) + x2 * cos(ang)
return out # p = 1: RoPE; p = 0: no encoding
Take a toy head with dh = 8, so i = 0…3 and θi = base−i/4. The table gives each pair's speed θi and wavelength λi in tokens.
| Pair | Speed, base 10,000 | Wavelength | Speed, base 1,000,000 | Wavelength |
|---|---|---|---|---|
| 0 | 1 | 6.3 | 1 | 6.3 |
| 1 | 0.1 | 62.8 | 0.0316 | 198.7 |
| 2 | 0.01 | 628 | 0.001 | 6,283 |
| 3 | 0.001 | 6,283 | 3.16 × 10−5 | 198,692 |
With base 10,000 the slowest pair turns 32,768 / 6,283 ≈ 5.2 times across a 32K context, so two tokens about 6,283 apart get the same angle on that pair. With base 1,000,000 it turns about 0.16 times. For a head that rotates all 128 dimensions, the longest wavelength is 2π · base126/128: about 54,000 tokens at base 10,000 and about 5.1 million at 1,000,000. Labs raise the base as context grows:
Many models rotate only some dimensions of each query and key. The rotated ones carry position; the unrotated ones score the same at any distance, so they compare content alone.
Why want such channels? Barbero et al. examined Gemma 7B and found it builds positional patterns from the fastest pairs and gives the slowest pairs the largest query and key norms, apparently using them as channels that compare content. Even slow pairs drift over long distances, so those channels are not robust. Their p-RoPE keeps the fraction p of pairs with the highest frequencies and stops rotating the rest: p = 0 is no encoding and p = 1 is RoPE. In Figure 4.1's toy head, 0.75-RoPE would freeze pair 3. Training 2B models at 8K, 0.75-RoPE reached validation perplexity 4.4414 on Wiki, against 4.4627 for RoPE and 4.8594 with no encoding.
Among the models here:
A decoder can learn position with no encoding at all (NoPE). The causal mask gives each token a different set of visible tokens, and Kazemnejad et al. prove that the first layer can then recover absolute positions: a head that spreads attention evenly and reads a flag set only on the first token outputs 1/t at position t (at position 4 it averages four tokens, one flagged, and outputs 1/4). In their length-generalization tests, NoPE beat RoPE, ALiBi and absolute embeddings.
Several labs now drop RoPE on global layers and get order elsewhere (Figure 4.2):
The results cut both ways. In Kimi Linear's matched runs, NoPE beat a RoPE variant on long-context benchmarks, 54.5 vs 51.8 on average (RULER 84.3 vs 78.8), and without a rotary key, MLA also runs as pure MQA at inference. Qwen, however, tested NoPE for Qwen3.8-Flash-Next and kept partial RoPE: the two looked alike in pretraining, but the NoPE variant fell into endless generation far more often after post-training.
Inkling takes a third route: instead of RoPE it learns a relative position embedding, citing Shaw et al. and the Music Transformer. It works like a table indexed by distance. In Shaw's form, each offset s − t, clipped to [−c, c], has its own learned vector aKts, added to the key inside the score:
With c = 2 the table holds five vectors, and keys more than two tokens away share the edge one. Clipping also "enables the model to generalize to sequence lengths not seen during training". Hugging Face's write-up of Inkling describes a fourth projection, besides query, key and value, that produces "a per-token, per-head relative feature R", adjusted by the query-key distance and added into the attention logits. The exact formula has not been published.
RoPE models are usually pretrained at shorter lengths and extended later (see the midtraining notes). The trouble is the slow pairs. If training stopped at length L, a pair whose wavelength exceeds L only ever saw part of a turn, and positions beyond L give it angles it never saw; the fast pairs have already turned many times, so every angle is familiar to them. In the base-10,000 toy head, 4,096 training tokens show pair 3 (λ = 6,283) about 0.65 of a turn and pair 0 about 650 turns. Four fixes are in use:
"The global attention layers do not use any position encoding (Kazemnejad et al., 2023), which performs comparably to RoPE while being more efficient."
"Kimi K3 uses no explicit positional embedding (NoPE), and instead encodes positional information implicitly through the recurrent gating and decay mechanism of KDA."
"We find that encoding position with a relative positional embedding … performs better and extrapolates better to longer sequences than the more widely adopted Rotary Positional Embedding (RoPE)."
The query is rotated by Rt and the key by Rs. Because Rt⊤Rs = Rs−t, the score is q⊤Rs−tk, which is unchanged if both positions shift by the same amount.
λ = 2π · 10,000126/128 ≈ 54,000 tokens. If training stopped at 32K, that pair only ever covered about 0.6 of a turn; at 128K it sweeps through angles it never saw. Raising the base, YaRN or NoPE global layers address this.
How does a 61-layer model like DeepSeek-V4-Pro keep activations and gradients at a usable size from the first layer to the last, and why did DeepSeek widen that path into four parallel streams?
Picture each token carrying one vector of width d up through the network. Each sublayer (attention, then an MLP or MoE, alternating) reads the vector, computes an update and adds it back. Nothing is overwritten, so at the top the vector is the token's embedding plus every update written along the way. This running sum is the residual stream.
A toy with d = 2: the embedding (1, 0), plus attention's update (0, 2), plus the MLP's (1, −1), gives (2, 1). The MLP read (1, 2), which already held attention's write.
Why build it this way? Adding layers should never hurt a network, since the extra layers could learn to copy their input; in practice it did. He et al. called this the degradation problem: deeper plain networks reached higher training error, which rules out overfitting as the cause. Their shortcut lets each block learn only a correction F(x), and adds "neither extra parameter nor computational complexity". The unrolled sum shows why depth becomes trainable: the signal from layer l reaches layer L unchanged, and the gradient ∂xL/∂xl contains an identity term.
The rest of the chapter changes this picture: normalize what sublayers read (pre-norm) and what attention compares (QK-norm), widen the stream into several (HC, mHC, the Gated Residual), or weight earlier writes unequally (AttnRes).
Each read passes through a normalization so the sublayer sees a predictable scale. Most of these models use RMSNorm (Zhang and Sennrich), which divides a vector by its root-mean-square size and multiplies by a learned gain g:
For x = (1, 7), RMS = √((1 + 49)/2) = 5, so with g = (1, 1) the output is (0.2, 1.4), the same as for (10, 70). RMSNorm drops LayerNorm's mean subtraction; it matched LayerNorm's quality and cut running time by 7% to 64%. Where the norm sits changes the picture:
Pre-LN's cost is that the stream is an unweighted sum whose size keeps growing with depth, which Kimi's Attention Residuals paper says ends up "progressively diluting each layer's relative contribution."
Pre-norm bounds the input to attention, but the logit q·k/√dh also grows with Wq and Wk. When Dehghani et al. scaled vision transformers to about 8B parameters, training diverged because logits grew past 50,000 and attention became almost one-hot. QK-norm normalizes each head's queries and keys right before the dot product:
The logits then stay in a range set by the learned norm gains: with RMSNorm and gains of 1, query and key each have length √dh, so a logit is at most √dh, about 11.3 for dh = 128. GLM-4.5, Gemma 3, Gemma 4, MAI-Thinking-1 and Muse Spark Open use QK-norm, and Llama 4 Scout applies a weightless version on its RoPE layers (Maverick's config turns it off). Qwen3 added QK-norm to ensure stable training, but in Qwen3-Next Qwen found some of those norm weights growing abnormally large and moved to zero-centered RMSNorm with weight decay on the norm weights. DeepSeek V4 normalizes each query head and its single compressed KV head. QK-norm needs explicit key vectors. In absorbed MLA (Chapter 03) the per-head keys are never formed at inference, so there is nothing to normalize; Chapter 10 shows how Kimi K2, which uses MLA, kept its logits bounded instead.
Hyper-connections (HC), from ByteDance, keep nhc copies of the stream (the HC paper's "expansion rate"), a matrix Xl ∈ ℝnhc×d, with three small maps per sublayer:
With nhc = 1 and A = B = C = 1 this is the plain residual. F still sees one d-vector, so the sublayers are unchanged, and since nhc is much smaller than d the extra FLOPs are small. In the dynamic version, A, B and C are predicted from the current token. With nhc = 4, OLMoE-1B-7B with HC "converges 1.8 times faster" than without.
The trouble is depth. The signal from sublayer l reaches sublayer L through the product BL−1 ⋯ Bl, and nothing keeps that product near the identity. Small gains compound: sixty mixes that each scale a signal by 1.1 scale it by 1.160 ≈ 304. In DeepSeek's 27B test model, HC's loss surged near step 12k along with the gradient norm, and the worst-case gain of the product (its largest absolute row or column sum) peaked near 3000. A plain residual has gain 1.
DeepSeek's manifold-constrained hyper-connections (mHC) make every mix an average. Each Bl must be doubly stochastic: non-negative, with every row and every column summing to 1, so each output stream is a weighted average of the input streams. With two streams holding 4 and 0, the matrix with rows (0.75, 0.25) and (0.25, 0.75) gives 3 and 1: the total stays 4, and repeated mixing moves the streams toward 2 and 2 without ever growing them. The set of such matrices is called the Birkhoff polytope:
Such a matrix has spectral norm (largest singular value) at most 1, so mixing cannot amplify a signal in the forward or backward pass. Products of doubly stochastic matrices are doubly stochastic, so the bound holds at any depth. For nhc = 1 the only such matrix is 1, the ordinary residual.
The spectral norm is at most the geometric mean of the largest column sum ‖B‖1 and the largest row sum ‖B‖∞, both 1 here. Equivalently, a doubly stochastic matrix is an average of permutation matrices (Birkhoff–von Neumann), each with spectral norm 1.
To put each predicted B on this set, mHC exponentiates it so every entry is positive, then runs the Sinkhorn–Knopp algorithm, dividing columns and then rows by their sums, for 20 iterations. A = σ(·) and C = 2σ(·) are kept positive so streams cannot cancel each other. C ranges over (0, 2), so a write weight of 1, the value a plain residual uses, sits in the middle. Twenty iterations give an approximate projection: each layer's own gain stays close to 1, and the gain of the product across all layers of the 27B model peaked at about 1.6 instead of 3000. It finished with 0.021 lower loss than the residual baseline, and BBH rose from 43.8 to 51.0. Figure 5.1 sets the two pictures side by side.
def mhc_sublayer(X, F, norm, phi, b, alpha, n_hc=4, t_max=20):
# X: [n_hc, d] streams of one token
# phi: [n_hc*d, n_hc**2 + 2*n_hc]; b: [n_hc**2 + 2*n_hc]
x = X.reshape(-1) # [n_hc*d]
h = (x / rms(x)) @ phi # [n_hc**2 + 2*n_hc]
rd, wr = slice(0, n_hc), slice(n_hc, 2 * n_hc) # read / write coefficient slots
A = sigmoid(alpha[0] * h[rd] + b[rd]) # [n_hc] read weights
C = 2 * sigmoid(alpha[1] * h[wr] + b[wr]) # [n_hc] write weights in (0, 2)
M = exp(alpha[2] * h[2*n_hc:] + b[2*n_hc:]).reshape(n_hc, n_hc)
for _ in range(t_max): # Sinkhorn-Knopp
M = M / M.sum(axis=0, keepdims=True) # columns sum to 1
M = M / M.sum(axis=1, keepdims=True) # rows sum to 1
y = F(norm(A @ X)) # [d] one sublayer call
return M @ X + C[:, None] * y[None, :] # [n_hc, d]
The gating scalars α start at small values, so the input-dependent part of each map starts small.
DeepSeek-V4-Flash and V4-Pro use nhc = 4 and 20 Sinkhorn iterations. DeepSeek-V4.1-Flash's "Single-Pass mHC" has each block use the read weights predicted by the previous block, so a fused deployment kernel reads and writes the residual once. Z.ai's GLM-5.3-Flash also uses mHC, with hc_mult = 4 and 20 Sinkhorn iterations in its config.
V4-Pro has d = 7,168, 61 layers, nhc = 4 and 1.6T parameters.
| Per token | Plain residual | mHC, nhc = 4 |
|---|---|---|
| Residual state, elements | d = 7,168 | nhc·d = 4 × 7,168 = 28,672 |
| Residual state in BF16 (b = 2) | 14,336 bytes | 57,344 bytes |
| Elements read and written per token per sublayer to maintain the stream, unfused | 3d = 21,504 | 34d + 48 = 243,760 (mHC Table 2: (8nhc+2)d + 2nhc² + 4nhc) |
The plain residual's 3d is: read x, read F's output, write the sum. In pipeline parallelism, consecutive layers live on different GPUs and pass the residual state between them, so a 4× wider state means 4× more traffic between those GPUs (each group is a pipeline stage). Unfused memory traffic grows 243,760 / 21,504 ≈ 11.3×, so DeepSeek fused the mHC kernels, recomputes most hidden states between layers in the backward pass, and reworked its pipeline schedule, which brought the wall-time overhead to 6.7% of a pipeline stage.
Parameters are almost free. The projection that predicts A, B and C is nhc·d × (nhc² + 2nhc) = 28,672 × 24 = 688,128 weights. The V4 inference code has one for the attention sublayer and one for the MoE sublayer in each block, so the 61 blocks hold 61 × 2 × 688,128 = 83,951,616 ≈ 84M weights, about 0.005% of 1.6T.
Kimi attacks the same equally weighted sum from another side. In the plain stream, layer l reads the sum of all earlier outputs, each with weight 1; with Attention Residuals (AttnRes) it learns the weights. Layer l holds one learned vector wl ∈ ℝd, a "pseudo-query", and attends over the earlier layer outputs vi: αi→l = softmaxi(wl⊤ RMSNorm(vi)) and hl = ∑i αi→l vi. The RMSNorm keeps layers with large outputs from dominating the weights.
Keeping every layer's output costs O(L·d) memory per token, so K3 sums the layers inside each block and attends only over block summaries: 8 blocks of up to 12 layers (the last one partial), 9 sources counting the embedding. In Kimi's scaling runs, Block AttnRes matched a baseline trained with 1.25× more compute, at a per-layer memory traffic of 5.5d against 34d for four-stream mHC.
Qwen's Qwen3.8-Flash-Next (Aug 2026) takes a third route. Its Gated Residual also widens the stream to four branches but has no mixing matrix B: each sublayer reads the branches through an elementwise, data-dependent sigmoid gate and writes back with one scalar per branch. Qwen credits the gate as a key contributor to the run's stability (chapter 10).
| Design | Streams | A sublayer reads | Mixing |
|---|---|---|---|
| Pre-norm residual | 1 | the normalized sum | none |
| HC / mHC | nhc | weighted sum A | B: free / doubly stochastic |
| Block AttnRes | 1, plus block summaries | softmax over earlier blocks | none |
| Gated Residual | 4 | elementwise sigmoid gate | none |
"the core idea of mHC is to constrain the residual mapping onto a specific manifold, and thus enhance the stability of signal propagation across layers while preserving model expressivity."
"each layer selectively retrieves representations from all preceding layers rather than accumulating them uniformly."
"We also incorporate QK-Norm [15] to stabilize the range of attention logits."
Each doubly stochastic matrix has spectral norm at most 1, and the product is again doubly stochastic, so it also has norm at most 1. Unconstrained matrices with gains slightly above 1 multiply; HC's composite gain reached about 3000 in the 27B model, against about 1.6 for mHC, whose 20 Sinkhorn iterations are only approximate.
nhc·d = 4 × 4,096 = 16,384 numbers (32,768 bytes in BF16). The projection is nhc·d × (nhc² + 2nhc) = 16,384 × 24 = 393,216 weights.
How can a model emit several tokens per pass of its large network without changing its output, and why did labs in 2026 start training the network that proposes those tokens separately?
Language models are trained on next-token prediction: at every position t, minimize the cross-entropy of the true next token given all earlier tokens.
Generation mirrors this: 100 new tokens take 100 forward passes in sequence. Leviathan et al. observed that such inference is often limited by memory bandwidth: each pass reads all the weights and the KV cache to produce one token, and much of the compute sits idle (chapter 00). Two separate ideas put that idle compute to work:
The two meet when a trained MTP module serves as the drafter.
Gloeckle et al. (2024) asked the model to predict the next few tokens at every position, with a shared trunk and one independent output head per future token. As an extra loss this helped at scale: their 13B models solved 12% more HumanEval and 17% more MBPP problems than next-token baselines, while small models got worse.
DeepSeek-V3 made the extra predictions sequential. Take "the cat sat on": at "cat" the main model predicts "sat", and the first MTP module, given the main model's hidden state at "cat" and the embedding of the true "sat", predicts "on". In general, the j-th module takes the hidden state htj−1 at position t (from the main model when j = 1) and the embedding of token xt+j, merges them, and runs one transformer block TRMj to predict token xt+j+1. Embedding and output head are shared with the main model. DeepSeek says this keeps "the complete causal chain at each prediction depth": because module j sees the actual token xt+j, its prediction is conditioned on every earlier token x1 … xt+j, as the main model's is.
Dmtp is the number of extra tokens predicted (DeepSeek calls it the depth). V3 used Dmtp = 1 and loss weight λ = 0.3 for the first 10T tokens, then 0.1 for the last 4.8T. GLM-4.5 and MiMo-7B used the same 0.3 → 0.1 schedule. In V3's ablation on a 228.7B-parameter MoE, the module raised HumanEval from 44.5 to 53.7 at identical inference cost, since the module can be discarded after training.
Speculative decoding pairs the large target model (next-token distribution p) with a cheap draft model (distribution q). One cycle has four steps:
Each cycle yields between 1 and γ + 1 tokens, and the output is distributed exactly as p, however poor the draft. A toy with three words and one drafted token shows why. For each word the table gives q, p, the chance min(1, p/q) of keeping it if drafted, and its leftover max(0, p − q):
| Word | Draft | Target | Kept if drafted | Leftover |
|---|---|---|---|---|
| "the" | 0.6 | 0.5 | 5/6 | 0 |
| "a" | 0.3 | 0.4 | 1 | 0.1 |
| "an" | 0.1 | 0.1 | 1 | 0 |
A drafted "the" is rejected one time in six, 0.6 × 1/6 = 0.1 of all cycles, and every rejection resamples "a", the only word with leftover mass. So "the" comes out 0.6 × 5/6 = 0.5 of the time, "a" 0.3 + 0.1 = 0.4 and "an" 0.1: exactly p. The draft survives 0.9 of the time, the overlap of the two distributions:
TV is total-variation distance (the paper calls it DLK); the box gives the general proof.
First term: x is drafted and kept. Second: a rejection (probability 1 − β), after which the leftover distribution picks x.
Now chain γ drafts. Let α be the average β and assume acceptances are independent. Draft i is kept only if drafts 1 … i all are, with probability αi, and the target always adds one token, so the expected yield is a geometric sum. For α = 0.9 and γ = 4 it is 1 + 0.9 + 0.81 + 0.729 + 0.656 ≈ 4.1.
The expected tokens per target pass is called the acceptance length. Let c be the cost of one draft step relative to one target pass, and assume verifying γ + 1 tokens costs about one ordinary pass, as it does while decoding is memory-bound. A cycle then costs γc + 1 passes:
With c = 0.1 the toy's 4.1 tokens take 1.4 passes, about 2.9× faster. Figure 6.1 draws such cycles to scale.
def speculative_step(target, draft, prefix, gamma):
xs, qs = [], []
for _ in range(gamma): # 1. draft gamma tokens
q = draft.next_dist(prefix + xs) # [V]
xs.append(sample(q)); qs.append(q)
ps = target.dists(prefix, xs) # 2. one pass: [gamma+1, V]
for i, x in enumerate(xs): # 3. verify left to right
if uniform() < min(1.0, ps[i][x] / qs[i][x]):
continue # accepted
resid = maximum(ps[i] - qs[i], 0.0) # rejected: resample from
t = sample(resid / resid.sum()) # the leftover mass of p
return prefix + xs[:i] + [t]
return prefix + xs + [sample(ps[gamma])] # 4. all kept: bonus token
Here the two ideas meet. A trained MTP module is a ready-made draft model: small, fed by the target's hidden state, and trained to predict ahead. DeepSeek-V3's single module reached 85–90% acceptance for the second token and 1.8× tokens per second. MiMo-7B fine-tuned two extra MTP layers after pretraining; the first layer accepted about 90% and the third above 75%.
Drafting several steps from one module creates a mismatch: training feeds each step the target's hidden states, inference feeds later steps the module's own outputs. GLM-5 trained 3 MTP steps with shared weights to close that gap and reported an acceptance length of 2.76 against 2.55 for DeepSeek-V3.2 at 4 speculative steps. GLM-5.2 changed three more things:
In an ablation on the GLM-5.1 backbone with 7 MTP steps in training and inference, acceptance length on coding rose from 4.56 to 5.47.
During 2026 several labs took the drafter out of backbone pretraining or retrained it afterwards. A separate drafter no longer shapes backbone pretraining, can be retrained whenever the policy changes (which matters for RL, where generating rollouts is the bottleneck), and can draft many tokens per pass while training on acceptance itself.
The recipe descends from EAGLE (2024), a one-decoder-layer draft head on the frozen target's features that gave 2.7×–3.5× speedups on LLaMA2-Chat 70B.
DeepSeek-V3, one draft token. With γ = 1 and α = 0.85–0.90, tokens per target pass are (1 − α2)/(1 − α) = 1 + α = 1.85 to 1.90. DeepSeek measured 1.8× tokens per second, so drafting and verification cost only a few percent.
GLM-5.2, seven draft tokens. If acceptances were independent, an acceptance length of 5.47 at γ = 7 means α ≈ 0.888, since (1 − 0.8888)/(1 − 0.888) = 0.613/0.112 ≈ 5.47. The 4.56 baseline means α ≈ 0.830. Under 6 points per token compounds over seven steps into a 20% longer acceptance length.
Draft cost. GLM does not state c; assume c = 0.02. Then speedup ≈ 5.47 / (1 + 7 × 0.02) = 5.47 / 1.14 ≈ 4.8×, against 4.56 / 1.14 ≈ 4.0× for the baseline. With the draft cost unchanged, the 20% gain in acceptance length passes straight into speed.
"The training-inference discrepancy reduces the acceptance rate of the second token. Therefore, we propose sharing the parameters of 3 MTP layers during training."
"MTP acceleration is particularly well-suited for RL training (RadixArk Team, 2025), where the rollout phase consistently emerges as the dominant bottleneck"
"This keeps DSpark aligned with the evolving policy, enabling it to accelerate both online serving and rollout generation for RL and OPD."
No. Accepting each draft with probability min(1, p/q) and resampling a rejection from norm(max(0, p − q)) produce exactly p, as the toy and the proof above show. The bad draft only drives α toward p("the"), so most cycles yield a single token and the drafting cost makes decoding slower.
(1 − 0.83)/0.2 = 2.44 and (1 − 0.86)/0.2 ≈ 3.69, matching the 2.44× and 3.69× speeds in Leviathan et al.'s Table 1 for c = 0. Later drafts count only if all earlier ones survive, so returns diminish.
How does a language model take in a photo or a sound clip, and why are labs replacing attached vision encoders with raw pixel patches fed straight into the model?
A transformer reads a sequence of vectors of width d; for text they come from an embedding table. The Vision Transformer (ViT) turns an image into such a sequence in three moves:
A 28×28 image with p = 14 gives a 2×2 grid of patches of 14 · 14 · 3 = 588 numbers each. In general an H×W image gives P = HW / p² patches, the rows of X:
Transformer layers of width dv follow. Vectors like these that enter a language model (LM) are soft tokens (Gemma 3's term), written "at the corresponding image-token positions in the input embedding sequence" (DeepSeek-V4.1-Flash).
Audio is cut along time. Gemma 4 12B slices 16 kHz audio into 40 ms chunks of 16,000 × 0.04 = 640 samples. Other systems encode a log-mel spectrogram, the energy in each frequency band of each short frame. dMel skips the encoder: it rounds each of 80 mel energies to one of 16 levels, a discrete code per frame.
Every design ends with soft tokens in the LM's input. The designs differ in what makes those vectors and when the LM first trains on them:
Figure 7.1 draws the routes side by side, splitting the native route by whether an encoder remains; the bar under each column marks when image data joins training.
Raw pixels through one matrix are a weak start, so early vision-language models borrowed an encoder that had already learned to see. Contrastive training gives matching image-caption pairs similar vectors. SigLIP replaces CLIP's softmax over the whole batch with a sigmoid on each pair, which "does not require a global view of the pairwise similarities for normalization".
LLaVA (2023) set the baseline: a frozen CLIP ViT-L/14 and one trainable matrix W into the LM's word-embedding space. Stage 1 trains only W, "a compatible visual tokenizer for the frozen LLM"; stage 2 also tunes the LM.
Next, labs attached the encoder to a text checkpoint and trained the whole model on mixed data for trillions of tokens:
This reuses the costly text model but abruptly shifts its input distribution. With the total vision-text budget fixed, Kimi found that adding vision from 0% of training at a 10:90 ratio beat adding it at 80% with 50:50 on all six vision and text scores (text knowledge 45.5 against 43.1), and that later injection caused a "dip-and-recover" in text ability. K2.5 therefore mixes vision at a constant ratio.
Native models see images from the LM's first step. Several labs now train this way:
DeepSeek-V4.1-Flash prepares its encoder in steps:
The main run also gives each modality its own MoE load-balancing biases, because image and text tokens may prefer different experts.
Kimi K3 trains its 401M MoonViT-V2 from scratch with next-token prediction. In Kimi's ablation, a SigLIP-initialized encoder made joint training unstable: its gradient norms stayed higher and spiked often. Kimi also argues that a contrastive loss favors global meaning over fine text and structure. The from-scratch encoder matched the SigLIP-initialized one on vision evaluations.
Encoder-free designs keep only a light patch embedding and let the LM's own layers do the perception. Gemma 4 12B maps each 48×48×3 RGB patch (6,912 numbers) through one 35M-parameter matmul in place of a 550M ViT, then adds 2D coordinate embeddings and a LayerNorm. Its 305M audio encoder is gone too: each raw 40 ms chunk is projected directly.
Inkling feeds audio as dMel spectrograms and images as 40×40-pixel patches through a four-layer hMLP. This hierarchical MLP stem (Touvron et al.) merges sub-patches step by step with linear layers and never mixes neighboring patches, like a convolution whose kernel equals its stride. MAI-Base-2 is also encoder-free; vision-language data made up 18% of its 50T-token mix, from 4.5T unique tokens.
Google cites latency, memory use and memory fragmentation, and notes the 12B can be fine-tuned in one pass. Thinking Machines chose it to fit its interaction models, which let users collaborate "using voice and vision in real time".
Every vector costs the LM one context position, so most designs merge neighboring patches first:
With patch side p and merge factor r, each LM token covers an rp × rp square of pixels. At p = 14 and r = 2 that is one token per 28² = 784 pixels, so a 448×448 image costs (448/28)² = 16² = 256 tokens. In shapes, for an encoder of width dv and an LM of width d:
def patchify(img, p): # img (H, W, C); p divides H, W
H, W, C = img.shape
x = img.reshape(H//p, p, W//p, p, C).transpose(0, 2, 1, 3, 4)
return x.reshape(H//p, W//p, p * p * C) # flattened patches
def unshuffle(g, r): # r x r neighbours -> channels
h, w, c = g.shape
g = g.reshape(h//r, r, w//r, r, c).transpose(0, 2, 1, 3, 4)
return g.reshape(h//r, w//r, r * r * c)
def encoder_tokens(img, vit, mlp, p=14, r=3): # DeepSeek-V4.1
f = vit(patchify(img, p)) # (H/p, W/p, 1024)
return mlp(unshuffle(f, r)).reshape(-1, d) # (HW/(rp)^2, d)
def encoder_free_tokens(img, W_in, pos, p=48): # Gemma 4 12B
x = patchify(img, p) @ W_in # 6912 -> d
return layer_norm(x + pos).reshape(-1, d)
def audio_tokens(wave, W_a, n=640): # 40 ms of 16 kHz per token
frames = wave[: len(wave) // n * n].reshape(-1, n)
return frames @ W_a # 25 tokens/s
x = embed(ids) # text rows
x[image_slots] = encoder_tokens(img, vit, mlp) # soft tokens
DeepSeek-V4.1-Flash at its largest input, about 1344×1344, with patch 14, ViT width 1,024 and 3×3 unshuffle:
| Model | Pixels per token | Image | Tokens |
|---|---|---|---|
| Gemma 3 | 56² (4×4 pool, 14-px patch) | 896² | 16² = 256 |
| DeepSeek-V4.1-Flash | 42² (3×3 merge, 14-px patch) | 1344² | 32² = 1,024 |
| Gemma 4 12B | 48² (no merge) | 768² | 16² = 256 |
| Kimi K3 | 28² (2×2 merge, 14-px patch) | 3584² | 128² = 16,384 |
Gemma 3's 14-pixel patch is implied: 256 = 16² tokens after 4×4 pooling means a 64-wide grid, and 896 / 64 = 14. Gemma 4 12B's processor caps images at 280 tokens by default. Kimi K3's 1,048,576-token context fits 1,048,576 / 16,384 = 64 maximum-size images. At 40 ms per token, audio costs 25 tokens per second; Gemma 4 12B's default of 750 audio tokens is 30 seconds.
"Early fusion with a lower vision ratio yields better results given a fixed total vision-text token budget."
"We depart from this practice primarily for training stability. When a pre-trained encoder is attached to the LLM, joint optimization becomes unstable"
"Because these split encoders add latency and increase memory usage, we trained Gemma 4 12B with an encoder-free architecture to integrate audio and vision input directly."
1792 / 14 = 128 patches per side, so 128² = 16,384 patches. The 2×2 pixel shuffle divides by 4: 4,096 tokens.
Joint training with the SigLIP-initialized encoder showed persistently higher gradient norms and frequent spikes. Next-token training shapes the encoder for the LM instead of for global semantics, and the from-scratch encoder matched the SigLIP baseline.
What does Muon do to a weight matrix's update that AdamW does not, and why did seven of the eleven labs that name their pretraining optimizer move their weight matrices to it by October 2026?
Stochastic gradient descent moves each weight by −η times its gradient, where η is the learning rate. Momentum keeps a running sum of past gradients, Mt = μMt−1 + Gt, and steps along that instead, which smooths out batch-to-batch noise. One learning rate η must then suit every weight, whether its gradients are large or small.
Adam (Kingma and Ba) sets the scale per coordinate. For each weight it keeps a running mean of the gradient, m, and of the squared gradient, v, and steps by about η · m/√v. Multiplying every gradient by 1,000 leaves that ratio unchanged; what it measures is how consistent the gradient has been:
In full, β1 and β2 set how long each average remembers, and the hats correct both averages for starting at zero:
The paper's defaults are β1 = 0.9 and β2 = 0.999; DeepSeek-V3 and MiMo use a shorter memory for v, β2 = 0.95. Adam stores two extra numbers per parameter.
The λθ term is weight decay: each step also shrinks every weight by the fraction ηλ. Libraries used to implement it as an L2 penalty added to the gradient. Loshchilov and Hutter showed that inside Adam that penalty also gets divided by √v̂, so weights with large past gradients are regularized less. AdamW applies the decay directly to the weights, outside the adaptive scaling, as in the equation above. DeepSeek-V3, MiMo-7B and MAI-Thinking-1 all use λ = 0.1 (MAI lowers it for attention and embedding weights).
Adam treats a d×d weight matrix as d² unrelated numbers. Keller Jordan observed that the updates SGD-momentum and Adam produce for transformer weight matrices "typically have very high condition number": they are close to low-rank, dominated by a few directions. Muon (MomentUm Orthogonalized by Newton–Schulz) works on the whole matrix: it keeps SGD momentum and replaces the momentum matrix by the nearest semi-orthogonal matrix (orthonormal rows or columns, so all its singular values equal 1).
A 2×2 example shows what that does:
The columns of G, (3, 4) and (−0.4, 0.3), are 5 × (0.6, 0.8) and 0.5 × (−0.8, 0.6). They are perpendicular, so these are G's singular directions, with singular values 5 and 0.5. Orthogonalizing keeps both directions and sets both strengths to 1. The step rescales O anyway (by η and, below, a shape factor), so what matters is the ratio of strengths: it falls from 10 to 1, and the weak direction now moves as far as the strong one (Figure 8.1).
In general, the singular value decomposition M = UΣV⊤ splits a matrix into directions (U, V) and strengths (the singular values in Σ). Muon's update is UV⊤: every singular value becomes 1, so every direction moves by the same amount, including "rare directions" that Jordan suspects matter for learning.
In practice Muon orthogonalizes the Nesterov form μMt + Gt instead of Mt: it looks one momentum step ahead, which Jordan found works a bit better in every case he tested, and Moonlight and DeepSeek V4 do the same. The pseudocode below follows that form.
An SVD every step would be too slow. A Newton–Schulz iteration reaches nearly the same UV⊤ without computing U or V, by repeating one step built from matrix multiplies:
Because XX⊤ = UΣ2U⊤, one step applies the odd polynomial φ to every singular value and leaves U and V alone. Two choices make φ pull every singular value toward 1:
The iteration runs stably in bfloat16 and costs little. For an A×B weight with B ≤ A, each of T steps costs at most about 6AB2 FLOPs, while the forward and backward pass costs about 6AB per token, so Jordan bounds Muon's extra FLOPs, as a fraction of the training FLOPs, by T·B / (tokens per batch). That is about 0.5% for a Llama-405B-sized run. Figure 8.2 follows the singular values of a 3×3 example, worked through below.
Take G = [[3, 1, 3], [3, 0, 1], [1, 0, 1]] as a stand-in momentum matrix. Its singular values are 5.406, 1.302 and 0.284, so the strongest direction is 19 times the weakest. ‖G‖F = √31 = 5.568, and X0 = G/5.568 has singular values 0.971, 0.234 and 0.051.
One quintic step on the smallest: φ(0.0511) = 3.4445 × 0.0511 − 4.7750 × 0.05113 + 2.0315 × 0.05115 = 0.1760 − 0.0006 + 0.0000 ≈ 0.175, a 3.4× increase. On the largest: φ(0.971) = 3.3445 − 4.3709 + 1.7531 = 0.727, a decrease. Running the full matrix iteration gives the same numbers, step by step:
| Step | σ1 | σ2 | σ3 | largest ÷ smallest |
|---|---|---|---|---|
| 0 (X0) | 0.971 | 0.234 | 0.051 | 19.0 |
| 1 | 0.727 | 0.746 | 0.175 | 4.3 |
| 2 | 1.082 | 1.057 | 0.578 | 1.9 |
| 3 | 0.691 | 0.682 | 1.200 | 1.8 |
| 4 | 1.125 | 1.134 | 0.937 | 1.2 |
| 5 | 0.736 | 0.752 | 0.767 | 1.04 |
The quintic map overshoots and oscillates around 1, roughly inside Jordan's 0.7 to 1.3 band (step 3 dips to 0.68). DeepSeek V4 runs 8 quintic steps, then 2 with (2, −1.5, 0.5). Here step 8 gives 1.131, 1.134 and 1.130, and the two final steps give 1.017 to 1.018 and then 1.000 for all three. The result matches the exact UV⊤ to within 0.0002 in every entry.
def newton_schulz(M, steps=5, eps=1e-7):
a, b, c = 3.4445, -4.7750, 2.0315 # Jordan's quintic coefficients
X = M / (frobenius_norm(M) + eps) # all singular values <= 1
tall = X.shape[0] > X.shape[1]
if tall:
X = X.T # keep X @ X.T the smaller Gram matrix
for _ in range(steps):
A = X @ X.T
X = a * X + (b * A + c * A @ A) @ X # sigma -> a*s + b*s^3 + c*s^5
return X.T if tall else X
def muon_step(W, G, M, lr, mu=0.95, wd=0.1, rms=0.2):
# W, G, M: [A, B] weight, gradient, momentum buffer
M = mu * M + G # SGD momentum
O = newton_schulz(mu * M + G) # Nesterov look-ahead, then orthogonalize
O = O * rms * sqrt(max(W.shape)) # update RMS ~ rms, as for AdamW (Moonlight)
W = W * (1 - lr * wd) - lr * O # decoupled weight decay, then step
return W, M
Muon is defined for 2D weight matrices, so scalars and vectors such as RMSNorm gains and biases stay on AdamW. Jordan also found that the embedding and the output head, though 2D, train best with AdamW. Moonlight keeps "RMSNorm, LM head, and embedding parameters" on AdamW, DeepSeek V4 adds mHC's static biases and gating factors to that list, and MAI-Base-2 runs Muon on its "non-embedding 2D parameters". GLM-4.5 lists only word embeddings, biases and RMSNorm weights as non-Muon parameters. DeepSeek V4.1-Flash adds Engram, a conditional memory: huge embedding tables indexed by hashes of the last 2 to 4 tokens, whose looked-up vectors are gated into the hidden state. It trains those tables, its embeddings and its prediction head with a "Sinkhorn-balanced" momentum update instead of Adam, which needs one state buffer instead of Adam's two. Qwen3.8-Flash-Next has a similar hashed memory, 51B parameters of n-gram embedding tables, trained with Adam without weight decay, and keeps its embeddings, output head, MoE router and gates on AdamW.
Muon set speed records on small models. Kimi's Moonlight report found two problems at scale:
Moonlight multiplies each update by 0.2·√max(A, B), which puts every update's RMS near 0.2 (for the 100×400 matrix, 0.05 × 0.2 × 20 = 0.2), inside the 0.2 to 0.4 range AdamW's updates already have:
Muon can then reuse the learning rate and weight decay tuned for AdamW. In Moonlight's scaling-law fits, Muon matched AdamW with about 52% of the training FLOPs, which the paper calls "∼2× computational efficiency". Kimi K2 calls this recipe plus QK-Clip (Chapter 10) MuonClip.
The recipe spread:
Jordan's post had already reported that Muon works better on Q, K and V separately than on one fused QKV matrix. The 2026 models go one level further. A projection such as WQ stacks all heads in one matrix, and orthogonalizing it as a whole couples them. In Kimi K3's words, heads with larger gradient or momentum scales "dominate the shared update direction, while smaller-scale heads receive insufficiently normalized updates". Splitting the momentum along the head dimension and orthogonalizing each head's block separately gives every head the same update scale.
Three labs adopted versions of this within seven months. GLM-5's Muon Split orthogonalizes the per-head slices of the MLA up-projections, which closed MLA's gap to GQA in their ablation and kept attention-logit scale stable without any clipping. Kimi K3's Per-Head Muon does it for Q, K and V, and notes that the tall per-head blocks also make Newton–Schulz slightly cheaper. DeepSeek V4.1-Flash's head-wise Muon splits the Query and Key weights; DeepSeek describes it as one preconditioner per head instead of one for all heads, and found that it "outperforms vanilla Muon".
Of the eleven labs that state a pretraining optimizer, seven moved their weight matrices to Muon:
Figure 8.3 places the main releases on a timeline.
"We employ the Muon (Jordan et al., 2024; Liu et al., 2025) optimizer for the majority of modules in DeepSeek-V4 series due to its faster convergence and improved training stability."
"When scaling up to MAI-Base-2, we found that Muon delivered more stable training dynamics and large efficiency gains over AdamW."
"per-head orthogonalization equalizes the update scale across heads."
The coefficients are tuned so that singular values in [0, 1] are pulled toward 1; values well above 1 grow without bound. The Frobenius norm is at least the largest singular value, so the division puts all of them in that range, and it does not change UV⊤, since scaling M leaves U and V unchanged.
UV⊤ has RMS 1/√max(7168, 3072) = 1/84.7 ≈ 0.0118. V4 multiplies by √7168 × 0.18 ≈ 15.2, giving an update RMS of 0.18. Moonlight's 0.2 would give a factor of about 16.9.
How do labs set the learning rate at every step of a trillion-token run, and the batch size and weight decay that go with it, when the final model can be trained only once?
The learning rate η scales every optimizer update. A large η makes fast progress but leaves the weights bouncing in a noisy region; a small η lets them settle. A schedule plans when to do which.
A schedule is assembled from a few pieces (Figure 9.1). Warmup comes first. After it, the shapes differ in when they commit to the run's length:
Warmup raises η linearly from zero over the first steps; the original Transformer used 4,000. Xiong et al. traced the need to Post-LN blocks (normalization after the residual add), whose gradients near the output are large at initialization, and showed that Pre-LN models can train without it. Labs still warm up: DeepSeek-V3 for 2K steps, Kimi K2 for 500 steps, Kimi K3 for 1% of training, MAI-Base-1 for about 12B tokens.
The cosine schedule then lowers η along half a cosine wave from the peak ηmax to a floor ηmin at the final step S (s is the current step, W the last warmup step):
S must be the true run length. Chinchilla got the best final loss when the cosine length matched the token count, and overshooting it by more than 25% noticeably hurt. A cosine run commits to its budget on day one: mid-run checkpoints still have high η, and training longer means re-warming η (as Moonlight did for its last 0.5T tokens, after a full cosine) or starting over.
MiniCPM split training into a long phase at constant η and a short decay, the warmup-stable-decay (WSD) schedule. With T the end of the stable stage and 0 < f ≤ 1 any decreasing function:
On a 0.036B model, the loss dropped sharply once decay began and reached or beat the cosine run of the same length. Decaying over 10% of total tokens was enough; 2.5% fell short.
The stable phase does not need to know when training ends, so any stable checkpoint can be branched: decay a copy to get a finished model and keep training the original. With a 10% decay, one stable run yields a 6T-token model (5.4T stable tokens, then 0.6T of decay on a copy) and later a 10T-token model (resume to 9T, decay 1T), for 10.6T tokens of compute instead of 16T for two separate runs. MiniCPM measured data scaling this way, with one stable run per model size and decayed branches at several token counts. For m model sizes × m data sizes, this cuts the work from about m2 training runs to about m. A lab can also extend a run by resuming from the last checkpoint before decay.
def lr_cosine(s, W, S, peak, floor):
if s < W:
return peak * s / W # linear warmup
p = (s - W) / (S - W) # 0 -> 1, needs the final step S
return floor + 0.5 * (peak - floor) * (1 + cos(pi * p))
def lr_wsd(s, W, T, S, peak, floor):
if s < W:
return peak * s / W
if s < T:
return peak # stable: no end date needed
p = (s - T) / (S - T) # short decay, e.g. S - T = 0.1 * S
return floor + 0.5 * (peak - floor) * (1 + cos(pi * p)) # cosine-shaped decay
# WSD branching: one stable run yields finished models at 6T and 10T tokens
ckpt = train(init(), lr=peak, tokens=5.4e12) # stable phase
model_6T = decay(copy(ckpt), tokens=0.6e12) # branch off and decay 10%
ckpt = train(ckpt, lr=peak, tokens=3.6e12) # resume the stable run to 9T
model_10T = decay(ckpt, tokens=1.0e12) # total compute: 10.6T tokens
Figure 9.2 puts cosine and WSD on one run, with a branch, and then draws DeepSeek-V3's published schedule to scale.
DeepSeek, Kimi K2 and MiMo use a WSD shape whose decay follows a cosine:
| Release | Peak η | Held constant | Decay |
|---|---|---|---|
| DeepSeek-V3 | 2.2×10−4 | to 10T tokens | cosine to 2.2×10−5 over 4.3T, then low constants |
| Kimi K2 | 2×10−4 | first 10T tokens | cosine to 2×10−5 over 5.5T, then to 7×10−6 |
| MiMo-7B | 1.07×10−4 | 10.2T tokens | cosine to 3×10−5 over 7.5T, held for 5.5T, then to 1×10−5 |
| DeepSeek V4.1-Flash | 2.6×10−4 | to 28T tokens | cosine to 2.6×10−5 by 40T, held to 45T |
GLM-4.5 chose plain cosine from 2.5×10−4 to 2.5×10−5 because its WSD runs scored worse on SimpleQA and MMLU, which it read as underfitting in the stable stage. MAI-Base-1 decays along a cosine from 2×10−4 to 2×10−5, a 0.1× floor rather than the common 0.01×, because decaying less helped after RL. Kimi used WSD for K2 and moved to cosine for K3 after tuning each schedule separately, since their optimal peak η and batch sizes differ:
"Under their respective optimal hyperparameter settings, cosine decay consistently achieves a lower final loss than WSD."
Two of the labs the atlas added change how the run ends. NVIDIA's Nemotron 3 Super holds its peak for 20T of 25T tokens, then decays along a minus-square-root curve over the last 5T, and the base it hands to post-training is a merge of checkpoints over a 500B-token window. Ant drops the decay entirely: Ling 2.0's warmup-stable-merge (WSM) schedule keeps η constant to the end and gets the annealing effect by merging checkpoints, which for Ling-1T come from its mid-training.
Gemma, gpt-oss, Llama 4, Inkling and Mistral do not publish their schedule shapes.
DeepSeek-V3 trained on 14.8T tokens: warmup to 2.2×10−4 over 2K steps; constant until 10T tokens; cosine to 2.2×10−5 over 4.3T; then 2.2×10−5 for 333B tokens and 7.3×10−6 for the last 167B.
Budget: 10 + 4.3 + 0.333 + 0.167 = 14.8T; 10/14.8 = 67.6% of tokens see the peak, 4.3/14.8 = 29.1% the cosine, 0.5/14.8 = 3.4% the tail.
Midway through the cosine (12.15T), cos(π/2) = 0, so η = 2.2×10−5 + ½(2.2×10−4 − 2.2×10−5) = 1.21×10−4; this is also the half-cosine's average. Token-weighted mean η, ignoring warmup: (10 × 2.2 + 4.3 × 1.21 + 0.333 × 0.22 + 0.167 × 0.073) × 10−4 / 14.8 = 27.29×10−4 / 14.8 = 1.84×10−4, or 84% of peak. A plain cosine over all 14.8T with the same peak and floor averages 1.21×10−4, 55% of peak. WSD keeps η high longer and settles late.
The batch size B interacts with η, and MiniCPM found that the optimal batch size grows as the loss falls. Labs either ramp B up early in the run or hold it fixed:
The best η, B and weight decay λ shift with model size and token count, and no lab can grid-search them on the final model. Labs take one of two routes:
MAI's rungs are set by the layer count L alone (width d = (256/3)·L, so L78 gives 6,656) and trained at a fixed number of tokens per active parameter (TPP). For MAI-Base-2, Microsoft found the best pair (η*, λ*) at several sizes and TPPs and fit two laws in the number of optimizer steps:
These laws set MAI-Base-2's hyperparameters, and Kimi K3 likewise retuned batch size, η, TPP and model shape with scaling-law studies.
Small rungs must also see data the way the big run does. MAI's source scaling shrinks the unique data on small rungs so that a dataset repeated 8 times over 50T tokens is looped 8 times over 1/10 of the data in a 5T-token run:
"Without source scaling, experiments at smaller scale can favor under-regularized architecture and hyperparameter choices that overfit at larger scales."
Inkling instead ties weight decay to η:
"We coupled the weight decay strength to the square of the learning rate, which we found kept the overall size of the model weights stable across training horizons"
At the midpoint of a cosine with a 0.1× floor, η is still (1 + 0.1)/2 = 55% of peak, so the weights never got the decay that produces the late loss drop. Its cosine is 100% longer than its 10T of training, far past Chinchilla's harmful 25% overshoot. With WSD, the lab would decay a copy of a 9T stable checkpoint for 1T tokens.
η* scales by 2−0.573 ≈ 0.672. The product η*λ* halves, so λ* scales by 0.5 / 0.672 ≈ 0.744.
What makes a long pretraining run's loss spike or diverge, and how did Kimi K2 train 15.5T tokens with Muon "without a single loss spike"?
A loss spike is a sudden jump in training loss, usually together with a jump in gradient norm. Some spikes recover on their own; others keep rising until training diverges and never comes back. Either way they waste compute: the steps inside the spike teach the model little, and the usual repair is to roll back to an earlier checkpoint and redo work.
A spike begins when some quantity grows past what the model or the number format can absorb. The reports point to five places where that growth starts, each with its own guards:
| Where it starts | What grows | Guards |
|---|---|---|
| Logits | attention scores; output and router logits drift upward | QK-norm, QK-Clip, soft-capping, z-loss |
| Activations | outliers in MoE layers, amplified by routing | clamped or tanh-bounded activations, Anticipatory Routing, gates |
| Gradients | one example's gradient swamps the batch | global clipping, activation-gradient clipping, per-layer clamps |
| Optimizer updates | Wq and Wk under Muon; Kimi's hypothesis is that its full-rank updates push up their largest singular values | QK-Clip after each step, per-head Muon, update clipping |
| Numerics | roundoff, worse for large values and fewer bits | FP32 sums, z-loss, sensitive layers in BF16 or FP32 (chapter 11) |
The sections below follow the rows in order; published incidents come after them.
Softmax turns attention scores into weights. With four keys and logits (2, 0, 0, 0), the top key gets weight e2 / (e2 + 3) = 0.71. At (20, 0, 0, 0) it gets all but 6 × 10−9. When one logit is far above the rest, softmax becomes one-hot and stops passing gradient to the other keys.
The attention logit between query position t and key position s in head h is qt·ks/√dh. Kimi bounds it by the sizes of the inputs and of the two projection matrices:
Here x is a token's input to the layer, and a matrix's spectral norm ‖W‖2, its largest singular value, is the most it can stretch a vector. RMSNorm keeps the x terms bounded, so growth comes from the spectral norms of Wq and Wk, and since the logit uses their product, any growth is compounded.
Muon makes the growth worse; this is the optimizer row of the table. Its updates have equal singular values (full rank), while Adam's are dominated by a few directions, and Kimi's hypothesis is that full-rank updates are more likely to line up with a weight's own singular directions and push its largest singular values up. Moonlight already showed max logits above 100 in some layers early in training. In a 9B-active, 53B-total MoE trained with vanilla Muon, max logits "quickly exceed a magnitude of 1000", and K2 adds that "Max logits at this level usually result in instability during training, including significant loss spikes and occasional divergence."
QK-Clip is Kimi's answer. It watches each head's largest logit and, when that passes a threshold τ, shrinks the head's query and key weights just enough to bring it back. In each training step:
With α = ½ each matrix shrinks by √γh. The forward and backward pass of the current step are not changed. Clipping is per head because "only a small subset of heads exhibit exploding logits".
Why not a standard remedy? QK-norm (Chapter 5) needs explicit keys, and the keys of MLA, K2's attention, "are not fully materialized during inference". A logit soft-cap bounds the logit after the dot product, but "the dot products between queries and keys can still grow excessively before capping is applied." QK-Clip acts on the weights instead. MLA needs one adjustment, because each head's logit is a content part qC·kC plus a rotary part qR·kR: K2 scales the head-specific content parts qC and kC by √γ, scales the head-specific rotary query qR by γ, and leaves the rotary key kR alone because every head shares it.
def qk_clip(attn_layers, tau=100.0):
# Runs right after the Muon update. S_max[h] was recorded in this
# step's forward pass: max over the batch of q_t . k_s / sqrt(d_h).
for layer in attn_layers:
for h in range(layer.num_heads):
s = layer.S_max[h]
if s <= tau:
continue # most heads, most steps
gamma = tau / s # logits of head h shrink by gamma
if layer.is_mla:
layer.W_qc[h] *= sqrt(gamma) # head-specific content query
layer.W_kc[h] *= sqrt(gamma) # head-specific content key
layer.W_qr[h] *= gamma # head-specific rotary query
# W_kr (rotary key) is shared by all heads: left unchanged
else:
layer.W_q[h] *= sqrt(gamma)
layer.W_k[h] *= sqrt(gamma)K2 ran with τ = 100. Max logits rose to the cap early and stayed there; 12.7% of heads triggered QK-Clip at least once in the first 70,000 steps, after which every head stayed below 100 and the clip stopped firing. K2 pretrained on 15.5T tokens without a loss spike. In a small ablation with an aggressive τ = 30, the loss curve was almost unchanged. Kimi K3 keeps "the weight-clipping mechanism introduced in Kimi K2". Kimi calls the whole optimizer, Muon with weight decay and update-RMS matching (Chapter 08) plus QK-Clip, MuonClip. Figure 10.1 contrasts the two runs.
Suppose head h of an MLA layer records Shmax = 125 in a step, with K2's τ = 100 (the 125 and its 90 + 35 split below are illustrative). Then γ = τ / Smax = 100 / 125 = 0.8 and √γ = 0.894.
| Part of the max logit | Before | Scaling | After |
|---|---|---|---|
| Content: qC · kC | 90 | 0.894 × 0.894 = 0.8 | 72 |
| Rotary: qR · kR | 35 | 0.8 × 1 (kR shared, untouched) | 28 |
| Total logit | 125 | 0.8 | 100 = τ (same batch, same weights) |
Both parts shrink by exactly γ, so with the weights and batch that produced the 125, the max logit would land exactly on τ. QK-Clip runs after the same step's Muon update, which has already moved the weights a little, so the head's next max logit is only near τ. If qR were scaled only by √γ, the rotary part would become 35 × 0.894 = 31.3 and the total 103.3, still above τ. For a plain multi-head layer the result is the same: Wq and Wk each shrink by 0.894. Had a head been allowed to reach 1,000, the level of the vanilla run, γ would be 0.1 and each matrix would lose 1 − √0.1 = 68% of its scale in one step. Because QK-Clip runs every step from the start, a head only drifts one update past τ before it is pulled back.
Soft-capping bounds a logit smoothly: logit ← cap · tanh(logit / cap). Values well below the cap pass almost unchanged and large values approach ±cap. With cap 50, logits of 10, 100 and 1,000 become 9.87, 48.2 and 50.0. Gemma 2 used cap 50 on attention logits and 30 on the final output logits. Gemma 3 replaced Gemma 2's soft-capping with QK-norm, while Meta's Muse Glimmer sets final_logit_softcapping = 20.0 in its config. The cost is gradient: the derivative of a soft-cap is 1 − tanh²(logit / cap), which falls toward zero near the cap (0.07 at a logit of 100 under cap 50), so a capped value receives almost no gradient.
Softmax depends only on differences between logits, so a whole logit vector can drift upward without changing the loss, and nothing in the loss pulls it back. Large values are a precision hazard: ST-MoE notes that bfloat16 roundoff can be up to 65,536× worse than float32, and roundoff grows with magnitude. PaLM adds an auxiliary z-loss of 10−4 · log2Z, where Z = ∑j exp(logitj) is the output softmax normalizer, to keep log Z near 0. ST-MoE applied the idea to the router of an MoE layer, whose logits xij score expert j for token i in a batch of B tokens and E experts:
It joins the training loss with a small weight:
ST-MoE compared it with the blunter alternative of tightening the update clipping in Adafactor, the memory-saving optimizer it trained with. Quality is negative log perplexity on English, so higher is better:
| Setting | Stable runs | Quality |
|---|---|---|
| Baseline | only 4 of 6 | −1.755 |
| Router z-loss | 3 of 3 | −1.741, slightly better |
| Tighter Adafactor update clipping | 3 of 3 | −4.206, wrecked |
Activations can blow up too, and the MoE reports describe two kinds of guard:
Global gradient clipping rescales the whole gradient when its norm exceeds a threshold c: g ← g · min(1, c / ‖g‖). DeepSeek-V3, MiMo-7B, MiMo-V2-Flash, MAI-Thinking-1 and PaLM all use c = 1.0. Its weakness is that one bad example shrinks the gradient of the whole batch. If the well-behaved examples give a gradient of norm 0.5 and one outlier adds norm 50, clipping at c = 1 divides everything by about 50 and the useful part shrinks to 0.01. Two narrower guards limit the outlier instead:
Read against the table, most published incidents have a clear origin; some do not.
The newest designs try to keep logits bounded without a clip. DeepSeek V4 applies RMSNorm to its queries and compressed KV entries and so does "not employ the QK-Clip technique". GLM-5's per-head Muon Split keeps "the scale of attention logits" stable "without any clipping strategy". Gemma 3 and 4, GLM-4.5 and MAI-Thinking-1 use QK-norm. Qwen relies on gates: an attention output gate in Qwen3-Next removed attention sinks and massive activations, and Qwen3.8-Flash-Next trained without a single loss spike and without QK-Clip or SwiGLU clipping, which Qwen credits largely to the gate in its Gated Residual (chapter 05).
"scaling up Muon training reveals a challenge: training instability due to exploding attention logits, an issue that occurs more frequently with Muon but less with AdamW in our experiments."
"An outlier can therefore cause the gradients from the entire batch to be scaled down, including contributions from otherwise well-behaved examples."
"Empirically, we identified that the occurrence of spikes is consistently tied to outliers in the MoE layers, and the routing mechanism itself appears to exacerbate the emergence of these outliers."
The content logit is qC·kC, so scaling both by √γ scales it by γ. The rotary key kR is shared by all heads and cannot be scaled for one head without changing the others, so the full factor γ goes on that head's qR. Both parts then shrink by γ.
Cross-entropy is unchanged, because softmax ignores a common shift. log Z rises by 10, so if log Z was 0 the z-loss goes from 0 to 10−4 × 102 = 0.01, and its gradient pushes the logits back down.
How do labs run the matrix multiplies of pretraining on 8-bit numbers without the loss drifting away from a 16-bit run?
Fewer bits per number mean less memory, less bandwidth, and more multiply-adds per second from tensor cores, the GPU's matrix-multiply units. DeepSeek says that running its GEMMs (general matrix multiplications) in 8-bit floating point, FP8, theoretically doubles the computational speed of 16-bit BF16. Memory adds up: Kimi K2's BF16 parameters plus an FP32 gradient buffer take about 6 TB, which is 1.04 trillion parameters times 2 + 4 bytes.
A floating-point number has a sign bit, exponent bits that set its range, and mantissa bits that set its precision between two powers of two: with m mantissa bits, each such span holds 2m evenly spaced values. BF16 has 7, so it stores 0.1 as 0.10009765625.
FP16 (1, 5 and 10 bits) has a narrow range: its normal values run from 6.10×10−5 to 6.55×104, so FP16 training needs loss scaling, a multiplier on the loss that keeps small gradients from underflowing. BF16 keeps FP32's 8 exponent bits with a 7-bit mantissa, so it covers FP32's range without loss scaling. Figure 11.1 draws the layouts to scale.
The standard recipe feeds BF16 tensors to each GEMM, accumulates in FP32, and keeps an FP32 master copy of the weights for the optimizer.
The FP8 formats paper (NVIDIA, Arm, Intel) splits 8 bits two ways: E4M3 (4 exponent bits, 3 mantissa bits) and E5M2 (5 and 2). E5M2 follows IEEE conventions and reaches 57,344. E4M3 drops infinities and keeps one NaN pattern to gain a power of two: it spans 2−9 to 448. The paper recommends E4M3 for weights and activations, E5M2 for gradients. A stored value decodes as
With 3 mantissa bits, E4M3 holds only 1, 1.125, 1.25, …, 1.875 between 1 and 2, so rounding costs at most 1/16 (6.25%) of a value. Below the smallest normal value, 2−6, numbers are subnormal: they sit on a fixed grid of spacing 2−9, so relative error grows as values shrink.
In simulated FP8 (inputs rounded, arithmetic in 16-bit), the paper matched 16-bit GPT training up to 175B parameters, perplexity 6.68 against 6.65. It relied on a scaling factor, chosen so that a tensor's largest magnitude lands near the format's maximum, then divided out after the GEMM. How finely that scale is chosen is what separates the FP8 recipes below.
The simplest choice is one scale per tensor, s = 448 / max|x|, often with delayed scaling, which infers the maximum from earlier steps. Here s multiplies values before rounding and is divided out after the GEMM. The weakness is outliers, which DeepSeek says often limit low-precision training. One huge value sets max|x| and shrinks the scale for everything else. Any value more than 448 / 2−6 = 28,672 times below the maximum falls among the subnormals or rounds to zero.
The cure is one scale per small block, so an outlier shrinks only its own block's scale. Block formats differ in block size and in how the scale is stored:
| Format | Values per scale | Scale | Bits per value |
|---|---|---|---|
| FP8, one scale per tensor | whole tensor | FP32 | 8 |
| FP8, DeepSeek-V3 blocks | 128 (activations), 16,384 (weights) | FP32 | 8.25 (activations), about 8 (weights) |
| MXFP8 | 32 | E8M0, a power of two | 8.25 |
| MXFP4 | 32 | E8M0, a power of two | 4.25 |
| NVFP4 | 16 | E4M3, plus FP32 per tensor | 4.5 |
DeepSeek's FP32 scale is 448 / max|x| over each block. MX picks a power of two instead and divides by it:
The shared scale sblk divides the block's values before rounding. emax is the exponent of the element format's largest normal value: 8 for E4M3, since 448 = 1.75 · 28, and 2 for E2M1, since 6 = 1.5 · 22. The scale adds 8 / 32 = 0.25 bits per element, which is why gpt-oss counts its MXFP4 weights as 4.25 bits per parameter. A power of two is cheap to apply but coarser than an FP32 scale. Quantizing and transposing do not commute: the backward pass multiplies by W⊤, whose 32-element blocks run along the other dimension, so training keeps separately quantized copies of a weight and its transpose.
Eight illustrative activations in two blocks of four (standing in for 1×128 tiles or 32-element blocks): A = [0.012, −0.0071, 0.0033, 0.0185], B = [0.021, −0.0094, 640, 0.0047]. Each scheme scales, rounds and divides back.
One tensor scale (FP8): s = 448 / 640 = 0.7. Then −0.0071 × 0.7 = −0.00497 < 2−6 = 0.0156, so it rounds on the subnormal grid 2−9 = 0.00195: 0.00497 / 0.00195 = 2.54 → 3 steps = 0.00586, and 0.00586 / 0.7 = 0.00837. Error 17.9%.
Own FP32 scale for A (FP8): sA = 448 / 0.0185 = 24,216. Then 0.0071 × 24,216 = 171.9, in [128, 256) where E4M3 steps are 27−3 = 16: 171.9 / 16 = 10.75 → 11 → 176, and 176 / 24,216 = 0.00727. Error 2.4%.
Power-of-two scales (MX): for E4M3 the scale is 2⌊log2 0.0185⌋ − 8 = 2−6 − 8 = 2−14, so dividing by it multiplies by 16,384 instead of the FP32 scale's 24,216. For E2M1 it is 2−6 − 2 = 2−8, and 0.0185 / 2−8 = 4.74 rounds to 4, so even the block's largest value loses 15.5%.
NVFP4: with scale 0.0185 / 6 (ignoring the scale's own rounding to E4M3), 0.0185 maps exactly to 6, E2M1's largest value, and −0.0071 to −2.30, which rounds to −2.
| Block A | FP8, tensor scale | FP8, own FP32 scale | MXFP8, ÷ 2−14 | MXFP4, ÷ 2−8 | NVFP4, ÷ 0.0185/6 |
|---|---|---|---|---|---|
| 0.012 | 0.01116 (7.0%) | 0.01189 (0.9%) | 0.01172 (2.3%) | 0.01172 (2.3%) | 0.01233 (2.8%) |
| −0.0071 | −0.00837 (17.9%) | −0.00727 (2.4%) | −0.00732 (3.2%) | −0.00781 (10.0%) | −0.00617 (13.1%) |
| 0.0033 | 0.00279 (15.4%) | 0.00330 (0.1%) | 0.00342 (3.6%) | 0.00391 (18.4%) | 0.00308 (6.6%) |
| 0.0185 | 0.01953 (5.6%) | 0.01850 (0.0%) | 0.01758 (5.0%) | 0.01563 (15.5%) | 0.01850 (0.0%) |
| RMS error | 8.2% | 0.85% | 4.3% | 13.0% | 4.3% |
The RMS error is the relative √(∑(x̂ − x)² / ∑x²) over block A. Block B keeps scale 0.7 under both FP32-scale schemes (under the MX rule its E4M3 scale is 21): per-block scales confine an outlier's damage to its own block. The FP4 columns show what 4 bits cost in this block: MXFP4's power-of-two scale triples MXFP8's error, and NVFP4's finer scale brings it back to MXFP8's level.
A matrix multiply sums over the inner dimension K: Y[m, n] = ∑k xmkwkn. One scale per row of X and per column of W is constant along that sum and can be divided out afterwards. DeepSeek's scales change every 128 channels along K, cannot be factored out, and so cannot be applied by standard FP8 GEMMs. DeepSeek applies them during accumulation instead, slice by slice.
The hardware gives a second reason to split the sum. H800 tensor cores keep only about 14 bits in FP8 sums, which gave a maximum relative error of nearly 2% at K = 4096. So every NC = 128 elements, partial sums move to FP32 registers on the CUDA cores (the general-purpose units), where they are unscaled and summed. Figure 11.2 draws one such GEMM.
def quantize_x(X): # X: [M, K] activations
T = X.reshape(M, K // 128, 128) # 1x128 tiles
s = 448.0 / T.abs().amax(-1) # [M, K/128], one scale per tile
return to_e4m3(T * s[..., None]).reshape(M, K), s
def quantize_w(W): # W: [K, N] weights
B = W.reshape(K // 128, 128, N // 128, 128) # 128x128 blocks
s = 448.0 / B.abs().amax((1, 3)) # [K/128, N/128]
return to_e4m3(B * s[:, None, :, None]).reshape(K, N), s
def fp8_gemm(Xq, sx, Wq, sw): # returns Y: [M, N] in FP32
Y = zeros(M, N, dtype=fp32)
for k in range(K // 128): # one 128-wide slice of K
cols = slice(128 * k, 128 * (k + 1))
P = Xq[:, cols] @ Wq[cols, :] # tensor cores, ~14-bit sums
sw_k = sw[k].repeat_interleave(128) # [N]
Y += P / (sx[:, k, None] * sw_k[None, :]) # FP32 on CUDA cores
return YSmall groups let DeepSeek use E4M3 even for gradients, as grouping effectively shares exponent bits: each group's scale acts as a shared exponent, so an element's own exponent only covers the range inside its group, where E4M3's 4 bits suffice. Embedding, output head, MoE gating, normalization and attention stay in BF16 or FP32. The tile shape matters: with 128×128 blocks for activation gradients, a 16B MoE diverged after about 300B tokens, which DeepSeek hypothesizes comes from token-correlated outliers. Against BF16 runs at about 16B and 230B parameters, the final recipe stayed below 0.25% relative loss error.
The published pretraining recipes sort into five groups:
GLM-5, Gemma 4, Inkling, Qwen3, MiniMax and Mistral do not state their pretraining precision.
Four-bit formats enter mainly through quantization-aware training (QAT): the forward pass sees quantized weights while gradients update a high-precision copy. gpt-oss quantized its MoE weights to MXFP4 in post-training. DeepSeek-V4's MXFP4 expert weights dequantize losslessly to FP8 provided the largest and smallest 1×32 FP4 scales inside each 128×128 FP8 block differ by less than a set ratio, which DeepSeek checked empirically for its weights; its FP8 framework then runs unchanged. Kimi K3 runs MXFP4 expert-weight, MXFP8-activation QAT through all of post-training, and MiMo-V2.6 starts MXFP4 QAT in mid-training. The posttraining notes compares these schemes.
NVIDIA is the exception. It pretrained Nemotron 3 Super in NVFP4 for most linear layers over all 25T tokens, keeping the last 15% of the network, the attention projections and the embeddings in BF16; Nemotron 3 Ultra followed over 20T tokens. Underflow was the visible cost: by the end of Super's run, zero-valued weight gradients covered 7% of the parameters.
"Through the support for FP8 computation and storage, we achieve both accelerated training and reduced GPU memory usage."
"Due to potential risks of performance degradation that we observed during preliminary study, we do not apply FP8 in computation."
E5M2's extra exponent bit buys range that a single per-tensor scale needs. Each 1×128 tile has its own scale and spans a narrower range, so E4M3 suffices and its extra mantissa bit adds precision.
⌊log2 3.0⌋ − 8 = −7, so the scale is 2−7 and 3.0 maps to 384. Each element costs 8 bits plus 8 / 32 = 0.25 for the shared scale: 8.25 bits.
Where do the 14 to 50 trillion training tokens of a large 2025-26 base model come from, and how do labs choose, clean, rewrite and check them?
How many tokens should a model of a given size see? A pretraining run reads a budget of D tokens. The Chinchilla study trained over 400 models and found that, for compute-optimal training, "the model size and the number of training tokens should be scaled equally". Chinchilla itself had 70B parameters and saw 1.4T tokens: 1.4×1012 / 7×1010 = 20 tokens per parameter. Compute-optimal counts training cost only; a smaller model also "reduces inference cost considerably".
Open models train far past that ratio:
Labs count differently (MiMo includes later mixture stages), so small gaps mean little.
Given a budget, how much of it should each kind of text get? A data mix gives each source family i (web, code, math, books) a share of the run, and the share decides how often that family's text is repeated:
Ui is what cleaning left of the family, so ei is how many times it is seen on average; ei above 1 means repetition. A 10T-token run that gives code w = 20% draws 2T code tokens; with 1T unique code tokens, each is seen e = 2 times.
Most labs describe the mix only in words. DeepSeek-V3 raised "the ratio of mathematical and programming samples", and Kimi K2 names four domains without shares. A few give numbers:
Table 5 of the MAI-Thinking-1 report gives each family's share wi and unique tokens Ui for a D = 30T run. Multiply, then divide.
| Family | wi | Di = wi × 30T | Ui | ei |
|---|---|---|---|---|
| Code | 54.6% | 16.38 ≈ 16.4T | 7.4T | 16.4 / 7.4 = 2.22 |
| STEM | 15.8% | 4.74 ≈ 4.7T | 2.2T | 2.17 |
| Web text | 14.9% | 4.47 ≈ 4.5T | 8.1T | 0.55 |
| Math | 5.4% | 1.62 ≈ 1.6T | 0.3T | 5.28 |
| PDFs | 4.7% | 1.41 ≈ 1.4T | 2.7T | 0.53 |
| Books and journals | 3.1% | 0.93 ≈ 0.9T | 0.6T | 1.65 |
| Multilingual (other) | 1.6% | 0.48 ≈ 0.5T | 8.1T | 0.5 / 8.1 = 0.06 |
| Total | 100% | 30.0T | 29.2T | 30.0 / 29.2 = 1.03 |
Small gaps from the report's ei (STEM: 4.7 / 2.2 = 2.14 vs 2.17) come from rounding Ui. Code is 7.4 / 29.2 = 25% of the unique tokens but 54.6% of training. Web and multilingual text are 16.2 / 29.2 ≈ 55% of the unique tokens but 16.5% of training. Math, the scarcest family, is repeated 5.28 times, near MAI's cap of 8 repetitions per dataset. The mix decides where repetition is spent.
How does a crawl of a trillion pages become a few billion training documents? Most of a web crawl never reaches training. MAI's pipeline shows the funnel:
Rules can cut the wrong pages: MiMo found that rule-based filters "incorrectly filter high-quality web pages containing substantial mathematical and code content" and switched to small LLM taggers.
Deduplication matters because repetition drives memorization, and a page copied with small edits is still a repeat. Broder's resemblance measures the overlap: cut each document into w-word "shingles" (every run of w consecutive words) and compare the two shingle sets:
A copy with one word changed still scores high. In a 1,000-word document with 7-word shingles, a changed word alters the 7 shingles that contain it: each version has 994 shingles, 987 are shared, and r = 987 / 1,001 = 0.986.
Comparing every pair of shingle sets is out of reach at this scale, so labs estimate r from short sketches. Under a random permutation of shingles, the smallest element of the union lands in the intersection with probability r, so short sketches of each document's smallest hashed shingles estimate r. MAI runs MinHash with locality-sensitive hashing (LSH) at a threshold of 0.8. LSH works on a signature of several MinHash values: it splits the signature into bands, hashes each band, and compares only documents that share a bucket in at least one band, which avoids comparing all pairs. GLM-4.5 found templated pages that MinHash missed and removed them by comparing document embeddings (SemDedup).
def shingles(doc, w=7):
toks = doc.split()
return {tuple(toks[i:i + w]) for i in range(len(toks) - w + 1)}
def sketch(doc, s=128):
# a fixed 64-bit hash stands in for Broder's random permutation
return set(sorted(hash64(sh) for sh in shingles(doc))[:s]) # MIN_s
def resemblance(m_a, m_b, s=128):
union_min = set(sorted(m_a | m_b)[:s]) # MIN_s(M(A) ∪ M(B))
return len(union_min & m_a & m_b) / len(union_min)
def dedup(docs, threshold=0.8): # MAI's fuzzy threshold
kept = [] # (doc, sketch) pairs
for d in docs:
m = sketch(d)
if all(resemblance(m, mk) < threshold for _, mk in kept):
kept.append((d, m))
return [d for d, _ in kept] # all-pairs loop for clarity; MAI uses MinHash-LSHHow do labs keep benchmark answers out of training? Decontamination removes benchmark items from the training data, so that a high score cannot come from memorized answers. The usual test drops any text that shares a run of n consecutive words with a test item. DeepSeek used 10-grams and removed about six million math texts; MAI uses 20-grams at 80% similarity. DeepSeek notes the method "cannot prevent the paraphrase of testset".
The knowledge cutoff is the last date of collected data, so benchmarks published later cannot leak in: V3's July 2024 cutoff predates CNMO 2024. Cutoffs trail releases by months: Gemma 3 (released March 2025) stops at August 2024, and MAI-Base-2 (October 2026) at March 2026. MAI-Base-1 lists one per source, from June 2025 for GitHub code to March 2026 for books.
When high-quality text runs short, is it better to repeat it or to rewrite it? Kimi K2 rewrites: a language model paraphrases knowledge-heavy documents in varied styles, chunk by chunk, and a fidelity check compares each rewrite with its source. The method follows WRAP, which reported that rephrasing noisy web text "speeds up pre-training by ∼ 3×". On an early K2 checkpoint, rewriting beat repetition on SimpleQA:
| Wiki text in training | SimpleQA |
|---|---|
| raw text, repeated 10 times | 23.76 |
| one rephrasing, repeated 10 times | 27.39 |
| 10 rephrasings, each seen once | 28.94 |
K3 reuses "the rephrasing recipe of Kimi K2". Other labs split on text written by models:
Can a teacher model give a richer target than the next word of the text? Next-token training uses a one-hot target: after "I drank a cup of", the text says "tea", and the loss rewards "tea" alone. A teacher might put 0.5 on "tea", 0.4 on "coffee" and 0.1 on "water", which also tells the student that coffee was nearly as likely. Knowledge distillation trains a student on such a predicted distribution. Hinton and colleagues note that high-entropy "soft targets" give "much more information per training case than hard targets". Gemma 2 used distillation to "simulate training beyond the number of available tokens".
Gemma 3 keeps only part of each teacher distribution. It samples 256 ids per position by teacher probability, zeroes the rest and renormalizes, which keeps 256 / 262,144 ≈ 1/1,000 of each distribution. Gemma 3 does not say why it samples. A plausible reason is cost: a full distribution over its 262k-entry vocabulary takes about 0.5 MB per token at 2 bytes per entry, for trillions of tokens. Sampling sometimes keeps low-probability tokens, which a top-k cut always drops; that dropped tail is the truncation bias Muse Glimmer addresses below. With teacher pT, student pS, context xc and sampled set S, the target q renormalizes the teacher over the sample and the loss is the cross-entropy to it:
def sampled_kd_loss(student_logits, teacher_logits, k=256):
# logits: [n, V] for n token positions, vocabulary size V
p_t = softmax(teacher_logits, axis=-1) # [n, V]
idx = sample_by_prob(p_t, k) # [n, k] ids, weighted by p_t
rows = arange(p_t.shape[0])[:, None] # [n, 1]
q = zeros_like(p_t) # [n, V]
q[rows, idx] = p_t[rows, idx] # non-sampled entries stay 0
q = q / q.sum(axis=-1, keepdims=True) # renormalize over the sample
log_p_s = log_softmax(student_logits, axis=-1) # [n, V]
return -(q * log_p_s).sum(axis=-1).mean() # cross-entropy to sparse target
# the sparse target is k ids and k probabilities per tokenMeta saves most teacher passes by distilling while the teacher trains. Llama 4 Maverick was "codistilled" from the nearly two-trillion-parameter Behemoth with a loss that "dynamically weights the soft and hard targets through training". Muse Glimmer uses the top 256 logits logged during Muse Spark's own pretraining. A KL-divergence loss, equal to cross-entropy up to a constant, runs for the first 95% of pretraining; plain cross-entropy on the true token runs for the last 5%, to address "the truncation bias inherent to top-k logit distillation". DeepSeek, Kimi, GLM and MiMo name no pretraining teacher, and MAI rules one out. NVIDIA's Nemotron 3.5 Lightning distills the top-128 logits of Nemotron 3 Super's base over 1T tokens into the existing Nemotron 3 Nano base, which these notes count as midtraining because it continues an earlier base.
"Specifically, a carefully designed rephrasing pipeline is employed to amplify the volume of high-quality tokens without inducing significant overfitting."
"Our pre-training is built on organic data which does not involve distillation from other models."
"Codistillation from Llama 4 Behemoth during pre-training amortizes the computational cost of resource-intensive forward passes needed to compute the targets for distillation"
No. 10% of 30T is 3T, which is 3T / 0.3T = 10 passes. The cap allows 8 × 0.3T = 2.4T, or 8% of the run. More would need more unique math text or rewritten copies of it.
The target keeps only the teacher's top 256 tokens and gives every other token zero probability, which biases it. The true next token can be any vocabulary entry, so a final stretch on it addresses that bias.
When a lab redesigns its model from one generation to the next, how do the separate choices in chapters 00 to 12 fit together?
Each earlier chapter took one design choice in isolation. Real redesigns change several at once, and the changes usually answer the same few pressures. This chapter walks through two model families in 2025 and 2026. Each walkthrough starts from one model's whole recipe, read in chapter order, then follows what each release changed. A sizing exercise that uses the calculator closes the chapter.
The starting recipe. DeepSeek-V3 (December 2024) set the template many labs copied. In chapter order: a large mixture of experts, 671B total and 37B active, with auxiliary-loss-free balancing (chapters 01 and 02); MLA attention (03); 128K context through YaRN after pretraining (04); one multi-token-prediction module (06); AdamW (08); FP8 training with 1×128 tiles and 128×128 blocks (11); and 14.8T tokens (12). Over the next 21 months DeepSeek changed almost every part of it.
| Choice | V3 · Dec 2024 | V3.2-Exp · Sep 2025 | V4-Pro · Apr 2026 | V4.1-Flash · Sep 2026 |
|---|---|---|---|---|
| Size | 671B total, 37B active | same 671B / 37B shape, continued from V3.1-Terminus | 1.6T total, 49B active | 552B backbone + 196B Engram memory; 16B active when generating |
| Attention | MLA, every layer full | MLA + sparse top-2,048 (DSA) | compressed sparse (CSA) interleaved with heavily compressed attention (HCA) | CSA2: KV and indexer keys shared across layers |
| Context | 128K, YaRN after pretraining | 128K | 1M, grown inside pretraining (4K → 1M) | 1M |
| Residual stream | standard | standard | mHC, 4 streams | single-pass mHC |
| Optimizer | AdamW | (inherited) | Muon for most matrices | head-wise Muon for queries and keys |
| Draft model | MTP module | (inherited) | MTP module | no MTP in pretraining; separate DSpark drafter |
| Modalities | text | text | text | images from the start of pretraining |
| Pretraining tokens | 14.8T | +0.95T of continued training | 33T | 45T |
V3.2-Exp: one change on the same base. Read the attention row left to right and the logic of chapter 03 appears in order. V3's MLA shrank the cache. V3.2-Exp kept the cache but read only the 2,048 most relevant tokens per query, which cuts compute and memory traffic at long context. DeepSeek described it as an experiment:
"V3.2-Exp builds upon V3.1-Terminus by introducing DeepSeek Sparse Attention—a sparse attention mechanism designed to explore and validate optimizations for training and inference efficiency"
V4-Pro: a new base model. V4 grew to 1.6T total and 49B active parameters, adopted Muon for most matrices (chapter 08) and 4 mHC residual streams (chapter 05), and read 33T tokens. Its attention attacked memory itself by compressing the stored keys and values before selecting from them, and it moved to 1M context, grown inside pretraining. The payoff DeepSeek reports at 1M tokens is large: V4-Pro needs 27% of the per-token compute and 10% of the KV cache of V3.2, despite having more active parameters. V4 also carries the stability guards of chapter 10: RMSNorm on its queries and compressed KV entries in place of QK-Clip, and SwiGLU clamping and Anticipatory Routing against MoE outliers.
V4.1-Flash: a layout for agents. V4.1-Flash went further for a specific workload. Agents call tools constantly, and each tool result has to be processed (prefilled) before the model can continue:
"In agentic workflows, frequent tool calls generate extensive prefill requests, imposing severe computational overhead when KV caches miss."
Its answer was a causal encoder-decoder layout. The bottom half of the layers works as usual. The top half does not compute its global keys and values from its own hidden states; DeepSeek writes that they "are projected directly from the hidden state of the (L/2)-th layer" using a separate projection per layer. So when a long tool result arrives, its tokens only need to pass through the bottom half: the top half's keys and values for those tokens come from one cheap projection each. New tokens generated afterwards still run through all layers. This is why V4.1-Flash activates 8B parameters per token during prefill and 16B during decode. Two other terms in the table are new. CSA2 is V4.1's version of compressed sparse attention, in which groups of layers share one set of compressed keys and values and one indexer instead of each storing its own. Engram is a large table of embeddings that the model reads through hashing; DeepSeek describes it as adding "sparsely accessed conditional memory", meant to separate memorization from computation.
V4.1-Flash also takes images from the start of pretraining (chapter 07) and moves drafting out of pretraining into a separate DSpark drafter (chapter 06). The optimizer and residual-stream rows changed only with a new base model: V4 brought Muon and mHC in together, and V4.1 refined both.
Moonshot's path shows a different habit: test a risky idea on a small research model first, then adopt it in a flagship.
The starting recipe. Kimi K2 (July 2025), in chapter order: 1.04T total and 32.6B active parameters, with 384 routed experts, 8 active and 1 shared, balanced by a bias without an auxiliary loss (01 and 02); MLA with 64 heads (03); 128K context with YaRN (04); MuonClip, Muon plus QK-Clip (08 and 10); a warmup-stable-decay schedule (09); BF16 compute with FP8 storage of selected activations (11); and 15.5T tokens, including rephrased knowledge text (12).
| Choice | K2 · Jul 2025 | Kimi Linear · Oct 2025 | K2.5 · Jan 2026 | K3 · Jul 2026 |
|---|---|---|---|---|
| Size | 1.04T total, 32.6B active | 48B total, 3B active (research) | K2 plus a 400M vision encoder | 2.78T total, 104.2B active |
| Attention | MLA, 64 heads | 3 KDA (linear) : 1 MLA, no RoPE in MLA | MLA | 3 KDA : 1 gated MLA, no RoPE in MLA |
| Experts | 384 routed, 8 active, 1 shared | 256 routed, 8 active incl. 1 shared | (inherited) | 896 routed, 16 active, 2 shared |
| Balancing | bias, no auxiliary loss | bias | (inherited) | quantile balancing |
| Optimizer | MuonClip | MuonClip | MuonClip | per-head Muon with clipping |
| Schedule | warmup-stable-decay | warmup-stable-decay | (continued training) | cosine |
| Residual stream | standard | standard | standard | block attention residuals |
| Modalities | text | text | vision added by continued training | vision from the start |
| Context | 128K with YaRN | 1M | 256K | 1M, no RoPE changes |
K2: fewer heads. K2's main architectural decision was to use half as many attention heads as DeepSeek-V3. The reason was the serving bill at long context:
"with a sequence length of 128k, increasing the number of attention heads from 64 to 128, while keeping the total expert count fixed at 384, leads to an 83% increase in inference FLOPs"
Kimi Linear: the hybrid, tested small. Kimi Linear tested the linear-attention hybrid from chapter 03 at 48B parameters, where a failed experiment is cheap. Moonshot reported that the hybrid "reduces memory and KV-cache usage by up to 75% during long-sequence generation while preserving global information flow via the full attention layers."
K3: the hybrid at flagship scale. Nine months later, K3 used the same 3:1 pattern at 2.78T parameters. K2.5 had added vision to K2 by continued training; K3 trains on images from the start. K3 also changed things that only matter at very large scale: quantile balancing because 896 experts are harder to keep evenly loaded, bounded activations (chapter 10) because unbounded SwiGLU outputs risked overflowing in low precision, and a cosine schedule because Moonshot's own scaling-law searches found it reached a lower loss than WSD. It also bounded KDA's decay rate (chapter 03) to a fixed range so that, in Moonshot's words, "both diagonal and off-diagonal tiles" of the chunked computation can "use dense Tensor Core matrix multiplications", which makes the linear layers faster on GPUs. From K2 it keeps the weight clipping and the rephrasing recipe.
Almost every change in both walkthroughs answers one of four pressures. Figure 13.1 groups the choices from all chapters by the pressure they relieve: DeepSeek's attention changes fall under serving memory, and MuonClip and K3's bounded activations under stability.
Two of these pressures grew sharply in 2025 and 2026. Serving cost grew because agents keep hundreds of thousands of tokens in context for many steps. Stability became harder because models crossed 1T and then nearly 3T parameters, and failures that were rare at smaller scale (exploding logits, overflowing activations, uneven expert loads) became common enough to need their own fixes.
You must serve 64 agent sessions at once, each with 256K tokens of context (262,144 tokens). After loading the weights, 300 GB of accelerator memory is left for the KV cache. Use the calculator (cache stored at 2 bytes per number) to decide which attention designs fit, and how many sessions each could hold.
The cache per session is (numbers stored per token) × 262,144 tokens × 2 bytes, plus any fixed state. From the model configurations:
Multiplying out and dividing 300 GB by the result gives the number of sessions that fit (the calculator above does the same arithmetic):
| Model (attention design) | Cache per session | Sessions in 300 GB |
|---|---|---|
| GLM-4.5 (GQA, full) | 98.8 GB | 3 |
| DeepSeek-V3.2 (MLA + sparse) | 22.5 GB | 13 |
| DeepSeek-V3 (MLA) | 18.4 GB | 16 |
| gpt-oss-120b (1:1 window 128) | 9.7 GB | 31 |
| Kimi K3 (3 KDA : 1 MLA) | 7.5 GB | 40 |
| MiMo-V2-Flash (5:1 window 128) | 6.1 GB | 49 |
| Kimi Linear (3 KDA : 1 MLA, small) | 2.1 GB | 140 |
Only the small linear-attention hybrid fits all 64 sessions. The rows compare attention designs at one length: GLM-4.5, gpt-oss-120b, DeepSeek-V3 and V3.2 were released for contexts of about 128K tokens, so their 256K figures show what the design would need, not a supported setting. Three lessons follow. First, sparse attention (DSA) does not save memory: V3.2 stores every token plus an index key, so its cache is larger than V3's; it saves compute and reads, which matter for speed. Second, the biggest memory savings come from not storing most tokens at all (sliding windows, linear-attention state) or from compressing what is stored (MLA, and V4's compression, which DeepSeek reports at 10% of V3.2's cache at 1M tokens). Third, storing the cache in FP8 instead of BF16 halves every cache size in the table and roughly doubles the session counts, so DeepSeek-V3 would fit 32 sessions. Each saving has a price: windows and linear layers lose exact access to distant tokens, which is why every window or linear design here keeps some full-attention layers.
Sparse attention changes which tokens each query reads, not which tokens are stored. V3.2 still keeps the MLA latent for every token and adds a small index key per token for the lightning indexer. What it saves is the work and memory traffic of attending: each query reads 2,048 selected latents plus the cheap index keys instead of all latents.
A new attention design changes the whole pretraining run, so a failure at flagship scale wastes months of compute. A small model shows whether quality holds and whether the kernels are fast enough at a fraction of the cost. Kimi also changed details before K3 (for example, bounding the decay so all tiles run on tensor cores), which needed the experience from the smaller model.
Short definitions of the terms used in these notes, with the chapter that explains each one.
To give credit if you find this work useful, please cite:
@misc{ma2026openrecipespretrain,
title={Reading Open-Weight Recipes, Part 1: What 2025--26 Model Reports Disclose About Pretraining},
author={Ma, Ziqiao},
howpublished={\url{https://mars-tin.github.io/blogs/posts/open_recipes_pretraining}},
note={Blog post},
year={2026}
}