Transformer Architecture
Level 3 — Production (from-scratch derivations, code-from-memory, explain-out-loud) Time limit: 45 minutes Total marks: 60
Instructions: Show all derivations. Where code is requested, write clean pseudo-code or PyTorch-style code from memory. "Explain-out-loud" prompts require prose reasoning, not just formulas.
Question 1 — Scaled Dot-Product Attention from scratch (12 marks)
(a) Write the full equation for scaled dot-product attention given matrices , , . State the output shape. (3)
(b) Derive why we divide by . Assume components of and are independent random variables with mean and variance . Compute the mean and variance of the dot product , and explain the effect on softmax gradients. (5)
(c) Implement scaled dot-product attention as a from-memory PyTorch function attention(Q, K, V, mask=None). Include the masking step. (4)
Question 2 — Multi-Head Attention (10 marks)
(a) Given model dimension and heads, state and under the standard convention, and explain why total compute is roughly independent of . (4)
(b) Count the total number of learnable parameters (weights only, ignore biases) in one multi-head attention block with , , including the output projection . Show the arithmetic. (6)
Question 3 — Positional Encodings & RoPE (12 marks)
(a) Write the sinusoidal positional encoding formulas for position and dimension index . Explain out loud why this scheme lets the model attend to relative positions. (5)
(b) Rotary Positional Embeddings (RoPE) rotate query/key pairs by an angle proportional to position. For a 2D sub-vector at position with frequency , write the rotation applied. Then prove that the dot product between a RoPE-rotated query at position and key at position depends only on the relative position . (7)
Question 4 — Masked Attention & Autoregression (9 marks)
(a) Explain out loud why decoder self-attention must be masked, and what would go wrong at training time without it. (4)
(b) Write code (from memory) to construct a causal mask for sequence length and show how it is applied to the attention score matrix before softmax. Explain why you use (or a large negative) rather than . (5)
Question 5 — Complexity & Efficient Attention (9 marks)
(a) Derive the time and memory complexity of full self-attention for sequence length and dimension . Identify which operation dominates for long sequences. (4)
(b) Explain out loud the core idea behind FlashAttention: what quantity does it avoid materializing, and how does online softmax (running max + running sum) let it stay numerically stable while tiling? State its memory complexity. (5)
Question 6 — Architecture & Norm Placement (8 marks)
(a) Contrast Post-LN and Pre-LN transformer blocks. Write the residual+norm equations for a sublayer in both cases. Explain out loud why Pre-LN trains more stably without learning-rate warmup. (5)
(b) In one sentence each, state the defining structural difference between encoder-only, decoder-only, and encoder–decoder transformers, giving one model example of each. (3)
Answer keyMark scheme & solutions
Question 1 (12)
(a) (3)
- (1), softmax over rows (1), output shape (1).
(b) (5)
- (independence) ⇒ . (1)
- . (1)
- Independence of terms ⇒ , so std . (1)
- Dividing by rescales variance back to 1. (1)
- Without scaling, large-magnitude logits push softmax into saturated regions where gradients vanish; scaling keeps softmax in a well-conditioned regime. (1)
(c) (4)
import torch, math
def attention(Q, K, V, mask=None):
d_k = Q.size(-1)
scores = Q @ K.transpose(-2, -1) / math.sqrt(d_k) # (1)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf')) # (1)
weights = torch.softmax(scores, dim=-1) # (1)
return weights @ V # (1)Question 2 (10)
(a) (4)
- Convention . (2)
- Each head works in dimension ; summing heads gives total per-head cost , independent of . (2)
(b) (6)
- each project (all heads concatenated): each . (2)
- Three of them: . (2)
- Output projection : . (1)
- Total . (1)
Question 3 (12)
(a) (5) (3)
- Explanation: for a fixed offset , is a linear function of (rotation by a fixed angle per frequency), so the model can learn to attend by relative offset via a linear transform of encodings. Different frequencies give a multi-scale positional signal. (2)
(b) (7)
- Rotation of at position : (2)
- Proof of relative dependence: let rotated query , rotated key . (2) (2) since rotation matrices satisfy and . The result depends only on . (1)
Question 4 (9)
(a) (4)
- Decoder generates tokens left-to-right; at position it may only use tokens . (2)
- Without masking, self-attention at position would see future tokens (positions ), leaking the answer during teacher-forced training — the model would learn to "cheat" and fail at inference where future tokens don't exist. (2)
(b) (5)
mask = torch.tril(torch.ones(L, L)) # lower-triangular 1s (2)
scores = scores.masked_fill(mask == 0, float('-inf')) # (1)
weights = torch.softmax(scores, dim=-1) # (1)- Use because exactly, zeroing out disallowed positions; using would leave a nonzero weight , still leaking future info. (1)
Question 5 (9)
(a) (4)
- : times ⇒ time. (1)
- Softmax and : also . (1)
- Memory: storing the score matrix ⇒ . (1)
- For long sequences the term dominates (quadratic in sequence length). (1)
(b) (5)
- FlashAttention avoids materializing the full attention matrix in HBM. (1)
- It tiles Q, K, V into blocks and computes attention block-by-block in fast SRAM. (1)
- Online (streaming) softmax maintains a running max and running normalizer ; when a new block arrives it rescales the accumulated output by to correct for the updated max, keeping exponentials numerically stable. (2)
- Memory complexity is (linear) instead of . (1)
Question 6 (8)
(a) (5)
- Post-LN: (1)
- Pre-LN: (1)
- Explanation: in Pre-LN the residual path is an identity (un-normalized) stream, so gradients flow cleanly to lower layers and activation magnitudes stay bounded; Post-LN normalizes the summed output, causing large gradients at the top early in training that require warmup. Pre-LN's bounded gradients remove that need. (3)
(b) (3)
- Encoder-only: bidirectional self-attention, no causal mask — e.g. BERT. (1)
- Decoder-only: causal-masked self-attention, autoregressive — e.g. GPT. (1)
- Encoder–decoder: encoder (bidirectional) + decoder with masked self-attention and cross-attention to encoder outputs — e.g. original Transformer / T5. (1)
[
{"claim": "Variance of dot product of two d_k=64 dim unit-variance vectors is d_k=64, std=8=sqrt(64)",
"code": "dk=64; var=dk; std=sqrt(var); result = (var==64) and (std==8)"},
{"claim": "Multi-head attention params for d=512,h=8 = 4*512^2 = 1048576",
"code": "d=512; total=4*d**2; result = (total==1048576)"},
{"claim": "d_k = d_model/h = 512/8 = 64",
"code": "result = (512/8 == 64)"},
{"claim": "RoPE dot product reduces to R_{n-m}: R_m^T R_n = R_{n-m}",
"code": "th=symbols('theta'); m,n=symbols('m n'); Rm=Matrix([[cos(m*th),-sin(m*th)],[sin(m*th),cos(m*th)]]); Rn=Matrix([[cos(n*th),-sin(n*th)],[sin(n*th),cos(n*th)]]); Rd=Matrix([[cos((n-m)*th),-sin((n-m)*th)],[sin((n-m)*th),cos((n-m)*th)]]); result = simplify(Rm.T*Rn - Rd)==zeros(2,2)"}
]