At which step in the attention pipeline does the mask actually take effect?
The mask is added to the scaled score matrix just before softmax; masked positions get -inf, which softmax converts to exactly 0 weight.
Imagine a vote where every candidate's score gets converted into a vote percentage by a rule that exponentiates first. To truly disqualify a candidate, you have to push their score down to 'negative infinity bad', which is the only score the rule converts to zero votes. A score of zero, surprisingly, still gives them about one vote out of N. So the disqualification has to happen at the right step (before counting) using the right value (negative infinity), or the disqualified candidate still ends up with influence.
Detailed answer & concept explanation~6 min readEverything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
4m: why the mask must precede softmax, additive vs multiplicative masking, why -inf is the correct value, dtype-safe mask values, how causal and padding masks differ, and how FlashAttention fuses masking with softmax.
import torch
import math
B, H, T, D = 1, 1, 4, 8
q = torch.randn(B, H, T, D)
k = torch.randn(B, H, T, D)
v = torch.randn(B, H, T, D)
# Build causal mask: 1 at allowed (i >= j), 0 at forbidden
causal_mask = torch.tril(torch.ones(T, T))
# Compute scaled scores
scores = (q @ k.transpose(-2, -1)) / math.sqrt(D)
# Apply mask BEFORE softmax with -inf at forbidden positions
scores = scores.masked_fill(causal_mask == 0, float('-inf'))
# Softmax produces a valid distribution over unmasked positions
weights = torch.softmax(scores, dim=-1)
# Verify: row 0 attends only to col 0
assert weights[..., 0, 1:].sum().item() == 0.0
# Output
out = weights @ vReal products, models, and research that use this idea.
- PyTorch torch.nn.functional.scaled_dot_product_attention with is_causal=True applies the additive -inf mask internally before softmax.
- FlashAttention 2 and 3 fuse the mask application with the softmax computation, never materializing the full mask matrix.
- HuggingFace transformers uses torch.finfo(dtype).min as the mask value for dtype safety across fp16/bf16/fp32.
- Karpathy's nanoGPT uses the canonical pattern: scores.masked_fill(causal_mask == 0, float('-inf')) followed by softmax.
- vLLM and SGLang's attention kernels handle padding masks per-request and causal masks synthesized in-kernel for efficiency.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow would you detect a misplaced mask in production?
QWhy does FlashAttention support is_causal=True as a kernel-level flag instead of always passing a mask tensor?
Don't say thisRed flags and common mistakes that signal junior thinking. Click to expand.
Red flags and common mistakes that signal junior thinking. Click to expand.
Applying the mask after softmax by zeroing weights. This breaks the row sum to 1 invariant and requires extra renormalization that fused kernels don't do for you.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.
Same topic, related formats. Practice these next.