Part 1 · Chapter 4

The transformer block

Many heads, a residual stream, normalisation, positions, and the full pass from token to logits.

Where we are

You now hold every component: embeddings and matrices (Chapter 2), the MLP (Chapter 2), one attention head (Chapter 3), and the softmax-and-loss at the end (Chapter 1). This chapter assembles them into the repeating unit of every frontier model, the block, stacks the blocks, and follows one token all the way through to a distribution. When you finish, the box on the map labelled f(context; θ) is fully open. Chapter 5 then asks how its numbers were found.

The question this chapter answers: how are attention and the MLP arranged so that stacking dozens of them produces a model that can be trained, and what does the whole forward pass look like?

Picture this

A document passes through a chain of specialists. Each specialist reads it, then staples a note to it, and passes it on. Nobody rewrites the document. Nobody removes an earlier note. By the time it reaches the end, the document is the original page plus forty stapled notes, and the last reader can consult any of them, including the original.

Some specialists are gossips: they read other documents in the same batch and staple "document 3 mentions the same client" onto this one. Others are analysts: they read only this document and its notes and staple "this is an invoice, probably overdue". Before each specialist reads, an assistant flattens the pile so it is a readable thickness, because forty notes of wildly different sizes would be unmanageable.

The gossips are attention. The analysts are MLPs. The stapling is the residual connection. The flattening is normalisation. The pile itself, original plus notes, is the residual stream: the token's vector as it travels through the model.

Map it
In the pictureIn the machineThe word we will use
The document plus all notes stapled so farThe token's vector at a given depth, shape [C]residual stream
Stapling a note instead of rewritingx ← x + f(x): the sub-layer's output is added, not substitutedresidual connection
A gossip who reads other documentsMulti-head attention: several heads reading other positions at onceattention sub-layer
An analyst who reads only this documentThe MLP from Chapter 2, applied per tokenMLP sub-layer
Flattening the pile before readingRescale the vector to a standard size before each sub-layernormalisation (RMSNorm)
One gossip plus one analystAttention sub-layer then MLP sub-layer, each with its residualblock (also "layer")
The specialists need to know page orderAttention is blind to order; positions are injected by rotating q and kpositional encoding (RoPE)
The last reader scoring every possible verdictFinal norm, then W_U to V logits, softmaxunembedding

4.1Many heads at once

Chapter 3 built one head of width d = 128 from a token of width C = 4096. One head can implement one kind of lookup: previous token, or subject-of-verb, or "find my earlier occurrence". A token needs several of those at the same time. So the block runs H heads in parallel, each with its own W_Q, W_K, W_V, each producing a [T, d] output. The outputs are concatenated side by side into [T, H·d], and H·d is chosen to equal C: 32 heads × 128 = 4096 for Llama 3 8B public. One more matrix, W_O of shape [C, C], mixes the concatenated heads into the vector that gets added to the stream.

                    ┌── head 1: Q₁K₁V₁ → out₁ [T, d] ──┐
X [T, C] ── norm ───┼── head 2: Q₂K₂V₂ → out₂ [T, d] ──┼── concat [T, H·d = C] ── · W_O [C, C] ──► [T, C]
                    │            …                      │
                    └── head H: Q_H K_H V_H → out_H ────┘
Static view of the widget. For the token "sat" in "The cat that I saw sat": head 1 attends to "saw" (previous token), head 2 to "cat" (its subject), head 3 to itself, head 4 to "The" (a sink). Four d-wide outputs concatenate into one C-wide vector before W_O.

Why not one wide head of width C? Because one head has one softmax, so it produces one weighting over positions. It can attend mostly to the previous token or mostly to the subject, and a blend of the two is a compromise on both. Splitting the width into H independent heads gives H independent weightings for the same cost in matrix parameters: H matrices of [C, d] hold exactly as many numbers as one matrix of [C, H·d]. Multi-head attention is free in parameters and expensive only in the T² score matrices, of which there are now H per layer.

