Transformer anatomy
Embeddings, multi-head attention, feed-forward blocks, residual and layer norm, and the token pipeline.
Why this matters
The 2017 Transformer paper is the most-cited deep learning paper of all time. Every LLM you use β GPT-4o, Claude 4, Gemini 2.5, Llama 4, Qwen 3 β is a direct descendant. If you can sketch the block diagram of a Transformer on a whiteboard, label every shape, and explain each component, you can pass 70% of senior ML interviews. Today's lesson gives you that fluency.
Learning objectives
- Sketch a decoder-only Transformer block from memory.
- Distinguish encoder-only, decoder-only, and encoder-decoder variants.
- Read shapes through every layer.
- Explain residuals, normalisation, and the FFN.
- Identify which parts are the same across all modern LLMs and which differ.
1. The three families
Encoder-only (BERT, RoBERTa, DeBERTa, ModernBERT)
- Bidirectional attention (every token sees every other).
- Trained with masked-language-modelling (MLM): mask 15% of tokens, predict them.
- Used for understanding tasks: classification, retrieval embeddings, NER.
- Not used for generation.
Decoder-only (GPT-2/3/4, Llama, Mistral, Qwen, DeepSeek, Claude)
- Causal attention β each token only sees the past.
- Trained with next-token prediction (autoregressive LM).
- The default LLM architecture since 2020.
- Same model can do generation, classification (read final hidden state), embeddings (with pooling).
Encoder-decoder (T5, BART, mT5, FLAN-T5, Whisper)
- Encoder reads input bidirectionally; decoder generates output causally; decoder uses cross-attention to look at encoder outputs.
- Strong on translation, summarisation, ASR.
- More parameters and complexity than decoder-only β has fallen out of fashion for general chat models.
For the rest of this course we focus on decoder-only, since that's >95% of the field today. Lessons referencing encoder-decoder will say so explicitly.
2. The Transformer block (decoder-only, pre-norm)
ββββββββββββββββββββββββββββ
x ββββββββββββββΊ β RMSNorm β
β Multi-head self-attention β
β (causal mask, RoPE) β
ββββββββββββ¬βββββββββββββββββ
β
residual + (add)
β
ββββββββββββΌβββββββββββββββββ
β RMSNorm β
β FFN (SwiGLU) β
ββββββββββββ¬βββββββββββββββββ
β
residual + (add)
βΌ
yIn code (modern flavour):
class Block(nn.Module):
def __init__(self, d, h, mlp_mult=4):
super().__init__()
self.norm1 = RMSNorm(d)
self.attn = MultiHeadAttention(d, h, causal=True)
self.norm2 = RMSNorm(d)
self.mlp = SwiGLU(d, mlp_mult * d)
def forward(self, x):
x = x + self.attn(self.norm1(x))
x = x + self.mlp (self.norm2(x))
return xThat is the block. The whole Transformer is L of these stacked.
3. The full forward pass β step by step
Inputs: tokens of shape (B, T).
1. emb = token_embedding(tokens) # (B, T, d)
2. emb = emb + positional_encoding # if absolute pos enc; for RoPE this happens inside attention
3. for block in blocks:
x = block(x) # (B, T, d) -> (B, T, d)
4. x = final_norm(x) # RMSNorm
5. logits = x @ token_embedding.weight.T # (B, T, V) β weight tyingNote weight tying: the output projection often shares weights with the input embedding. Saves parameters and (mildly) improves perplexity. Used by GPT-2, Llama-3, Mistral.
4. The FFN β bigger than you think
For each token independently:
ffn(x) = W2 (act(W1 x)) # vanilla
ffn(x) = W2 ((SiLU(W1 x)) * (W3 x)) # SwiGLUThe hidden dim is typically 4 Γ d_model (vanilla) or ~2.7 Γ d_model (SwiGLU, tuned to keep parameter count similar).
Surprising fact: the FFN holds ~2/3 of all transformer parameters. Attention is glamorous; the FFN is where the model stores its knowledge. Recent interpretability work (Phase 7) shows that factual associations live in the FFN's down-projection.
5. Sizes you should know by heart
| Model | Layers L | d_model | Heads h | FFN dim | Params |
|---|---|---|---|---|---|
| GPT-2 small | 12 | 768 | 12 | 3072 | 124M |
| GPT-2 XL | 48 | 1600 | 25 | 6400 | 1.5B |
| Llama-3 8B | 32 | 4096 | 32 | 14336 (SwiGLU) | 8B |
| Llama-3 70B | 80 | 8192 | 64 | 28672 | 70B |
| Llama-3 405B | 126 | 16384 | 128 | 53248 | 405B |
| GPT-4 (rumoured MoE) | ~120 | ~14336 | 128 | MoE | ~1.8T total / ~280B active |
You will memorise GPT-2 / Llama 8B as your default mental models.
6. What every modern model has changed (vs the 2017 paper)
| Component | 2017 original | 2026 default |
|---|---|---|
| Norm | LayerNorm | RMSNorm |
| Norm position | Post-norm | Pre-norm (some Sandwich) |
| Activation | ReLU FFN | SwiGLU |
| Positional | Sinusoidal | RoPE (some YaRN/NTK extensions) |
| Attention | MHA | GQA (rarely full MHA, never MQA except inference tricks) |
| Bias terms | Yes | No (Llama, Mistral); slight regularisation effect |
| Tokenizer | Word-piece | Byte-level BPE / SentencePiece |
| Mixed precision | fp32 | bf16 training, fp8 at scale |
You will study each of these in the next lessons.
7. Encoder-decoder details (so you can read T5/Whisper papers)
ENCODER block:
x = x + SelfAttn(LN(x), bidirectional)
x = x + FFN(LN(x))
DECODER block:
y = y + SelfAttn(LN(y), causal)
y = y + CrossAttn(LN(y), encoder_output) # NEW
y = y + FFN(LN(y))In cross-attention, Q comes from y, K, V come from the encoder's final output. That is the "looking at the source" mechanic Bahdanau introduced.
T5 also uses relative position biases instead of absolute encodings β old but still relevant.
8. Putting numbers to it (parameter count)
For a decoder-only transformer with vocab V, L layers, hidden d, heads h, FFN multiplier m:
- Embedding (weight-tied):
V * d - Per block: attention
4 d^2(Q, K, V, O projections, no bias) + FFN~3 m d^2(with SwiGLU) - Total:
V*d + L * (4 d^2 + 3 m d^2)βL * (4 + 3m) d^2 + V*d
Llama-3 8B: L=32, d=4096, m=3.5 β 32 * (4 + 10.5) * 4096^2 β 7.78B plus 0.5B for embeddings β 8B. Sanity-checks the model card.
This is the formula that powers napkin-math during interviews.
Hands-on lab (3 hours)
anatomy_drills.ipynb:
- Print the structure of
gpt2from HuggingFace:for n, m in model.named_modules(): print(n). Identify embedding, blocks, FFN, attention, final norm. - Compute parameter count by component. Verify you get 124M for
gpt2-small. - Inspect a single block's
c_attn(combined QKV) shape. Reshape it to(d, 3, d)and verify. - Forward-pass a batch of 4 sequences of length 16 through
gpt2. Print outputlast_hidden_stateshape andlogitsshape. - Replace
torch.nn.LayerNormwith a customRMSNorm. Verify forward outputs are similar (not identical). - Bonus: compute napkin-FLOPs per token of a 32-layer, 4096-d transformer. (Hint: ~6 Γ params per token forward+backward.)
Common pitfalls
- Confusing encoder-only (BERT) with decoder-only (GPT) β they share the block but differ in attention pattern (and training objective).
- Forgetting weight tying β your model has 2Γ the params it should.
- Treating attention as the whole story β most params live in the FFN.
- Mixing pre-norm and post-norm conventions β silently miss-trains.
- Not zero-initializing the output projection of attention/FFN to stabilise early training (a popular trick from Megatron and GPT-J).
Self-check
- Sketch a decoder-only transformer block and label every component.
- Where does most of the parameter count live in a transformer?
- Why use pre-norm instead of post-norm?
- What is weight tying?
- Difference between MHA, GQA, and MQA (preview β we will go deep next lesson).
References
- Vaswani et al. (2017), "Attention Is All You Need."
- Radford et al. (2019), "Language Models Are Unsupervised Multitask Learners" (GPT-2).
- Touvron et al. (2024), "The Llama 3 Herd of Models" β the architecture appendix is gold.
- Karpathy, nanoGPT β the canonical clean implementation.
- HuggingFace
transformerssource β readmodeling_llama.py.
Sign in to save your progress and earn badges.