Lesson 18 of 23 · Transformer block
Hide the future before probabilities exist.
Your win: build one scaled query-key score row, apply a causal mask, and explain why the mask must precede softmax.
Scores have one row per query
For one head, \(Q'\in\mathbb R^{T\times d_h}\) and \(K'^{\mathsf T}\in\mathbb R^{d_h\times T}\), so \(S\in\mathbb R^{T\times T}\). Row \(i\) asks: “How strongly does query token \(i\) match every key token \(j\)?”
Numbers substituted: one scaled dot product
Let \(d_h=4\), \(\mathbf q'_3=(1,2,0,-1)\), and \(\mathbf k'_2=(2,0,1,1)\). Then
The factor \(1/\sqrt{d_h}\) keeps dot-product magnitudes from growing merely because a head has more coordinates.
The causal mask is triangular
At query position \(i=3\), raw scores \((0.7,1.1,0.2,2.0)\) become \((0.7,1.1,0.2,-\infty)\). The tempting future score \(2.0\) is removed before normalization.
Explore which keys are visible
| Key | Raw score | After mask | Softmax weight | Value |
|---|
Weighted output:
Code checkpoint · mask scores before softmax
The upper triangle represents future key positions. Replacing those scores with \(-\infty\) guarantees zero probability after softmax.
Show score scaling and the causal mask
scores = q @ k.transpose(-2, -1) / math.sqrt(head_dim)
# scores: [B, H, query_token, key_token]
tokens = scores.size(-1)
future = torch.ones(tokens, tokens, dtype=torch.bool, device=x.device)
future = future.triu(diagonal=1)
scores = scores.masked_fill(future, float("-inf"))Trace it: In row 2 of a \(4\times4\) mask, which column receives \(-\infty\)?
Retrieval check
Which keys are visible to query position \(i=2\)?
Practice before moving on
- For \(T=4\), write all four rows of the causal mask using \(0\) and \(-\infty\).
- Mask the row \((3,-1,2,5)\) for query position \(i=2\).
- Compute \(S_{1,2}\) when \(d_h=2\), \(\mathbf q'_1=(2,1)\), and \(\mathbf k'_2=(1,-1)\).
- If \(Q'\) has shape \(6\times8\), give the shapes of \(K'^{\mathsf T}\) and \(S\).
- Why is the mask added before softmax?
- Does causal masking remove a key vector from memory, or only prevent particular query-key connections?
Check solutions
- \((0,-\infty,-\infty,-\infty)\), \((0,0,-\infty,-\infty)\), \((0,0,0,-\infty)\), and \((0,0,0,0)\).
- \((3,-1,-\infty,-\infty)\).
- \(((2)(1)+(1)(-1))/\sqrt2=1/\sqrt2\approx0.707\).
- \(K'^{\mathsf T}:8\times6\); \(S:6\times6\).
- So forbidden entries exponentiate to zero and receive no probability mass.
- Only particular connections are blocked; the key still exists for queries at its own or later positions.
Primary source: Vaswani et al., Sections 3.2.1 and 3.2.3.