Part III closed with an evaluation chapter and Part IV opened with a representation one, so this chapter picks the architecture thread back up where Chapter 13 put it down. That chapter ended with two complaints gating could not touch.
The first is sequential computation, since a recurrence needs before it can produce and the time steps therefore cannot run in parallel. That is a hard ceiling, and it arrived precisely when hardware became massively parallel. The second is the bottleneck, because everything the model knows about the prefix has to fit in one fixed size vector however long the prefix is.
Both complaints have one root, which is that information is forced to travel through the sequence one step at a time. So remove the travelling, and let every position look directly at every other position in one operation with nothing in between. Every position is now one step from every other, so the distance a signal travels stops growing with the sentence, and the computation parallelises because nothing waits for anything. Position itself still matters, and Equation (16.4) puts it back deliberately. That is attention.
Attention did not arrive with the transformer but as a patch on a recurrent translation system (Bahdanau et al. 2014). Encoder-decoder translation compressed the whole source sentence into one vector and then decoded from it, so long sentences degraded badly for the obvious reason.
Bahdanau’s fix let the decoder look back at all the encoder states and learn a weighting over them at each output step, which opened the bottleneck. The transformer (Vaswani et al. 2017) took the next step, and its title says it: attention could replace recurrence entirely.
The mechanism borrows its vocabulary from databases, and the analogy is exact enough to be useful. A query is what a position is looking for, a key advertises what a position offers, and a value is what it actually contributes.
In a dictionary lookup you match one query against one key and take that value, whereas attention is the soft version: match the query against every key, turn the match scores into weights, then take the weighted average of all the values. Each of the three is a learned linear projection of the input:
where holds the input vectors, one row per position.
The whole operation is then one line:
Read it right to left. scores every query against every key, dividing by keeps those scores in a workable range, the softmax turns each row into a distribution, and multiplying by takes the weighted average.
The word self in self-attention means , and all come from the same sequence, so every position queries the sentence it is part of, itself included.
Take the lecture’s example, with a query for who and three keys in three dimensions, where the values equal the keys to keep the arithmetic short.
Here , so the scale is .
| key | ||||
|---|---|---|---|---|
| is | 0.3200 | 0.1848 | 1.2029 | 0.3369 |
| the | 0.1300 | 0.0751 | 1.0779 | 0.3019 |
| best | 0.4400 | 0.2540 | 1.2892 | 0.3611 |
| sum | 3.5701 | 1.0000 |
The output is the weighted sum of the values:
Now read the column, which runs , , and is nearly uniform, with an entropy of bits against a maximum of . This query attended to everything about equally and the output is close to a plain average, so the mechanism ran correctly and selected nothing, because these three keys all point in much the same direction.
That is worth seeing once. Attention is not magic, and it selects only when the keys give it something to select on.
Same machinery, now with keys that disagree, over three dimensions standing for animal, vehicle and verb.
| query | entropy | |||
|---|---|---|---|---|
| 0.8520 | 0.0634 | 0.0846 | 0.7507 | |
| 0.0829 | 0.0829 | 0.8343 | 0.8135 | |
| 0.3333 | 0.3333 | 0.3333 | 1.5850 |
The first two queries commit while the third cannot, and its entropy sits exactly at the bit maximum for three keys.
Entropy is the useful reading here, because low entropy means the head has made a choice while high entropy means it is averaging. Both are legitimate behaviours, and telling them apart is how attention maps get interpreted.
The scale looks arbitrary, and it is not, because the reason is measurable.
Let the entries of and have unit variance. Their dot product is a sum of terms, so its standard deviation grows like .
Feed larger and larger numbers to a softmax and it saturates. Here is what that does, averaged over 200 random draws with eight keys.
| sd of | entropy, raw | entropy, scaled | largest , raw | |
|---|---|---|---|---|
| 4 | 1.756 | 1.7771 | 2.4764 | 0.5492 |
| 16 | 3.568 | 0.9743 | 2.4970 | 0.7488 |
| 64 | 7.202 | 0.5058 | 2.4844 | 0.8577 |
| 256 | 14.483 | 0.2512 | 2.4555 | 0.9282 |
| 1024 | 28.591 | 0.1333 | 2.4894 | 0.9611 |
The maximum possible entropy over eight keys is bits.
Read the second column with one caveat, because it does not quite equal . It is the spread measured across the eight dot products of a single draw, and a population standard deviation taken from eight samples runs low by a predictable factor of about , which is why reads rather than . Pool the scores across all 200 draws instead and the column comes out at , , , and , which is to within sampling noise. What matters either way is the growth: each fourfold rise in doubles the spread, exactly as a square root should.
Now read the raw column. By the distribution has collapsed onto a single key, which holds per cent of the weight, and this is before any training has happened. A saturated softmax has almost no gradient, so the head cannot learn its way out of a position it was put in by initialisation alone.
The scaled column barely moves across the whole range, because dividing by cancels exactly the growth the second column shows. That is why the constant is a square root rather than something tuned.
A language model must not read the future, so when predicting the token at position only positions may contribute. Enforce that on the scores, before the softmax:
Since , the blocked positions receive exactly zero weight and the surviving ones renormalise to sum to one. Take four raw scores and apply it.
| the | dog | barked | loudly | sum | |
|---|---|---|---|---|---|
| the | 1.0000 | . | . | . | 1.0000 |
| dog | 0.1824 | 0.8176 | . | . | 1.0000 |
| barked | 0.2312 | 0.1402 | 0.6285 | . | 1.0000 |
| loudly | 0.2760 | 0.1674 | 0.1015 | 0.4551 | 1.0000 |
Every row sums to one, and that is the entire reason the mask goes before the softmax. Do it the other way, with the softmax first and the blocked cells zeroed afterwards, and the rows no longer sum to one.
| the | dog | barked | loudly | sum | |
|---|---|---|---|---|---|
| the | 0.4551 | . | . | . | 0.4551 |
| dog | 0.1230 | 0.5512 | . | . | 0.6742 |
| barked | 0.1878 | 0.1139 | 0.5105 | . | 0.8122 |
| loudly | 0.2760 | 0.1674 | 0.1015 | 0.4551 | 1.0000 |
The first row now sums to , because the weight that belonged to the blocked positions has been discarded rather than redistributed. The damage is worst for the earliest tokens, which have the most future to hide from, so masking first lets the visible positions inherit that weight while masking second quietly scales the whole row down.
One attention operation computes one kind of relationship, and language has many at once, so run several in parallel. Multi-head attention splits the model dimension into heads of size , each head runs Equation (16.2) independently, and the outputs are concatenated and projected once more with .
Nobody assigns the heads their jobs. They start from different random initialisations and specialisation emerges, because heads that duplicate each other contribute nothing extra to the loss.
Trained heads are often found tracking recognisable things, with some following syntactic dependencies, some tracking the previous token and some resolving coreference, while many appear to do nothing identifiable at all.
Attention alone is not a layer, and three more pieces complete it. A feed-forward network is applied at each position independently, expanding to a larger hidden size, applying a non-linearity and projecting back, and this is where most of the layer’s parameters live. A residual connection adds each sublayer’s input to its output, which gives the gradient a path with no multiplication on it, the same repair the LSTM cell state made in Chapter 13. And layer normalisation rescales each position’s vector to zero mean and unit variance, which keeps the scale stable through a deep stack.
Equation (16.2) has a property that is easy to miss. It is permutation equivariant, which is the precise term and worth using, because permuting the input rows permutes the output rows in exactly the same way and leaves the set of outputs unchanged. The word sometimes heard instead, invariant, would mean the outputs did not move at all, and they do move: they travel with their tokens.
What the mechanism has no access to is order. Shuffle the input positions and every attention weight follows its token untouched, so nothing in the operation records which token came first. That is fatal for language, so position must be added to the input explicitly, and the original transformer adds sinusoids of different frequencies:
The frequencies span many scales, so nearby positions get similar encodings while distant ones do not. Later models learn the positional vectors instead, or inject relative position directly into the attention scores.
Take the original base configuration, with , , layers, heads and a feed-forward hidden size of .
| component | formula | parameters |
|---|---|---|
| attention, | 1,048,576 | |
| feed-forward | 2,099,712 | |
| layer norms | 2,048 | |
| one layer | 3,150,336 | |
| all six layers | layer | 18,902,016 |
| embeddings | 25,600,000 | |
| total | 44,502,016 |
Two things in that table are worth naming. The heads are free, because splitting into eight heads of costs nothing when , which makes multi-head attention a reshape rather than an extra budget. And the feed-forward block is the larger one at million against million, so attention gets the name and the diagrams while most of the parameters sit next door.
No entry in that table mentions sequence length, though the computation does, because every position attends to every position.
| tokens | scores per head | relative to |
|---|---|---|
| 128 | 16,384 | 0.06 |
| 512 | 262,144 | 1 |
| 2,048 | 4,194,304 | 16 |
| 8,192 | 67,108,864 | 256 |
| 32,768 | 1,073,741,824 | 4096 |
Quadruple the context and the attention cost rises sixteen-fold. That is the price paid for removing the recurrence, and Chapter 24 is largely about paying it down.
Try it yourself.
code/worked_examples/attention.pyproduces every table in this chapter.--oneruns the lecture example,--selectshows a query that commits and one that cannot,--scalemeasures the softmax collapsing without the square root,--maskcontrasts the two orderings, and--paramscounts a layer.
Three properties explain the takeover, and only one of them is about quality. Parallelism means every position is computed at once, where a recurrent network of the same size takes as many sequential steps as there are tokens and no amount of hardware fixes that. Constant path length means any two positions are one attention step apart, where in an RNN they are as far apart as the text between them, which is where the gradient died in Chapter 12. And scale follows from the parallelism, since a model that trains in parallel can be made larger, and larger turned out to keep helping for far longer than anyone expected.
Chapter 18 takes this block and builds the two families that followed, one reading in both directions and one reading forwards only.
Vaswani et al. (2017) is the transformer paper, and it is short.
Bahdanau et al. (2014) introduced attention as a repair to recurrent
translation, which is the clearest way to see what problem it solves.
Alammar (2018) is the diagram-led walkthrough most practitioners learn
from. Jurafsky and Martin (2025) cover the same material with more
attention to the encoder decoder distinction. In a framework the whole
operation is a single call,
torch.nn.functional.scaled_dot_product_attention, and
torch.nn.MultiheadAttention wraps that call together with
the projections and the head split, which is worth reading once you can
do the arithmetic without it.
Attend by hand. Reproduce the lecture example: compute the three dot products, scale by , exponentiate, normalise, and confirm . Then compute the entropy of the weights and say what it tells you about this particular query.
Make it select. Using the animal, vehicle and verb keys of this chapter, find a query whose largest attention weight exceeds . What did you have to change, the direction of the query or its magnitude? What does that say about the role of the projection matrices and ?
Kill the scaling. Remove the from Equation (16.2) and measure the entropy of the attention weights for over random inputs. At which does the largest weight exceed ? Explain in two sentences why a saturated softmax cannot train.
Mask in the wrong order. Build a four by four score matrix, apply the causal mask before the softmax and after it, and report both row sums. Which row is worst affected, and why is it that one?
Count a layer. For , and , compute the attention, feed-forward and layer-norm parameters. Which is largest? Now double to and recompute. What changed, and why is the answer surprising?
Permutation equivariance. Show that Equation (16.2) gives the same set of outputs when the input rows are permuted, and that each output travels with its own token rather than staying put. Then explain precisely what Equation (16.4) adds that breaks the symmetry, and say why equivariant is the right word for the property and invariant is not.
The quadratic wall. A model has a context of tokens. Compute the number of attention scores per head, and the factor by which it grows at tokens. Then name one strategy from Chapter 24 that avoids computing all of them.
The same numbers, from a library. Pass this
chapter’s query and its three keys to
torch.nn.functional.scaled_dot_product_attention and
confirm that it hands back
,
the vector you computed by hand in Problem 1. Then set
is_causal=True on a three token input and say which rows of
the output change and which cannot. Finally read the signature and name
one argument whose default would quietly alter your answer without
raising any error.
Each one reproduces the tables above. Open it in Colab, change an input and watch which numbers move.
attention.pySelf-attention, one query at a time
The lecture's three-key example with every number, the scaling, and the causal mask.
pip install numpy Run it in Colab Notebook SourceFrom Natural Language Processing: From Corpus Statistics to Grounded LLMs, by Ramaseshan Ramachandran.