The projection W_O matters more than it looks. Each head writes into its own 128-dimensional slot of the concatenation; W_O lets the block route any head's finding into any direction of the residual stream, and lets heads' findings interact. Without it, head 3 could only ever write into coordinates 256 to 383.

Grouped-query attention. In the widget's parameter calculator later in this chapter you will see that Llama 3 has 32 query heads but only 8 key/value heads. Groups of four query heads share one set of keys and values. This changes nothing conceptually and saves a great deal of memory at inference time; Chapter 12 explains why it is nearly free in quality, and Chapter 19 why it matters for serving.

4.2The residual stream

Here is the design decision that makes deep transformers trainable, and it is one plus sign. Each sub-layer does not replace the token's vector with its output. It adds its output to the vector:

x ← x + Attention(norm(x))    then    x ← x + MLP(norm(x))

The vector x that flows down the middle, receiving additions from every sub-layer, is the residual stream. Nothing in the stack can erase what an earlier layer wrote; it can only add something that cancels it. So the stream is an accumulating record: the embedding, plus what attention in layer 1 fetched, plus what the MLP in layer 1 concluded, and so on. By layer 30, the vector at position "it" contains the token identity, its position, what it refers to, its syntactic role, and a growing guess about the next token, all as a sum of arrows in the sense of Chapter 2.

Static view of the widget. An 8-coordinate stream starts as [1.0, 0, 0.2, 0, 0, 0, 0, 0] (token identity and position). Attention 1 adds 0.9 to coordinate 2; MLP 1 adds 0.8 to coordinate 4; and so on. After six sub-layers, with the residual on, every earlier contribution is still present, slightly adjusted. With the residual off, the final vector is just MLP 3's output and the token identity is gone.
x [C] x [C] the residual stream: the same vector, growing by addition norm attentionH heads, W_O + norm MLPper token + one block: two branches off the highway, two additions back onto it. Repeated L times.
Why is a block drawn as a highway with side roads? The vector on the highway is never replaced. Each sub-layer reads a normalised copy, computes something, and merges its result back by addition. Chapter 5 shows the second reason this matters: the highway is also the path along which learning signals travel backwards, unobstructed.

Two consequences, one now and one deferred. Now: because every sub-layer reads from and writes to the same stream, the stream is a shared workspace. A head in layer 8 can read what an MLP in layer 3 wrote, provided it wrote it in a direction the head's W_K can detect. Interpretability research treats the stream as exactly this, a bus that components communicate over public. Deferred: without residual connections, networks deeper than a dozen layers were extremely hard to train, and the reason is gradient flow. Chapter 5 makes that precise, and it is the main reason the plus sign is there.

4.3Normalisation: reset the size, keep the direction

The stream grows by addition, forty or a hundred times. Left alone, its size would drift: some tokens' vectors would become ten times larger than others depending on how many sub-layers had something to say. The matrices downstream would then see inputs of wildly varying scale, and the softmax inside attention, which is sensitive to score magnitude (Chapter 3), would sharpen or flatten for no semantic reason.

The fix is to rescale the vector to a standard size just before each sub-layer reads it. Modern models use RMSNorm: divide the vector by its root-mean-square, then multiply each coordinate by a learned gain.

Math box · RMSNorm
rms(x)  =  √( (x₁² + x₂² + … + x_C²) / C )          a single number: the typical size of a coordinate
x̂       =  x / rms(x)                                 same direction, coordinates now have rms 1
output  =  x̂ ⊙ g                                      g: a learned vector of C gains, one per coordinate

Only the length changes; the direction, which is where meaning lives (Chapter 2), is untouched. The gains g let training re-amplify particular coordinates if the model finds that useful. Older architectures used LayerNorm, which also subtracts the mean; RMSNorm drops that step because it turned out not to matter and costs time public (Llama 3 uses RMSNorm).

Static view of the widget. Input [1.2, −0.4, 0.3, 2.0, −1.5, 0.1, 0.8, −0.6] has rms 1.06; divided through, the pattern of the coordinates is identical and the output rms is 1. Scaling the input by 10 leaves the output unchanged.

