AI / ML Theory
Chapter 8 · 28 min

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 hh to gg. The shrink is linear in h/gh/g, capped at hh (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 cKV∈Rrc^{KV} \in \mathbb{R}^{r} with r≪hdhr \ll h d_h, store only this latent in the KV cache, and reconstruct the per-head KK and VV at attention time by multiplying with two fixed up-projection matrices. The cache shrink is hdh/rh d_h / r — typically ∼10×\sim 10\times 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-88.[3]


2. The latent factorisation

Fix hidden size dd, query heads hh, per-head dimension dhd_h, KV latent dimension rr (with r≪hdhr \ll h d_h). The standard MHA projections are WK,WV∈Rd×hdhW_K, W_V \in \mathbb{R}^{d \times h d_h}; the input token's hidden x∈Rdx \in \mathbb{R}^d produces Ki=xWK∈RhdhK_i = x W_K \in \mathbb{R}^{h d_h} stored in cache.

MLA factors each of WKW_K and WVW_V 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:

 ciKV=xi WDKV∈Rr(down-projection, stored in cache)Ki=ciKV WUK∈Rhdh(up-projection, applied at attention time)Vi=ciKV WUV∈Rhdh(up-projection, applied at attention time) \boxed{\ \begin{aligned} c_i^{KV} &= x_i\, W^{DKV} \in \mathbb{R}^{r} &\text{(down-projection, stored in cache)} \\ K_i &= c_i^{KV}\, W^{UK} \in \mathbb{R}^{h d_h} &\text{(up-projection, applied at attention time)} \\ V_i &= c_i^{KV}\, W^{UV} \in \mathbb{R}^{h d_h} &\text{(up-projection, applied at attention time)} \end{aligned} \ }

with WDKV∈Rd×r,WUK,WUV∈Rr×hdhW^{DKV} \in \mathbb{R}^{d \times r}, W^{UK}, W^{UV} \in \mathbb{R}^{r \times h d_h}. The composition WDKVWUKW^{DKV} W^{UK} has rank at most rr, so MLA-K is a low-rank approximation to MHA-K (and similarly for V). The choice of rr 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):

Qi=xi WQ∈Rhdh.Q_i = x_i\, W^Q \in \mathbb{R}^{h d_h}.

2.1 The cache

At decode, the KV cache stores per token only the latent ciKV∈Rrc_i^{KV} \in \mathbb{R}^{r} — not Ki,ViK_i, V_i. So the per-token cache footprint per layer is rr scalars (vs 2hdh2 h d_h for MHA and 2gdh2 g d_h for GQA-gg). Per layer over the full sequence:

#bytesKVMLA=bpe⋅L⋅r⋅T⋅batch.\#\text{bytes}_\text{KV}^\text{MLA} = \text{bpe} \cdot L \cdot r \cdot T \cdot \text{batch}.

(There is no factor of 22 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) | h=64,dh=128,g=8,r=512h = 64, d_h = 128, g = 8, r = 512 | |---|---|---:| | MHA | 4⋅h⋅dh4 \cdot h \cdot d_h | 32,76832{,}768 | | GQA-88 | 4⋅g⋅dh4 \cdot g \cdot d_h | 4,0964{,}096 | | MLA | 2⋅r2 \cdot r | 1,0241{,}024 |

With r=512r = 512 as in DeepSeek-V2, the MLA cache per token is ∼32×\sim 32\times smaller than MHA and ∼4×\sim 4\times smaller than GQA-88. The DeepSeek-V2 paper reports a KV-cache reduction of ∼93%\sim 93\% 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 tt needs to attend to past positions, we read the latents c1:tKV∈Rt×rc_{1:t}^{KV} \in \mathbb{R}^{t \times r} from cache, expand to K and V via WUK,WUVW^{UK}, W^{UV}, then run scaled dot-product attention as in MHA. Per-step expansion cost: 2trhdh2 t r h d_h FLOPs for KK and the same for VV, total 4trhdh4 t r h d_h.

