Cheat sheets

Transformers and Attention

Compute attention, mask it, split heads, add positions and stack the block. PyTorch and NumPy from the lessons.

  • Concepts and terms
  • Tasks and commands
  • Annotated code
  • Diagrams
  • Defaults and numbers
  • Troubleshooting
  • Questions and tips

Source

One pre-LN decoder block#

x 12Input embeddingtoken + position, B, T, DLayerNorm 1Multi head causal attentionQ, K, V and maskAdd residualx + attn(ln1(x))LayerNorm 2Position wise MLPD to 4D to DAdd residualx + mlp(ln2(x))Final LayerNorm, LM headlogits B, T, 50257x 12Inputembeddingtoken +position,B, T, DLayerNorm1MultiheadcausalattentionQ, K, Vand maskAddresidualx +attn(ln1(x))LayerNorm2PositionwiseMLPD to 4Dto DAddresidualx +mlp(ln2(x))FinalLayerNorm,LMheadlogitsB, T,50257

From Lesson 19.34, 19.34 code/main.py, Lesson 19.35

Attention forward#

1qkv_projone linear layer maps (B, T, D) to (B, T, 3 *D).2Splitchunk into Q, K, V, then reshape to (B, H, T,d_head).3Scoresq @ k.transpose(-2, -1) gives (B, H, T, T).4Scaledivide the scores by sqrt(d_head).5Mask and softmaxfuture entries get -inf, then softmax over keys.6Weighted sumweights @ V, then merge heads to (B, T, D).7out_proja (D, D) linear layer mixes the heads.

From Lesson 19.33, 19.33 code/main.py

Shapes#

TensorShape
Token ids(B, T), int64
Token embedding matrix(V, D)
Input to attention(B, T, D)
QKV projection output(B, T, 3 * D)
Q, K, V per head(B, H, T, d_head), d_head = D // H
Scores and weights(B, H, T, T)
Logits(B, T, 50257) for GPT-2 small
Cross attention weights(batch, T_dec, T_enc)

From Lesson 19.32, Lesson 19.33, Lesson 19.35, Lesson 05.10

Key formulas#

Attention
\text{Attention}(Q, K, V) = \mathop{\mathrm{softmax}}\left( \frac{Q K^T}{\sqrt{d_k}} \right) V
Head width
d_{\text{head}} = d_{\text{model}} / n_{\text{heads}}
Causal mask
M_{ij} = \begin{cases} 0 & \text{if } j \le i \\ -\infty & \text{if } j > i \end{cases}
Sinusoidal even
PE_{(pos, 2i)} = \sin\left(pos / 10000^{2i / d_{\text{model}}}\right)
Sinusoidal odd
PE_{(pos, 2i+1)} = \cos\left(pos / 10000^{2i / d_{\text{model}}}\right)
RoPE angle
\theta_i = \text{base}^{-2i / d_{\text{head}}}. Score depends on (m - n).
ALiBi
\text{attn\_score}[i, j] = \frac{q_i \cdot k_j}{\sqrt{d}} - m_h \cdot |i - j|
Stable softmax
\mathop{\mathrm{softmax}}(x - c) = \mathop{\mathrm{softmax}}(x)

From Lesson 07.02, Lesson 07.03, Lesson 07.04, Lesson 07.07, Lesson 10.04

Tasks in code#

TaskCode
Apply causal maskscores.masked_fill(mask_slice == 0, float("-inf"))
Softmax over keysF.softmax(scores, dim=-1)
Merge headsx.transpose(1, 2).contiguous().view(b, t, h * dh)
Add positionsx = tok + pos.unsqueeze(0)
Fill sinusoidal tablepe[:, 0::2] = torch.sin(angle)
Rotate Q and Kapply_rope(x, pos, base=10000)
Fused kernel with GQAscaled_dot_product_attention(q, k, v, is_causal=True, enable_gqa=True)
Tie LM headself.lm_head.weight = self.tok_embed.weight

From 19.33 code/main.py, 19.32 code/main.py, Lesson 07.03, Lesson 07.04, 19.35 code/main.py