Where the norm sits matters. The original 2017 transformer normalised after the addition ("post-norm"). Every frontier model since about 2020 normalises the copy that goes into the sub-layer and leaves the highway itself un-normalised ("pre-norm") public. The difference: with pre-norm the residual stream is a pure sum from embedding to output, never rescaled in between, and that keeps the backward path of Chapter 5 clean. With post-norm each layer's norm sits on the highway and distorts it.

4.4Positions: attention cannot tell order

A fact hiding in Chapter 3: nothing in attention knows where a token is. The score between "cat" and "it" is q_it · k_cat, which depends on the two vectors and not on whether "cat" is 2 or 200 tokens back. Shuffle the keys and every query gets exactly the same weights, reordered. The mask blocks the future, but among the past, attention is a bag. Yet "the dog bit the man" and "the man bit the dog" must get different next-token distributions. Position has to be put in.

Early transformers added a position vector to each token's embedding public. Current frontier models do something cleverer, called RoPE (rotary position embedding): they rotate the query and key vectors by an angle that grows with position, inside each attention head, just before the dot product.

Math box · rotating a pair of coordinates

Take two coordinates of a vector, (a, b), as a point in the plane. Rotating it by angle φ gives

(a, b)  →  (a·cos φ − b·sin φ,  a·sin φ + b·cos φ)

This is a matrix, so it is linear, and it preserves length. The key fact: rotate two vectors by the same angle and their dot product does not change (the angle between them is what the dot product measures, and both moved together). Rotate them by different angles φ₁ and φ₂, and the dot product changes as if only one had been rotated by φ₁ − φ₂.

RoPE splits the d coordinates of q and k into d/2 pairs, and rotates pair i of the token at position m by m·θᵢ. Each pair has its own base angle θᵢ, ranging from fast-turning (sensitive to nearby offsets) to very slow (sensitive to long-range offsets).

Now the score between a query at position m and a key at position n depends on the two content vectors and on m − n, the distance between them, and on nothing else. Not on m. Not on n. The head can learn "attend two positions back" as a specific rotation to match, and that rule works identically at position 5 and position 50,000. That is why RoPE is what every current open frontier model uses public and why it generalises to contexts longer than it was trained on better than the alternatives, with some adjustment (Chapter 12).

Static view of the widget. q₀ = [1.0, 0.3], k₀ = [0.8, 0.6], θ = 20°. At positions m = 5 and n = 2, q is rotated by 100° and k by 40°, and their dot product equals what you get by rotating q alone by (5 − 2)·20° = 60°. Moving both positions by the same amount leaves the score unchanged.

4.5The stack, and one token's journey

Assemble it. Embed the tokens, run L blocks, normalise, unembed, softmax. Every frontier model that has been described publicly is this, with the block details varying at the margins public.

ids [T]
  │  W_E lookup
  ▼
x [T, C]  ─────────────────────────────┐
  │                                    │
  │  ┌─ block 1 ─────────────────┐     │
  ├─►│ x += Attn(RMSNorm(x))     │     │  the residual stream:
  ├─►│ x += MLP(RMSNorm(x))      │     │  the same [T, C] tensor
  │  └───────────────────────────┘     │  all the way down
  │  ┌─ block 2 ─────────────────┐     │
  ├─►│ …                         │     │
  │        ⋮   (L blocks)              │
  ▼                                    │
RMSNorm  ◄─────────────────────────────┘
  │
  ▼  · W_U  [C, V]
logits [T, V]
  │  softmax per row
  ▼
p [T, V]      T distributions, one per position, each over the whole vocabulary

Read the last line again. The forward pass produces a next-token distribution at every position, all at once, from one pass. Position 3's distribution used only positions 1 to 3, thanks to the mask. During training every one of those T distributions is billed against its true next token; during generation only the last row is used. Chapter 7 is about that asymmetry.

Static view of the widget. Eleven stages: token id → embedding [C] → (norm, attention with residual, norm, MLP with residual) × L → final norm → unembedding [V] → softmax → loss. The shape stays [C] through every block and becomes [V] only at the end.

