Layer Normalization
A TransformerBlock class builds its attention module, feed-forward network, two LayerNorm modules, and a dropout module from a configuration dictionary, but the forward method alone defines execution order.
Try it: Neural Network Forward Pass
How a neural network turns inputs into an output: each neuron computes a weighted sum plus bias, applies an activation function, and passes the result on.
How it works
- Each hidden neuron computes z = w1·x1 + w2·x2 + b.
- It applies an activation: ReLU keeps positive values and zeroes negatives; sigmoid squashes z into 0–1.
- The output neuron does the same with the hidden activations as its inputs.
- Changing any input, weight or bias changes every value downstream of it.
Default run (7 steps): Inputs x1 = 1, x2 = 0.5. Activation: ReLU. … Prediction y = ReLU(1.25) = max(0, 1.25) = 1.25.
Simplified: Tiny 2-2-1 network with hand-set weights, not a trained model. No training happens here. Activations offered: ReLU, sigmoid and sign (texts differ on the value of sign at exactly 0; here it is 0).
Loading the simulation…
Why the Order of Operations Matters
A common assumption among learners is that a transformer block is simply attention followed by a feed-forward network, with normalization placed wherever it happens to fit. Under that assumption, the order of the operations looks like a detail that can be ignored. The code for a transformer block shows otherwise. The same two components, attention and feed-forward, can be wrapped by normalization in two different ways, and the implementation commits to one of them.
This article corrects that belief in four moves. First you will predict which operation runs first on the input. Then you will see how the block is constructed from a configuration dictionary. Next you will follow the forward pass step by step, including the shortcut connections that add the block input back into its output. Finally you will examine the difference between Pre-LayerNorm and Post-LayerNorm, which is the point where the wrong belief does real damage.
What do you think happens?
A transformer block receives an input tensor x and must produce an output. Which operation touches x first?
Reveal answer
The first LayerNorm (norm1) touches x first, before attention runs.
Attention is the most prominent component, so it is natural to assume it sits at the front. The forward method shows that the input is first saved as a shortcut and then normalized by norm1 before it reaches the attention module. Keep this ordering in mind, because the rest of the article depends on it.
What the Block Builds in Its Constructor
The TransformerBlock class is a PyTorch module that inherits from nn.Module. Its constructor does not hard-code sizes. Instead it reads everything from a configuration dictionary called cfg, which is passed in once when the block is created. The source describes the block as including a multi-head attention mechanism and a feed-forward network, both configured from that dictionary, with the GPT_CONFIG_124M dictionary given as an example of such a configuration.
Inside the constructor, the block creates five components, and each one is stored as an attribute of the block. The attention module, stored as self.att, is a MultiHeadAttention built from the embedding dimension, the context length, the number of heads, the dropout rate, and a flag for query, key, and value bias. The feed-forward module, stored as self.ff, is a FeedForward network built directly from cfg. Two LayerNorm modules, norm1 and norm2, are both sized to the embedding dimension. Finally, a dropout module called drop_shortcut is created from the dropout rate.
The constructor only builds these parts. It does not decide the order in which they run on the data. That job belongs to the forward method, which is why the constructor alone cannot tell you how the block behaves. Two blocks with the same five components could in principle wire them differently, so the forward method is the place to look when you need to know what the block actually does.
Tracing the Forward Pass
The forward method is where the block does its work, and it is where most debugging questions are answered. Reading it in order gives a fixed sequence of actions. The block first saves the input as a shortcut, then normalizes x, runs attention, applies dropout, and adds the saved input back. The same pattern then repeats for the feed-forward half: save the current value as a new shortcut, normalize it, run the feed-forward network, apply dropout, and add the shortcut back.
- 1Save the block input x as a shortcut.
- 2Normalize x with norm1.
- 3Run the attention module
self.atton the normalized value. - 4Apply dropout with
drop_shortcut. - 5Add the saved shortcut back to the result.
- 6Save the current value as a new shortcut.
- 7Normalize with norm2.
- 8Run the feed-forward network
self.ff. - 9Apply dropout with
drop_shortcut. - 10Add the saved shortcut back to the result to produce the block output.
Read the diagram as two passes over the same data. The first pass takes the block input and produces the attention contribution, which is then added back to that input. The second pass takes that new value and produces the feed-forward contribution, which is again added back to what it started from. The output of the block is therefore the result of the second addition, not the raw output of either component.
- shortcut = x saves the block input before any processing
- norm1 runs before attention, so this block is Pre-LayerNorm
- x = x + shortcut is the first shortcut addition, covering the attention half
- norm2 runs before the feed-forward network, covering the second half
- the second shortcut addition produces the value the block returns
Shortcut Connections and the Second Path
A shortcut connection is a rule that adds the input of a block to the block's output. In the code, the shortcut is nothing more than a saved reference to a tensor, followed by a plus operation that combines it with the processed value.
The source states that this feature helps gradients flow through the network during training and improves the learning of deep models. It gives a specific reason for the design. A block that stacks attention and feed-forward components with additions back to the input keeps the original signal available alongside the processed signal. The processed path transforms the data, and the shortcut path carries the data through unchanged, so the two are combined at each addition.
Pre-LayerNorm Versus Post-LayerNorm
Pre-LayerNorm and Post-LayerNorm are not two names for the same arrangement. The source defines Pre-LayerNorm as applying layer normalization before each of the two components, which is what this block does. Older architectures, including the original transformer model, applied layer normalization after the self-attention and feed-forward networks, which is called Post-LayerNorm.
| Design | Where normalization runs | Used by this block |
|---|---|---|
| Pre-LayerNorm | Before each attention and feed-forward component | Yes |
| Post-LayerNorm | After the self-attention and feed-forward networks | No |
Placement of layer normalization in the two designs
The before-and-after view isolates the single difference that matters for the attention half. The same two operations appear in both arrangements, but their order is reversed. The block in this article is the Pre-LayerNorm form, because norm1 runs before the attention module and norm2 runs before the feed-forward module. A reader who assumes the two designs are interchangeable will misread every line that involves a norm.
Locating a Divergence in the Forward Pass
Suppose a block produces an output that does not match what a colleague expects. The fastest way to find the cause is to ask a question about each step in the forward pass: is the value at this point what I expect it to be? Because the block has a fixed sequence of steps, the first step where the expected and actual values disagree marks the place where the divergence starts.
Finding the first point of divergence
A colleague expects the block to return a value that has passed through both the attention half and the feed-forward half. The returned value looks as if the feed-forward contribution is missing. Where should you look first?
- 1Check the saved input
Confirm that shortcut = x was stored before any processing. If the saved value is wrong, both additions will be wrong.
- 2Check the output of norm1 and attention
Confirm that norm1 runs before
self.att, and that the attention output has the expected shape before the first addition. - 3Check the first addition
Confirm that the saved input is added after dropout. If the value after this addition is already wrong, the divergence starts in the attention half.
- 4Check the feed-forward half
Confirm that the current value is saved again before norm2, that
self.ffruns on the normalized value, and that the second addition uses the value saved in this half.
The first step whose value differs from expectation identifies where the divergence starts. If the first addition is correct but the second is not, the problem lies in the feed-forward half, not in attention.
This is the same reasoning used when stepping through the execution trace for this lesson. The method works because the forward method has a fixed order. You do not need to guess where the block went wrong. You check each stage in sequence and stop at the first stage that disagrees with what you expect.
Mistakes Beginners Make With Block Order
Common mistake Assuming attention runs first because it is the most prominent component
What it looks likeReading a block and expecting x to reach self.att before any other operation.
Why it failsThe forward method saves the input and normalizes it with norm1 before attention runs.
Do this insteadStart from the forward method and read the operations in the order they appear.
Common mistake Treating Pre-LayerNorm and Post-LayerNorm as interchangeable
What it looks likeMoving norm1 to after the attention module and assuming the block still behaves the same way.
Why it failsPre-LayerNorm applies normalization before each component, while Post-LayerNorm applies it after. The same operations in reversed order are a different design.
Do this insteadIdentify the placement first. In this block, norm1 and norm2 both run before their sublayers.
Common mistake Deleting the shortcut lines because they look like redundant additions
Why it failsThe source says shortcut connections help gradients flow through the network and improve the learning of deep models. Removing them changes what the block computes.
Common mistake Reading the constructor to determine the execution order
Why it failsThe constructor only creates the submodules. The source states that the forward method alone defines execution order.
Do this insteadUse the constructor to learn what exists, and the forward method to learn what runs and when.
Why Deep Stacks Are Hard to Train
A common intuition in machine learning is that a bigger network should be able to learn more. Adding layers gives a network more room to represent complicated relationships. In practice, however, stacking many layers together does not simply make learning easier. Training a deep network can become unreliable, and the network may fail to learn the patterns that would let it make accurate predictions.
Training a neural network is a search. The network has a set of parameters, called weights, and the learning process looks for a particular set of weights that minimizes the loss function. The loss function scores how far the network's outputs are from what they should be. A lower loss means the network is doing a better job on the training data.
The adjustments to the weights during this search are guided by gradients. A gradient carries information about how the loss would change if a weight were nudged slightly. For this article you only need the working idea: gradients are the signals that tell the learning process which way to move the weights. Deep mathematical understanding of gradients is not required to follow the material.
The source names two specific problems that cause this difficulty: vanishing gradients and exploding gradients. Before reading about them, try to reason about the core issue. Imagine a signal that is multiplied by a number, passed through a layer, and then multiplied again by the same kind of number at the next layer. The question is what happens to the size of that signal after many such steps.
Vanishing and Exploding Gradients
The source identifies vanishing or exploding gradients as the problem that can make training deep networks challenging. These two failure modes are named together because they share a cause and a consequence. Both are problems with the size of the gradients that flow through the network during training. Both lead to unstable training dynamics.
The diagram uses an illustrative factor of 0.5 at each layer. It is a simplified picture of the compounding idea, not a measurement of a real network. Each backward step multiplies the gradient by a number, so the gradient reaching earlier layers is the product of many such numbers.
The formula shows the same compounding in both directions. Over ten steps, a factor of 2 grows a signal to about 1024, which is the exploding pattern. A factor of 0.5 shrinks it to roughly one thousandth of its starting size, which is the vanishing pattern. Real networks do not multiply by one fixed number, but the compounding logic is the same, and it explains why depth amplifies whatever tendency a gradient has to shrink or grow.
Symptoms That Motivate Normalization
The source describes the effects of these gradient problems in three connected ways. First, they lead to unstable training dynamics, meaning the training process does not progress in a steady, predictable manner. Second, they make it difficult for the network to effectively adjust its weights. Third, as a result, the learning process struggles to find a set of weights that minimizes the loss function.
The consequence the source emphasizes is a failure to learn the underlying patterns in the data. The network may then be unable to make accurate predictions or decisions. This is a failure of learning itself, not merely a slower version of learning. A network with unstable gradients can end up with weights that do not capture the structure the task requires.
Putting the pieces together gives the motivation for layer normalization. Deep networks can suffer from unstable training, and the gradients that guide weight updates can shrink or grow as they pass through layers. Any remedy has to address that instability so that the network can again adjust its weights and find parameters that minimize the loss. The need is a need for stability in the training signal, and that is the problem layer normalization is introduced to address.
Checking Your Reading of the Forward Pass
A block's forward method runs self.att on the output of norm1, and runs self.ff on the output of norm2. Each sublayer's output is added back to the value saved just before that sublayer's normalization. Is this block Pre-LayerNorm or Post-LayerNorm, and what does the block output as its final value?
Hints
- Ask whether the normalization runs before or after each sublayer.
- Find the addition that comes last in the method.
- The block output is the result of the second addition, not the raw output of the feed-forward network.
Key Takeaways
- A TransformerBlock builds its attention module, feed-forward network, two LayerNorm modules, and a dropout module from a configuration dictionary, but the forward method alone defines execution order.
- The forward pass saves the input as a shortcut, normalizes it with norm1, runs attention and dropout, and adds the shortcut back; then it repeats the pattern with norm2 and the feed-forward network.
- Shortcut connections add the block input to the block output, which the source says helps gradients flow and improves learning in deep models.
- This block uses Pre-LayerNorm, which applies normalization before each component, rather than Post-LayerNorm, which applies it after.
- Vanishing and exploding gradients compound across many layers, producing unstable training, and this instability is the motivation for layer normalization.