Linear algebra for LLMsPart 5: Attention, one operation at a time

Multi-head attention

7 min

Many heads, in parallel

headi=Attention⁡(XWQ(i), XWK(i), XWV(i))\displaystyle \text{head}_i = \operatorname{Attention}(XW_Q^{(i)},\, XW_K^{(i)},\, XW_V^{(i)})

Instead of one wide head, use hh narrow ones, each with its own projections of width dh=d/hd_h = d/h. Each head computes its own T×TT \times T attention pattern: one might follow the previous token, another the subject of the sentence.

Write

hh heads, each width dh=d/hd_h = d/h, each with its own attention weights.

One big projection, cut into heads

XWQ=X [ WQ(1)∣⋯∣WQ(h) ]=[ Q(1)∣⋯∣Q(h) ]\displaystyle XW_Q = X\,[\,W_Q^{(1)} \mid \dots \mid W_Q^{(h)}\,] = [\,Q^{(1)} \mid \dots \mid Q^{(h)}\,]
→ →

Code keeps one d×dd \times d weight per projection. Its column blocks are the heads (Part 2), so a single matmul computes all heads’ queries, and view + transpose cut the result into heads (Part 4).

Concatenate, then mix with WOW_O

MHA⁡(X)=[ head1∣⋯∣headh ]  WO\displaystyle \operatorname{MHA}(X) = [\,\text{head}_1 \mid \dots \mid \text{head}_h\,]\; W_O
(T×hdh)(d×d)=T×d\displaystyle (T \times h d_h)(d \times d) = T \times d

The heads’ outputs are placed side by side, back to width dd, and one more d×dd \times d matrix WOW_O mixes information across heads. In block form that’s ∑iheadi WO(i)\sum_i \text{head}_i\, W_O^{(i)}: every head writes its own contribution into the residual stream.

Write

MHA⁡(X)=concat⁡(heads) WO=∑iheadi WO(i)\operatorname{MHA}(X) = \operatorname{concat}(\text{heads})\,W_O = \sum_i \text{head}_i\,W_O^{(i)}

Parameters: 4d24d^2, whatever hh is

WQ,WK,WV,WO ⁣:  4×d×d=4d2\displaystyle W_Q, W_K, W_V, W_O\!: \; 4 \times d \times d = 4d^2
d=4096 ⁣:  4×40962=67,108,864\displaystyle d = 4096\!: \; 4 \times 4096^2 = 67{,}108{,}864

Splitting into heads doesn’t add parameters: hh heads of width d/hd/h use the same d×dd \times d matrices as one head of width dd. That’s about 67 million parameters per layer for standard multi-head attention at d=4096d = 4096, without biases.

Write

Multi-head attention: 4d24d^2 parameters (no biases), independent of hh.

Each head is low-rank

rank⁡(WQ(i)WK(i)⊤)≤dh\displaystyle \operatorname{rank}\big(W_Q^{(i)} W_K^{(i)\top}\big) \le d_h

From Part 3: each head compares tokens through a d×dd \times d bilinear form of rank at most dhd_h. Many narrow heads see many different low-dimensional “views” of the tokens, instead of one wide view.

✏️ Your turn

Size a GPT-2 small layer

d=768,h=12\displaystyle d = 768, \quad h = 12

Write dhd_h and the number of attention weight parameters per layer (ignore biases).

Work it out on paper, then go to the next card to check.

Answer

dh=768/12=64\displaystyle d_h = 768 / 12 = 64
4×7682=2,359,296\displaystyle 4 \times 768^2 = 2{,}359{,}296

About 2.4 million per layer, 28 million across GPT-2 small’s 12 layers.

In PyTorch

q = (x @ W_q).view(B, T, h, d_h).transpose(1, 2)   # (B, h, T, d_h)
k = (x @ W_k).view(B, T, h, d_h).transpose(1, 2)
v = (x @ W_v).view(B, T, h, d_h).transpose(1, 2)
o = F.scaled_dot_product_attention(q, k, v, is_causal=True)
o = o.transpose(1, 2).reshape(B, T, d) @ W_o       # concat heads, mix

📌 Recap

Multi-head attention in one card

  • hh heads of width dh=d/hd_h = d/h, each with its own attention pattern.
  • One big projection, cut into heads by view + transpose.
  • Concatenate and mix: concat⁡(heads) WO\operatorname{concat}(\text{heads})\,W_O.
  • 4d24d^2 parameters regardless of hh; each head has rank ≤dh\le d_h.

Write

The four lines above.