4.1.12 · D4Transformer Architecture

Exercises — The original - Attention is All You Need - architecture

3,521 words16 min readBack to topic

Before we begin, let us pin down every symbol so nothing is used unexplained.


Level 1 — Recognition

Problem 1.1

The encoder has layers, each with two sub-layers; the decoder has layers, each with three sub-layers. How many sub-layers are there in the whole model?

Recall Solution

WHAT: count sub-layers per stack, then add. Encoder: . Decoder: . Total sub-layers. WHY this matters: every one of these sub-layers is separately wrapped in a residual connection + layer norm, so there are also "Add & Norm" blocks. Knowing this count tells you exactly how many places gradients must flow through cleanly — it's why residual connections (a shortcut past each block) are non-negotiable in a stack this deep.

Problem 1.2

With and heads, what is (the dimension of one head)?

Recall Solution

By definition . Same for (the value width per head is also in the paper). WHY this matters: because heads each of width recombine to , running heads costs roughly the same compute as one full-width head — you get diverse "viewpoints" essentially for free. This budget-splitting is the whole trick of multi-head attention.

Problem 1.3

Name the three sub-layers of a decoder layer in the order the data flows through them.

Recall Solution
  1. Masked multi-head self-attention (each position sees only itself and earlier positions).
  2. Encoder–decoder (cross) attention ( from decoder, from encoder).
  3. Position-wise feed-forward network. Each is followed by Add & Norm.

Level 2 — Application

Problem 2.1

For , compute the four positional-encoding values at position .

