The missing square root in attention

The missing square root in attention

28 min read

Self-attention is fully assembled except for one symbol. The three projections Q\mathbf{Q}, K\mathbf{K} and V\mathbf{V} have their argument, the weight matrix A\mathbf{A} is square (T×TT \times T) because the sentence looks at itself, and the softmax by rows shares one unit out among the positions of each row. But the dk\sqrt{d_k} in the denominator was written into the formula without any explanation of why it is there. It is the only loan still outstanding, and this lesson pays it off.

The question is sharper than it looks. Why a root, and not dkd_k itself? And why of dkd_k, and not of dmodeld_{\text{model}} or of the length of the sentence? Both are answered at once, because both depend on finding out how far a sum moves away from zero as terms are added to it. That takes no elaborate probability theory, only averaging, and the calculation starts by averaging over a finite list that can be walked through in full.

Take the crudest possible version of a query and a key, that is, of a row of Q\mathbf{Q} and a row of K\mathbf{K}: all their coordinates are +1+1 or −1-1. The score is then a count of agreements, because each term contributes +1+1 if the two coordinates share a sign and −1-1 if they do not. With four coordinates, three agreements and one disagreement give 3−1=23 - 1 = 2. With four hundred, the same proportion (three hundred against one hundred) gives 200200.

What is worth noticing is not that the terms have grown, since they are still ±1\pm 1, but that there are a hundred times as many of them. A small deviation from an even split, repeated over more terms, turns into a large number, and the softmax is about to exponentiate that number.

With random signs the proportion does not stay fixed at three to one, and the score grows more slowly: its typical size goes with dk\sqrt{d_k}, as the next section derives. The figure takes one row of six scores at that size in three widths and tries three divisors before the softmax: 11 (that is, not dividing), dk\sqrt{d_k} and dkd_k itself. Each group of bars is the row's share-out, one weight per position, and the number above it is what the winning position takes. Look for the row that does not change from one column to the next.

Three columns, one per width: d_k equal to 4, 16 and 64. At the top, the same row of six undivided scores in each column, with bars that grow with the width. Below, three rows of softmax share-outs, each with six bars, by the divisor applied first. Undivided, the largest weight rises from 0.68 to 0.92 and to 0.99 until it fills the whole bar. Dividing by the root of d_k, the highlighted row, the share-out is the same in all three columns, with the largest weight at 0.43. Dividing by d_k, the largest weight falls from 0.29 to 0.23 and to 0.20, and the six bars even out.
Undivided, the wider the model, the more the softmax closes on a single position. Dividing by d_k does the opposite, flattening the share-out towards 1/6 at every position. Only √d_k gives the same share-out at all three widths: the divisor we are after is the one that stops the share-out depending on d_k.

A score grows with the root of the width

Each cell of QK⊤\mathbf{Q}\mathbf{K}^{\top} is the dot product of a row of Q\mathbf{Q} (the query of one position) with a row of K\mathbf{K} (the key of another). Both live in Rdk\mathbb{R}^{d_k}, and I write them q\mathbf{q} and k\mathbf{k} without a position index, because the calculation is the same for any pair. The score is

e=q⊤k=∑m=1dkqmkm,e = \mathbf{q}^{\top}\mathbf{k} = \sum_{m=1}^{d_k} q_m k_m,

a sum of dkd_k terms. The size that comes out depends on the size that goes in, so let's fix the exact case first: the coordinates are +1+1 or −1-1, as in the count of agreements above. Call sm=qmkms_m = q_m k_m the mm-th term, which is then +1+1 or −1-1, and e=∑msme = \sum_m s_m.

Squaring separates the diagonal from the rest:

e2=(∑msm)2=∑msm2+∑m≠nsmsn=dk+∑m≠nsmsn,e^{2} = \left(\sum_{m} s_m\right)^{2} = \sum_{m} s_m^{2} + \sum_{m \neq n} s_m s_n = d_k + \sum_{m \neq n} s_m s_n,

because sm2=1s_m^{2} = 1 whatever the sign. The first term no longer depends on anything. The second is the one that has to be averaged, and averaging here means something perfectly finite: running through the 2dk2^{d_k} sign combinations q\mathbf{q} can take, with k\mathbf{k} fixed, and taking the mean. We write it E[⋅]\mathbb{E}\left[\cdot\right], the mean of a quantity over the randomness it is assumed to have. The cross terms all disappear.

See why each cross term averages to zero

