Linear algebra for LLMsPart 5: Attention, one operation at a time
The causal mask
6 min
No looking ahead
A decoder predicts each token from the ones before it, so token may attend to tokens only. During training all positions are computed at once, so the future has to be hidden explicitly.
Write
The mask
0 on and below the diagonal, above: the lower-triangular shape from Part 2. It’s added to the scores, so allowed scores pass unchanged.
Write
Add it to the scaled scores
“the” keeps only its own score; “cat” keeps two; “sat” keeps all three.
Softmax each row
becomes exactly 0. Row “cat”: and , so the weights are and . Every row sums to 1. The shading shows where each query looks.
Write
✏️ Your turn
Which weights?
Which weight in this row is exactly 0? Which of the other two is bigger, and roughly how much bigger?
Work it out on paper, then go to the next card to check.
Answer
The masked one (key “sat”) is exactly 0. “cat” beats “the” by a factor of : a gap of 2.83 in score.
Why not zero the weights afterwards?
Zeroing after the softmax leaves a row that no longer sums to 1: “the” would pass on only a third of its value. Adding before the softmax makes the remaining weights renormalize among themselves.
Write
In PyTorch
mask = torch.ones(T, T, dtype=torch.bool).tril()
scores = scores.masked_fill(~mask, float("-inf"))
weights = scores.softmax(dim=-1)
# or all at once:
out = F.scaled_dot_product_attention(q, k, v, is_causal=True)📌 Recap
The causal mask in one card
- Mask: 0 on and below the diagonal, above, added to the scaled scores.
- Softmax each row: future weights become exactly 0, the rest sum to 1.
- Example rows of : , , .
- Mask before the softmax, not after.
Write