MultiHeadSelfAttention forward#

self.qkv_proj = nn.Linear(d_model, 3 * d_model, bias=True)Q, K, V in one matmul
causal_mask = torch.tril(torch.ones(max_context_length, max_context_length))
self.register_buffer("causal_mask", causal_mask, persistent=False)no grad
qkv = self.qkv_proj(x)(B, T, 3D)
q, k, v = qkv.chunk(3, dim=-1)three (B, T, D) views
q = self._split_heads(q)(B, H, T, d_head)
k = self._split_heads(k)
v = self._split_heads(v)
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_head)
mask_slice = self.causal_mask[:t, :t]top left (T, T)
scores = scores.masked_fill(mask_slice == 0, float("-inf"))
weights = F.softmax(scores, dim=-1)rows sum to 1
context = self._merge_heads(torch.matmul(weights, v))(B, T, D)
out = self.out_proj(context)mixes the heads

From 19.33 code/main.py

Defaults and numbers#

SettingDefaultNote
Sinusoidal and RoPE base10000raise RoPE base for longer context
mlp_expansion4MLP is D to 4D to D
d_head6464 or 128 in production
Init std0.02Linear and Embedding weights
Residual projection scale1 / sqrt(2 * num_layers)out_proj and fc2
GPT-2 small d_model76812 heads, 12 layers, about 124M
KV bytes per token per layer2 * d_head * dtype_sizeper head, K and V
Compute optimal ratio20tokens per parameter

From Lesson 07.04, 19.34 code/main.py, Lesson 07.03, 19.35 code/main.py, Lesson 19.35, Lesson 07.12, Lesson 07.13

Troubleshooting#

SymptomCauseFix
ValueError on d_model and n_headsD is not divisible by Hpick H that divides D
seq_len exceeds max_context_lengthT is larger than the maskcrop to the last context_length tokens
Model cheats on next tokenno causal maskmask the upper triangle with -inf
Softmax gives infexp of a large numbersubtract the row max first
Learning stallsscores not scaleddivide by sqrt(d_head)
Deep stack divergespost-LN on the residual pathuse pre-LN
Weight tying saves nothingweights copied, not sharedassign the same parameter
Bad output past trained lengthpositions do not extrapolateuse RoPE with base scaling

From 19.33 code/main.py, Lesson 19.33, Lesson 19.34, Lesson 19.35, Lesson 10.04, Lesson 05.10, Lesson 07.04

Abbreviations#

MHA
Multi-head attention. N query heads, N K/V heads.
MQA
Multi-Query Attention. One shared K/V projection.
GQA
Grouped-Query Attention. Fewer K/V heads, repeated to match Q.
MLA
Multi-head Latent Attention. K and V in a low rank latent.
RoPE
Rotary Position Embeddings. Rotates Q and K by position.
ALiBi
Attention with Linear Biases. Distance penalty on scores.
RMSNorm
LayerNorm without the mean.
KV cache
stored K and V of every prefix token.

From Lesson 07.03, Lesson 07.04, Lesson 07.05, Lesson 07.12

Questions#

Why divide by sqrt(d_k)?
Large dot products saturate softmax and gradients vanish.
Why -inf in the mask?
exp(-inf) = 0, so future positions get zero weight.
Why add positions?
Attention alone is permutation invariant.
Why GQA?
It cuts KV cache memory by N/G with nearly full quality.
Is Flash Attention approximate?
No. A running (max, sum) keeps it exact.

From Lesson 07.02, Lesson 07.07, Lesson 07.04, Lesson 07.03, Lesson 07.12

Tips#

  • Test that weight rows sum to 1.
  • Test that future tokens do not change past outputs.
  • Expect a loss near log(V) at random init.
  • Count parameters against the reference model.
  • Put dropout after the softmax and after the second MLP linear.
  • Do not use attention weights as proof of reasoning.

From Lesson 19.33, 19.33 code/tests/test_attention.py, Lesson 19.34, Lesson 19.35, Lesson 05.10