Chapter 07Three that forget, one that doesn't

Kimi K3: the assembled system

The sequence-mixing argument finished in chapter 6. What remains is everything else a 2.8-trillion-parameter model needs in order to exist: a cache small enough to serve, parameters cheap enough to run, activations stable enough to train in low precision — and one genuinely new idea that runs attention along depth instead of along the sequence.

The floor plan

The K3 language backbone is chapter 6's hybrid, repeated. Twenty-three macrocycles of four layers each — ninety-two layers. Inside every macrocycle, three KDA layers and one MLA layer, in that order. The first layer of the network uses a dense feed-forward block; every layer after it uses a latent mixture-of-experts.

Compared with Kimi Linear, the changes are a short list, and the rest of this chapter is that list:

92 LAYERS · 23 MACROCYCLES · 8 ATTNRES BLOCKS ONE MACROCYCLE KDA+ latent MoE KDA+ latent MoE KDA+ latent MoE MLA+ latent MoE Constant-state memory three times over, then one layer that still has the raw context. THE STACK, BOTTOM TO TOP AttnResAttnResAttnRes AttnResAttnResAttnRes Every twelfth layer, the stack pauses and lets the next block choose which earlier blocks it wants to read.
Ninety-two layers, two rhythms. The fast rhythm is the macrocycle: three constant-state KDA layers (pale) then one MLA layer (red) that still holds a real cache. The slow rhythm is the AttnRes boundary every twelve layers (plum), which is not a layer at all but a re-weighting of the residual stream. Everything else in this chapter hangs off one of those two rhythms.

MLA: cache one vector, not one per head

Chapter 1 established that decoding is bound by bytes moved, not arithmetic, and that the bytes are the KV cache. Ordinary multi-head attention stores, per token per layer, a key and a value for every head:

\[ \text{cache per token per layer} \;=\; 2 \cdot H \cdot d_h \quad\text{numbers} \] 7.1

Multi-head Latent Attention removes the factor of \(H\). Instead of caching \(H\) keys and \(H\) values, project the token once into a single low-dimensional latent and cache only that:

\[ c_t \;=\; x_t W_{DKV} \in \mathbb{R}^{d_c},\qquad K^{(h)} = c_t W_{UK}^{(h)},\qquad V^{(h)} = c_t W_{UV}^{(h)} \] 7.2

Every head's key and value are reconstructed from the same \(c_t\) on demand. The obvious objection is that reconstructing them costs a matmul per head per step, which sounds worse. It does not happen, because of a small algebraic trick worth seeing:

\[ q^{(h)}\big(c_t W_{UK}^{(h)}\big)^{\!\top} \;=\; q^{(h)} W_{UK}^{(h)\top} c_t^{\top} \;=\; \big(\underbrace{q^{(h)}W_{UK}^{(h)\top}}_{\text{fold into }W_Q\text{ once}}\big)\,c_t^{\top} \] 7.3

Matrix multiplication is associative, so \(W_{UK}\) can be absorbed into the query projection at load time, and \(W_{UV}\) likewise into the output projection. At inference no up-projection is ever performed: queries are pre-transformed into latent space and attend directly against the cached \(c_t\). You cache one vector per token per layer and pay nothing extra to use it.

SchemeNumbers cached / token / layerIllustrative, H=64, dh=128
Multi-head attention\(2Hd_h\)16,384
Grouped-query, 8 groups\(2Gd_h\)2,048
MLA\(d_c + d_h^{\text{rope}}\)≈ 576

Head counts are illustrative rather than K3's published configuration; the ratio is the point. Combined with chapter 6's hybrid — only one layer in four holds any cache at all — this is what makes a 2.8T model servable at long context.

K3 adds two refinements. Query LoRA factors the (now large) query projection through a low-rank bottleneck, cutting its parameters and activation memory during training. Gated MLA puts an elementwise gate, projected from the input, on the attention output before it re-enters the residual stream — so the model decides, per channel, how much of what it retrieved is actually admitted. That is the same idea as chapter 6's per-channel decay, applied at the exit instead of the store.

Mixture-of-experts: buy capacity, not compute

The constraint

Most of a transformer's parameters live in the feed-forward blocks, and that is where factual knowledge is stored. But a dense FFN runs all of its parameters for every token. At 2.8 trillion parameters that is arithmetically impossible to serve, and it is also wasteful: the parameters that know about Cantonese grammar have no business firing on a Python token.

A mixture-of-experts replaces the single wide FFN with many narrow ones and a router that picks a few per token. K3 uses 898 experts: two shared experts that process every token, and 896 routed experts of which the router selects 16.