Fix two different indices m≠nm \neq n and look at the term smsn=qmkmqnkns_m s_n = q_m k_m q_n k_n. Pair each sign combination with the one you get by flipping the sign of qmq_m and leaving the other dk−1d_k - 1 coordinates where they are. That pairs up the 2dk2^{d_k} combinations perfectly into 2dk−12^{d_k - 1} pairs: each combination has exactly one partner, and it is never itself.

Within a pair, qnq_n, kmk_m and knk_n have not moved and qmq_m has changed sign, so the two values of smsns_m s_n are opposites and sum to zero. Adding zero 2dk−12^{d_k - 1} times and dividing by 2dk2^{d_k} leaves

E[smsn]=0,m≠n.\mathbb{E}\left[s_m s_n\right] = 0, \qquad m \neq n.

The same pairing gives E[e]=0\mathbb{E}\left[e\right] = 0: in e=∑msme = \sum_m s_m each term changes sign along with its own coordinate, so each one averages zero on its own.

So of the square, only the first term survives:

E[e2]=dk.\mathbb{E}\left[e^{2}\right] = d_k.

With E[e]=0\mathbb{E}\left[e\right] = 0, that is all it takes. The score is spread around zero, and what measures how far it typically lands from it (the standard deviation) is the root of that mean:

E[e2]=dk,\sqrt{\mathbb{E}\left[e^{2}\right]} = \sqrt{d_k},

which answers half the question. Dividing by dk\sqrt{d_k} leaves E[(e/dk)2]=1\mathbb{E}\left[\left(e/\sqrt{d_k}\right)^{2}\right] = 1, and dkd_k does not appear in that 11: the same scale for queries of eight coordinates and for queries of a thousand.

With the divisor in place, the whole operation (product, division, softmax and mixture) gets a name of its own: the scaled dot-product, which is what the paper calls it. The dot product is the ordinary one, the one in the line above; what is scaled is this division and nothing more.

The signs were there to make the calculation exact, but notice which properties it really needed. Two: that E[sm]=0\mathbb{E}\left[s_m\right] = 0 and that E[sm2]=1\mathbb{E}\left[s_m^{2}\right] = 1. Any centred initialisation whose coordinates are independent and of typical size 11 meets them, and with them the same cancellation leaves the same E[e2]=dk\mathbb{E}\left[e^{2}\right] = d_k. The Gaussians from rng.normal that the course has used since the lesson on implementing an MLP are one such initialisation, and the first cell below measures it with them.

It is worth saying out loud what that assumption is about, because it is not about the model you will end up using. It is about a freshly initialised network: nothing guarantees that after thousands of steps the coordinates of q\mathbf{q} are still independent or still of size 11. That is why the divisor is fixed when the layer is designed rather than measured on the fly: it fixes the start, which is where the problem halts training before it has even begun.

That leaves the part usually taken for granted: why the root and not dkd_k. The divisor has to take away from the score exactly what the width put into it, and what the width put in is a factor of dk\sqrt{d_k}. Dividing by dkd_k would leave the scores at a typical size of 1/dk1/\sqrt{d_k}, closer and closer to zero the wider the model; and scores that tend to zero give a softmax that tends to the even share, 1/T1/T in every cell, so the row stops choosing anything. Those are two failures, one on each side, and the root is the only exponent that falls into neither.

A closed row returns no gradient

If the scores measure dk\sqrt{d_k}, the differences between the scores in a row measure that too, and what the softmax looks at is the differences:

αij=exp⁡(eij)∑m=1Texp⁡(eim),\alpha_{ij} = \frac{\exp\left(e_{ij}\right)}{\sum_{m=1}^{T}\exp\left(e_{im}\right)},

with mm running over the positions of row ii. Two scores 88 apart (an ordinary gap when the typical size is 88, which is what dk=64d_k = 64 gives) end up a factor exp⁡(8)≈2 981\exp(8) \approx 2\,981 from each other, and with that the share-out is over: one position takes almost the whole unit.

A row that chooses firmly does not sound like a defect. The defect is in the derivative.

Differentiate αij\alpha_{ij} with respect to the scores of its own row. It is the same calculation that the lesson on loss functions did with the softmax of the output layer, and the same two cases come out, depending on whether the indices match:

∂αij∂eij=αij(1−αij),∂αij∂eim=−αij αim(m≠j).\frac{\partial \alpha_{ij}}{\partial e_{ij}} = \alpha_{ij}\left(1 - \alpha_{ij}\right), \qquad \frac{\partial \alpha_{ij}}{\partial e_{im}} = -\alpha_{ij}\,\alpha_{im} \quad (m \neq j).

