Multi-Head Attention and Representation Subspaces
Four constructed patterns for the same query, “sat.” Each head projects the full input. Concatenation and Wₒ combine their outputs.
Project each token into h query, key, and value spaces, then compute a separate attention pattern in each head.
A single attention head uses one set of , , matrices to produce one attention pattern per position. What if a token needs different patterns for different relationships?
For an unmasked, bidirectional example, 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, combining these relationships into one weighted average of value vectors. Multiple heads give the model separate distributions to learn, without assigning each head a linguistic role in advance.
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. The examples also use matrix shapes and parameter counting.
Splitting the representation
Each head projects the full input vector into its own query, key, and value spaces. Attention then runs independently in those projected spaces. The split is in the projection outputs, not a partition that prevents a head from reading some input coordinates.
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 :
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
More heads need not add projection parameters. In standard multi-head attention, keeping fixes the total width while changing how many independent attention patterns the block computes.
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 .
Every track uses the same scale: full width = 65,536 parameters. Biases are excluded; Q, K, and V each have h heads.
W_Q: 4 × 4,096 = 16,384 parameters
W_K: 4 × 4,096 = 16,384 parameters
W_V: 4 × 4,096 = 16,384 parameters
W_O: 16,384 = 16,384 parameters
Total = 4d_model² = 65,536, independent of h
Within the same parameter budget, multi-head attention trades representational capacity per head (each head produces projected features from the full input) for multiple independent attention patterns.
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. A fused projection and batched matmul usually use the GPU more efficiently than a Python loop over heads.
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.
The big matrix is 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. 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
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 heatmaps use hand-constructed weights to illustrate these two patterns.
Toy head 0 (positional): each token attends mostly to itself and its immediate neighbors.
Toy head 1 (semantic): 'sat' attends strongly to 'cat' (its subject), not just nearby positions.
What heads actually learn
Voita et al. (2019) studied what individual heads specialize in and found a few recurring 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 putting more attention weight toward tokens that are harder to predict.
Constructed examples of patterns studied in trained transformers. Tap or click a head to inspect its weights. A diffuse attention map alone does not establish that a head can be pruned.
Michel et al. (2019) pruned heads from translation and fine-tuned BERT models and found substantial redundancy in some layers and tasks. Cross-attention in their translation model was more sensitive. A recognizable attention pattern alone does not tell us whether a head is necessary; that requires an intervention and evaluation.
Pruning heads tests whether an already-trained model needs them for a particular task. Sharing K and V projections is a separate architectural choice that reduces cache storage during generation.
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.
Choose any stage to read its vector below the diagram. The updates are specified example values; only their additions are shown.
Select successive stages to inspect the attention and FFN updates to the same token vector.
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. At fixed head width and cache precision, this cuts the KV cache by a factor of . The paper reports faster decoding with a small quality tradeoff in its experiments; the latency benefit depends on hardware and batch shape.
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 .
KV cache: 8 × (K + V) per layer per token, at fixed head width and dtype.
MHA (Multi-Head): Each query head has its own K and V projections. 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.
At fixed head width and precision, the cache grows with both sequence length and KV-head count. GQA reduces the latter; Post 5.4 derives the resulting memory budget.
Misconceptions
"More heads means more parameters." With standard multi-head attention and , the Q/K/V/O projection budget stays at (ignoring biases). If head width stays fixed while head count grows, the parameter count grows too.
"Head width must equal ." That's a common choice, not a mathematical requirement. A projection can produce features, reshape them into heads, and map the concatenated outputs back through . The fixed parameter count above assumes .
"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.
"One wide head can reproduce several heads just by changing its weight matrices." In general it cannot. Each head computes its own softmax distribution over positions. One head has only one such distribution per query; block-diagonal projections do not supply the missing independent softmax operations.
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 in models including Llama 2 70B.
- 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.