Type at least 2 letters. Enter opens the first result, Esc closes.

    Deep Learning · Lesson 9 · Attention and transformers

    Attention: queries, keys, values and masks

    Intermediate 70 min 5 interactive figures 4 videos Colab notebook

    Before this lesson: lesson 03 (backpropagation), lesson 05 (LayerNorm and residual connections) and lesson 08 (tokens, embeddings and what attention replaced). The softmax Jacobian is also derived in linear-algebra lesson 16. You need the rule "the variance of a sum of independent terms is the sum of their variances" (probability-and-statistics track). Time: about 70 minutes, plus the notebook.


    Why this matters #

    A recurrent network (lesson 08) carries information from token 1 to token 1000 through 999 sequential steps. Attention lets every token read from every other token in one step, and all tokens are processed in parallel on a GPU. This one operation is the core of large language models (GPT, LLaMA), of BERT, of the vision transformer (lesson 11), of speech models such as Whisper, of AlphaFold 2, and of most weather and climate foundation models. Interviews test it at three levels: write it from a blank file, derive the 1/dk1/\sqrt{d_k} scaling, and count its cost. The cost grows with the square of the number of tokens, which is the main limit of long contexts.

    1. Predict first #

    2. The picture #

    One query: a soft dictionary lookup #

    A Python dictionary {"cat": v1, "dog": v2} does a hard lookup: the query "cat" matches exactly one key and returns its value. Attention does a soft lookup. There are TT items. Item jj has a key kj∈Rdkk_j \in \mathbb{R}^{d_k} and a value vj∈Rdvv_j \in \mathbb{R}^{d_v}. A query q∈Rdkq \in \mathbb{R}^{d_k} asks for something. Attention does three steps:

    1. Score every item: sj=q⊤kj/dks_j = q^\top k_j / \sqrt{d_k}. A large dot product means "this key matches the query".
    2. Weights: aj=esj/∑lesla_j = e^{s_j} / \sum_l e^{s_l}, the softmax of the scores. So aj>0a_j > 0 and ∑jaj=1\sum_j a_j = 1.
    3. Output: the weighted average o=∑jajvjo = \sum_j a_j v_j.

    The output is a blend of all values, with more weight on the values whose keys match the query. Because aj>0a_j > 0 and ∑jaj=1\sum_j a_j = 1, the output is a convex combination: it always lies inside the region spanned by the values. A hard lookup is the limit where one weight is 1 and the others are 0.

    Interactive figure: loading…

    Drag the yellow query in the left plane. The scores are dot products with the violet keys; the table shows them and their softmax weights. In the right plane the blue output oo is the weighted average of the grey values; the rings show the weights. Try a short query and a long query: a longer query (or a smaller temperature τ\tau) makes the weights nearly one-hot and the output jumps to one value. Notice that the keys decide the weights, but the values decide where the output lands.

    Quick check

    Three items have scores s=(ln⁡3, 0, 0)s = (\ln 3,\ 0,\ 0) and values v1=(1,0)v_1 = (1, 0), v2=(0,1)v_2 = (0, 1), v3=(0,0)v_3 = (0, 0). What is the attention output?

    Self-attention: every token is a query #

    A sequence of TT tokens is a matrix X∈RT×DX \in \mathbb{R}^{T \times D}: one row per token, DD is the model width. (We use the row convention of PyTorch, as in the whole track.) In self-attention, the queries, keys and values all come from the same sequence. Three learned matrices WQ,WK∈RD×dkW_Q, W_K \in \mathbb{R}^{D \times d_k} and WV∈RD×dvW_V \in \mathbb{R}^{D \times d_v} give

    Q=XWQ∈RT×dk,K=XWK∈RT×dk,V=XWV∈RT×dv.\begin{aligned} Q &= XW_Q \in \mathbb{R}^{T \times d_k}, \\ K &= XW_K \in \mathbb{R}^{T \times d_k}, \\ V &= XW_V \in \mathbb{R}^{T \times d_v}. \end{aligned}

    Row ii of QQ is the query of token ii; row jj of KK and of VV are the key and the value of token jj. Now every token does the lookup above at the same time:

    QK⊤/dk⏟T×T scores  →softmax of each row  A⏟T×T  ⟶  AV⏟T×dv.\underbrace{QK^\top / \sqrt{d_k}}_{T \times T \text{ scores}} \;\xrightarrow{\text{softmax of each row}}\; \underbrace{A}_{T \times T} \;\longrightarrow\; \underbrace{AV}_{T \times d_v}.

    Entry (i,j)(i, j) of QK⊤QK^\top is qi⊤kjq_i^\top k_j, the score of query ii for key jj. Row ii of AA holds the attention weights of token ii: it is positive and sums to 1. Row ii of AVAV is ∑jAijvj\sum_j A_{ij} v_j, the output of token ii.

    The mixing weights AA are computed from the input. A convolution (lesson 07) also mixes neighbouring tokens, but with weights that are fixed after training and that depend only on the offset. Attention decides for every input again which tokens to mix.

    Interactive figure: loading…

    The whole T×TT \times T matrix of one head, for six tokens and head size 2 (this figure is shared with linear-algebra lesson 15, which writes dhd_h for dkd_k and uses the column convention). Left: the scaled scores; right: the weights AA. Click a row to pick a query; its output is drawn as the weighted average of the values. Move the scale β\beta to 0: every row becomes uniform. Turn on the causal mask: the upper triangle of AA becomes 0.

    Quick check

    One attention head with T=10T = 10 tokens, model width D=64D = 64 and head size dk=dv=8d_k = d_v = 8. What is the shape of QK⊤QK^\top, and of the head output AVAV?

    3. Definitions and main results #

    Scaled dot-product attention #

    Definition. For queries Q∈RTq×dkQ \in \mathbb{R}^{T_q \times d_k}, keys K∈RTk×dkK \in \mathbb{R}^{T_k \times d_k}, values V∈RTk×dvV \in \mathbb{R}^{T_k \times d_v} and a mask M∈{0,−∞}Tq×TkM \in \{0, -\infty\}^{T_q \times T_k}:

    Attn⁡(Q,K,V)=AV,A=softmax⁡(QK⊤dk+M)∈RTq×Tk,\begin{aligned} \operatorname{Attn}(Q, K, V) &= AV, \\ A &= \operatorname{softmax}\Big(\frac{QK^\top}{\sqrt{d_k}} + M\Big) \in \mathbb{R}^{T_q \times T_k}, \end{aligned}

    where the softmax acts on each row separately. With no mask, M=0M = 0.

    Cost. QK⊤QK^\top needs 2TqTkdk2T_qT_kd_k floating-point operations (FLOPs; one multiply and one add per term), AVAV needs 2TqTkdv2T_qT_kd_v, and AA needs TqTkT_qT_k numbers of memory. For self-attention Tq=Tk=TT_q = T_k = T: the cost grows like T2T^2.

    Why divide by dk\sqrt{d_k} #

    The figure first, then the proof. The figure draws one query and 16 keys with random entries and shows the softmax weights, with and without the scaling.

    Interactive figure: loading…

    Move the head size dkd_k and switch the scaling. Top: the 16 softmax weights of one query, sorted. Bottom: the median size of the softmax Jacobian ∥J∥F\|J\|_F over many random draws, with the scaling and without it; the yellow dot is the current draw. Without scaling, a larger dkd_k gives larger logits, one weight close to 1, and a Jacobian close to 0: almost no gradient reaches the queries and keys. With scaling, the picture is the same for every dkd_k.

    Theorem (variance of the dot product). Let q,k∈Rdkq, k \in \mathbb{R}^{d_k} be independent, with independent entries of mean 0 and variance 1. Then E[q⊤k]=0\mathbb{E}[q^\top k] = 0 and Var⁡(q⊤k)=dk\operatorname{Var}(q^\top k) = d_k.

    Proof.

    1. q⊤k=∑i=1dkqikiq^\top k = \sum_{i=1}^{d_k} q_ik_i. By independence, E[qiki]=E[qi] E[ki]=0\mathbb{E}[q_ik_i] = \mathbb{E}[q_i]\,\mathbb{E}[k_i] = 0. So E[q⊤k]=0\mathbb{E}[q^\top k] = 0.
    2. Var⁡(qiki)=E[qi2ki2]−02=E[qi2] E[ki2]=1⋅1=1\operatorname{Var}(q_ik_i) = \mathbb{E}[q_i^2k_i^2] - 0^2 = \mathbb{E}[q_i^2]\,\mathbb{E}[k_i^2] = 1 \cdot 1 = 1.
    3. The terms qikiq_ik_i for different ii are independent, so their variances add: Var⁡(q⊤k)=dk\operatorname{Var}(q^\top k) = d_k. ■\blacksquare

    So q⊤kq^\top k has standard deviation dk\sqrt{d_k}, and q⊤k/dkq^\top k / \sqrt{d_k} has variance 1 for every head size.

    Theorem (softmax Jacobian). For p=softmax⁡(s)p = \operatorname{softmax}(s), ∂pi/∂sj=pi(δij−pj)\partial p_i/\partial s_j = p_i(\delta_{ij} - p_j), that is,

    J=∂p∂s=diag⁡(p)−pp⊤.J = \frac{\partial p}{\partial s} = \operatorname{diag}(p) - pp^\top .

    Proof.

    1. pi=esi/Zp_i = e^{s_i}/Z with Z=∑leslZ = \sum_l e^{s_l}, and ∂Z/∂sj=esj\partial Z/\partial s_j = e^{s_j}.
    2. Quotient rule: ∂pi∂sj=δijesiZ−esiesjZ2=δijpi−pipj\dfrac{\partial p_i}{\partial s_j} = \dfrac{\delta_{ij}e^{s_i}Z - e^{s_i}e^{s_j}}{Z^2} = \delta_{ij}p_i - p_ip_j. ■\blacksquare

    JJ is symmetric and positive semidefinite, and J1=0J\mathbf{1} = 0: adding the same constant to all logits changes nothing. Backpropagation needs only the product with the upstream gradient g=∂L/∂pg = \partial \mathcal{L}/\partial p:

    ∂L∂s=Jg=p⊙(g−(p⊤g) 1),\frac{\partial \mathcal{L}}{\partial s} = Jg = p \odot \big(g - (p^\top g)\,\mathbf{1}\big),

    which costs O(T)O(T) instead of O(T2)O(T^2).

    Saturation. Let the largest weight be pm=1−εp_m = 1 - \varepsilon. Then every entry of JJ is at most ε\varepsilon in absolute value. Why: every entry JijJ_{ij} has a factor pip_i or pjp_j with an index different from mm, and that factor is at most ε\varepsilon; the one exception is Jmm=pm(1−pm)≤εJ_{mm} = p_m(1 - p_m) \le \varepsilon. So a nearly one-hot softmax passes almost no gradient back to its logits, and from there to WQW_Q and WKW_K.

    Putting the two together. Without the scaling, logits have standard deviation dk\sqrt{d_k}: 8 for dk=64d_k = 64. The difference of two logits has standard deviation 2dk≈11\sqrt{2d_k} \approx 11, and e11≈60,000e^{11} \approx 60{,}000. The softmax then puts almost all weight on one or a few keys. In a simulation with 128 keys and dk=64d_k = 64, the median of the largest weight is about 0.8 and about 95% of the keys get a weight below 10−310^{-3}; with dk=256d_k = 256 the largest weight is about 0.98. The effect grows with dkd_k. Dividing by dk\sqrt{d_k} keeps the logits of order 1 at initialization, for every head size.

    Backward pass through attention, and the scaling used by μP

    With S=QK⊤/dkS = QK^\top/\sqrt{d_k}, A=softmax⁡(S)A = \operatorname{softmax}(S) (rows), O=AVO = AV and the upstream gradient dO=∂L/∂OdO = \partial \mathcal{L}/\partial O:

    dV=A⊤dO,dA=dO V⊤,dS=A⊙(dA−rowsum⁡(A⊙dA) 1⊤),dQ=dS K/dk,dK=dS⊤Q/dk.\begin{aligned} dV &= A^\top dO, & dA &= dO\,V^\top, \\ dS &= A \odot \big(dA - \operatorname{rowsum}(A \odot dA)\,\mathbf{1}^\top\big), & & \\ dQ &= dS\,K/\sqrt{d_k}, & dK &= dS^\top Q/\sqrt{d_k}. \end{aligned}

    The line for dSdS is the softmax VJP above, applied to each row. FlashAttention uses the same formulas, computed tile by tile. The notebook checks them against autograd.

    The variance argument assumes that qq and kk are independent. If they are strongly correlated, for example q=kq = k, then q⊤k=∥q∥2q^\top k = \|q\|^2 grows like dkd_k, not like dk\sqrt{d_k}. The maximal-update parameterization (μP, Yang et al., Tensor Programs V) therefore scales the logits by 1/dk1/d_k, so that the right learning rate transfers across model widths.

    Quick check

    The entries of q,k∈R64q, k \in \mathbb{R}^{64} are independent with mean 0 and variance 1. What is the variance of q⊤kq^\top k, and of q⊤k/64q^\top k/\sqrt{64}?

    Masks #

    A mask is added to the logits before the softmax. An entry Mij=−∞M_{ij} = -\infty gives e−∞=0e^{-\infty} = 0: query ii gives no weight to key jj.

    • Causal mask (decoders, language models): query ii may attend to key jj only if j≤ij \le i. So Mij=0M_{ij} = 0 for j≤ij \le i and −∞-\infty above the diagonal. Then token ii never reads a future token, and training on all positions at once gives the same outputs as generating one token at a time.
    • Padding mask: sequences in a batch have different lengths, so short ones are filled with padding tokens. A padded key jj gets Mij=−∞M_{ij} = -\infty in every row.
    • Masks combine by adding them (for boolean "may attend" masks: a logical AND).
    • Add the mask before the softmax. Then the masked weights are exactly 0, and the visible weights of each row still sum to 1.
    • A fully masked row has every logit equal to −∞-\infty. A direct implementation computes 0/00/0 and returns NaN, and the NaN spreads through the whole network. Some library kernels return zeros for such rows instead; do not rely on either. Make sure every query sees at least one key.
    Interactive figure: loading…

    Choose a mask and look at the weights AA on the right. Masked cells are magenta; the column "sum" is the sum of each row. Try "causal + padding" with left padding: rows 1 and 2 have no visible key and become NaN. Then choose "after softmax (bug)": the masked weights are set to 0 after the softmax, and the rows no longer sum to 1.

    Conventions differ between libraries. In F.scaled_dot_product_attention, a boolean mask entry True means "may attend". In nn.MultiheadAttention, True in attn_mask or key_padding_mask means "blocked". Read the documentation of the function you call.

    Quick check

    A student implements causal attention by computing softmax⁡(QK⊤/dk)\operatorname{softmax}(QK^\top/\sqrt{d_k}) first and then setting the weights above the diagonal to 0. What goes wrong?

    Multi-head attention #

    One head gives each query one probability distribution over the keys. Multi-head attention runs HH heads in parallel, each with its own projections, so that a token can attend to several places at once (for example, to the previous token and to a related word far away).

    Definition. Let HH divide DD, and let dk=dv=D/Hd_k = d_v = D/H be the head size. Take WQ,WK,WV,WO∈RD×DW_Q, W_K, W_V, W_O \in \mathbb{R}^{D \times D} and bias vectors bQ,bK,bV,bO∈RDb_Q, b_K, b_V, b_O \in \mathbb{R}^D. Head h∈{1,…,H}h \in \{1, \dots, H\} uses the columns (h−1)dk+1(h - 1)d_k + 1 to hdkhd_k of the first three matrices: WQh,WKh,WVh∈RD×dkW_Q^h, W_K^h, W_V^h \in \mathbb{R}^{D \times d_k}, and the same entries of the biases. Then

    head⁡h=Attn⁡(XWQh+1bQh,  XWKh+1bKh,  XWVh+1bVh)∈RT×dk,MHA⁡(X)=[head⁡1,…,head⁡H] WO+1bO∈RT×D.\begin{aligned} \operatorname{head}_h &= \operatorname{Attn}\big(XW_Q^h + \mathbf{1}b_Q^h,\; XW_K^h + \mathbf{1}b_K^h,\; XW_V^h + \mathbf{1}b_V^h\big) \in \mathbb{R}^{T \times d_k}, \\ \operatorname{MHA}(X) &= [\operatorname{head}_1, \dots, \operatorname{head}_H]\, W_O + \mathbf{1}b_O \in \mathbb{R}^{T \times D}. \end{aligned}

    Here [⋅,…,⋅][\cdot, \dots, \cdot] puts the head outputs side by side (T×DT \times D), and WOW_O mixes the heads.

    • Implementation: no loop over heads. One matrix product gives Q=XWQ+1bQQ = XW_Q + \mathbf{1}b_Q of shape (T,D)(T, D). View it as (T,H,dk)(T, H, d_k), then move the head axis to the front: (H,T,dk)(H, T, d_k). Batched matrix products then compute all heads at once. With a batch axis: (B,T,D)→(B,T,H,dk)→(B,H,T,dk)(B, T, D) \to (B, T, H, d_k) \to (B, H, T, d_k).
    • Parameters: WQ,WK,WV,WOW_Q, W_K, W_V, W_O give 4D24D^2, the four biases give 4D4D: in total 4D2+4D4D^2 + 4D, for every HH. Heads split the width; they do not add parameters.
    • Cost for TT tokens: the four projections need 4⋅2TD2=8TD24 \cdot 2TD^2 = 8TD^2 FLOPs; the scores and the weighted sums of all heads need H⋅4T2dk=4T2DH \cdot 4T^2d_k = 4T^2D. The two are equal at T=2DT = 2D; for longer sequences the T2T^2 term dominates. Lesson 11 adds the MLP of the transformer block to this count.
    • Low rank: the score of head hh is xi⊤WQh(WKh)⊤xj/dkx_i^\top W_Q^h (W_K^h)^\top x_j / \sqrt{d_k}, a bilinear form whose D×DD \times D matrix has rank at most dkd_k (linear-algebra lesson 15).
    Interactive figure: loading…

    Step through the reshapes. Each cell is one number of QQ; its text is the token it came from, and its colour is the head it belongs to (in this figure, colours mark heads). With "view, then transpose" every head block holds all 4 tokens and only its own features. Switch to "reshape directly (bug)" at step 3: the shapes are the same, but each block now mixes tokens and features. No error is raised, and the model is silently wrong.

    Quick check

    Multi-head attention with D=512D = 512, biases, and H=8H = 8 heads. How many parameters does it have? And with H=16H = 16?

    Cross-attention #

    In cross-attention, the queries come from one sequence and the keys and values from another: Q=XdecWQQ = X_{\text{dec}}W_Q with TqT_q rows, and K=ZWKK = ZW_K, V=ZWVV = ZW_V from a second sequence ZZ with TkT_k rows (for example, the output of an encoder). Then AA is Tq×TkT_q \times T_k, and the output has one row per query. Cross-attention uses the padding mask of ZZ, but never a causal mask: the whole second sequence is known. It is the standard way to condition a network on other information, such as a translation model reading the source sentence, or an image generator reading a text prompt.

    Permutation equivariance #

    A permutation matrix P∈RT×TP \in \mathbb{R}^{T \times T} reorders the rows of a matrix: (PX)i=Xπ(i)(PX)_i = X_{\pi(i)} for a permutation π\pi of {1,…,T}\{1, \dots, T\}. It is orthogonal: P⊤P=IP^\top P = I.

    Theorem (self-attention is permutation-equivariant). Without a mask, and with Q=XWQQ = XW_Q, K=XWKK = XW_K, V=XWVV = XW_V: Attn⁡(PX)=P Attn⁡(X)\operatorname{Attn}(PX) = P\,\operatorname{Attn}(X) for every permutation matrix PP.

    Proof.

    1. The input PXPX gives Q′=PXWQ=PQQ' = PXW_Q = PQ, and in the same way K′=PKK' = PK and V′=PVV' = PV.
    2. The scores are S′=Q′K′⊤/dk=PQK⊤P⊤/dk=PSP⊤S' = Q'K'^\top/\sqrt{d_k} = PQK^\top P^\top/\sqrt{d_k} = PSP^\top. In entries: Sij′=Sπ(i)π(j)S'_{ij} = S_{\pi(i)\pi(j)}. Rows and columns are reordered in the same way.
    3. The row-wise softmax commutes with this reordering: Aij′=eSπ(i)π(j)∑leSπ(i)π(l)=eSπ(i)π(j)∑l′eSπ(i)l′=Aπ(i)π(j)A'_{ij} = \dfrac{e^{S_{\pi(i)\pi(j)}}}{\sum_l e^{S_{\pi(i)\pi(l)}}} = \dfrac{e^{S_{\pi(i)\pi(j)}}}{\sum_{l'} e^{S_{\pi(i)l'}}} = A_{\pi(i)\pi(j)}, because l′=π(l)l' = \pi(l) runs over all indices. So A′=PAP⊤A' = PAP^\top.
    4. A′V′=PAP⊤PV=PAVA'V' = PAP^\top PV = PAV, because P⊤P=IP^\top P = I. ■\blacksquare

    The same holds for multi-head attention (each head is equivariant, and the concatenation and WOW_O act on each token separately), and for a whole transformer encoder, because the MLP, LayerNorm and residual connections also act on each token separately. Without positions, the model sees a set of tokens, not a sequence: "dog bites man" and "man bites dog" give the same three output vectors, only in a different order. Lesson 10 adds positions.

    A causal mask breaks the symmetry. The masked scores of the input PXPX are PSP⊤+MPSP^\top + M, and PMP⊤≠MPMP^\top \ne M for P≠IP \ne I. Query ii sees exactly ii keys, so a decoder can infer positions even without a positional encoding.

    Answers to section 1. (1) q⊤kq^\top k has standard deviation 64=8\sqrt{64} = 8, and the difference of two such logits has standard deviation 82≈118\sqrt{2} \approx 11. The softmax is then nearly one-hot (almost all weight on one or a few keys), most entries of its Jacobian are close to 0, and the gradients to WQW_Q and WKW_K become very small. Dividing by dk=8\sqrt{d_k} = 8 fixes this. (2) The outputs are shuffled in the same way and are otherwise unchanged (permutation equivariance). A pooled output, such as the mean over the tokens, does not change at all.

    4. Worked example #

    One head, three tokens. Take T=3T = 3, dk=dv=2d_k = d_v = 2 and WQ=WK=WV=IW_Q = W_K = W_V = I, so Q=K=V=XQ = K = V = X:

    X=(100111),QK⊤=(101011112),S=QK⊤2=(0.70700.70700.7070.7070.7070.7071.414).X = \begin{pmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \end{pmatrix}, \qquad QK^\top = \begin{pmatrix} 1 & 0 & 1 \\ 0 & 1 & 1 \\ 1 & 1 & 2 \end{pmatrix}, \qquad S = \frac{QK^\top}{\sqrt{2}} = \begin{pmatrix} 0.707 & 0 & 0.707 \\ 0 & 0.707 & 0.707 \\ 0.707 & 0.707 & 1.414 \end{pmatrix}.

    No mask. Row 1: e0.707=2.028e^{0.707} = 2.028 and e0=1e^0 = 1. The sum is 2.028+1+2.028=5.0562.028 + 1 + 2.028 = 5.056, so the weights are (2.028,1,2.028)/5.056=(0.401,0.198,0.401)(2.028, 1, 2.028)/5.056 = (0.401, 0.198, 0.401). The other rows in the same way:

    A=(0.4010.1980.4010.1980.4010.4010.2480.2480.504),O=AV=(0.8020.5990.5990.8020.7520.752).A = \begin{pmatrix} 0.401 & 0.198 & 0.401 \\ 0.198 & 0.401 & 0.401 \\ 0.248 & 0.248 & 0.504 \end{pmatrix}, \qquad O = AV = \begin{pmatrix} 0.802 & 0.599 \\ 0.599 & 0.802 \\ 0.752 & 0.752 \end{pmatrix}.

    Check row 1 of OO: 0.401 (1,0)+0.198 (0,1)+0.401 (1,1)=(0.802,0.599)0.401\,(1, 0) + 0.198\,(0, 1) + 0.401\,(1, 1) = (0.802, 0.599).

    Causal mask. Row 1 sees only key 1: weights (1,0,0)(1, 0, 0), output (1,0)(1, 0). Row 2 sees keys 1 and 2, with logits (0,0.707)(0, 0.707): e0=1e^0 = 1, e0.707=2.028e^{0.707} = 2.028, weights (1,2.028)/3.028=(0.330,0.670)(1, 2.028)/3.028 = (0.330, 0.670), output 0.330 (1,0)+0.670 (0,1)=(0.330,0.670)0.330\,(1, 0) + 0.670\,(0, 1) = (0.330, 0.670). Row 3 sees all keys and does not change: (0.752,0.752)(0.752, 0.752). The output of token 1 no longer depends on tokens 2 and 3.

    Jacobian of row 2 (causal). p=(0.330,0.670)p = (0.330, 0.670), so

    J=(p1−p12−p1p2−p1p2p2−p22)=p1p2(1−1−11)=(0.221−0.221−0.2210.221),J = \begin{pmatrix} p_1 - p_1^2 & -p_1p_2 \\ -p_1p_2 & p_2 - p_2^2 \end{pmatrix} = p_1p_2\begin{pmatrix} 1 & -1 \\ -1 & 1 \end{pmatrix} = \begin{pmatrix} 0.221 & -0.221 \\ -0.221 & 0.221 \end{pmatrix},

    using p1−p12=p1(1−p1)=p1p2p_1 - p_1^2 = p_1(1 - p_1) = p_1p_2.

    Saturation in numbers. Logits (2,1,0)(2, 1, 0) give p=(0.665,0.245,0.090)p = (0.665, 0.245, 0.090) and max⁡ij∣Jij∣=0.223\max_{ij}|J_{ij}| = 0.223. The same logits times 8, which is what dk=64d_k = 64 gives without the scaling, are (16,8,0)(16, 8, 0): p=(0.99966,0.00034,0.0000001)p = (0.99966, 0.00034, 0.0000001) and max⁡ij∣Jij∣=3.4⋅10−4\max_{ij}|J_{ij}| = 3.4 \cdot 10^{-4}. The largest Jacobian entry is about 660 times smaller.

    Counting. Multi-head attention with D=512D = 512, H=8H = 8 (so dk=64d_k = 64) and biases has 4(5122+512)=1,050,6244(512^2 + 512) = 1{,}050{,}624 parameters. For T=512T = 512 tokens: projections 8TD2=8⋅5123≈1.078TD^2 = 8 \cdot 512^3 \approx 1.07 GFLOP; scores and weighted sums 4T2D=4⋅5123≈0.544T^2D = 4 \cdot 512^3 \approx 0.54 GFLOP. At T=2D=1024T = 2D = 1024 the two parts cost the same.

    5. In code #

    PyTorch checks the worked example and the claims of section 3. The notebook builds the same functions from NumPy.

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    torch.manual_seed(0)
    f64 = torch.float64
    
    # (a) the worked example, without and with the causal mask
    X = torch.tensor([[1., 0.], [0., 1.], [1., 1.]], dtype=f64)
    print(F.scaled_dot_product_attention(X, X, X).numpy().round(3))
    # [[0.802 0.599] [0.599 0.802] [0.752 0.752]]
    print(F.scaled_dot_product_attention(X, X, X, is_causal=True).numpy().round(3))
    # [[1.    0.   ] [0.33  0.67 ] [0.752 0.752]]
    
    # (b) Var(q·k) = d_k, and Var(q·k / sqrt(d_k)) = 1
    for d_k in (16, 64, 256):
        q, k = torch.randn(100_000, d_k, dtype=f64), torch.randn(100_000, d_k, dtype=f64)
        s = (q * k).sum(-1)
        print(d_k, round(s.var().item(), 1), round((s / d_k**0.5).var().item(), 2))
    # 16 16.0 1.0 / 64 63.8 1.0 / 256 255.3 1.0   (Monte Carlo: close to d_k and 1)
    
    # (c) softmax Jacobian = diag(p) - p pᵀ, and it is tiny when p is nearly one-hot
    for z in ([2., 1., 0.], [16., 8., 0.]):
        z = torch.tensor(z, dtype=f64)
        J = torch.autograd.functional.jacobian(lambda s: torch.softmax(s, -1), z)
        p = torch.softmax(z, -1)
        print(torch.allclose(J, torch.diag(p) - torch.outer(p, p)), f"{J.abs().max().item():.2e}")
    # True 2.23e-01
    # True 3.35e-04
    
    # (d) 4D² + 4D parameters for every H, and permutation equivariance
    print([sum(p.numel() for p in nn.MultiheadAttention(512, H).parameters()) for H in (1, 8, 64)])
    # [1050624, 1050624, 1050624]
    mha = nn.MultiheadAttention(64, 4, batch_first=True, dtype=f64)
    attn = lambda x, mask=None: mha(x, x, x, attn_mask=mask, need_weights=False)[0]
    x, perm = torch.randn(1, 10, 64, dtype=f64), torch.randperm(10)
    print(torch.allclose(attn(x)[:, perm], attn(x[:, perm])))                  # True
    causal = nn.Transformer.generate_square_subsequent_mask(10, dtype=f64)
    print(torch.allclose(attn(x, causal)[:, perm], attn(x[:, perm], causal)))  # False
    

    F.scaled_dot_product_attention divides by dk\sqrt{d_k} itself, with dkd_k the last dimension of the queries. generate_square_subsequent_mask returns the additive mask MM (0 and −∞-\infty).

    6. Where it shows up #

    • Language models. GPT and LLaMA are stacks of causal self-attention (decoder-only). BERT uses self-attention without a causal mask (encoder-only). At generation time the keys and values of past tokens are stored in a KV cache; multi-query and grouped-query attention share key and value heads between query heads to make this cache smaller (lesson 11).
    • Long inputs. The T2T^2 term limits the context length. FlashAttention computes exact attention in tiles with an online softmax: the extra memory becomes O(T)O(T), while the FLOPs stay O(T2)O(T^2).
    • Conditioning by cross-attention. Machine translation (the original transformer), speech recognition (Whisper: the text decoder reads the audio encoder), and text-to-image latent diffusion (the image tokens of the denoiser read the text tokens).
    • Many inputs, few queries. The Perceiver lets a small set of learned query vectors cross-attend to a very large input, so the cost grows like TT instead of T2T^2. ClimaX merges the physical variables of each weather patch by cross-attention with one learned query.
    • Science. AlphaFold 2 applies attention along the rows and columns of a sequence alignment and over pairs of residues. Attention restricted to the neighbours of each node is graph attention (lesson 13). Attention without positions is a natural layer for sets, such as point clouds or particles.

    7. Common mistakes #

    • Masking after the softmax. The visible weights then sum to less than 1, and they depend on the masked keys through the normalizer. Add −∞-\infty to the logits before the softmax.
    • A fully masked row. It gives NaN (or silent zeros). Check padding masks for rows with no visible key.
    • A flipped mask convention. True means "attend" in one API and "blocked" in another.
    • A wrong head split. Reshaping (B,T,D)(B, T, D) directly to (B,H,T,dk)(B, H, T, d_k) mixes tokens and features. View as (B,T,H,dk)(B, T, H, d_k), then transpose.
    • Scaling by D\sqrt{D} instead of dk\sqrt{d_k}, the head size.
    • "More heads, more parameters." The count is 4D2+4D4D^2 + 4D for every HH that divides DD.
    • A causal mask in an encoder or in cross-attention. Only decoder self-attention is causal.
    • Reading row jj of AA as "who attends to token jj". Row ii holds the weights of query ii; column jj shows how much each query reads from token jj.

    8. Check yourself #

    1. Cross-attention with batch B=2B = 2, Tq=5T_q = 5 queries, Tk=7T_k = 7 keys, D=64D = 64 and H=4H = 4 heads. Give the shapes of the per-head queries, the keys, the attention weights and the output.
      Answerdk=64/4=16d_k = 64/4 = 16. Queries (2,4,5,16)(2, 4, 5, 16); keys and values (2,4,7,16)(2, 4, 7, 16); weights (2,4,5,7)(2, 4, 5, 7); per-head outputs (2,4,5,16)(2, 4, 5, 16); after merging the heads and WOW_O: (2,5,64)(2, 5, 64), one row per query.
    2. Logits s=(3,0)s = (3, 0). Compute pp and the softmax Jacobian.
      Answerp=(e3,1)/(e3+1)=(0.953,0.047)p = (e^3, 1)/(e^3 + 1) = (0.953, 0.047). J=p1p2(1−1−11)J = p_1p_2\begin{pmatrix}1&-1\\-1&1\end{pmatrix} with p1p2=0.045p_1p_2 = 0.045.
    3. The entries of qq and kk are independent with mean 0 and variance σ2\sigma^2. What is Var⁡(q⊤k/dk)\operatorname{Var}(q^\top k/\sqrt{d_k})? What keeps σ\sigma close to 1 in a network?
      Answerσ4\sigma^4: each term qikiq_ik_i has variance σ2⋅σ2\sigma^2 \cdot \sigma^2, and there are dkd_k terms, divided by dkd_k. The scaling works only if qq and kk have entries of unit scale. A normalized input to the attention layer (LayerNorm, lesson 05) and a variance-preserving initialization of WQ,WKW_Q, W_K (lesson 04) give this at initialization; qk-norm keeps it during training.
    4. Why is the mask added before the softmax and not applied after it?
      AnswerBefore: masked weights are exactly 0 and the visible weights are normalized to sum to 1. After: the normalizer still contains the masked (for example, future) keys, so the visible weights do not sum to 1 and they depend on the masked tokens. For a causal model this is a leak of future information.
    5. A model has D=1024D = 1024. For which number of tokens TT do the attention scores and weighted sums (4T2D4T^2D FLOPs) cost more than the four projections (8TD28TD^2)?
      Answer4T2D>8TD2  ⟺  T>2D=20484T^2D > 8TD^2 \iff T > 2D = 2048.
    6. Can a decoder-only model without any positional encoding know token positions?
      AnswerYes, it can infer them. Under the causal mask, query ii averages over exactly ii tokens. For example, a head with equal weights gives the first token the weight 1/i1/i, which encodes ii. The mask breaks permutation equivariance.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. Write scaled dot-product attention with a mask.

    2. Scores (0,ln⁡3)(0, \ln 3) for two keys. What are the attention weights?

    3. Why do we divide the logits by dk\sqrt{d_k}?

    4. What is the Jacobian of p=softmax⁡(s)p = \operatorname{softmax}(s)?

    5. What does a causal mask do, and how is it built?

    6. What happens to an attention row where every key is masked?

    7. How many parameters does multi-head attention with width DD and HH heads have?

    8. How do you split QQ of shape (B,T,D)(B, T, D) into HH heads?

    9. How does the cost of self-attention grow with the number of tokens TT?

    10. What does permutation equivariance of self-attention mean, and what breaks it?

    Open the review →

    9. Interview angle #

    • "Implement multi-head attention with a causal mask." — Write the shapes first; view then transpose for the heads; add the mask before the softmax; compare with F.scaled_dot_product_attention.
    • "Why divide by dk\sqrt{d_k}?" — q⊤kq^\top k has variance dkd_k at initialization; large logits saturate the softmax, its Jacobian diag⁡(p)−pp⊤\operatorname{diag}(p) - pp^\top goes to 0, and learning stops.
    • "What is the cost of self-attention?" — 8TD2+4T2D8TD^2 + 4T^2D FLOPs per layer and O(T2)O(T^2) memory for the weights unless the computation is tiled (FlashAttention).
    • "Why several heads, and does it add parameters?" — Several attention patterns at once; no, the count stays 4D2+4D4D^2 + 4D.
    • "Self-attention vs cross-attention?" — Same formula; in cross-attention the queries come from one sequence and the keys and values from another.
    • "What does attention do if you shuffle the tokens?" — Without positions or a causal mask, the outputs are shuffled in the same way (permutation equivariance).

    10. Practice #

    The notebook for this lesson runs in Colab. It uses NumPy for the exercises and compares with PyTorch when it is installed (it is on Colab). Five exercises, each with a check cell and a folded solution:

    1. Scaled dot-product attention with a boolean mask, checked on the worked example and against F.scaled_dot_product_attention.
    2. A Monte Carlo check of Var⁡(q⊤k)=dk\operatorname{Var}(q^\top k) = d_k, and a plot of the largest weight with and without the scaling.
    3. The softmax Jacobian and its VJP, checked by finite differences, and its size as the logits grow.
    4. Multi-head attention from scratch (view and transpose), matching nn.MultiheadAttention with copied weights.
    5. A permutation-equivariance test, with and without the causal mask.

    11. Watch and read #

    Queries, keys and values as pictures; the parts on masking and multiple heads match section 3 of this lesson.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The matrix form QKᵀ, softmax and AV with small numbers, as in the worked example.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    From 1:02:00, self-attention in code, the causal mask, the scaling by the head size and multi-head attention.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    A walk through the original paper.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    • Vaswani et al., Attention Is All You Need (2017): §3.2 (scaled dot-product and multi-head attention; footnote 4 is the variance argument) and Table 1 (cost per layer).
    • Simon Prince, Understanding Deep Learning (free PDF), chapter 12: §12.2–12.3 (dot-product self-attention, the scaling, multiple heads), §12.7 (the causal mask in a decoder) and §12.8 (encoder–decoder and cross-attention).
    • Rush et al., The Annotated Transformer, sections "Attention" and "Applications of Attention in our Model": the paper as runnable code.
    • Zhang, Lipton, Li, Smola, Dive into Deep Learning, chapter 11 (attention as queries, keys and values; multi-head and self-attention), with code.
    • Stanford CS224N, the lecture "Self-Attention and Transformers" (slides and notes on the course page).