Now take the closed row, with one weight at almost 11 and the rest at almost 00. The diagonal case gives 11 times 00; the cross case, in any cell, carries at least one factor that is almost zero. Both derivatives switch off at once, and the whole row with them.

What that costs becomes clear when you look at what comes before the scores: only WQ\mathbf{W}^Q and WK\mathbf{W}^K, since the scores come out of QK⊤\mathbf{Q}\mathbf{K}^{\top} and nothing else goes in. If the gradient does not get through the softmax, those two matrices receive nothing from that row, and they are precisely the ones that would have to learn which positions each position should look at. The layer goes on computing (a saturated output is a perfectly well-defined output); what it can no longer do is change its mind.

It is worth pausing on why Block 2 never ran into this, because the difference is one line. There the softmax was followed by the cross-entropy, and the −log⁡-\log of the loss supplies the exact inverse factor: together the two gave ∇z ℓ=y^−y\nabla_{\mathbf{z}}\,\ell = \hat{\mathbf{y}} - \mathbf{y}, which is y^c−1\hat{y}_c - 1 in the correct class and does not vanish while the network is wrong, however sure of itself it is. Here there is no logarithm behind the softmax, only the multiplication by V\mathbf{V}, the matrix of values. The Jacobian cancels against nothing: it stays whole, zeros included.

And so two things the course had closed, in different blocks, turn up again. The curve α(1−α)\alpha\left(1 - \alpha\right) is the same as the derivative of the sigmoid, with its maximum of 0.250.25 in the middle and its two flat tails: the saturation from the lesson on activation functions, in a layer with no sigmoid in it. And a gradient that arrives switched off at the matrices that were meant to learn is the whole of Block 3, in an architecture without a single recurrence. What joined them was never the sigmoid or the recurrent state: it was that in both there was a function flattening out.

A whole row comes down to one number, which comes from adding the diagonal case over the TT positions:

∑j=1Tαij(1−αij)=∑jαij−∑jαij2=1−∑j=1Tαij2,\sum_{j=1}^{T} \alpha_{ij}\left(1 - \alpha_{ij}\right) = \sum_{j} \alpha_{ij} - \sum_{j} \alpha_{ij}^{2} = 1 - \sum_{j=1}^{T} \alpha_{ij}^{2},

where the last equality uses that the row sums to 11. With the row shared out evenly it is 1−1/T1 - 1/T; with the whole row on one position it is 00. It is the sum of the diagonal of the Jacobian, and it measures the same thing from both sides: how much that row shares out, and how much it still has left to learn.

The divisor, measured in NumPy

The first cell takes the calculation done for signs and checks it where it actually applies: Gaussian coordinates, five widths, with the measured typical size set beside the dk\sqrt{d_k} column.

import numpy as np

rng = np.random.default_rng(7)
N = 8000 # pairs of vectors per width

print(f"{'d_k':>5} {'mean':>7} {'typical size':>13} {'sqrt(d_k)':>9} {'divided':>12}")
for d_k in (4, 16, 64, 256, 1024):
Q = rng.normal(size=(N, d_k)) # centred coordinates, of typical size 1
K = rng.normal(size=(N, d_k))
e = np.sum(Q * K, axis=1) # one score per pair
typical = np.sqrt(np.mean(e ** 2))
print(f"{d_k:5d} {e.mean():7.3f} {typical:13.2f} {np.sqrt(d_k):9.2f} {typical / np.sqrt(d_k):12.2f}")
numpy

The first run downloads the Python interpreter (~15 MB). After that it stays in the browser cache and is reused across every lesson.

The two middle columns agree as far as a sample of eight thousand pairs allows, and the one on the right stays pinned at 11: dividing puts the size in the same place whatever the width. The mean column is small next to the typical size on its row (at dk=1 024d_k = 1\,024, 0.2360.236 against 32.0332.03), which is what E[e]=0\mathbb{E}\left[e\right] = 0 says when it is measured instead of proved.

The second cell moves from the scores to what comes out of them. Pay particular attention to the last column, which decides whether the layer can learn.

import numpy as np

rng = np.random.default_rng(11)
T, N = 8, 4000 # 8 positions per row, 4000 rows measured


def softmax_rows(E):
Z = np.exp(E - E.max(axis=1, keepdims=True))
return Z / Z.sum(axis=1, keepdims=True)