Notice what the stack does not contain. No recurrence: block 5 does not run again after block 6. No loop: a token's vector passes through each block exactly once. The depth L is a fixed number of refinement steps, and the only way to "think longer" about a token is to generate more tokens, each of which gets its own pass. That constraint is the starting point for the reasoning models of Chapter 16.

4.6Counting the parameters

You can now count every number in θ from the shapes, and the count reproduces published model sizes. Per block:

attention   W_Q [C, C]  +  W_O [C, C]  +  W_K [C, C·r]  +  W_V [C, C·r]      r = kv heads / q heads
            = 2C² + 2C²·r                                                     (r = 1 without GQA)
MLP         W_in [C, H] + W_out [H, C]  (+ W_gate [C, H] if gated)  = 2CH or 3CH
norms       2 gain vectors of C            (negligible)

whole model L · (attention + MLP)  +  W_E [V, C]  +  W_U [C, V]
By hand

Llama 3 8B: C = 4096, L = 32, H = 14336, V = 128256, 32 query heads and 8 KV heads so r = 0.25, gated MLP public.

attention per block   2·4096² + 2·4096²·0.25  =  33.6M + 8.4M   =   41.9 M
MLP per block         3 · 4096 · 14336                          =  176.2 M
one block                                                       =  218.1 M
× 32 blocks                                                     =    6.98 B
embedding + unembedding   2 · 128256 · 4096                     =    1.05 B
total                                                           ≈    8.03 B
$ python code/ch04/param_count.py
Llama 3 8B       total   8.03 B   per-layer attn    41.9 M  mlp   176.2 M   emb 1.05 B
Llama 3 70B      total  70.55 B   per-layer attn   151.0 M  mlp   704.6 M   emb 2.10 B
Llama 3.1 405B   total 405.85 B   per-layer attn   570.4 M  mlp  2617.2 M   emb 4.20 B

Three shapes and a formula give 8.03 billion, and the model is called 8B. The same formula gives 70.55B and 405.85B for the larger two. Two things to notice: the MLPs hold about four times the parameters of attention in every size, and the embedding tables, which dominate tiny models, are 1% of the 405B. When someone says "a 400-billion-parameter model", this is what they are counting, and now you can count it yourself.

Static view of the widget. The Llama 3 8B preset gives 8.03 B total: 70% in MLPs, 17% in attention, 13% in the two embedding tables. GPT-2 small at C = 768, L = 12 gives 162 M with the unembedding counted separately; GPT-2 ties it to the embedding table, hence the familiar 124 M, and even so the tables are nearly half the model.

4.7Beacon's numbers

QuantityLlama 3 8BLlama 3.1 405BEvidence
Blocks L32126public
Width C4,09616,384public
Query heads / KV heads32 / 8128 / 8public
Normalisation, positions, activationpre-norm RMSNorm, RoPE, SwiGLUpublic
Parameters8.03 B405.85 Bcomputed above; matches release
Closed frontier modelsSame block structure is the consensus assumption; depths, widths, and any departures are not disclosedinferred / unknown
Back of the envelope

What does one token cost, and where do the weights live? Chapter 2 found that a matmul costs about two operations per matrix entry per token. Almost all of θ is matrix entries, so a forward pass costs about 2N operations per token, where N is the parameter count. Attention's T² terms come on top and dominate only at long contexts.

Llama 3.1 405B
  operations per token, forward     2 × 405 × 10⁹  ≈  8 × 10¹¹   (0.8 TFLOP)
  weights in memory at 2 bytes      405 × 10⁹ × 2  ≈  810 GB
  largest single GPU memory (2025)                  ≈  192 GB

  ⇒ the weights alone need at least 5 GPUs before a single token can be processed
  ⇒ a 1,000-token reply is ≈ 10¹⁵ operations of matrix arithmetic, about one second of one GPU's peak

The first arrow is why Part 2 exists: a frontier model does not fit on one device, for training or for serving, and everything about how it is built follows from that. The second arrow is why a reply costs what it costs.

Break it

