What this is: Training a language model on "The cat sat on the ___" only works if the network cannot peek at the answer while predicting it. Causal (look-ahead) masking enforces that rule. In encoder–decoder models, cross-attention lets each target position read from the source sequence.
Why it matters: Together they explain GPT-style stacks (mask only) and classic Transformer translation (mask + cross-attention).
Think of writing a sentence on a strip of paper covered by a sliding sleeve: you may see everything up to the pen tip, never beyond it. That sleeve is the causal mask.
During training we still process the whole target in parallel for speed, but illegal future links get score -inf before softmax so their weights become zero.
Example: Training sentence "I love neural networks"
When the model runs on word 2 ("love"), the mask forces positions 3 ("neural") and 4 ("networks") to score -inf. After softmax, those weights are exactly 0. The model can only look at "I" and "love" to predict what comes next — it must learn language structure, not copy answers.
The decoder brings questions (queries from the target side). The encoder brings a library of notes (keys and values from the source). Generating the French word for "cat" can attend strongly to the English token "cat" even though decoder self-attention on the French prefix knows nothing about English.
| Model type | What it uses |
|---|---|
| Decoder-only LLM (GPT-style) | Causal self-attention only — prompt and generated tokens in one stream |
| Encoder–decoder (translation, T5) | Causal self-attention + cross-attention to encoder outputs |
For length n, build an n × n mask matrix M where M_ij = 0 if j ≤ i and -inf if j > i:
e_ij = (Q_i * K_j^T) / sqrt(d_k) + M_ij where M_ij = 0 if j ≤ i else -inf
scores = Q * K^T / sqrt(d_k) + M
weights = softmax(scores) # future positions → ~0
output = weights * V
Position i may attend to keys 0..i only. Training can still use teacher forcing: feed the full gold target, predict all next tokens in parallel under the mask — much faster than truly sequential loops.
The mask is a lower triangular matrix — all spaces above the diagonal are blocked.
Let H_enc be encoder outputs (n_src, d) and H_dec the current decoder states (n_tgt, d):
Q = H_dec * W_Q # questions from decoder
K = H_enc * W_K # keys from encoder
V = H_enc * W_V # values from encoder
CrossAttn = softmax(Q * K^T / sqrt(d_k)) * V
No causal mask is needed across the source — the encoder already saw the full input. Masking still applies to decoder self-attention so the target side remains autoregressive.
| Mechanism | Q from | K/V from | Typical role |
|---|---|---|---|
| Decoder self-attention | Target | Target (masked) | Fluency, local target context |
| Cross-attention | Target | Source / encoder | Alignment to input |
| Encoder self-attention | Source | Source (unmasked) | Bidirectional source context |
Padding masks (separate from causality): set scores for pad tokens to -inf so empty slots do not soak probability mass. Production batches always combine padding masks with causal masks on the decoder side.
Apply a causal mask and contrast with unmasked attention on the same scores:
import numpy as np
def softmax(x, axis=-1):
x = x - np.nanmax(x, axis=axis, keepdims=True)
e = np.exp(x)
return e / e.sum(axis=axis, keepdims=True)
rng = np.random.default_rng(3)
n, d = 4, 6
X = rng.normal(size=(n, d))
W_Q = W_K = W_V = np.eye(d)
Q, K, V = X @ W_Q, X @ W_K, X @ W_V
raw = (Q @ K.T) / np.sqrt(d)
causal = np.triu(np.ones((n, n)) * -1e9, k=1) # forbid j > i
masked_weights = softmax(raw + causal, axis=-1)
open_weights = softmax(raw, axis=-1)
print("causal weights (row 1):", np.round(masked_weights[1], 3))
print("open weights (row 1):", np.round(open_weights[1], 3))
# Toy cross-attention: 3 source positions, 2 target queries
H_enc = rng.normal(size=(3, d))
H_dec = rng.normal(size=(2, d))
cross_scores = (H_dec @ H_enc.T) / np.sqrt(d)
cross_w = softmax(cross_scores, axis=-1)
print("cross-attn weights:\n", np.round(cross_w, 3))
Row 1 under the causal mask should put ~0 on columns 2 and 3, while the open matrix can peek ahead — exactly the training cheat masking prevents.
Causal masks stop decoder self-attention from seeing future tokens, while cross-attention lets decoder queries read encoder keys/values for source–target alignment.