AI / ML Theory
Chapter 6 · 14 min

6.5 — Causal (autoregressive) masking

Goal: Add the causal mask to the attention computation: a triangular pattern of −∞ scores that forbids any query from attending to keys at future positions. Understand how this preserves the autoregressive factorisation while letting the forward pass remain fully parallel.

1. Why this session exists

The language-modelling objective of 5.3 requires the prediction at position tt to depend only on tokens x<tx_{<t}. Self-attention as written in 6.4 violates this: every query position ii attends to every key position j∈{1,…,T}j \in \{1, \dots, T\}, including future tokens.

The fix is the causal mask: a one-line modification of the score matrix that sets attention to future positions to exactly zero, while leaving the attention to past positions free. The mask is what lets a decoder-only Transformer be trained in parallel on all TT positions of a sequence from a single forward pass — the trick that makes Transformer pretraining feasible at LLM scale.

This session does two things:

  1. Define the causal mask and write it inside the scaled dot-product formula.
  2. Explain why we add to the pre-softmax scores rather than zeroing out post-softmax weights.[1, 2]

2. The causal constraint

For an autoregressive language model, the conditional distribution pθ(xt∣x<t)p_\theta(x_t \mid x_{<t}) must be a function of x1,…,xt−1x_1, \dots, x_{t-1} only. If a self-attention layer at position tt attended to xTx_T for some T>tT > t, then pθ(xt∣… )p_\theta(x_t \mid \dots) would depend on future tokens — the factorisation would be invalid, and at inference time we would not be able to use the model autoregressively.

Concretely: for query position ii, the attention weight AijA_{ij} on key position jj must satisfy

 Aij=0whenever j>i. \boxed{\ A_{ij} = 0 \quad \text{whenever } j > i. \ }

This is the causal mask condition. The output at position ii is then a convex combination of values at positions 1,…,i1, \dots, i — never anything from the future.


3. The mask matrix and where to put it

Define the mask M∈RT×TM \in \mathbb{R}^{T \times T}:

