LayerNorm, GELU, MLP and the residual connection — the four pieces that let you stack 48 layers without training blowing up.
Attention solves the transport of information: it moves content from one position to another. But notice what it is, mathematically: a weighted average of
And there lies the problem. Stacking linear operations gains nothing: the composition of two linear transformations is another linear transformation. A hundred layers of pure attention would have exactly the same expressiveness as a single layer. You would gain nothing by stacking.
We need non-linearity. And we need a place where the model can process the information attention brought in, instead of merely transporting it. That place is the MLP.
Attention = communication. The tokens talk to each other. It is where information travels between positions.
MLP = computation. Each token thinks alone, in silence. No information crosses positions here — the MLP processes each token independently of the others.
A Transformer block is one round of conversation followed by one round of reflection. Twelve blocks = twelve rounds. It is one of the most useful descriptions there is, and worth memorizing.
The MLP is surprisingly simple: two linear layers with a non-linearity in between.
The shape is characteristic: expand, process, contract. Why expand to 3072 before coming back? Because the wide space gives the model room to separate patterns that are tangled in 768 dimensions — it is the same principle as a kernel in an SVM. The wide layer is where concepts become linearly separable.
The MLP has twice as many parameters as attention in each block:
| Component | Calculation | Parameters | % |
|---|---|---|---|
Attention — c_attn (QKV) | 768 × 2304 | 1,769,472 | 25% |
Attention — c_proj ( | 768 × 768 | 589,824 | 8% |
MLP — c_fc (expands) | 768 × 3072 | 2,359,296 | 33% |
MLP — c_proj (contracts) | 3072 × 768 | 2,359,296 | 33% |
| Total per block | ≈ 7,079,000 | 100% | |
There is a strong line of research (Geva et al., 2021) showing that MLPs work as key-value memories. The first layer (c_fc) detects patterns — "the current context is talking about the capital of a European country" — and the second (c_proj) writes the corresponding information back into the residual stream: "so push up the probability of Paris".
This has a practical consequence: when someone says that "an LLM's factual knowledge is in the weights", the weights in question are, for the most part, the MLPs, not attention. Model fact-editing techniques (ROME, MEMIT) operate exactly there.
GPT-2 uses GELU (Gaussian Error Linear Unit). The difference from the classic ReLU matters:
Read GELU as a probabilistic gate: instead of cutting sharply at zero, it multiplies the input by the probability that it "deserves to pass". Very negative values are nearly zeroed out, very positive values pass almost untouched, and in between there is a smooth transition.
Two concrete advantages over ReLU:
In practice, GPT-2 uses a hyperbolic-tangent approximation of GELU — cheaper to compute and numerically identical for the model's purposes.
If you can keep only one thing from this chapter, keep this one. The residual connection is what makes deep networks trainable, and it is ridiculously simple:
In a deep network without residuals, the gradient has to cross every layer by successive multiplication. If each layer multiplies the gradient by, say, 0.9, then after 48 layers what remains is 0.948 ≈ 0.006 — the gradient has vanished. The first layers never learn. This is the classic vanishing gradient problem, which stalled deep learning for years.
With the residual, the derivative of the sum is:
There is a better way to see this, and it changed how people think about Transformers. Instead of "layers that transform", think of a shared blackboard of 768 dimensions that runs through the entire model from start to finish.
This board is called the residual stream. It is the central concept of modern Transformer interpretability, and it explains why you can "read" what the model is thinking in the intermediate layers — the information is literally accumulated there, available.
As the vectors pass through dozens of blocks adding contributions, their magnitudes tend to grow out of control. LayerNorm renormalizes:
BatchNorm normalizes each dimension across the batch. That does not work for language: the texts in a batch have different lengths, and the result for one example would come to depend on the other examples that happened to be in the same batch — which breaks at inference, when you process a single text. LayerNorm normalizes within each token, independently. That is why NLP uses LayerNorm and vision uses BatchNorm.
This is the most important architectural change between GPT-1 and GPT-2, and it is only a matter of where to put the LayerNorm.
| Post-LN (GPT-1, BERT, 2018) | Pre-LN (GPT-2 onward) |
|---|---|
x = LN(x + Attention(x))x = LN(x + MLP(x)) |
x = x + Attention(LN(x))x = x + MLP(LN(x)) |
| The LayerNorm sits inside the residual path. The gradient is renormalized at every layer. | The LayerNorm sits on the branch. The residual path becomes a completely clean highway from end to start. |
| Needs careful learning-rate warmup, and becomes unstable above ~20 layers. | Trains stably with 48+ layers, no aggressive warmup needed. |
This change alone is what allowed GPT-2 XL to have 48 layers. With Post-LN, training 48 layers would be a battle of numerical stability. After it, normalizing before attention became the standard choice for large Transformers, often in the form of RMSNorm.
GPT-2 also adds a final LayerNorm (called ln_f) after the last block, before projecting to the logits — needed precisely because, with Pre-LN, the output of the last block has never been through a normalization.
class Bloco(nn.Module): def __init__(self, d=768, h=12): super().__init__() self.ln_1 = nn.LayerNorm(d) self.attn = AtencaoMultiCabeca(d, h, causal=True) self.ln_2 = nn.LayerNorm(d) self.mlp = nn.Sequential( nn.Linear(d, 4 * d), # c_fc : expands 768 -> 3072 nn.GELU(), nn.Linear(4 * d, d), # c_proj: contracts 3072 -> 768 ) def forward(self, x): # communication: the tokens talk x = x + self.attn(self.ln_1(x)) # computation: each token thinks alone x = x + self.mlp(self.ln_2(x)) return x
Two lines in the forward. Notice x = x + … in both: the original x is never destroyed, only added to. It is the blackboard being filled in.
The flow inside a block (5 tokens, GPT-2 small):
x (5, 768) ---------------------------+ keeps the copy
ln_1 (5, 768) |
attn (5, 768) |
v
x = x + attn(ln_1(x)) (5, 768) <- sum
|
+----------------------------------------------+
ln_2 (5, 768) |
c_fc (5, 3072) <- expands 4x |
GELU (5, 3072) <- non-linearity |
c_proj (5, 768) <- contracts |
v
x = x + mlp(ln_2(x)) (5, 768) <- sum
INPUT SHAPE = OUTPUT SHAPE. That is why you can stack
12, 24, 36 or 48 of these without changing anything else.
.venv\Scripts\python.exe lab\05_block.py
If information accumulates in the residual stream, you can peek at what the model "is thinking" at each layer: just apply ln_f and the output matrix to an intermediate state. Running this on gpt2 with the prompt "The capital of France is", the following appears:
' destro', ' mathemat'). No context has been processed yet.' now', ' not', ' also'. It has solved the grammar ("after is an adverb usually comes") before any content. Syntax first, semantics later.' France'=0.62 and ' Paris'=0.18 appear out of nowhere. Factual knowledge enters here — in the MLPs of the middle layers, exactly where interpretability research says the facts live.' the'. The model gave up on Paris.That last step is the finding. gpt2 small knew the answer at block 10 and lost it along the way — which matches exactly what it actually generates: "The capital of France is the capital of the French Republic…". gpt2-medium, in the same test, gets it right.
The lesson: the difference between a model that "knows" and one that "answers" is not only about having the information stored. It is about managing to sustain it all the way to the output. That is a good part of what scale buys — and you will measure it in chapter 06.
ln_f at the end while GPT-1 does not?x + f(x), what happens if f learns to always return zero? Is that a problem or a desirable property? (Hint: think about what it means for a layer to "give up" contributing.)The primary sources for the concepts in this chapter. The numbers in the text were measured in the lab; the ideas come from here.
Technical terms here carry their machine-learning meaning. The course glossary shows that meaning next to the others the same words have in IT.
You now know all the pieces. Chapter 06 assembles them into the complete GPT-2
and shows what really changes when you go from 124 million to 1.5 billion parameters.