Chapter 94 — Transformer Architecture Deep Dive
📖 Definition
A transformer is a neural network built from self-attention and feed-forward layers, with residual connections and normalisation. Every modern LLM (GPT, Claude, LLaMA, Gemini) is a decoder-only transformer trained to predict the next token.
Senior interviews expect you to walk through one forward pass, write attention from memory, and explain the design choices (RoPE, GQA, SwiGLU, RMSNorm).
🔍 One Forward Pass, Step by Step
- Tokenise the text into token ids (
"Hello world"→[15496, 995]). - Embed: look up a vector of size
d_modelfor each id. - Repeat N transformer blocks (e.g. 32–120 layers). Each block:
h = x + Attention(Norm(x))— tokens exchange information.out = h + FeedForward(Norm(h))— each token is processed independently.
- Final norm, then the LM head (a linear layer to vocabulary size) produces one score (logit) per vocabulary token.
- Softmax turns logits into probabilities for the next token; a sampling strategy picks one.
- Append the token and repeat (autoregressive generation).
🔍 Self-Attention in Plain Words
Each token asks a question (Query), advertises what it contains (Key), and carries information (Value). A token's new representation is a weighted average of all Values, weighted by how well its Query matches each Key.
Q = X · Wq K = X · Wk V = X · Wv (X: tokens × d_model)
scores = Q · Kᵀ / sqrt(d_k) tokens × tokens similarity
scores = scores + causal_mask −∞ above the diagonal: no peeking at future tokens
weights = softmax(scores) each row sums to 1
output = weights · VWhy divide by sqrt(d_k)? Dot products grow with dimension; large values push softmax into tiny gradients. Scaling keeps the variance around 1.
Why the causal mask? During training the model sees the whole sentence at once; the mask stops position t from using tokens after t, so it learns to predict the next token honestly.
💻 Code Example — Attention from Scratch (NumPy)
import numpy as np
def softmax(x, axis=-1):
x = x - x.max(axis=axis, keepdims=True) # numerical stability: avoid exp overflow
e = np.exp(x)
return e / e.sum(axis=axis, keepdims=True)
def causal_self_attention(X, Wq, Wk, Wv):
"""X: (T, d_model). Returns (T, d_k)."""
Q, K, V = X @ Wq, X @ Wk, X @ Wv
d_k = Q.shape[-1]
scores = Q @ K.T / np.sqrt(d_k) # (T, T)
T = X.shape[0]
mask = np.triu(np.ones((T, T), dtype=bool), k=1)
scores = np.where(mask, -np.inf, scores) # block the future
return softmax(scores) @ V
rng = np.random.default_rng(0)
T, d_model, d_k = 5, 16, 8
X = rng.normal(size=(T, d_model))
out = causal_self_attention(X, *(rng.normal(size=(d_model, d_k)) for _ in range(3)))
print(out.shape) # (5, 8)💻 Code Example — Multi-Head Attention (PyTorch)
import torch
import torch.nn.functional as F
from torch import nn
class MultiHeadAttention(nn.Module):
def __init__(self, d_model: int, n_heads: int):
super().__init__()
assert d_model % n_heads == 0
self.h, self.d = n_heads, d_model // n_heads
self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
self.out = nn.Linear(d_model, d_model, bias=False)
def forward(self, x): # x: (B, T, d_model)
B, T, C = x.shape
q, k, v = self.qkv(x).split(C, dim=-1)
# (B, T, C) → (B, heads, T, head_dim)
q, k, v = (t.view(B, T, self.h, self.d).transpose(1, 2) for t in (q, k, v))
y = F.scaled_dot_product_attention(q, k, v, is_causal=True) # uses FlashAttention when available
y = y.transpose(1, 2).contiguous().view(B, T, C) # concat heads
return self.out(y)Why multiple heads? Each head can learn a different kind of relation (syntax, coreference, position) in its own sub-space; cost is the same as one big head.
📊 Complexity
| Part | Compute | Memory |
|---|---|---|
| Attention scores | O(T² · d) | O(T²) per head (naive) |
| Feed-forward | O(T · d²) | O(T · d) |
For short sequences the FFN dominates; for long contexts attention's T² dominates — hence FlashAttention, sliding-window attention, and other long-context tricks.
🔍 Positional Information
Attention by itself ignores order ("dog bites man" = "man bites dog"). Options:
| Method | Idea | Used by |
|---|---|---|
| Sinusoidal | Fixed sine/cosine added to embeddings | Original transformer |
| Learned absolute | A trained vector per position | GPT-2, BERT — can't go past trained length |
| RoPE (rotary) | Rotates Q and K by an angle that depends on position, so the dot product depends on relative distance | LLaMA, most modern LLMs |
| ALiBi | Adds a distance-based penalty to attention scores | Some long-context models |
Context extension: RoPE scaling methods (position interpolation, NTK-aware, YaRN) stretch a model trained on 4k tokens to 32k+ with a little fine-tuning.
🔍 Feed-Forward Network (FFN / MLP)
FFN(x) = W2 · act(W1 · x) classic: hidden size ≈ 4 × d_model
SwiGLU(x) = W2 · (swish(W1 · x) ⊙ (W3 · x)) modern: gated, hidden ≈ 8/3 × d_modelThe FFN holds about two-thirds of the parameters and is where much "knowledge" is thought to be stored.
🔍 MHA vs MQA vs GQA (Inference Memory)
| Variant | Key/Value heads | Effect |
|---|---|---|
| Multi-Head (MHA) | One K/V per query head | Best quality, largest KV cache |
| Multi-Query (MQA) | One shared K/V for all heads | Smallest KV cache, some quality loss |
| Grouped-Query (GQA) | Groups of query heads share one K/V | Middle ground — used by LLaMA 2 70B / LLaMA 3 |
The KV cache (Inference chapter) is often the memory bottleneck when serving, so fewer K/V heads = more concurrent users.
📊 Encoder vs Decoder Architectures
| Type | Attention | Trained on | Good for | Examples |
|---|---|---|---|---|
| Encoder-only | Bidirectional (sees all tokens) | Masked-token prediction | Classification, embeddings, NER | BERT, RoBERTa |
| Decoder-only | Causal (left to right) | Next-token prediction | Generation, chat, general LLMs | GPT, Claude, LLaMA |
| Encoder–decoder | Encoder bidirectional + decoder causal with cross-attention | Span corruption / translation | Translation, summarisation | T5, BART, original Transformer |
Why decoder-only won: one simple objective that scales, works for any task framed as text continuation, and is efficient to train on huge unlabelled data.
🔍 Tokenisation
| Method | Idea | Used by |
|---|---|---|
| BPE (Byte-Pair Encoding) | Start from bytes/chars, repeatedly merge the most frequent pair | GPT family |
| WordPiece | Merge by likelihood gain | BERT |
| Unigram / SentencePiece | Start large, prune; language-agnostic, works on raw text | T5, LLaMA |
Practical effects:
- ~0.75 English words per token; many other languages and code use more tokens per word → higher cost and shorter effective context.
- Numbers split oddly → arithmetic weaknesses; spelling/character tasks are hard because the model doesn't "see" letters.
- Vocabulary size trade-off: bigger vocab = shorter sequences but bigger embedding/LM-head matrices.
🔍 Mixture of Experts (MoE)
- Replace each FFN with many expert FFNs plus a small router that sends each token to the top-k (often 2) experts.
- Total parameters are huge, but active parameters per token are small → more capacity for the same compute (e.g. Mixtral 8x7B: ~47B total, ~13B active).
- Challenges: load balancing (auxiliary loss so experts get similar traffic), all experts must still fit in memory, more complex distributed training (expert parallelism).
🔍 Decoding / Sampling Strategies
| Strategy | How | Use |
|---|---|---|
| Greedy | Always highest probability | Deterministic, can repeat itself |
| Beam search | Keep top-B partial sequences | Translation; bland for open-ended chat |
| Temperature | Divide logits by T: T<1 sharper, T>1 more random | Creativity control |
| Top-k | Sample from the k most likely tokens | |
| Top-p (nucleus) | Sample from the smallest set whose probability sums to p | Common default with temperature |
| Repetition / frequency penalties | Lower scores of already used tokens | Reduce loops |
| Constrained decoding | Only allow tokens matching a grammar/JSON schema | Structured output |
🧠 What a Senior Answer Adds
- Connects architecture choices to serving cost: GQA and KV cache, MoE active params, context length and
T². - Knows tokenisation affects cost, multilingual quality and arithmetic.
- Can write attention, multi-head reshaping and the causal mask without notes.
🎯 Likely Interview Questions
- Explain self-attention. — Queries compare with keys to make weights; output is a weighted sum of values; scaled by sqrt(d_k); causal mask for decoders.
- Why scale by
sqrt(d_k)? — Keeps dot-product variance stable so softmax doesn't saturate. - Why multi-head attention? — Different heads learn different relations in parallel sub-spaces.
- What is the complexity of attention? — O(T²·d) compute, O(T²) memory naive; long contexts are expensive.
- How do transformers know word order? — Positional encodings; modern LLMs use RoPE (relative positions via rotations).
- Pre-LN vs post-LN? — Normalising before sub-layers is more stable for deep models.
- Encoder-only vs decoder-only? — Bidirectional understanding (BERT) vs causal generation (GPT).
- What is GQA and why use it? — Query heads share K/V heads → smaller KV cache, faster inference, small quality cost.
- What is a Mixture of Experts? — Router sends tokens to a few expert FFNs: many total params, few active per token.
- How does BPE work and why does tokenisation matter? — Merges frequent pairs; affects cost, multilingual fairness, arithmetic, spelling.
- Temperature vs top-p? — Temperature reshapes the whole distribution; top-p truncates to the probable set.
- Where are most parameters in a transformer? — Feed-forward layers (~2/3), then attention projections; embeddings matter for big vocabularies.
← Deep Learning & Training | Index | Next: Pretraining & Alignment →