Multi-Head Attention
Split the model dimension into several smaller subspaces and run independent attention in each one, so different heads can specialize in different kinds of relationship.
Why Does This Exist?
A single self-attention computation produces exactly one weighted average per token, using one set of , , projections. That's a real limitation: language relationships aren't one-dimensional. The same sentence needs one kind of attention to track which pronoun refers to which noun, a different kind to track subject-verb agreement, and another to track which adjective modifies which noun. One softmax distribution per token can express only one weighting scheme at a time — it can't simultaneously be sharply focused on a distant antecedent and broadly averaged over nearby modifiers, because it's one distribution, not several.
The fix researchers landed on wasn't to make one attention computation smarter. It was to run several smaller, independent attention computations side by side, each free to learn a different pattern, and combine their outputs afterward.
Think of It Like This
Several specialists reading the same document
Hand one contract to a single generalist reviewer and they have to track indemnity clauses, payment terms, and termination conditions all in one pass, splitting attention across concerns that don't share much structure. Hand the same contract to three specialists instead — one who only tracks indemnity language, one who only tracks payment terms, one who only tracks termination — and each one can focus narrowly and get sharper results, because they're not fighting each other for the same attention budget.
Multi-head attention is that specialist panel, except the "specialties" aren't assigned by a human — they emerge from training, in whatever subspaces turn out to be useful for the objective.
How It Actually Works
Split, attend, concatenate, project
Take the diagram above literally. The model dimension splits into heads, each of dimension . Each head gets its own , , — independently learned projections into that head's smaller subspace — and runs the full self-attention computation entirely on its own:
Every head produces its own output, computed independently and in parallel — head 1's attention weights have no influence on head 2's. Once all heads finish, their outputs concatenate back into a single matrix, restoring the original width:
The final matrix is a learned linear mixing layer applied after concatenation — it's not optional. Without it, each output dimension would come from exactly one head with no way for heads to interact in the final representation; lets the model combine information across heads before passing it on.
Same total compute, different allocation
Here's the detail worth sitting with: splitting into heads of size doesn't add extra parameters or extra compute compared to one attention computation at the full dimension — the total work is roughly the same, just divided among smaller, parallel computations instead of one large one. What multi-head buys isn't more capacity in the aggregate; it's more independent degrees of freedom in how that capacity gets used, since each head learns its own projections and can specialize.
What heads actually learn
Empirically, when researchers inspect trained multi-head attention, individual heads often turn out to specialize — one head consistently attending to the previous token, another consistently attending to a syntactic dependency, another spreading attention broadly across the whole sequence. None of that specialization is designed in; it falls out of training the same way self-attention's linguistic structure does. Not every head specializes cleanly, and some heads turn out to be redundant enough that they can be pruned after training with little quality loss — a genuine, measured finding, not a hypothetical.
Show Me the Code
Splitting a model dimension into heads, running self-attention in each, and concatenating back — the entire mechanism in one function.
import numpy as np
def softmax(z: np.ndarray) -> np.ndarray: e = np.exp(z - z.max(axis=-1, keepdims=True)) return e / e.sum(axis=-1, keepdims=True)
def multi_head_attention(x: np.ndarray, w_q: np.ndarray, w_k: np.ndarray, w_v: np.ndarray, w_o: np.ndarray, n_heads: int) -> np.ndarray: n, d = x.shape d_k = d // n_heads q, k, v = x @ w_q, x @ w_k, x @ w_v # (n, d) each heads = [] for i in range(n_heads): sl = slice(i * d_k, (i + 1) * d_k) # this head's slice of the dim scores = q[:, sl] @ k[:, sl].T / np.sqrt(d_k) heads.append(softmax(scores) @ v[:, sl]) # (n, d_k), independent of other heads return np.concatenate(heads, axis=-1) @ w_o # concat back to (n, d), then mix
rng = np.random.default_rng(0)x = rng.normal(size=(4, 8))w_q, w_k, w_v, w_o = (rng.normal(scale=0.3, size=(8, 8)) for _ in range(4))out = multi_head_attention(x, w_q, w_k, w_v, w_o, n_heads=2)print(out.shape) # -> (4, 8) — same shape as the input, regardless of head countNotice each head only ever sees its own slice of q, k, v — head 0 never reads head 1's portion of the dimension, which is exactly the "independent, in parallel" claim above made concrete.
Watch Out For
Choosing head count without checking d_k
More heads means a smaller per head, and a head dimension that's too small genuinely limits what that head can represent — there's a real trade-off, not a free lunch. Doubling head count from 8 to 16 while holding fixed halves each head's dimension, and past a point that starts hurting quality rather than helping it, because each head simply has less room to work with. Head count and head dimension are a joint design choice, not two independent knobs.
Skipping the output projection when implementing by hand
It's tempting, when hand-rolling multi-head attention, to just concatenate the heads and call it done — the shapes work out, and nothing errors. But without , the model loses its only way to mix information across heads in the combined output; each output dimension stays permanently tied to whichever single head produced it. The model can still train, but it's measurably weaker, and the failure is silent — no error, just a ceiling on quality that's easy to misattribute to something else.
The Quick Version
- One self-attention computation produces one weighting scheme per token; multi-head attention runs several in parallel, each in its own subspace.
- Each head gets its own , , projections into a dimension , computed independently of the other heads.
- Head outputs concatenate back to the original width, then pass through a learned output projection that mixes across heads.
- Total compute is roughly the same as one full-dimension attention computation — multi-head redistributes capacity, it doesn't add much of it.
- Individual heads often specialize during training, though not all of them, and some turn out to be prunable.
What to Read Next
- Self-Attention is the single computation multi-head attention runs several copies of, in parallel.
- Cross-Attention can also be split across multiple heads, using the same mechanism this page describes.
- Transformer Architecture is where multi-head attention sits as one of the two sub-layers in every block.
- Attention Complexity covers the cost of running attention, whether single-head or multi-head.