Mij={0if j≤i,−∞if j>i.M_{ij} = \begin{cases} 0 & \text{if } j \le i, \\ -\infty & \text{if } j > i. \end{cases}

Pictorially, MM is lower triangular with zeros on and below the diagonal and −∞-\infty above:

M=(0−∞−∞−∞00−∞−∞000−∞0000)M = \begin{pmatrix} 0 & -\infty & -\infty & -\infty \\ 0 & 0 & -\infty & -\infty \\ 0 & 0 & 0 & -\infty \\ 0 & 0 & 0 & 0 \end{pmatrix}

(here T=4T = 4). Add MM to the pre-softmax scores S=QK⊤/dkS = Q K^\top / \sqrt{d_k}:

 Attncausal(Q,K,V)=softmax ⁣(QK⊤dk+M)V. \boxed{\ \text{Attn}_\text{causal}(Q, K, V) = \text{softmax}\!\left( \frac{Q K^\top}{\sqrt{d_k}} + M \right) V. \ }

The exponential of −∞-\infty is exactly 00, so any entry of the softmax with a −∞-\infty score is zero in the output. The remaining entries are renormalised so that each row sums to 11 — automatically, by the softmax denominator being computed only over the unmasked positions.

In code, −∞-\infty is replaced by a very large negative number (e.g. −1010-10^{10}, or the dtype's most-negative finite value). The result is numerically indistinguishable from −∞-\infty after softmax.


4. Pre-softmax vs post-softmax masking

A naive alternative would be to zero out the post-softmax weights:

Aijnaive={Aijunmaskedif j≤i,0if j>i.A_{ij}^\text{naive} = \begin{cases} A_{ij}^\text{unmasked} & \text{if } j \le i, \\ 0 & \text{if } j > i. \end{cases}

This is wrong. Two reasons:

(a) The rows no longer sum to 1. The softmax denominator was computed over all TT positions; removing some entries afterwards breaks normalisation. The output is now a fraction of a convex combination, scaled by the total weight that survived the mask. Different rows scale differently. The attention sub-layer's output magnitude varies wildly with the mask, which makes training unstable.

(b) Gradients still flow to masked positions. If AijunmaskedA_{ij}^\text{unmasked} depended on QQ and KK at masked positions through the softmax denominator, then ∂Aij′/∂Kj\partial A_{ij'} / \partial K_j for j>ij > i is non-zero — gradient leaks to the future. Pre-softmax masking with −∞-\infty stops gradient flow because ∂exp⁡(−∞+ϵ)/∂ϵ=0\partial \exp(-\infty + \epsilon) / \partial \epsilon = 0.

Both problems vanish when we add −∞-\infty inside the softmax: the denominator is computed only over unmasked positions, the rows sum to 1 by construction, and the gradients to masked positions are exactly zero.

This is the structural reason every Transformer implementation uses pre-softmax additive masking. Anywhere you see "boolean mask," what's actually being done is "replace masked-out scores by a large negative number before softmax."


5. Training-time parallelism

The payoff of pre-softmax masking is the one that motivates the whole design.

Suppose you have a document x1,x2,…,xTx_1, x_2, \dots, x_T and you want to train the model to predict xtx_t from x<tx_{<t} at every position tt. With an RNN (5.5), this required TT sequential steps of the forward pass. With a Transformer using causal masking, it requires one forward pass:

  1. Embed the input sequence: X∈RT×dX \in \mathbb{R}^{T \times d}.
  2. Apply self-attention with the causal mask. Each row ii of the output is a function of x≤ix_{\le i} only — guaranteed by the mask even though all rows were computed in parallel.
  3. Apply the rest of the block (FFN, residuals, norms), all of which are per-position operations (6.8) that automatically preserve causality.
  4. Compute the per-position logits and the per-position cross-entropy loss (5.3 §4).

In one pass: TT supervised next-token predictions, TT corresponding losses, a single backward pass that updates the parameters using all TT gradient signals.

This is the trick. RNNs cannot do this — the sequential dependency in the recurrence forbids it. Self-attention can, because the causal mask makes parallel computation safe.

5.1 Inference is still sequential

The training parallelism does not extend to inference. To generate token xt+1x_{t+1} given x≤tx_{\le t}, we need to feed the model the current prefix, compute the logits at position tt, sample, append, and repeat. The mask is the same at inference — it just doesn't help: we are computing one new attention row per generation step, and we have to do them in order.

The mismatch is the source of the KV cache trick we will see in 6.14: at inference, the keys and values at positions 1,…,t−11, \dots, t-1 never change once computed, so we cache them and only compute the new kt,vt,qtk_t, v_t, q_t at each step. Training fills the entire T×dT \times d buffer in one pass; inference incrementally appends rows.


6. Other masks: padding, sliding window, prefix-LM

The causal mask is the canonical one, but the additive-MM machinery handles any pattern of "this query may not attend to this key":

  • Padding masks. When sequences in a batch have different lengths, the shorter ones are padded. The pad tokens must not contribute to attention. Add −∞-\infty to the columns of the score matrix corresponding to padded keys.
  • Sliding-window mask (Mistral, Longformer). Each query can attend only to a fixed window of ww past tokens. The mask is a "thick diagonal" — block all (i,j)(i, j) pairs with j<i−wj < i - w or j>ij > i. Cuts attention cost from O(T2)O(T^2) to O(Tw)O(T w). Detailed in 6.13 (Part 2).
  • Prefix-LM mask. The first kk tokens of the sequence (the "prefix") attend bidirectionally; positions after kk attend causally. The mask is a block matrix: full ones in the prefix block, lower triangular in the autoregressive block. Used by T5 and some instruction-tuned variants.
  • Cross-attention masks. When attending between two sequences, masks can express "this target token may attend to these source tokens." Used in encoder-decoder Transformers.

All of these are additive masks applied pre-softmax, like the causal one. The only thing that varies is the pattern of zeros vs −∞-\infty.


7. What we keep going forward

The causal mask is the single line of code that turns a self-attention layer into an autoregressive one:

Attncausal(Q,K,V)=softmax ⁣(QK⊤dk+M)V,Mij={0j≤i−∞j>i.\text{Attn}_\text{causal}(Q, K, V) = \text{softmax}\!\left( \frac{Q K^\top}{\sqrt{d_k}} + M \right) V, \qquad M_{ij} = \begin{cases} 0 & j \le i \\ -\infty & j > i \end{cases}.

From this point on, "self-attention in a decoder-only LLM" means this masked form. Everything else — multi-head (6.4), positional encoding (6.6, 6.7), FFN (6.8), residuals and norms (Part 2) — operates under the causal-mask assumption.

Self-test

(a) Show that applying softmax row-wise to S+MS + M (with MM the causal mask) gives a lower-triangular attention matrix AA whose every row sums to 1. Verify on a 4×44 \times 4 example by hand.

(b) Show that ∂Aij/∂Skj\partial A_{ij} / \partial S_{kj} for j>ij > i is exactly zero. (Use exp⁡(−∞)=0\exp(-\infty) = 0 and the softmax formula.) Why does this mean "no gradient flows to future-position keys"?

(c) Suppose you implement causal masking by zeroing out the post-softmax weights for j>ij > i. Construct a small example showing that the rows no longer sum to 1, and explain why this breaks the convex-combination interpretation of attention.

(d) A document of length T=2048T = 2048 is trained with the causal-attention objective. How many supervised next-token predictions are made from a single forward + backward pass? Compare with an RNN trained on the same document.

(e) Construct the additive mask MM for a sliding-window attention with window w=4w = 4 and sequence length T=8T = 8. Confirm that the mask is sparse (most entries are −∞-\infty) and that attention cost drops from O(T2)=64O(T^2) = 64 entries to O(Tw)=32O(T w) = 32 entries.


References

  1. [1]

    Raschka, S. (2024). Build a Large Language Model (From Scratch), Chapters 3–4. Manning. — Self-attention implementation (6.3, 6.4), causal masking (6.5), pre-norm block (6.10–6.11).

    ↩
  2. [2]

    Radford, A., Narasimhan, K., Salimans, T., & Sutskever, I. (2018). "Improving language understanding by generative pre-training" (GPT-1). OpenAI technical report. — Decoder-only Transformer with causal self-attention (6.3, 6.5); first use of learned PE (Appendix 6.B).

    ↩