\text{PE}(\text{pos},2i+1)=\cos\!\left(\frac{\text{pos}}{10000^{2i/d_{\text{model}}}}\right)$$ > [!recall]- Solution > **WHAT/WHY**: dimensions come in pairs $(2i,2i+1)$; $i$ indexes the pair. With $d_{\text{model}}=4$ we have $i=0$ (dims $0,1$) and $i=1$ (dims $2,3$). > > Pair $i=0$: denominator $10000^{0/4}=1$, argument $=1/1=1$. > - $\text{PE}(1,0)=\sin(1)=0.8415$ > - $\text{PE}(1,1)=\cos(1)=0.5403$ > > Pair $i=1$: denominator $10000^{2/4}=10000^{0.5}=100$, argument $=1/100=0.01$. > - $\text{PE}(1,2)=\sin(0.01)=0.0100$ > - $\text{PE}(1,3)=\cos(0.01)=0.99995$ > > The low-index pair swings fast with position; the high-index pair barely moves. The figure below makes this concrete: it plots the sine wave used by the fast pair (dim 0, lavender) against the sine wave used by the slow pair (dim 2, coral), with the dashed mint line marking $\text{pos}=1$. Notice how at $\text{pos}=1$ the lavender wave has already climbed near its peak while the coral wave has barely left zero — that's exactly why the fast pair encodes *fine* position and the slow pair encodes *coarse* position. Think of many clock hands spinning at different speeds: read them together and you know the position uniquely. ![[deepdives/dd-ai-ml-4.1.12-d4-s01.png]] ### Problem 2.2 A single-head attention computes a score row before softmax as $[0.15,\ 0.35,\ 0.20]$ (this is row 1 of the parent note's example). Compute the softmax weights. > [!recall]- Solution > **WHAT**: exponentiate each entry, then divide by their sum. > $e^{0.15}=1.1618,\quad e^{0.35}=1.4191,\quad e^{0.20}=1.2214.$ > Sum $=1.1618+1.4191+1.2214=3.8023$. > Weights: > - $1.1618/3.8023 = 0.3056$ > - $1.4191/3.8023 = 0.3732$ > - $1.2214/3.8023 = 0.3213$ > Check they sum to $1$: $0.3056+0.3732+0.3213=1.0001$ (rounding). Token 1 attends slightly most to token 2. ### Problem 2.3 The embedding scaling factor is $\sqrt{d_{\text{model}}}$. Compute it for $d_{\text{model}}=512$ and explain why we scale. > [!recall]- Solution > $\sqrt{512}=\sqrt{256\cdot 2}=16\sqrt{2}\approx 22.627$. > **WHY (precise)**: the input to layer $1$ is $\text{Embed}(x)\cdot\sqrt{d_{\text{model}}} + \text{PE}(\text{pos})$, where $\text{Embed}(x)$ is the learned lookup vector for token $x$ and $\text{PE}(\text{pos})$ is the fixed positional vector (both defined in the symbol dictionary). The positional encoding is built from $\sin$/$\cos$, so each of its entries is bounded in $[-1,1]$ with a typical magnitude around $0.7$. Embedding entries, by contrast, are usually initialized with variance $\sim 1/d_{\text{model}}$ (Xavier-style) or shared with the output softmax weights, making each *individual* embedding entry small. Multiplying the embedding by $\sqrt{d_{\text{model}}}\approx 22.6$ pushes its per-entry magnitude up to the same order as the positional entries, so **the two summands have comparable scale** rather than one swamping the other. > **Impact on training dynamics**: if the embedding were left tiny, early gradients would be dominated by the fixed (non-learnable) positional signal and the model would struggle to move its semantic embeddings; if it were far too large, positional information would be invisible and the model would be order-blind at the start. Matching magnitudes keeps both meaning and position learnable from step one. > [!mistake] The L2 trap: mis-pairing the PE dimensions > Many students plug $2i$ = the raw dimension index (so for dim $2$ they write $10000^{2/4}$ but for dim $3$ they *also* use exponent $3$). **Why it feels right:** the dimension index and the "$2i$" look interchangeable. **The fix:** $i$ counts *pairs*. Dimensions $2$ and $3$ share the **same** frequency (both use $i=1$); dim $2$ takes $\sin$, dim $3$ takes $\cos$. Sine/cosine of the *same angle* is exactly what lets a position be rotated to another — pairs must share a frequency. --- ## Level 3 — Analysis ### Problem 3.1 Suppose $d_k=64$ and each entry of $Q$ and $K$ is an independent value with mean $0$ and variance $1$. What is the variance of a single dot product $q\cdot k=\sum_{m=1}^{d_k} q_m k_m$, and what does the scaling factor $\sqrt{d_k}$ do to it? > [!recall]- Solution > **WHAT/WHY**: we want to know how big the raw scores get, because huge scores saturate softmax. > Each product $q_m k_m$ has mean $0$ and variance $\mathrm{Var}(q_m)\mathrm{Var}(k_m)=1\cdot 1=1$ (independent, zero-mean). Summing $d_k$ independent terms adds variances: > $$\mathrm{Var}(q\cdot k)=d_k = 64.$$ > So the typical magnitude (standard deviation) is $\sqrt{64}=8$. > Dividing by $\sqrt{d_k}=8$ rescales the standard deviation back to $1$: > $$\mathrm{Var}\!\left(\frac{q\cdot k}{\sqrt{d_k}}\right)=\frac{d_k}{d_k}=1.$$ > **Consequence**: scores stay $\mathcal{O}(1)$, softmax keeps healthy gradients instead of collapsing onto one token. ### Problem 3.2 Write the $3\times 3$ causal mask (before softmax) for a decoder sequence of length $3$, using $0$ for "allowed" and $-\infty$ for "blocked". Then state which tokens position $2$ can attend to. > [!recall]- Solution > Rule: position $i$ may attend to position $j$ only if $j\le i$ (row $i$, column $j$). > $$\text{mask}=\begin{bmatrix}0 & -\infty & -\infty\\ 0 & 0 & -\infty\\ 0 & 0 & 0\end{bmatrix}$$ > Row $2$ (position $2$) has zeros in columns $1$ and $2$, $-\infty$ in column $3$. So position $2$ attends to positions $1$ and $2$ only — it cannot peek at the future token $3$. > After adding $-\infty$ and applying softmax, $e^{-\infty}=0$, so blocked positions get exactly weight $0$. The figure below is this exact mask drawn as a grid: green cells (value $0$) are the allowed attention links and coral cells (value $-\infty$) are the blocked ones. Read it row by row — each row $i$ is a query, and the green cells stretching from the left up to the diagonal show precisely the past-and-present tokens that query is permitted to see. The staircase of green is the visual signature of "no looking into the future." ![[deepdives/dd-ai-ml-4.1.12-d4-s02.png]] ### Problem 3.3 Given a token vector $x=[2,\ 4,\ 4,\ 6]$ (so $d_{\text{model}}=4$), compute its layer-norm output using $\gamma=1,\ \beta=0,\ \epsilon=0$: $$\text{LayerNorm}(x)=\frac{x-\mu}{\sqrt{\sigma^2+\epsilon}}\cdot\gamma+\beta.$$ > [!recall]- Solution > **WHAT/WHY**: layer norm rescales *within one token* across its $4$ features so the vector has mean $0$, std $1$. > Mean: $\mu=(2+4+4+6)/4=16/4=4$. > Deviations: $[-2,\ 0,\ 0,\ 2]$. > Variance (population): $\sigma^2=(4+0+0+4)/4=8/4=2$. > Std: $\sqrt{2}\approx 1.4142$. > Output: $[-2,0,0,2]/\sqrt{2}=[-1.4142,\ 0,\ 0,\ 1.4142]$. > Check: mean $=0$, and mean of squares $=(2+0+0+2)/4=1$, so std $=1$. ✓ > Here $\gamma=1$ and $\beta=0$ leave the standardized vector untouched; in a trained model $\gamma$ and $\beta$ would stretch and shift each feature to whatever scale the next layer prefers. > [!mistake] The L3 trap: normalizing across the wrong axis > Students often average over the *batch* (like BatchNorm) instead of over the $d_{\text{model}}$ features of a single token. **Why it feels right:** most normalization they've seen (BatchNorm) works per-feature-across-samples. **The fix:** Layer**Norm** normalizes *per token, across its own features*. That's why it works with any batch size and any sequence length — each token is standardized on its own. Compute $\mu,\sigma$ over the $4$ numbers of *that* vector, nothing else. --- ## Level 4 — Synthesis ### Problem 4.1 You are asked to build a Transformer with $d_{\text{model}}=768$ and you want $h=12$ heads. (a) What is $d_k$? (b) If instead you demanded $d_k=100$, why is $h=12$ impossible, and what constraint links $h$ and $d_{\text{model}}$? > [!recall]- Solution > (a) $d_k=768/12=\boxed{64}$. > (b) The design requires $h\cdot d_k = d_{\text{model}}$ so that concatenating all heads returns to width $d_{\text{model}}$ (before the output projection $W^O$). With $d_k=100$ we'd need $h\cdot 100=768$, i.e. $h=7.68$ — not an integer. **Constraint**: $h$ must divide $d_{\text{model}}$ evenly. > **WHY this matters**: this single divisibility rule is what makes the concatenation of heads line up perfectly with $W^O$'s input width; break it and the matrix shapes no longer match. It's also the reason real models pick "round" widths like $512, 768, 1024$ — they factor nicely, giving you freedom to choose $h$. ### Problem 4.2 Full mini-attention. Two tokens have identical $Q=K=V$ rows: $$X=\begin{bmatrix}1&0\\0&1\end{bmatrix},\quad d_k=2,\quad W^Q=W^K=W^V=I.$$ Compute the attention output for **row 1** (token 1), showing scores, scaled scores, softmax, and weighted sum. > [!recall]- Solution > **Step 1 — scores** $QK^T$: dot products of rows. > $X_1\cdot X_1=1,\quad X_1\cdot X_2=0$. Row 1 scores $=[1,\ 0]$. > **Step 2 — scale** by $\sqrt{d_k}=\sqrt{2}\approx1.4142$: $[1/1.4142,\ 0]=[0.7071,\ 0]$. > **Step 3 — softmax**: $e^{0.7071}=2.0281,\ e^{0}=1$. Sum $=3.0281$. > weights $=[2.0281/3.0281,\ 1/3.0281]=[0.6698,\ 0.3302]$. > **Step 4 — weighted sum of $V$** (rows $[1,0]$ and $[0,1]$): > $$0.6698\cdot[1,0]+0.3302\cdot[0,1]=[0.6698,\ 0.3302].$$ > Output for token 1 $=[0.6698,\ 0.3302]$: it keeps most of its own value but blends in some of token 2. **WHY this shape of answer**: even with *identical* projections, a token doesn't just copy itself — the softmax forces a nonzero share onto every other token, so information always mixes. That gentle, unavoidable mixing is what attention buys you, and it is exactly why stacking attention layers lets every token gradually gather context from the whole sequence. ### Problem 4.3 In the decoder, cross-attention sets $Q=\text{DecoderOutput}$, $K=V=\text{EncoderOutput}$. Suppose the source has $n=5$ tokens and we have generated $m=3$ target tokens so far. What is the shape of the cross-attention score matrix $QK^T$, and what does entry $(i,j)$ mean? > [!recall]- Solution > $Q$ has one row per target token: $m=3$ rows. $K$ has one row per source token: $n=5$ rows. So > $$QK^T \in \mathbb{R}^{m\times n}=\mathbb{R}^{3\times 5}.$$ > Entry $(i,j)$ = how much target position $i$ should draw from source position $j$. No causal mask is used here (the whole source is legitimately visible); the mask lives only in the decoder's *self*-attention. > [!mistake] The L4 trap: masking cross-attention too > Students copy the causal mask into cross-attention "for consistency." **Why it feels right:** the decoder is autoregressive, so surely everything must be masked. **The fix:** the *no-cheating* rule is about future **target** tokens, not source tokens. The source sentence is fully known at every step — the decoder is *allowed* and *encouraged* to look at all of it. Only the decoder's self-attention (target-to-target) gets the causal mask. --- ## Level 5 — Mastery ### Problem 5.1 **Padding mask.** In a batch, sentences have different lengths, so short ones are padded with a special `<pad>` token to a common length. Consider a length-$4$ sequence where positions $1,2,3$ are real tokens and position $4$ is `<pad>`. Write the row of the padding mask that every query uses (before softmax, $0$ = keep, $-\infty$ = block), and explain why we must block the pad column. > [!recall]- Solution > **WHAT**: a padding mask blocks *columns* (keys) that correspond to `<pad>` positions, for **every** query row. Position $4$ is padding, so its column is $-\infty$ everywhere; the real columns $1,2,3$ stay $0$: > $$\text{pad-mask row} = [\,0,\ 0,\ 0,\ -\infty\,].$$ > Every query row $i$ (for $i=1,2,3,4$) uses this same column pattern. After softmax, $e^{-\infty}=0$, so the pad position receives exactly weight $0$ and contributes nothing to any output. > **WHY we must**: `<pad>` is a filler with no meaning. If we left its column unblocked, its (arbitrary) value vector would leak into every token's output, and the softmax denominator would include a bogus term — corrupting the real tokens' representations. **Contrast with the causal mask**: the causal mask is *triangular* (each query blocks different future columns); the padding mask blocks the *same* columns for all queries. In the decoder both are simply added together before softmax. > [!mistake] The L5 trap: forgetting the padding mask entirely (or confusing it with the causal mask) > Beginners test on single sentences (no padding) and everything works, so they never add a padding mask — then batched training silently degrades. **Why it feels right:** the causal mask already "masks stuff," so it *seems* like masking is handled. **The fix:** the two masks answer different questions. Causal mask = "don't look at *future target* positions" (triangular, decoder self-attention only). Padding mask = "don't look at *filler* positions" (whole columns, used in **every** attention — encoder self, decoder self, and cross-attention on the source). Real code adds both. ### Problem 5.2 Parameter count of one attention block. For a **single** attention layer with $d_{\text{model}}=512$, $h=8$, $d_k=d_v=64$: count the parameters in the projections $W^Q,W^K,W^V$ (all heads together) plus the output projection $W^O$. Ignore biases. > [!recall]- Solution > Across all $8$ heads, the query projections stack to a single matrix of shape $d_{\text{model}}\times(h\cdot d_k)=512\times 512$. Same for keys and values. > - $W^Q$: $512\times 512 = 262144$ > - $W^K$: $262144$ > - $W^V$: $262144$ > - $W^O$: $(h\cdot d_v)\times d_{\text{model}}=512\times 512 = 262144$ > Total $=4\times 262144 = \boxed{1\,048\,576}=2^{20}\approx 1.05\text{M}$ parameters per attention block. > **WHY this matters**: knowing that attention is "four square matrices" lets you estimate a model's size and memory in your head — multiply by the number of layers, add the FFNs, and you have the parameter budget. It also reveals that attention's cost is *quadratic in $d_{\text{model}}$* but *linear in the number of heads*, which is why widening a model is expensive while adding heads is cheap. ### Problem 5.3 Parameter count of one position-wise FFN with $d_{\text{model}}=512$, $d_{ff}=2048$, **including** biases: $$\text{FFN}(x)=\text{ReLU}(xW_1+b_1)W_2+b_2.$$ > [!recall]- Solution > - $W_1$: $512\times 2048 = 1048576$; $b_1$: $2048$. > - $W_2$: $2048\times 512 = 1048576$; $b_2$: $512$. > Total $=1048576+2048+1048576+512 = \boxed{2\,099\,712}\approx 2.10\text{M}$. > **WHY this matters**: the FFN alone has about *twice* the parameters of the attention block ($2.10\text{M}$ vs $1.05\text{M}$). So while attention gets all the fame, the majority of a Transformer's raw capacity — its "memory" of facts and patterns — actually lives in these humble feed-forward layers. That is a genuinely useful mental model when you think about where a model "stores" what it learns. ### Problem 5.4 Relative-position property. The parent note claims $\text{PE}(\text{pos}+k)$ is a **linear** function of $\text{PE}(\text{pos})$. For one frequency $\omega$ (so a pair is $[\sin(\omega\,\text{pos}),\ \cos(\omega\,\text{pos})]$), show the shift by $k$ is a rotation, and verify numerically for $\omega=0.5,\ \text{pos}=2,\ k=3$. > [!recall]- Solution > **WHAT/WHY**: we want to prove that "advancing the position by $k$" is a fixed linear operation independent of $\text{pos}$ — that's what lets the model learn *relative* offsets. > > **Step 1 — angle-addition identities** (this is why sine/cosine were chosen): > $$\sin(\omega(\text{pos}+k))=\sin(\omega\,\text{pos})\cos(\omega k)+\cos(\omega\,\text{pos})\sin(\omega k)$$ > $$\cos(\omega(\text{pos}+k))=\cos(\omega\,\text{pos})\cos(\omega k)-\sin(\omega\,\text{pos})\sin(\omega k)$$ > > **Step 2 — write it as a matrix acting on the PE pair.** Stacking the two identities: > $$\begin{bmatrix}\sin(\omega(\text{pos}+k))\\ \cos(\omega(\text{pos}+k))\end{bmatrix} > =\underbrace{\begin{bmatrix}\cos(\omega k) & \sin(\omega k)\\ -\sin(\omega k) & \cos(\omega k)\end{bmatrix}}_{R(\omega k)} > \begin{bmatrix}\sin(\omega\,\text{pos})\\ \cos(\omega\,\text{pos})\end{bmatrix}.$$ > The matrix $R(\omega k)$ depends only on $k$ (and $\omega$), **not** on $\text{pos}$ — it is a standard $2\times 2$ rotation matrix. So shifting position by $k$ *rotates* the PE pair by a fixed angle $\omega k$. That is exactly the claim "$\text{PE}(\text{pos}+k)$ is a linear function of $\text{PE}(\text{pos})$." > > **Step 3 — numerical check** ($\omega=0.5,\ \text{pos}=2,\ k=3$). > Left-hand side directly: $\omega(\text{pos}+k)=0.5\cdot 5=2.5$, so > $$\text{LHS}=[\sin(2.5),\ \cos(2.5)]=[0.5985,\ -0.8011].$$ > Right-hand side via the rotation: the base pair is $[\sin(1),\cos(1)]=[0.8415,\ 0.5403]$ and $\omega k=1.5$, so $R(1.5)=\begin{bmatrix}\cos 1.5 & \sin 1.5\\ -\sin 1.5 & \cos 1.5\end{bmatrix}=\begin{bmatrix}0.0707 & 0.9975\\ -0.9975 & 0.0707\end{bmatrix}$. > - Row 1: $0.0707\cdot 0.8415 + 0.9975\cdot 0.5403 = 0.5985$ > - Row 2: $-0.9975\cdot 0.8415 + 0.0707\cdot 0.5403 = -0.8011$ > $$\text{RHS}=[0.5985,\ -0.8011].$$ > LHS $=$ RHS ✓. The figure below shows both PE vectors on the unit circle with the mint arc marking the fixed rotation angle $\omega k = 1.5$. ![[deepdives/dd-ai-ml-4.1.12-d4-s03.png]] > [!mistake] The L5 trap: thinking the rotation depends on the current position > Students expect the "shift operator" to change as we move along the sequence. **Why it feels right:** everything else in attention is position-dependent. **The fix:** the rotation angle is $\omega k$ — it depends only on the *offset* $k$, never on where you currently are. That position-independence is the whole point: a head can learn "attend $k$ steps back" once and apply it everywhere. > [!mnemonic] Quick recall anchors > - Heads: $h\cdot d_k = d_{\text{model}}$ (concat must fit back). > - Scale by $\sqrt{d_k}$ → keeps score variance at $1$. > - Causal mask (triangular) only decoder **self**-attention; padding mask (whole columns) in **every** attention. > - LayerNorm: per **token**, across **features**. > - PE pairs share a frequency; shift = rotation.