Level 3 — ProductionTransformer Architecture

Transformer Architecture

45 minutes60 marksprintable — key stays hidden on paper

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 QRn×dkQ \in \mathbb{R}^{n \times d_k}, KRm×dkK \in \mathbb{R}^{m \times d_k}, VRm×dvV \in \mathbb{R}^{m \times d_v}. State the output shape. (3)

(b) Derive why we divide by dk\sqrt{d_k}. Assume components of qq and kk are independent random variables with mean 00 and variance 11. Compute the mean and variance of the dot product qk=i=1dkqikiq \cdot k = \sum_{i=1}^{d_k} q_i k_i, 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 dmodel=512d_{\text{model}} = 512 and h=8h = 8 heads, state dkd_k and dvd_v under the standard convention, and explain why total compute is roughly independent of hh. (4)

(b) Count the total number of learnable parameters (weights only, ignore biases) in one multi-head attention block with dmodel=512d_{\text{model}}=512, h=8h=8, including the output projection WOW^O. Show the arithmetic. (6)


Question 3 — Positional Encodings & RoPE (12 marks)

(a) Write the sinusoidal positional encoding formulas for position pospos and dimension index ii. 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 (x1,x2)(x_1, x_2) at position mm with frequency θ\theta, write the rotation applied. Then prove that the dot product between a RoPE-rotated query at position mm and key at position nn depends only on the relative position mnm-n. (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 LL and show how it is applied to the attention score matrix before softmax. Explain why you use -\infty (or a large negative) rather than 00. (5)


Question 5 — Complexity & Efficient Attention (9 marks)

(a) Derive the time and memory complexity of full self-attention for sequence length nn and dimension dd. 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 ff 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) Attention(Q,K,V)=softmax ⁣(QKdk)V\text{Attention}(Q,K,V) = \text{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

  • QKRn×mQK^\top \in \mathbb{R}^{n\times m} (1), softmax over rows (1), output shape Rn×dv\mathbb{R}^{n \times d_v} (1).

(b) (5)

  • E[qiki]=E[qi]E[ki]=0E[q_i k_i] = E[q_i]E[k_i] = 0 (independence) ⇒ E[qk]=0E[q\cdot k] = 0. (1)
  • Var(qiki)=E[qi2]E[ki2]0=11=1\text{Var}(q_i k_i) = E[q_i^2]E[k_i^2] - 0 = 1\cdot 1 = 1. (1)
  • Independence of terms ⇒ Var(qk)=i=1dk1=dk\text{Var}(q\cdot k) = \sum_{i=1}^{d_k} 1 = d_k, so std =dk=\sqrt{d_k}. (1)
  • Dividing by dk\sqrt{d_k} 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 dk=dv=dmodel/h=512/8=64d_k = d_v = d_{\text{model}}/h = 512/8 = 64. (2)
  • Each head works in dimension dmodel/hd_{\text{model}}/h; summing hh heads gives total per-head cost hn2(d/h)=n2d\propto h \cdot n^2 \cdot (d/h) = n^2 d, independent of hh. (2)

(b) (6)

  • WQ,WK,WVW^Q, W^K, W^V each project dmodeldmodeld_{\text{model}} \to d_{\text{model}} (all heads concatenated): each 512×512=262,144512 \times 512 = 262{,}144. (2)
  • Three of them: 3×262,144=786,4323 \times 262{,}144 = 786{,}432. (2)
  • Output projection WOW^O: 512×512=262,144512 \times 512 = 262{,}144. (1)
  • Total =786,432+262,144=1,048,576=4×5122= 786{,}432 + 262{,}144 = 1{,}048{,}576 = 4 \times 512^2. (1)

Question 3 (12)

(a) (5) PE(pos,2i)=sin ⁣(pos100002i/d),PE(pos,2i+1)=cos ⁣(pos100002i/d)PE_{(pos,2i)} = \sin\!\left(\frac{pos}{10000^{2i/d}}\right), \quad PE_{(pos,2i+1)} = \cos\!\left(\frac{pos}{10000^{2i/d}}\right) (3)

  • Explanation: for a fixed offset kk, PEpos+kPE_{pos+k} is a linear function of PEposPE_{pos} (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 (x1,x2)(x_1,x_2) at position mm: (2) Rm(x1x2)=(cosmθsinmθsinmθcosmθ)(x1x2)R_m\begin{pmatrix}x_1\\x_2\end{pmatrix} = \begin{pmatrix}\cos m\theta & -\sin m\theta\\ \sin m\theta & \cos m\theta\end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}
  • Proof of relative dependence: let rotated query =Rmq= R_m q, rotated key =Rnk= R_n k. (2) (Rmq)(Rnk)=qRmRnk=qRnmk(R_m q)^\top (R_n k) = q^\top R_m^\top R_n k = q^\top R_{n-m} k (2) since rotation matrices satisfy Rm=RmR_m^\top = R_{-m} and RmRn=RnmR_{-m}R_n = R_{n-m}. The result depends only on nmn-m. (1)

Question 4 (9)

(a) (4)

  • Decoder generates tokens left-to-right; at position tt it may only use tokens t\le t. (2)
  • Without masking, self-attention at position tt would see future tokens (positions >t>t), 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 -\infty because softmax()=0\text{softmax}(-\infty)=0 exactly, zeroing out disallowed positions; using 00 would leave a nonzero weight e0=1\propto e^0=1, still leaking future info. (1)

Question 5 (9)

(a) (4)

  • QKQK^\top: n×dn\times d times d×nd\times nO(n2d)O(n^2 d) time. (1)
  • Softmax and ×V\times V: also O(n2d)O(n^2 d). (1)
  • Memory: storing the n×nn\times n score matrix ⇒ O(n2)O(n^2). (1)
  • For long sequences the n2n^2 term dominates (quadratic in sequence length). (1)

(b) (5)

  • FlashAttention avoids materializing the full n×nn\times n 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 mm and running normalizer \ell; when a new block arrives it rescales the accumulated output by emoldmnewe^{m_{\text{old}} - m_{\text{new}}} to correct for the updated max, keeping exponentials numerically stable. (2)
  • Memory complexity is O(n)O(n) (linear) instead of O(n2)O(n^2). (1)

Question 6 (8)

(a) (5)

  • Post-LN:   xout=LayerNorm(x+f(x))\;x_{out} = \text{LayerNorm}(x + f(x)) (1)
  • Pre-LN:   xout=x+f(LayerNorm(x))\;x_{out} = x + f(\text{LayerNorm}(x)) (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)"}
]