This adds compute relative to MHA — we are doing an extra matmul to reconstruct K, V. But the bandwidth cost shrinks: we read rr scalars per token instead of 2hdh2 h d_h. 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 T≈1T \approx 1) the extra matmul is the larger cost. The DeepSeek-V2 paper reports inference speedups of 5.76×5.76\times over their equivalent dense MHA baseline on long-context generation.


3. The RoPE problem

RoPE (6.7) modifies QQ and KK by a position-dependent rotation Rθ(t)R_\theta(t) before the dot product:

Attnscore(i,j)=⟨Rθ(i) qi, Rθ(j) kj⟩/dh.\text{Attn}_\text{score}(i, j) = \langle R_\theta(i)\, q_i,\ R_\theta(j)\, k_j \rangle / \sqrt{d_h}.

The rotation acts on the per-head KK vectors. But in MLA, the cache stores the latent cjKVc^{KV}_j, not kjk_j — and the per-head kj=cjKVWUKk_j = c^{KV}_j W^{UK} is recovered only at attention time. So at what point do we apply RoPE?

Two options, both broken:

  • Apply RoPE to kjk_j after the up-projection. Then kjk_j's rotated form depends on position jj. But we cached cjKVc^{KV}_j, not kjk_j, and the rotation Rθ(j)R_\theta(j) does not commute with WUKW^{UK} (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 hdh×dhh d_h \times d_h per-head rotation per token per layer — non-trivial.
  • Apply RoPE to cjKVc^{KV}_j before caching. This puts the rotation on the wrong dimension: the latent has dimension rr, not hdhh d_h, and there is no factorisation of WUKW^{UK} 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:

qiR=xi WQR∈RhdhR,kiR=xi WKR∈RdhR,q_i^R = x_i\, W^{QR} \in \mathbb{R}^{h d_h^R}, \qquad k_i^R = x_i\, W^{KR} \in \mathbb{R}^{d_h^R},

with dhR≪dhd_h^R \ll d_h (DeepSeek-V2 uses dhR=64d_h^R = 64 and dh=128d_h = 128). The qRq^R projection is per head (size hdhRh d_h^R); the kRk^R projection is shared across heads (size dhRd_h^R). RoPE is applied only to these auxiliary projections:

q^iR=Rθ(i) qiR,k^jR=Rθ(j) kjR.\hat q_i^R = R_\theta(i)\, q_i^R, \qquad \hat k_j^R = R_\theta(j)\, k_j^R.

The final per-head Q and K at position ii and head ℓ\ell are concatenations of the latent-derived part (position-agnostic) and the decoupled-RoPE part (position-aware):

 qi,ℓ=[(ciQWUQ)ℓq^i,ℓR]∈Rdh+dhR,kj,ℓ=[(cjKVWUK)ℓk^jR]∈Rdh+dhR. \boxed{\ q_{i, \ell} = \begin{bmatrix} (c_i^Q W^{UQ})_\ell \\ \hat q_{i, \ell}^R \end{bmatrix} \in \mathbb{R}^{d_h + d_h^R}, \qquad k_{j, \ell} = \begin{bmatrix} (c_j^{KV} W^{UK})_\ell \\ \hat k_j^R \end{bmatrix} \in \mathbb{R}^{d_h + d_h^R}. \ }

The score is ⟨qi,ℓ,kj,ℓ⟩/dh+dhR\langle q_{i, \ell}, k_{j, \ell} \rangle / \sqrt{d_h + d_h^R} as standard. The dot product splits into a latent-derived term (no RoPE) and a decoupled-RoPE term (no latent):

⟨qi,ℓ,kj,ℓ⟩=⟨(ciQWUQ)ℓ,(cjKVWUK)ℓ⟩+⟨q^i,ℓR,k^jR⟩.\langle q_{i, \ell}, k_{j, \ell} \rangle = \langle (c_i^Q W^{UQ})_\ell, (c_j^{KV} W^{UK})_\ell \rangle + \langle \hat q_{i, \ell}^R, \hat k_j^R \rangle.

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 ciQc_i^Q in the formula is the analogous query-side latent, defined in §4 below; if you skip Q-side compression, replace (ciQWUQ)ℓ(c_i^Q W^{UQ})_\ell with (xiWQ)ℓ(x_i W^Q)_\ell.)

