8.5 — Multi-head Latent Attention (MLA)
Goal: Derive multi-head latent attention (DeepSeek-V2/V3/R1): factor each token's K and V through a small shared latent c^KV so the KV cache stores only the latent. Understand the decoupled-RoPE trick that lets the construction coexist with rotary positional encoding. Quantify the cache shrink at iso-quality vs MHA and GQA-8.
1. Why this session exists
GQA (§8.3) shrinks the KV cache by reducing the number of distinct K, V heads from to . The shrink is linear in , capped at (which would be MQA, with measurable quality cost). To go further, the cache has to be shrunk along a different axis — not the head count, but the per-head dimension.
DeepSeek-AI introduced Multi-head Latent Attention (MLA) in DeepSeek-V2 (2024[1], arXiv:2405.04434) and kept it in V3 and R1. The structural device: per token, compute a single low-rank latent vector with , store only this latent in the KV cache, and reconstruct the per-head and at attention time by multiplying with two fixed up-projection matrices. The cache shrink is — typically smaller than MHA at iso-quality.
The construction also requires a small surgical fix because the latent factorisation does not commute with RoPE. The fix — decoupled RoPE — uses a small auxiliary set of position-aware K heads that bypass the latent. The result is the most aggressive 2024-era KV-cache compression in any frontier LLM, and the dominant choice for DeepSeek's V2/V3/R1 line; it is plausibly the direction every long-context model will adopt within the next two years.
This session derives MLA from scratch, including the decoupled-RoPE trick, and compares the cache footprint against MHA and GQA-.[3]
2. The latent factorisation
Fix hidden size , query heads , per-head dimension , KV latent dimension (with ). The standard MHA projections are ; the input token's hidden produces stored in cache.
MLA factors each of and as a product of a down-projection (input to latent) and an up-projection (latent to per-head K or V). Crucially, the same latent is used for both K and V:
with . The composition has rank at most , so MLA-K is a low-rank approximation to MHA-K (and similarly for V). The choice of controls the rank-and-cache tradeoff.
The query side is not factored; queries are computed standardly per head (or, for parameter savings, with a separate latent on Q — see §5):
2.1 The cache
At decode, the KV cache stores per token only the latent — not . So the per-token cache footprint per layer is scalars (vs for MHA and for GQA-). Per layer over the full sequence:
(There is no factor of here because the latent is shared between K and V; see §3 below for what happens to the missing decoupled-RoPE K-head.) Compare:
| Variant | Per-token bytes (one layer, fp16) | | |---|---|---:| | MHA | | | | GQA- | | | | MLA | | |
With as in DeepSeek-V2, the MLA cache per token is smaller than MHA and smaller than GQA-. The DeepSeek-V2 paper reports a KV-cache reduction of vs the equivalent dense MHA baseline at the same total parameter count, and the V3 / R1 papers replicate this number.
2.2 At attention time
When a query at position needs to attend to past positions, we read the latents from cache, expand to K and V via , then run scaled dot-product attention as in MHA. Per-step expansion cost: FLOPs for and the same for , total .
This adds compute relative to MHA — we are doing an extra matmul to reconstruct K, V. But the bandwidth cost shrinks: we read scalars per token instead of . Since decode is bandwidth-bound (8.1 §3), the wall-clock latency at decode improves substantially. At long contexts the bandwidth savings dominate; at very short contexts (single-token decode with ) the extra matmul is the larger cost. The DeepSeek-V2 paper reports inference speedups of over their equivalent dense MHA baseline on long-context generation.
3. The RoPE problem
RoPE (6.7) modifies and by a position-dependent rotation before the dot product:
The rotation acts on the per-head vectors. But in MLA, the cache stores the latent , not — and the per-head is recovered only at attention time. So at what point do we apply RoPE?
Two options, both broken:
- Apply RoPE to after the up-projection. Then 's rotated form depends on position . But we cached , not , and the rotation does not commute with (it is a per-head block-diagonal rotation in the head dimension, not a global rotation), so we cannot push it through. Reapplying the rotation at decode time costs an extra per-head rotation per token per layer — non-trivial.
- Apply RoPE to before caching. This puts the rotation on the wrong dimension: the latent has dimension , not , and there is no factorisation of that makes the rotation commute with the up-projection.
Neither path is clean. DeepSeek-V2's fix, decoupled RoPE, is to not put the position information through the latent at all. Instead, augment each head with a small auxiliary positional component that bypasses the compression.
3.1 The decoupled-RoPE construction
Add two more projections:
with (DeepSeek-V2 uses and ). The projection is per head (size ); the projection is shared across heads (size ). RoPE is applied only to these auxiliary projections:
The final per-head Q and K at position and head are concatenations of the latent-derived part (position-agnostic) and the decoupled-RoPE part (position-aware):
The score is as standard. The dot product splits into a latent-derived term (no RoPE) and a decoupled-RoPE term (no latent):
The first term captures content-based attention through the low-rank latent; the second term encodes relative position through RoPE on the small auxiliary heads. This is the decoupling: position and content live in separate sub-channels of the K vector.
(The in the formula is the analogous query-side latent, defined in §4 below; if you skip Q-side compression, replace with .)
3.2 What goes into the cache
The KV cache stores two things per token per layer:
- (the shared latent)
- (the decoupled RoPE K — one per token, not per head, because is shared across heads)
Total per token per layer: scalars. For DeepSeek-V2 (), that is scalars per token per layer — still a shrink relative to MHA's .
The DeepSeek-V2 paper reports the cache as "" scalars per token per layer, using for the latent dimension; the symbol in this session matches Bishop-style notation for "rank" and is more transparent.
4. The Q-side latent
The construction can be applied to the query side too, for parameter savings rather than cache savings:
This is not about KV cache (queries are recomputed every step, not cached); it is about reducing the parameter count of . DeepSeek-V2 uses for , saving of the Q-projection parameters at a slight quality cost.
This is a smaller win and is sometimes omitted. The cache-side latent on K and V (and the decoupled RoPE for positions) is the structural MLA idea; the Q-side latent is an orthogonal parameter-shrinking trick.
5. Iso-quality comparison vs MHA and GQA
The DeepSeek-V2 paper reports head-to-head iso-FLOP comparisons against an equivalent-parameter MHA baseline:
| Attention | Per-token KV (bytes, fp16) | Validation perplexity (DeepSeek-V2 64B-active) | |---|---:|---:| | MHA, | | | | GQA-, | | | | MQA, | | | | MLA, | | |
The headline result: MLA achieves better perplexity than MHA at smaller cache. It outperforms GQA- at smaller cache. The MLA construction lets you cache substantially less than MQA and recover MHA-level quality, because the low-rank latent is a more expressive compression than "fewer heads."
The expressivity argument: GQA- collapses query heads into sharing one K, V. The mapping from input to K is rank- per group (). MLA's latent has rank- per all heads, and the up-projection adds back the per-head structure. If is set to about (so the per-token cache footprint is comparable), MLA spreads the rank across heads more flexibly than GQA does. The latent is, structurally, a richer compression than head sharing.
6. The full MLA forward pass
For concreteness, here is the MLA forward pass at a single layer, with both Q-side and KV-side latents and decoupled RoPE:
x : (T, d)
# Q-side
c_Q = x @ W_DQ # (T, r_Q)
Q = c_Q @ W_UQ # (T, h * d_h), reshape (T, h, d_h)
Q_R = x @ W_QR # (T, h * d_h^R), reshape (T, h, d_h^R)
Q_R = rope(Q_R) # apply per-head RoPE rotation
# KV-side (only c_KV and K_R are cached)
c_KV = x @ W_DKV # (T, r) <-- CACHE
K_R = x @ W_KR # (T, d_h^R) <-- CACHE, shared across heads
K_R = rope(K_R)
# Reconstruct per-head K and V from latent (at attention time)
K = c_KV @ W_UK # (T, h, d_h)
V = c_KV @ W_UV # (T, h, d_h)
# Concatenate latent-derived and decoupled-RoPE per-head K
K_full = concat(K, broadcast(K_R, h)) # (T, h, d_h + d_h^R)
Q_full = concat(Q, Q_R) # (T, h, d_h + d_h^R)
# Standard scaled dot-product attention
scores = Q_full @ K_full.transpose / sqrt(d_h + d_h^R)
scores = causal_mask(scores)
attn = softmax(scores)
out = attn @ V # (T, h, d_h)
out = reshape(out, T, d) @ W_O
The cache footprint per token per layer is the storage of c_KV and K_R — scalars total. The rest is recomputed each step.[2]
7. What we keep going forward
-
MLA factors and through a shared low-rank latent . Only the latent is cached; per-head are reconstructed at attention time. Cache shrinks by per token per layer.
-
Decoupled RoPE handles the position-encoding obstruction. A small auxiliary per-head and a shared carry the RoPE rotation in a parallel sub-channel; the position-aware sub-channel and the content-aware latent sub-channel are concatenated to form the full Q and K.
-
At iso-quality, MLA cache is – smaller than GQA-. This is the most aggressive 2024-era KV-cache compression; it is the dominant attention variant in the DeepSeek V2/V3/R1 lineage and is plausibly where the field is heading.
We have now covered both attention-side KV-cache shrinkers in Chapter 8: GQA (§8.3) and MLA (§8.5). The next session, MoE (§8.6), shifts to the FFN side and trades a different kind of cost — replacing the dense FFN with a sparse router-and-experts ensemble.
(a) Write the MLA factorisation of and via the shared latent . Identify the three matrices and their shapes. What is stored in the KV cache?
(b) Compute the per-token cache footprint per layer in fp16 for MHA, GQA-, and MLA at . Verify the numbers in §2.1 and §5.
(c) State the RoPE obstruction of §3 in your own words. Why does the rotation not commute with the up-projection ?
(d) Write the decoupled-RoPE concatenation for and . Show that the attention score decomposes into a latent-content term and a RoPE-position term that add.
(e) Argue why MLA's decode-time FLOPs are slightly higher than MHA's but its decode-time wall-clock latency is dramatically lower. Cite the bandwidth analysis of 8.1 §3.
(f) Why does the field not consider GQA "obsolete" in 2026 despite MLA's better iso-quality numbers? List the three reasons of §5.
References
- [1]
DeepSeek-AI (2024). "DeepSeek-V2: A strong, economical, and efficient mixture-of-experts language model" (MLA). arXiv:2405.04434 — Multi-head latent attention; low-rank K, V compression (6.14 §5.2; full treatment Cap 8.5).
↩ - [2]
DeepSeek-AI (2024). "DeepSeek-V3 technical report". arXiv:2412.19437 — V3 architecture: MLA + 256-expert fine-grained MoE + auxiliary-loss-free balancing (8.5, 8.6 §6).
↩ - [3]
DeepSeek-AI (2025). "DeepSeek-R1: Incentivizing reasoning capability in LLMs via reinforcement learning". arXiv:2501.12948 — R1 architecture inherits V3's MLA + MoE (referenced in 8.5; primary citation in Cap 9.7).
↩