Remove the residual connections. Each sub-layer now replaces the vector. After one block the embedding is gone; after two, whatever block 1 found is gone. More importantly, Chapter 5's gradients must pass through every sub-layer's matrices in sequence, and the product of forty matrices is either vanishingly small or explosively large. Networks this deep without residuals did not train at all before 2015, and the residual connection is the reason they do now public.

Use post-norm instead of pre-norm. Trainable, but the norm now sits on the highway, rescaling the accumulated stream at every layer. Deep post-norm transformers need careful warm-up schedules and are prone to diverging early in training; pre-norm removed that fragility, which is why the switch was universal public.

Remove positional encoding. Attention sees a bag of past tokens. "Dog bites man" and "man bites dog" produce identical distributions at the final position. The MLP cannot help: it never sees more than one token. The model can still learn a great deal (which words co-occur) but nothing about order, and word order is most of grammar. (A subtle exception: with a causal mask, the model can in principle infer something about position from how many tokens are attendable, and models trained without explicit positions do work somewhat public. They are worse.)

Use one head of width C per layer. One weighting over positions per layer. The 32 simultaneous lookups become 32 sequential ones spread across layers, so a fact that needed "previous token and subject at the same time" now needs two layers, and the model is effectively shallower. Published ablations show quality dropping steadily as heads are removed at fixed width public.

Make it very deep and very narrow, or very wide and shallow. Same parameter count either way. The narrow model's residual stream has too few directions to hold the accumulating notes. The shallow model has too few refinement steps to compose them. The published sweet spot has depth growing slowly with width: 32 layers at 4k wide, 126 at 16k wide public. Chapter 8 returns to how such ratios are chosen.

Rebuild the model

Say it back. A block is two sub-layers, each of which reads a normalised copy of the token's vector, computes an update, and adds it back onto the vector. The attention sub-layer runs H heads of width C/H in parallel, each attending with its own softmax to a different aspect of the past, concatenates their outputs, and mixes them with W_O. The MLP sub-layer is Chapter 2's expand-bend-compress, per token. The vector that flows through, growing by addition, is the residual stream, and nothing is ever erased from it. RMSNorm resets its size before each read so that scale cannot drift; pre-norm keeps the highway itself untouched. Attention is blind to order, so RoPE rotates queries and keys by position-dependent angles, making every score a function of the distance between tokens. Stack L blocks, normalise, multiply by W_U to get V logits at every position, softmax. Count the matrices and you get the model's size to the second decimal; multiply by two and you have the operations per token. Beacon is 126 of these blocks at width 16k, 405 billion numbers, 810 GB of weights that do not fit on a GPU.

ids[T] W_E[T,C] × L blocks x += Attn(norm(x)) x += MLP(norm(x)) norm W_U[T,V] softmaxp [T,V] RoPE rotates q, k by position inside every head · the residual stream is the same tensor from W_E to the final norm
What is the whole chapter in one line? Embed, then L times add attention and add MLP to the same stream, then norm, unembed, softmax. The box from Chapter 1 is now fully open.
Exercises
  1. By hand. A toy model has C = 4, one head of width 4, and this residual stream after the embedding: x = [1, 0, 0, 0]. The attention sub-layer outputs [0, 0.5, 0, 0] and the MLP sub-layer outputs [−0.2, 0, 0.8, 0]. Write the stream after each addition. Then compute RMSNorm of the final vector (ignore gains). What would the final vector be without residuals? Which of the two can still tell you which token it started as?
  2. Calculation. Using the parameter formula, design a model with about 30 billion parameters, V = 128k, gated MLP with hidden 3.5·C, r = 0.125, and depth-to-width in the Llama 3 range. Give C, L, and the head count. Then compute the weights' size in gigabytes at 2 bytes per parameter and at 1 byte, and the forward operations per token.
  3. Code. Write a NumPy function block(X, params) that implements one pre-norm block with a single head and a ReLU MLP, using the attention code from code/ch03/one_head_by_hand.py and the MLP from code/ch02/mlp_by_hand.py. Run it twice in sequence on a 4 × 4 input and confirm the output shape is unchanged. Then delete the two additions and observe what the second block's input has lost.
Further reading