\[ y \;=\; \sum_{s \in \text{shared}} E_s(x) \;+\; \sum_{i \in \text{top-}16} g_i\,E_i(x), \qquad g = \text{softmax}\big(\text{top-}16(xW_r)\big) \] 7.4

The accounting is the whole point:

The decoupling

Memory scales with total experts. FLOPs scale with active experts. Eighteen of 898 run per token — about 2%. So the model holds the knowledge of a 2.8T-parameter network while doing the arithmetic of a far smaller one. This is the cheapest kind of capacity there is, and it is why parameter counts and compute budgets stopped tracking each other around 2024.

The two shared experts are not a rounding detail. Without them, every routed expert has to independently learn the generic transformations that all tokens need, duplicating that capacity 896 times. Making a couple of experts unconditional lets the routed ones specialise on what is actually token-specific.

K3 then adds latent-space MoE: down-project the input into a compressed space, run the experts there, and up-project their summed output. Expert width falls, so expert FLOPs fall — roughly by half — for the same expert count.

898 EXPERTS · 18 RUN · IN A COMPRESSED SPACE x token down-proj shared expert 1 shared expert 2 ALWAYS ON · 2 ROUTER PICKS 16 OF 896 …896 total, 16 lit Σ, up-proj y THE LEDGER Parameters held898 experts Parameters run, per token18 experts · 2% Expert widthcompressed · ≈½ FLOPs The bill that does not shrink: all 898 must still be resident in memory, and routing scatters tokens across devices.
Sparse capacity. Two experts see every token; sixteen more are chosen for it. The routed experts are lit in blue, the 880 that stay dark in grey. Running experts in a down-projected space halves their arithmetic again. Note what is not saved: every expert must be resident, so MoE trades a compute problem for a memory-capacity and interconnect problem — which is why these models are served across many devices rather than one.

SiTU, and a lesson about what activations cost

The standard gated activation splits its input in half and computes \(\text{SiLU}(g) \odot u\). K3 replaces it with SiTU, which wraps both halves in a scaled hyperbolic tangent:

\[ \text{SiTU}(g,u) \;=\; \Big[\underbrace{\beta\tanh(g/\beta)}_{\text{soft clamp}}\cdot\,\sigma(g)\Big] \;\odot\; \Big[\beta_\ell\tanh(u/\beta_\ell)\Big] \] 7.5

The function \(\beta\tanh(x/\beta)\) is worth recognising: it is the identity for \(|x| \ll \beta\) and saturates smoothly at \(\pm\beta\). It is a soft clamp with a learnable ceiling, and it does nothing at all to typical activations. Its entire job is the tail.

β SiLU — unbounded soft-clamped activation magnitude → WHY A TANH
Bounding the tail. Training a model this size in fp8 fails on outliers, not on averages: one activation far outside the representable range destroys the scale factor for the whole tensor. The clamp costs nothing in the bulk of the distribution and makes the tail representable. This is a numerics decision wearing the costume of an architecture decision.
The engineering lesson

Written naively, SiTU is close to 3× slower than the activation it replaced. Not because a tanh is expensive in FLOPs — it is trivial — but because each extra elementwise operation is another full read and write of a very large activation tensor, and elementwise work is entirely memory-bandwidth bound. Chapter 1 made this point about the KV cache; it applies just as hard here. The fix is the same one it always is: fuse, so the tensor is read once, all the pointwise math happens in registers, and it is written once. Which is why a five-line change to an activation function is a kernel-engineering project, and why the latent-MoE compression that halves expert FLOPs is what pays for it.

AttnRes: attention along the depth axis

The last idea has nothing to do with tokens. It is about the residual stream — and it is the most interesting thing in K3.

In a standard transformer, layer \(\ell\) reads the sum of the embedding and every preceding layer's output:

\[ h_\ell \;=\; h_1 \;+\; \sum_{i=1}^{\ell-1} f_i(h_i) \] 7.6

where h1 is the token embedding and fi is the attention-plus-FFN block at layer \(i\). Every term enters with weight exactly 1.

Two things go wrong with that, and they are separate problems.

Problem one: the residual stream dilutes

If successive layer outputs are roughly independent with typical norm \(\sigma\), their sum grows like a random walk:

\[ \|h_\ell\| \;\approx\; \sigma\sqrt{\ell} \qquad\Longrightarrow\qquad \frac{\|f_\ell(h_\ell)\|}{\|h_\ell\|} \;\approx\; \frac{1}{\sqrt{\ell}} \] 7.7

