Multi-Head Attention and Representation Subspaces
Split d_model into h smaller subspaces so each head can run its own attention pattern in parallel.
So you've got single-head attention working. One set of , , matrices, one attention pattern per position, one weighted sum of values. It works. But there's a problem worth tracing through.
Consider the sentence: "The cat sat on the mat because it was tired."
The word "it" needs to figure out what to attend to, and the answer is kind of everything, for different reasons. It needs to attend to "cat" for coreference (that's what "it" refers to). It needs to attend to "was" for syntax ("it" is the subject, "was" is its verb). And it probably wants some sense of "tired" for semantic coherence.
A single attention head produces one softmax distribution per query position. So "it" has to smear its attention across "cat" and "was" and "tired" and hope that the weighted average of their value vectors captures all three relationships simultaneously. That's a lot to ask of a single soft lookup.
You're basically forcing the model to compute one compromise attention pattern when what it really needs is several independent ones running in parallel. One for syntax, one for coreference, one for whatever else the model discovers is useful.
That's exactly what multi-head attention does, and it's the form Vaswani et al. introduced in the original transformer paper.
Prerequisites
This post assumes you've read Self-Attention: Q, K, V from First Principles (Post 4.1). You should be comfortable with scaled dot-product attention, softmax over score vectors, and the idea that , , project each token into query, key, and value vectors. We'll also use basic matrix shapes and parameter counting, but nothing beyond what showed up in that post.
Splitting the representation
The idea is almost disappointingly simple. Instead of computing one attention function over the full -dimensional space, you split the representation into smaller subspaces and compute attention independently in each one.
If and , each head operates on vectors of dimension . Each head gets its own projection matrices: , , , each of shape . So head computes:
Then each head runs the same scaled dot-product attention from Post 4.1:
Each head's output has shape , a sequence of tokens, each represented in that head's -dimensional subspace. We concatenate all heads back together and project through one final matrix of shape :
Each head gets to learn its own attention pattern. One head might specialize in positional relationships, another in syntactic dependencies, another in something the model finds useful that we might not even have a name for. And then mixes everything back together.
Toggle between h=2 and h=4 to see how splitting d_model changes the subspace dimension d_k, and how each head's attention pattern over the same four tokens looks different.
The parameter count
When I first saw multi-head attention, I assumed it must add parameters. More heads, more matrices, more stuff. It doesn't. For a fixed , the total parameter count is the same whether you use 1 head or 16.
Here's the accounting. With heads, each head has three projection matrices of shape where . That's parameters per head, times heads:
Plus which is , so more. Total: .
A single head with full-dimensional Q, K, V? Three matrices of plus an output projection. Also .
Toggle h to watch the per-head matrices shrink exactly as fast as the head count grows. The W_O bar never moves, and the grand total stays pinned at 4 × d_model². You're rearranging the budget, not adding to it.
Same parameter budget. What changes is the structure. Multi-head attention trades representational capacity per head (each head only sees dimensions) for the ability to maintain multiple independent attention patterns. You're not getting a bigger model. You're getting a differently shaped one.
The reshape trick
In the math I wrote above, each head has its own separate matrix. You could implement it that way, with a loop over heads. Nobody does, because GPUs hate loops.
In practice, you use one big matrix of shape that computes all the queries at once. Then you reshape the output from to and transpose to . Now the dimension is a batch dimension, and a single batched matrix multiply handles all heads simultaneously.
This is one of those things where the math and the implementation look very different, but they're computing the exact same thing. The big matrix is literally the concatenation of all the per-head matrices side by side. The reshape just splits its output into the per-head chunks.
import torch
import torch.nn as nn
import torch.nn.functional as F
class MultiHeadAttention(nn.Module):
def __init__(self, d_model: int, num_heads: int):
super().__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.h = num_heads
self.d_k = d_model // num_heads
# One big projection for all heads, then reshape
self.W_Q = nn.Linear(d_model, d_model, bias=False)
self.W_K = nn.Linear(d_model, d_model, bias=False)
self.W_V = nn.Linear(d_model, d_model, bias=False)
self.W_O = nn.Linear(d_model, d_model, bias=False)
def forward(self, x: torch.Tensor) -> torch.Tensor:
b, n, _ = x.shape
# Project: (b, n, d_model) -> (b, n, d_model)
Q = self.W_Q(x)
K = self.W_K(x)
V = self.W_V(x)
# Reshape into heads: (b, n, d_model) -> (b, h, n, d_k)
Q = Q.view(b, n, self.h, self.d_k).transpose(1, 2)
K = K.view(b, n, self.h, self.d_k).transpose(1, 2)
V = V.view(b, n, self.h, self.d_k).transpose(1, 2)
# Scaled dot-product attention (all heads at once)
# (b, h, n, d_k) @ (b, h, d_k, n) -> (b, h, n, n)
scores = (Q @ K.transpose(-2, -1)) / (self.d_k ** 0.5)
attn = F.softmax(scores, dim=-1)
# (b, h, n, n) @ (b, h, n, d_k) -> (b, h, n, d_k)
out = attn @ V
# Concat heads: (b, h, n, d_k) -> (b, n, d_model)
out = out.transpose(1, 2).contiguous().view(b, n, self.d_model)
# Final projection
return self.W_O(out)That .view(b, n, self.h, self.d_k).transpose(1, 2) pattern is everywhere in transformer code. Once you recognize it, you can read pretty much any attention implementation. The view splits the last dimension into (heads, d_k), and the transpose moves the head dimension next to the batch dimension so the matmul broadcasts across both.
A worked example
Let me trace through a tiny case to make this concrete. We have , heads, so . Our input is four tokens: "The", "cat", "sat", "down", each represented as an 8-dimensional vector.
After projecting through and reshaping, each head gets its own 4-dimensional query for each token. Same for keys and values. Head 0 might end up with queries and keys that emphasize positional information, just because of how the weights happened to initialize and train. Head 1 might end up sensitive to semantic similarity.
Head 0 computes its own attention matrix, a grid of softmax weights. Maybe "sat" attends mostly to "cat" and "down" (nearby positions). Head 1 computes a different attention matrix. Maybe "sat" attends mostly to "cat" (the subject that did the sitting).
Each head produces a output, four tokens each with a 4-dimensional value. We concatenate along the last dimension: . Back to . Then we multiply by to get the final output.
The key insight is that those two heads computed genuinely different attention patterns. Token "sat" is looking at different things for different reasons, and the model gets both signals instead of having to compromise.
Here's what those two heads' attention matrices might look like. Head 0 concentrates on nearby tokens (positional), while Head 1 is doing something more semantic, linking subjects to their verbs.
Head 0 (positional): each token attends mostly to itself and its immediate neighbors.
Head 1 (semantic): 'sat' attends strongly to 'cat' (its subject), not just nearby positions.
What heads actually learn
This is where it gets interesting. Voita et al. (2019) did a careful study of what individual heads specialize in, and found a few clear patterns.
Positional heads learn to attend to tokens at a fixed relative offset. One head might always look at the previous token. Another might attend to the token two positions back. These are doing something like n-gram modeling inside the transformer.
Syntactic heads learn to attend along dependency parse edges. A head might consistently attend from a verb to its subject, or from a pronoun to its antecedent. These are the heads that would help with our "it" / "cat" example.
Rare-token heads light up disproportionately for infrequent words, possibly routing more computation toward tokens that are harder to predict.
Stylized examples of the patterns researchers actually find inside trained transformers. Click any head to see what it's doing. Notice how the diffuse head doesn't have an obvious job — those are the ones that often get pruned.
Michel et al. (2019) showed you can prune a huge number of heads at test time with minimal quality loss. In some models, removing 60% or more of heads barely moved the needle on downstream tasks. The specialized heads above are doing the heavy lifting. Many of the other heads seem to be partially redundant or learning soft variants of the same pattern.
This doesn't mean the extra heads are useless during training. Having many heads gives the optimizer more paths to explore, and some of that redundancy probably helps training stability. But at inference time, the model is over-provisioned. That observation directly motivates the KV-cache optimizations we'll get to in a moment.
Remember, the concatenated multi-head output gets projected through and then added back to the residual stream. Each layer's attention block is writing an update into the same shared vector. Click through the stages below to see how these additive updates accumulate.
The residual stream carries information forward. Each attention block (and each FFN block) reads from the stream, computes an update, and adds it back. Multi-head attention's output is one of those additive updates.
Sharing K and V: GQA and MQA
Multi-head attention as described above means every head has its own and projections. At inference time, when you're caching key-value pairs to avoid recomputation (the KV cache), that means storing separate K and V tensors for every head, at every layer, for every token in the context. For a model with 32 heads and a long context, this gets expensive fast.
Shazeer (2019) proposed Multi-Query Attention (MQA): keep separate Q projections for each head, but share a single K and V projection across all heads. Every head computes its own queries but looks up the same keys and values. This cuts the KV cache by a factor of and speeds up inference dramatically, at the cost of some quality.
Ainslie et al. (2023) proposed a middle ground: Grouped-Query Attention (GQA). Instead of one KV pair for all heads, or one KV pair per head, you divide the heads into groups. Each group shares one K and V projection. If you have heads and groups, each group of 4 heads shares one KV set. The KV cache shrinks by a factor of .
MHA (Multi-Head): Each query head has its own K and V projections. Full expressiveness, but the KV cache stores 8 separate key-value pairs per layer per token.
Toggle between MHA, GQA, and MQA to see how query heads map to KV heads. Color groups show which query heads share the same keys and values.
The reason this matters so much is tied to how autoregressive decoding works. During generation, you're producing one token at a time, and each new token needs to attend to all previous tokens. Without the KV cache, you'd recompute attention over the entire sequence at every step. With the cache, you store the K and V projections and only compute the new token's query. But the cache grows linearly with sequence length and with the number of KV heads. GQA directly attacks that second factor. More on this in Post 5.4.
Misconceptions
"More heads means more parameters." No. For a fixed , changing the number of heads doesn't change the parameter count. It changes the structure: each head is smaller, but there are more of them. Same total budget. What you actually buy with more heads is more parallel relation types, not more capacity.
"Each head dimension can be anything." It can't, at least not in standard multi-head attention. The whole reshape trick only works because , so concatenating heads of size gives you back something of size . If you wanted heads with arbitrary dimension you'd have to give up the clean reshape and pay for separate matmuls. Some research variants do that. The vast majority of production attention does not.
"Each head has a clear, interpretable role." Some do. Voita et al. found clear positional and syntactic heads, and induction heads are a known phenomenon in mechanistic interpretability work. But many heads don't have a clean interpretation, and a lot of them can be pruned without meaningful degradation. The picture is messier than the clean "one head per linguistic phenomenon" story.
"Multi-head attention is fundamentally different from single-head attention." It's the same computation, restructured. You could replicate multi-head attention with a single head that has a very specific block-diagonal structure in its weight matrices. Multi-head just makes that structure explicit and easy for the optimizer to find.
What's next
The attention mechanism as described so far lets every token attend to every other token. For language generation that's a problem, since a model predicting the next word shouldn't be allowed to peek at future words during training. The next post covers causal masking, how a single upper-triangular matrix enforces that constraint.
Additional reading (and watching)
- Vaswani, A., et al. (2017). Attention Is All You Need. NeurIPS 2017. Introduced multi-head attention as "h parallel attention functions."
- Voita, E., et al. (2019). Analyzing Multi-Head Self-Attention: Specialized Heads Do the Heavy Lifting, and the Rest Can Be Pruned. ACL 2019. Careful classification of head roles in NMT transformers.
- Shazeer, N. (2019). Fast Transformer Decoding: One Write-Head is All You Need. The MQA paper.
- Ainslie, J., et al. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. GQA, the interpolation between MHA and MQA used by most modern open-weights LLMs.
- Michel, P., Levy, O., & Neubig, G. (2019). Are Sixteen Heads Really Better than One?. NeurIPS 2019. Shows large fractions of heads can be pruned at test time with minimal loss.
- Elhage, N., et al. (2021). A Mathematical Framework for Transformer Circuits. Anthropic. The framework that identified induction heads and other cross-layer compositions of specific attention heads.
- Clark, K., et al. (2019). What Does BERT Look At? An Analysis of BERT's Attention. ACL 2019 Workshop. Probed individual attention heads in BERT and found heads attending to syntactic relations, coreference, and separator tokens.
- Liu, A., et al. (2024). DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model. Introduces Multi-head Latent Attention (MLA), compressing KV heads into a low-rank latent space as a next step beyond GQA.