This is your self-testing gym for masked attention. Work each problem before opening the solution. Difficulty climbs in five levels:
L1 Recognition — can you spot the right object?
L2 Application — can you run the machine?
L3 Analysis — can you explain why it behaves that way?
L4 Synthesis — can you combine masking with other ideas?
L5 Mastery — can you handle the edge cases and design decisions?
Every symbol used here is defined the moment it appears. If you ever feel a term arrived unearned, that is a bug — but let us re-anchor the objects we lean on most:
Before the problems, fix the geometry in your head with the figure below. Refer back to it whenever a solution says "row i" or "the diagonal" — it is the map every exercise navigates.
softmax(x)j=∑kexkexj
We use softmax (and not, say, "just normalize by dividing") because we want a differentiable function that always yields positive weights summing to one — that is what lets us treat attention weights as probabilities and train them with gradients.
Answer: A.
A valid causal mask keeps the diagonal and everything below it at 0 (past + self allowed), and sets everything above the diagonal (j>i) to −∞ (future silenced). That is a lower-triangular-of-zeros pattern — exactly the teal+orange cells of Figure s01.
A: diagonal and below are 0, strictly-upper is −∞. ✅
B: this is the transpose — it silences the past instead of the future. Wrong.
C: it masks the diagonal too (C11=−∞), which would forbid a token from attending to itself. That is the "diagonal-mask" error — a token must be allowed to attend to itself (we prove why in L5.2, where masking the diagonal makes the very first row empty and produces NaN). Wrong.
Recall Solution
Position i may attend to {1,2,…,i}. For i=3: {1,2,3}. It may not see {4,5} (the future). Count of allowed keys =i=3.
Position i=2 may see {1,2}; positions 3,4 are future ⇒ set to −∞.
Masked row: [2,4,−∞,−∞].
Softmax over the two survivors: e2=7.389, e4=54.598, sum =61.987.
[61.9877.389,61.98754.598,0,0]=[0.12,0.88,0,0]
Weights sum to 1, and the future columns are exactly 0. ✅
Recall Solution
Step 1 — scores QK⊤. (QK⊤ answers "similarity of each query to each key" — a dot product is large when two vectors point the same way, which is why it measures alignment.)
QK⊤=[1001][1120]=[1120]Step 2 — scale by 2 (recall from the top of the page: this divides by dk to stop the dot products from growing with dk): [0.7070.7071.4140].
Step 3 — mask. Row 1 (i=1) may see only {1}: [0.707,−∞]. Row 2 (i=2) sees {1,2}: [0.707,0].
Step 4 — softmax.
Row 1: only one survivor ⇒[1,0].
Row 2: e0.707=2.028, e0=1, sum =3.028⇒[0.6698,0.3302].
Step 5 — weights ×V.
Position 1: 1⋅[10,0]+0⋅[0,10]=[10,0].
Position 2: 0.6698⋅[10,0]+0.3302⋅[0,10]=[6.698,3.302].
Output=[106.7003.30].
Notice: token 1's output is pure V1 — it had no choice but itself. That is the causal constraint made visible.
No mask: every position attends to all T positions ⇒T×T=T2 edges.
Causal mask: position i attends to i positions, so
∑i=1Ti=2T(T+1).
Ratio (causal / full) =T2T(T+1)/2=2TT+1.
For T=1000: 20001001=0.5005. So masking keeps just over half the edges — the lower triangle plus the diagonal.
Insight: the cost is still Θ(T2); masking halves the count but not the asymptotic order. This is why long-context attention is expensive with or without a mask.
Recall Solution
The masked cell's weight after softmax is
row sume−109.e−109 underflows to 0.0 in floating point, so numerically it is identical to −∞ for any realistic real score s (typically ∣s∣<100). The difference only ever appears in pathological arithmetic where a genuine score also reaches ∼−109; that never happens for scaled dot products. So −109 is a safe, common implementation choice — and it avoids the −∞−∞=NaN hazard that can occur if an entire row is masked.
Recall Solution
Softmax computes e−∞+…e−∞=00=NaN (not-a-number). A single NaN propagates through the whole network and poisons the loss.
Cure: never mask a query's own diagonal (a token always keeps itself — see L5.2), or drop fully-padded query rows before softmax, or use the −109 trick from L3.2 which yields a uniform-but-finite row instead of NaN. This is the degenerate case masking must survive.
At step t we have only ever created keys/values for positions 1..t; positions t+1..T do not exist yet, so there is physically nothing future to attend to. The cache holds exactly K1:t,V1:t. Softmaxing qt over these t keys is identical to row t of the masked training matrix (the survivors are {1,…,t}). So the mask is "baked into" the geometry of incremental decoding — we get causality for free. This is why KV Caching and causal masking are consistent: training with the lower-triangular mask makes each row match what inference will later see.
Recall Solution
Teacher Forcing hands the model the whole ground-truth sequence at once so all T predictions can be computed in one parallel pass (fast). But parallelism means every position could peek at every other — including the answer sitting one slot to the right. The causal mask restores the promise "position i only sees 1..i", so the parallel computation produces exactly the same conditioning P(yi∣y<i) that the slow, one-at-a-time inference of L4.1 uses. Mask = the bridge that lets us train fast but behave sequentially.
Recall Solution
(a) Decoder self-attention — masked. The decoder is generating autoregressively, so it must not see its own future tokens.
(b) Cross-attention — not masked. The encoder has already read the entire source sentence (it is the input, fully available), so a decoder token is allowed to attend to all encoder positions. Blocking future source positions would throw away legitimately available information. Rule of thumb: mask only where a "future" would be cheating — that is only inside the decoder's self-attention.
Because each Vj is 10 on a distinct axis, the weighted sum out3=α1V1+α2V2+α3V3 separates coordinate-by-coordinate:
out3=[10α1,10α2,10α3].
Matching to [3,5,2]: 10α1=3,10α2=5,10α3=2⇒α=[0.3,0.5,0.2].
Sanity checks: all ≥0 ✅, sum =1.0 ✅ (a valid softmax distribution), no future column (there is none for i=3=T) ✅.
Recall Solution
For i=1 the allowed set becomes {1,…,0}=∅ — empty (there is no position 0 in our 1-based numbering). The score row is entirely −∞, and (L3.3) softmax returns 0/0=NaN. The first token would have nothing to attend to, poisoning training instantly. This is precisely why the correct mask keeps the diagonal (j≤i): every position, including the first, always has at least itself to attend to.
Recall Solution
The output is outi=∑j=1TαijVj with
αij=∑k=1Texp(sik+Mik)exp(sij+Mij).
For j>i, Mij=−∞⇒exp(sij−∞)=0⇒αij=0. Therefore
outi=∑j=1iαijVj+∑j=i+1T0⋅Vj=∑j=1iαijVj.
No Vj with j>i appears. Its coefficient is exactly 0, independent of the value sij. Causality is guaranteed for every row, every quadrant of the matrix — the future has zero influence by construction. ■
Recall Quick self-quiz (cover the answers)
What triangle of the mask holds −∞? ::: The strictly-upper triangle (j>i), the future.
Why mask before softmax, not after? ::: So weights renormalize over survivors and still sum to 1.
Why keep the diagonal unmasked? ::: A token must attend to at least itself; masking it gives an empty row and NaN for i=1.
Why divide scores by dk? ::: A dot product of dk-long vectors grows like dk; dividing keeps softmax out of the vanishing-gradient spike.
Does cross-attention in an encoder-decoder get a causal mask? ::: No — the full source is available; only decoder self-attention is masked.
Fraction of edges kept for large T? ::: About one half, 2TT+1→21.