At layer 4 a block's output is half the stream. At layer 92 it is a tenth of it. For a late layer to change the model's mind about anything, it must emit an output roughly \(\sqrt{92} \approx 9.6\) times larger than an early layer would — and large late-layer outputs are precisely the thing that destabilises training at depth. The uniform residual sum quietly imposes a penalty on depth that grows with depth.

Problem two: everyone gets the same undifferentiated bus

Layer 80 receives the same aggregated blob as layer 20. If what it actually needs is the output of layer 12 — some syntactic parse, a resolved coreference — it has to dig that out of a sum in which layer 12 is one of seventy-nine equally weighted contributors. There is no addressing.

The mechanism

Give the weights back to the model. Let each block form a query, treat every earlier block's output as a key and value, and take a softmax-weighted combination. This is ordinary attention — with the depth axis in the role usually played by the sequence axis.

\[ h_\ell \;=\; \sum_{i} \alpha_i^{(\ell)} f_i(h_i), \qquad \alpha^{(\ell)} \;=\; \text{softmax}_i\Big(\big\langle q_\ell,\; \text{norm}\big(f_i(h_i)\big)\big\rangle\Big) \] 7.8
# the core of block_attn_res — N previous blocks plus the one in progress
V = torch.stack(blocks + [partial_block])            # [N+1, B, T, D]  ← values: block outputs
K = norm(V)                                          # keys: the same, normalised
logits = torch.einsum('d, n b t d -> n b t', proj.weight.squeeze(), K)
h      = torch.einsum('n b t, n b t d -> b t d', logits.softmax(0), V)
return h

Read the softmax(0) carefully: the normalisation is over dimension 0, which is the block axis. Each token, independently, produces a distribution over the depth of the network and mixes the blocks accordingly.

And note that this fixes problem one for free. Because the weights are a softmax, they sum to one, so \(h_\ell\) is a convex combination of block outputs rather than an unbounded sum. Its norm is bounded by the largest single block output, no matter how deep the network gets. The \(\sqrt{\ell}\) dilution simply does not occur.

UNIFORM RESIDUAL · EVERY BLOCK WEIGHTED 1 1.01.01.01.01.01.01.0 layer ℓ Norm grows as √ℓ. A late block's contribution is 1/√ℓ of the stream. No way to prefer one block over another. ATTNRES · SOFTMAX OVER DEPTH, PER TOKEN .03.41.07.02.24.04.19 layer ℓ Σα = 1 A convex combination. Norm bounded by the largest single block, at any depth. And the layer chose what to read.
The same computation, addressed instead of summed. Left, the standard residual: every earlier block enters with weight 1, the stream's norm walks upward, and there is no mechanism for preference. Right, AttnRes: weights are produced per token by a dot product against a learned query, normalised over depth. The dilution problem and the addressing problem are both artefacts of the constant 1, and both go away when it is learned.

Why every twelfth layer, and not every layer

Because the cost is real. Applying AttnRes at every layer means keeping every earlier layer's output live, and computing a distribution over an ever-growing set of them, ninety-two times. K3 instead defines a block as the accumulated sum of attention and MLP outputs across 12 decoder layers, stores that single depth-representation, and mixes only at block boundaries. Across 23 macrocycles that yields eight AttnRes blocks.

The reported trade is about 2% additional inference latency for a 1.25× compute advantage — the latter because a network whose late layers can address early representations directly needs to spend less depth re-deriving them. Coarse granularity captures most of the benefit at a fraction of the bookkeeping, which is the same argument chunk size \(C\) made in chapter 4: full generality at the finest granularity is rarely what the hardware wants.

The unification

A fixed-size memory must discard. K3 answers that with three retrievals along three different axes, and they are the same operation each time. MLA retrieves across tokens — what did I read earlier? AttnRes retrieves across depth — what did I compute earlier? MoE routing retrieves across parameters — what do I know that is relevant here? Each is a learned query, a set of keys, and a normalised weighted read. The architecture is one idea applied three times, in three directions.

What this chapter did not solve

Every mechanism here is a selection mechanism, and selection is only as good as the thing selecting. A router that misroutes, a query that attends to the wrong block, a channel whose decay was learned badly — each fails silently, degrading quality without raising an error. The lineage has spent seven chapters replacing exhaustive computation with learned choice, and has thereby made model quality depend on the quality of those choices in ways that are much harder to measure than a loss curve. Chapter 8 puts the whole argument in one table and asks where the pressure goes next.