print(f"{'':<11} {'d_k':>5} {'top weight':>10} {'share-out':>9} {'max a(1-a)':>12}")
for d_k in (8, 64, 512):
Q = rng.normal(size=(N, d_k)) # N queries against the same T keys
K = rng.normal(size=(T, d_k))
E = Q @ K.T
for name, Esc in (("undivided", E), ("divided", E / np.sqrt(d_k))):
A = softmax_rows(Esc)
print(f"{name:<11} {d_k:5d} {A.max(axis=1).mean():10.3f} "
f"{(1 - (A ** 2).sum(axis=1)).mean():9.3f} {(A * (1 - A)).max(axis=1).mean():12.4f}")
numpy

The first run downloads the Python interpreter (~15 MB). After that it stays in the browser cache and is reused across every lesson.

Read it in pairs of lines. Undivided, the top weight climbs from 0.6700.670 to 0.9530.953 and the share-out 1−∑jαij21 - \sum_j \alpha_{ij}^{2} sinks from 0.4450.445 to 0.0660.066 (the most it can be with eight positions is 1−1/8=0.8751 - 1/8 = 0.875), while the largest cell on the diagonal of the Jacobian falls from 0.17550.1755 to 0.03210.0321: more than five times less gradient for no reason other than having widened the model. Divided, the three lines stop moving: 0.360.36 of top weight, 0.770.77 of share-out and 0.2160.216 of derivative at all three widths, 512512 included. That is the property the divisor buys. It does not make the numbers better; it makes them stop depending on dkd_k.

Test your intuition

Five questions: how big a score is, why the root and nothing else, what a closed row loses, why Block 2 escaped this, and what dividing does not touch.

The rows of Q\mathbf{Q} and K\mathbf{K} in a layer have dk=64d_k = 64 coordinates, centred and independent, each of typical size 11. Undivided, how big is a score q⊤k\mathbf{q}^{\top}\mathbf{k}, typically?

A margin of ±0 is accepted.

Why is the divisor dk\sqrt{d_k} and not dkd_k?

A row of the map has gone almost entirely to one position: αi1≈1\alpha_{i1} \approx 1 and all the rest ≈0\approx 0. Mark what is true.

Select every correct option. This is graded all-or-nothing: there is no partial credit.

Block 2 also put a softmax in the network and never mentioned it saturating. Why did this not happen there?

Dividing the scores of a row by dk\sqrt{d_k} can change which position gets the largest weight.

Write score_gradient(A, G), the backward pass of the softmax by rows. It takes the weight map already normalised, A, of shape (T,T)(T, T) with each row summing to 11, and the gradient coming down from above, G, of the same shape, with gij=∂ℓ/∂αijg_{ij} = \partial\ell/\partial\alpha_{ij}. It returns ∂ℓ/∂e\partial\ell/\partial e, of shape (T,T)(T, T).

The two cases of the derivative are worked out above; chained together, they give, cell by cell, ∂ℓ∂eij=αij(gij−∑mgimαim)\dfrac{\partial\ell}{\partial e_{ij}} = \alpha_{ij}\left(g_{ij} - \sum_{m} g_{im}\alpha_{im}\right), where mm runs over row ii.

Write it with array operations (no looping over cells, and no building of any Jacobian) and do not write into the arrays you receive.

The first run downloads the Python interpreter (~15 MB); after that it stays in the browser cache. This challenge is much easier to solve on a physical keyboard: on a phone, read it and come back later.


With the divisor in place, the scale of the scores stops depending on the width, and the share-out and the gradient stop depending on it by the same calculation. But look at what the divisor takes for granted: it receives dkd_k already decided. Nothing worked out so far says how long a query should be (only how to compensate for whatever length it has), and until now dkd_k has been a number that turned up because the projections of the previous lesson had to come out at some width.

There is a second thing this layer cannot do, and it points to the same place. A row of A\mathbf{A} is a share-out, and only one: in the keys to this car are here, the position of are has to decide, with a single set of weights, whether to look at keys, which is what it agrees with, or at to this car, which is what stands between them; and with one row it cannot do both. The answer to both questions is the same and takes up the next lesson, on multi-head attention: several pairs of projections working in parallel, each with its own share-out and its own width, carved out of dmodeld_{\text{model}}. There, dkd_k stops being a given number and becomes a decision with a reason.

Was this helpful?
Further reading1 source · 1 paper

Where this lesson comes from, and where to go next. None of it is needed to carry on with the course.

  • Attention Is All You Need
    paperVaswani, Shazeer, Parmar and others, 2017arXiv:1706.03762EN

    Its §3.2.1 and footnote 4 contain this lesson in two sentences: with q and k of mean 0 and variance 1, q·k has variance d_k, hence √d_k. It never gets to the saturated softmax or the vanishing gradient; that part is yours.