3.2 What goes into the cache

The KV cache stores two things per token per layer:

  • cjKV∈Rrc_j^{KV} \in \mathbb{R}^{r} (the shared latent)
  • k^jR∈RdhR\hat k_j^R \in \mathbb{R}^{d_h^R} (the decoupled RoPE K — one per token, not per head, because WKRW^{KR} is shared across heads)

Total per token per layer: r+dhRr + d_h^R scalars. For DeepSeek-V2 (r=512,dhR=64r = 512, d_h^R = 64), that is 576576 scalars per token per layer — still a ∼30×\sim 30\times shrink relative to MHA's 2hdh=16,3842 h d_h = 16{,}384.

The DeepSeek-V2 paper reports the cache as "dc+dhRd_c + d_h^R" scalars per token per layer, using dcd_c for the latent dimension; the symbol rr 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:

ciQ=xi WDQ∈RrQ,Qi=ciQ WUQ∈Rhdh.c_i^Q = x_i\, W^{DQ} \in \mathbb{R}^{r_Q}, \qquad Q_i = c_i^Q\, W^{UQ} \in \mathbb{R}^{h d_h}.

This is not about KV cache (queries are recomputed every step, not cached); it is about reducing the parameter count of WQW^Q. DeepSeek-V2 uses rQ=1536r_Q = 1536 for d=5120d = 5120, saving ∼60%\sim 60\% 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, h=128,dh=128h = 128, d_h = 128 | 65,53665{,}536 | 4.054.05 | | GQA-88, h=128,dh=128,g=8h = 128, d_h = 128, g = 8 | 4,0964{,}096 | 4.094.09 | | MQA, h=128,dh=128h = 128, d_h = 128 | 512512 | 4.204.20 | | MLA, r=512,dhR=64r = 512, d_h^R = 64 | 1,1521{,}152 | 4.044.04 |

The headline result: MLA achieves better perplexity than MHA at ∼56×\sim 56\times smaller cache. It outperforms GQA-88 at ∼4×\sim 4\times 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-gg collapses h/gh/g query heads into sharing one K, V. The mapping from input to K is rank-dhd_h per group (WK∈Rd×dhW_K \in \mathbb{R}^{d \times d_h}). MLA's latent cKVc^{KV} has rank-rr per all heads, and the up-projection WUKW^{UK} adds back the per-head structure. If rr is set to about gdhg d_h (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 — r+dhRr + d_h^R scalars total. The rest is recomputed each step.[2]


7. What we keep going forward

  1. MLA factors KK and VV through a shared low-rank latent cKVc^{KV}. Only the latent is cached; per-head K,VK, V are reconstructed at attention time. Cache shrinks by ∼hdh/r\sim h d_h / r per token per layer.

  2. Decoupled RoPE handles the position-encoding obstruction. A small auxiliary per-head qRq^R and a shared kRk^R 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.

  3. At iso-quality, MLA cache is ∼4\sim 4–30×30\times smaller than GQA-88. 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.

Self-test

(a) Write the MLA factorisation of KK and VV via the shared latent cKVc^{KV}. Identify the three matrices WDKV,WUK,WUVW^{DKV}, W^{UK}, W^{UV} and their shapes. What is stored in the KV cache?

(b) Compute the per-token cache footprint per layer in fp16 for MHA, GQA-88, and MLA at h=128,dh=128,g=8,r=512,dhR=64h = 128, d_h = 128, g = 8, r = 512, d_h^R = 64. 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 WUKW^{UK}?

(d) Write the decoupled-RoPE concatenation for qi,ℓq_{i, \ell} and kj,ℓk_{j, \ell}. 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. [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. [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. [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).

    ↩