You’ll learn why one attention head can capture only one relationship pattern at a time, and how multiple heads give the model several perspectives in parallel. You’ll also see how the model dimension is split across heads, what trained heads often specialize in, and how the output projection mixes their views back together.
The Problem: One Perspective Isn’t Enough
Self-attention lets every token look at every other token. But a single attention head computes one set of attention weights, a single perspective on the relationships.
Language is multi-faceted. Consider “The tired cat sat on the warm mat.” A reader simultaneously tracks grammar (“cat” is the subject of “sat”), meaning (“tired” describes “cat”), and local structure (“on” connects “sat” to “mat”). Can one attention head capture all of these at once?
Clear the 3 levels below to see why one head has to compromise:
Level 1 · Track the Subject & VerbIn Grammar mode, look along the “cat” row and click the cell where “cat” pays 85% of its attention.
Key (attends to) →
← Query
The
tired
cat
sat
on
the
warm
mat
The
tired
cat
sat
on
the
warm
mat
Think of it like a team of analysts reading the same document. One analyst tracks grammar, another tracks meaning, a third tracks nearby words. Each writes a summary, and a manager combines all summaries into a final report. Where this analogy breaks: unlike real analysts, all heads operate on the same token representations, and their outputs are mathematically concatenated and mixed, not summarized by a human.
✍️Fill in the Blanks
A single attention head can only capture type(s) of relationship pattern at a time.
Fill in every blank, then check your answer.
A blank is still empty.
Fill in every blank before checking your answer.
Not quite.
Try another answer, or reveal the solution.
Correct.
One accepted answer is shown in each blank.
Answer revealed.
The blanks now show the answers.
See Multiple Heads Working Together
What if we ran several attention heads in parallel, each free to specialize? Click on individual heads to see what each one captures:
Multi-Head Attention Visualizer
4 heads
Attention Heads (click to select)
All Heads (showing patterns)
H1
H2
H3
H4
Each head produces its own output vectors. These are concatenated and multiplied by an output projection matrix, producing one context-aware vector per token. That vector then feeds into a feed-forward network, and the whole block repeats. Click a head above to see its attention pattern.
What to look for: click each head and observe its attention matrix. Do you notice different patterns? One head might focus on nearby tokens, another on specific word relationships, another on broad context. That’s the whole point: each head independently discovers a different pattern.
How It Works: The Dimension Split
We don’t create separate attention mechanisms for each head. Instead, we split the model dimension across heads. If your model dimension is 512 and you have 8 heads, each head works in a 64-dimensional space (512 / 8 = 64). Step through the animation to see the full pipeline:
Step 0/4: Embedding
One Token's Embedding
0.2d1
-0.5d2
0.8d3
0.1d4
-0.3d5
0.7d6
-0.1d7
0.4d8
d_model = 8
One token's 8-dimensional embedding (real models use 512 or more).
What to look for: watch how the full embedding vector gets sliced into smaller chunks, one per head. Each head runs standard attention on its own slice, then the results are concatenated back to the original dimension. The total amount of data never changes, it’s just reorganized.
Why not just average the heads? Because the output projection WOlearns how to blend perspectives. Maybe head 3’s syntactic information should interact with head 7’s semantic information in a specific way. Averaging would lose those interactions.
MultiHead(Q,K,V)=Concat(head1,…,headh)WO
where each head is a standard attention operation:
headi=Attention(XWiQ,XWiK,XWiV)
Each head i has its own projection matrices WiQ, WiK, WiV that map from dmodel down to dk dimensions.
The per-head dimension is simply:
dk=dv=dmodel/h
Config
Model dim
Heads
Per-head dim
Total params
Original Transformer
512
8
64
~1M
GPT-2 Small
768
12
64
~2.4M
GPT-3
12288
96
128
~600M
The key insight: more heads doesn’t mean more parameters. It means the same capacity is divided into more specialized perspectives.
✍️Fill in the Blanks
With d_model = 512 and 8 attention heads, each head has d_k = dimensions.
Fill in every blank, then check your answer.
A blank is still empty.
Fill in every blank before checking your answer.
Not quite.
Try another answer, or reveal the solution.
Correct.
One accepted answer is shown in each blank.
Answer revealed.
The blanks now show the answers.
Parameter Count
Multi-head attention has four weight matrices (Q, K, V projections + output projection), each of shape (d_model × d_model). For d_model = 512, that’s 4 × 512² ≈ 1 million parameters.
The number of heads doesn’t change the total parameters. It only changes how they’re organized internally.
Weight Matrix
Shape
Parameters
WQ
(d_model, d_model)
dmodel2
WK
(d_model, d_model)
dmodel2
WV
(d_model, d_model)
dmodel2
WO
(d_model, d_model)
dmodel2
Total
4×dmodel2
For dmodel=512: 4×5122=4×262,144=1,048,576 parameters.
🤔Quick Check
If we increase the number of heads from 8 to 16 (keeping d_model = 512), what happens to the total parameter count?
Your turn
Choose an answer to check your understanding.
Try another option
That option does not fit. Try another answer, or reveal the solution.
Correct!
The total parameters are always 4 × d_model². Changing the number of heads only changes d_k (per-head dimension). With 16 heads, each head is d_k = 32 instead of 64, but the weight matrices remain the same size. More heads = more perspectives with smaller per-head capacity.
Answer: B
The total parameters are always 4 × d_model². Changing the number of heads only changes d_k (per-head dimension). With 16 heads, each head is d_k = 32 instead of 64, but the weight matrices remain the same size. More heads = more perspectives with smaller per-head capacity.
Attempts: 0
What Do Different Heads Learn?
Research on real transformer models reveals remarkable specialization:
Head Pattern
What It Learns
Example
Positional
Attend to adjacent/fixed-offset positions
Token at pos i always attends to pos i-1
Syntactic
Subject-verb agreement
”The dogs … are barking”
Semantic
Word meaning relationships
”bank” attends to “river” or “money”
Delimiter
Attend to punctuation/special tokens
Focus on periods, commas, [SEP]
Rare token
Attend to infrequent words
Proper nouns get extra attention
Some heads can even be pruned (removed) without hurting performance because they learn redundant or less useful patterns.
class MultiHeadAttention: def __init__(self, d_model, n_heads): assert d_model % n_heads == 0 self.n_heads = n_heads self.d_k = d_model // n_heads # One big projection per Q, K, V (split into heads after) self.W_Q = np.random.randn(d_model, d_model) * 0.01 self.W_K = np.random.randn(d_model, d_model) * 0.01 self.W_V = np.random.randn(d_model, d_model) * 0.01 self.W_O = np.random.randn(d_model, d_model) * 0.01 def split_heads(self, x): """(seq_len, d_model) → (n_heads, seq_len, d_k)""" seq_len = x.shape[0] x = x.reshape(seq_len, self.n_heads, self.d_k) return x.transpose(1, 0, 2) def combine_heads(self, x): """(n_heads, seq_len, d_k) → (seq_len, d_model)""" x = x.transpose(1, 0, 2) # (seq_len, n_heads, d_k) return x.reshape(x.shape[0], -1) def forward(self, X, mask=None): # 1. Project to Q, K, V (full d_model) Q = X @ self.W_Q K = X @ self.W_K V = X @ self.W_V # 2. Split into heads Q = self.split_heads(Q) # (n_heads, seq_len, d_k) K = self.split_heads(K) V = self.split_heads(V) # 3. Scaled dot-product attention PER HEAD scores = np.einsum('hid,hjd->hij', Q, K) / np.sqrt(self.d_k) if mask is not None: scores = scores + mask weights = softmax(scores, axis=-1) # 4. Weighted values per head attn_output = np.einsum('hij,hjd->hid', weights, V) # 5. Combine heads + output projection combined = self.combine_heads(attn_output) return combined @ self.W_O
KV Cache and Efficient Variants
During text generation, the model produces one token at a time. Without optimization, it would recompute K and V for every previous token at each step. The KV cache fixes this, and MQA/GQA shrink it. Step through the animation to see how:
The Problem: Redundant Work
Generating "The cat sat" one token at a time, without caching:
Step 1Generate"The"
K1, V1new
Step 2Generate"cat"
K1, V1wasted!
K2, V2new
Step 3Generate"sat"
K1, V1wasted!
K2, V2wasted!
K3, V3new
3 tokens generated, but 3 redundant K, V computations (red boxes). With 100 tokens, that is 4,950 wasted computations.
Without caching, each new token forces recomputation of K and V for every previous token. The wasted work grows quadratically.
Key Equations Reference
Multi-Head Attention:
MultiHead(Q,K,V)=Concat(head1,…,headh)WO
Per-Head Attention:
headi=Attention(XWiQ,XWiK,XWiV)
Per-Head Dimensions:
dk=dv=dmodel/h
Key References
Multi-head attention. Vaswani et al., “Attention Is All You Need” (2017) introduced the multi-head mechanism. Section 3.2.2 explains the motivation: “multi-head attention allows the model to jointly attend to information from different representation subspaces at different positions.”
Multi-Query Attention (MQA). Shazeer, “Fast Transformer Decoding: One Write-Head is All You Need” (2019) proposed sharing a single set of K/V projections across all heads to shrink the KV cache, trading a small quality loss for a large inference speedup.
I can explain why a single attention head is insufficient for capturing multiple relationship types simultaneously
I understand how the model dimension is split across heads without increasing total parameters
I can describe what the output projection WO does and why it’s needed after concatenation
I can give at least three examples of patterns that different trained heads learn (positional, syntactic, semantic, etc.)
Next: Positional Encoding
Self-attention treats all positions equally. It’s permutation-invariant: shuffle the tokens and the attention scores don’t change. We need to add position information. That’s Positional Encoding →