- Key idea
- Add −∞ above the diagonal of the score matrix: softmax turns it into weight 0, so position i only reads positions 1 to i
- Real world
- A teacher gives a class a story one line at a time, with a card covering the rest of the page, and asks after each line: “what word comes next?”. Everyone answers for every line, but nobody can read ahead.
- In an LLM
- GPT-2 reads “The cat sat on the mat because it was tired.” in one pass and makes 11 predictions, one per position. Thanks to the mask, the prediction made at “sat” was computed without seeing “on”, so it is a fair test.
The rule
A language model generates text left to right. When it predicts token 6, tokens 7, 8 and 9 don’t exist yet. So when it learns, it must learn under the same conditions: each position may only use the tokens up to and including itself.
Attention is the only place where tokens read from each other, so that is where the rule is enforced. Before the softmax, every score where the key comes after the query is replaced by :
Since , those keys get a weight of exactly zero, and the remaining weights in the row still add up to 1. The allowed cells form a lower triangle. This is the : “causal” because each token can only be influenced by what came before it, like causes before effects.
Switch to bidirectional and every word can read every other. The weights change: in our toy, “cat” now looks ahead to “sat”. That is the attention of BERT and other encoder models, built to understand a finished text. GPT, Llama, Claude and every other text generator are decoder models, and use the causal mask.
Reading a mystery novel, you can guess the culprit at the end of each chapter using everything you have read so far. If you had flicked to the last page first, your guesses would be perfect, and you would have learned nothing about spotting clues. The card that covers the rest of the page is the causal mask.
Training on every position at once
The mask has a second, less obvious benefit. Because no row can see its own answer, every row can be a separate training example, all in the same forward pass.
Feed in a text of tokens. Position 1 predicts token 2, position 2 predicts token 3, and so on: predictions to check against tokens you already have, and one more at the end that predicts what comes next. One big matrix multiplication computes all the rows at once on a GPU.
Tick remove the mask and the problem is obvious: every row can now read the token it is meant to predict. A model trained like that learns to copy the next token, its training loss drops to nearly zero, and it is useless the moment it has to write something new.
GPT-2 reading “The cat sat on the mat because it was tired.” (11 tokens) produces 11 probability distributions in a single pass. At “sat” it gives “ on” 22%; at “on” it gives “ the” 45%; at “the” its top guess is “ floor” and the real “ mat” isn’t in its top 5. In training, all ten of those checkable guesses contribute to the loss at once. Its top guess is right at 5 of the 10.
Generation reuses the past
During generation the model adds one token at a time. Thanks to the mask, the rows for the earlier tokens never change when a new token is appended: they couldn’t see it. So their keys and values can be kept from the previous step instead of recomputed. That saved work is the KV cache, and it only works because attention is causal.
Other masks
The mask is just a matrix of 0s and s, so it can have other shapes. Each shape is a different rule about who may read whom:
- Bidirectional (no mask): encoders such as BERT, and image models, where there is no “future”.
- Padding: when sentences of different lengths share a batch, the short ones are filled with padding tokens, and a padding mask hides them. It is combined with the causal mask in a decoder.
- Prefix-LM: the prompt is read in both directions, the answer is generated causally.
- Sliding window: each token sees only the last tokens, so cost grows with instead of the whole length. See efficient attention.
Why −∞ and not 0
Setting a weight to 0 after the softmax would leave the row summing to less than 1. Adding to the score before the softmax removes the key from both the numerator and the denominator, so the remaining weights are renormalised automatically:
In code a large negative number (or the dtype’s minimum) is used instead of a true , to avoid NaN from rows that are entirely masked.
Training objective
With the causal mask, one forward pass over gives for every , and the loss is the average over positions:
This is the chain rule of probability, evaluated in parallel. It is often called teacher forcing: the model always conditions on the true previous tokens, not its own guesses.
In code
T = x.shape[1]
causal = torch.ones(T, T, dtype=torch.bool).tril() # True = allowed
pad = attention_mask[:, None, None, :].bool() # (B, 1, 1, T)
allowed = causal & pad
neg = torch.finfo(scores.dtype).min
scores = scores.masked_fill(~allowed, neg)In papers
- Masked decoder self-attention: Vaswani et al., 2017, section 3.2.3.
- Bidirectional encoders: Devlin et al., BERT, 2018.
- Prefix-LM and other mask patterns compared: Raffel et al., T5, 2019, section 3.2.
- Sliding-window attention: Beltagy et al., Longformer, 2020; Jiang et al., Mistral 7B, 2023.