Normalization layers and residual connections
Before this lesson: lesson 03, backpropagation (adjoints, Jacobians) and lesson 04, activations and initialization (variance propagation). You also need the mean and variance of a list of numbers, and the projection onto a vector (linear algebra lesson 03). Time: about 75 minutes, plus the notebook.
Why this matters #
- ResNets (He et al., 2016) stack conv → BatchNorm → ReLU inside residual blocks and train with more than 100 layers. Before them, deeper plain networks had a higher training error.
- Transformers put a LayerNorm (or RMSNorm in LLaMA) in every block, around a residual stream. The position of that norm (pre-LN or post-LN) decides whether training needs a learning-rate warmup.
- Diffusion models and science models (weather, climate, physics) use GroupNorm or LayerNorm, because their batches per GPU are tiny and BatchNorm statistics would be noise.
- Interviewers ask you to derive the BatchNorm backward pass and to explain why transformers do not use BatchNorm. This lesson does both.
1. Predict first #
2. The picture #
Normalize, then rescale #
Take one feature and a batch of numbers. BatchNorm does three things:
- subtract the batch mean ;
- divide by the batch standard deviation ; now the numbers have mean 0 and variance 1;
- multiply by a learned scale and add a learned shift .
Add a constant to every input, or multiply every input by a positive constant: steps 1 and 2 undo it, so the output does not change. The layer forgets the location and the scale of the batch, and put back a location and a scale that the network learns.
Drag the blue dots, then press the shift and scale buttons. Row 2 (training mode, batch statistics) does not move when you shift or scale the whole batch. Now press Training step a few times. In eval mode (row 3, magenta) BatchNorm uses the running statistics (the magenta band in row 1). After one step they are still far from the batch, so the same input gives a very different output. Blue = forward values computed with the batch; magenta = the train/eval mismatch.
Which numbers share one mean and variance #
The normalization layers differ only in the group of numbers that share one .
| Layer | Input | Statistics over | One per | Learned |
|---|---|---|---|---|
| BatchNorm | or | the batch (and the image positions) | channel | |
| LayerNorm | the features | token | ||
| RMSNorm | the features, no mean | token | ||
| GroupNorm | a group of channels and the positions | example and group |
The key difference: BatchNorm mixes the examples of a batch; LayerNorm, RMSNorm and GroupNorm never do. The output for one example depends on the other examples only with BatchNorm.
Pick a layer and click any cell. Each panel is one example of the batch. The yellow cells share one mean and one variance. BatchNorm lights up one channel in every example; LayerNorm stays inside one example (images) or one token (tokens). Switch to tokens (B, T, D) and BatchNorm: the group contains other sequences and also future tokens.
Residual connections, simplest case #
Take 50 scalar layers. In a plain stack , the chain rule multiplies 50 derivatives . If each has size , the product has size : the first layer receives no signal.
In a residual stack , the derivative of one block is . When is small, this factor is close to 1, so the product of 50 such factors does not collapse to 0. Section 4 computes instead of for the same layer derivatives.
Move the depth and the size . Both stacks use the same layer derivatives . The plain product has size : a straight line on the log axis, down for and up for . The residual product stays near 1 for small . Now choose all + and a large : the residual product grows too. The identity path helps only while the branches are small.
A batch of one channel is . You change it to . How does the BatchNorm output (training mode) change?
. The mean becomes and the standard deviation , so . (With the result changes very slightly.) The mean and the spread do change, but the normalized values do not.
A transformer processes a tensor of shape with LayerNorm. How many numbers share one mean and variance, and how many means are computed?
LayerNorm normalizes each token over its own features. There is one token for every pair (sequence, position), so means. The first option describes BatchNorm over the batch and time axes.
3. Definitions and main results #
BatchNorm, LayerNorm and RMSNorm (forward) #
Take one group of numbers : one channel over the batch for BatchNorm, the features of one token for LayerNorm. Then
- is the normalized input; (scale, or gain) and (shift, or bias) are learned.
- (PyTorch default ) keeps the division safe when .
- The variance used to normalize is the biased one: divide by , not by .
- In LayerNorm, and are vectors over the features and act entry by entry.
RMSNorm (Zhang and Sennrich, 2019) drops the mean and :
It needs one reduction (a sum over the features) instead of two, and gives similar quality. LLaMA uses it; T5 uses the same layer under the name "layer norm without bias and mean".
Invariances. Let , , , and the all-ones vector. BatchNorm and LayerNorm give the same output for and for . RMSNorm gives the same output for and , but not for a shift.
BatchNorm in training and in eval mode #
In training, BatchNorm normalizes with the batch statistics. It also updates running estimates , of the mean and variance with momentum (PyTorch default 0.1, the weight of the new value):
The running variance uses the unbiased estimate ; the normalization itself uses the biased one. In eval mode BatchNorm uses and . Then it is a fixed affine map per channel,
which can be folded into the linear or convolution layer before it. LayerNorm and RMSNorm do not use the batch, so they compute the same function in training and in eval mode.
Scale-invariant weights
Suppose BatchNorm or LayerNorm follows a linear layer with weight (for LayerNorm, the linear layer must have no bias; BatchNorm's mean subtraction removes a bias anyway). With , the loss does not change when you scale : for every .
- Differentiate in at : . The gradient is orthogonal to .
- Differentiate in : , so .
- One SGD step: . So SGD never makes smaller.
The direction moves by about , and itself scales like by step 2. So the effective learning rate of the direction is about : it falls as grows. Weight decay keeps small and so keeps the effective learning rate up (lesson 06).
The backward pass removes two components #
Theorem (backward pass of BatchNorm and LayerNorm). Take one group in training mode. Let be the upstream gradient and . Then
Here and is the entry-wise product.
Why this is true, in one picture. Moving along (a shift) or along (a scale change) does not change the output. So the gradient with respect to cannot have a component in these two directions. The formula takes and removes exactly these two components.
Proof.
- From we get and directly, and .
- The mean: .
- The variance: , because . So .
- The quotient rule for :
- The chain rule: .
Geometry. Write with . We have (because ), , and , which equals when . In that case is the orthogonal projector onto the complement of (projections, linear algebra lesson 03). Hence
- exactly (for every ), and
- (exactly for ).
LayerNorm has the same formula over the feature axis of each token, with ; and are then summed over all tokens.
RMSNorm. The same steps without the mean give with . Only one component is removed, the one along , which matches the single invariance .
Step through the backward pass. Start at step 1: the faint bars are the upstream gradient (drag their tops). Step 2 removes the mean, so . Step 3 removes the part along (thin blue bars), so . Step 4 divides by . Orange = gradients, blue = forward values. Try Random g and the other batches: both checks are always 0 after step 3.
In the backward pass of BatchNorm (training mode, ), the upstream gradient is and . What is ?
lies entirely along , the shift direction. The backward pass removes this component: , and . Adding the same amount to every output is the job of ; it cannot be done by moving . (Here .)
Why transformers use LayerNorm or RMSNorm #
BatchNorm makes the output of one example depend on the other examples of the batch. In transformers this causes concrete problems:
- Small or variable batches. Large models fit only a few sequences on one device, so the batch statistics are noisy. Synchronizing BatchNorm across devices costs communication in every layer.
- Train/eval mismatch. Training uses batch statistics; inference uses running ones. When a language model generates text, it processes one new token of one sequence at a time: there is no batch.
- Padding and variable lengths. Pad tokens enter the statistics unless they are masked.
- Causal leakage. Statistics over batch and time include future tokens, which a causal language model must not see.
LayerNorm normalizes each token over its own features. It does not depend on other examples or positions, it computes the same function in training and inference, and it works one token at a time with a KV cache (lesson 11).
The identity path of residual networks #
A residual block computes . is the branch (for example conv–BN–ReLU–conv, or attention, or an MLP); the term is the skip connection. The sequence is often called the residual stream.
Theorem (the identity path). Let and let be the Jacobian of branch . Then for :
Proof.
- Differentiating the update gives .
- The chain rule multiplies these factors for , later layers on the left.
- Expanding the product gives one term for every subset of layers; the empty subset gives .
Why it helps. For the gradient (a row vector), . The term reaches every layer unchanged, however small the are: an addition node copies its upstream gradient to both inputs (lesson 03). A plain stack gives , which shrinks or grows exponentially unless every is close to an isometry. He et al. (2016) observed this as the degradation problem: deeper plain networks had a higher training error than shallower ones. A residual network with small branches starts close to the identity map, which is easy to optimize.
The expansion has terms, one per path through the network. Veit et al. (2016) found that most of the gradient flows through the short paths, so a ResNet behaves a bit like an ensemble of shallower networks.
The forward pass can still explode. At initialization the branch output is uncorrelated with (the weights have mean zero), so
A He-initialized branch keeps the variance of its input, so the variance doubles in every block. Standard fixes:
- a normalization inside the branch (pre-LN, below);
- zero initialization of the last layer, or of the last , in each branch, so that each block starts as the identity;
- scaling the branch by about . GPT-2 scales the initial weights of the layers that write into the residual stream by , where is the number of residual layers.
A residual block with a linear branch: with (column vectors). What is ?
The block is the map , so its Jacobian is . The identity part copies the gradient back unchanged; the branch adds the small correction . A plain block would have Jacobian , with singular values : ten such blocks shrink the gradient by .
Post-LN and pre-LN transformer blocks #
The original Transformer and BERT use post-LN. GPT-2, LLaMA (with RMSNorm) and most modern models use pre-LN.
- Identity path. In pre-LN, , so with no LayerNorm on the direct path. In post-LN, the gradient passes through LayerNorm Jacobians. Pre-activation ResNets (He et al., 2016, "Identity Mappings in Deep Residual Networks") made the same change for CNNs: BN and ReLU move into the branch, and the skip path is a pure identity.
- Gradient size at initialization (Xiong et al., 2020, Theorem 1). For the weight of the last FFN layer and width , the gradient norm is bounded by for post-LN, independent of the depth , and by for pre-LN. The reason: the Jacobian of a LayerNorm has norm , and in pre-LN the squared norm of the residual stream grows linearly with depth, so the final LayerNorm divides the gradient by about .
- Warmup. Large gradients near the output make a large initial learning rate unstable in post-LN. A learning-rate warmup (start with a tiny learning rate and increase it over the first steps) keeps the first updates small. Xiong et al. trained pre-LN transformers without warmup to similar results in less time.
- Final norm. In pre-LN the residual stream itself is never normalized, so the stack needs a final LayerNorm before the output head.
- Growth of the residual stream. In pre-LN each branch adds an amount to a stream whose squared norm grows about linearly in . So block changes the stream by a relative amount of about : late blocks change it less. Post-LN, when it trains, can reach slightly better quality; DeepNorm (Wang et al., 2022) up-weights the skip path in post-LN and trains transformers with 1000 layers.
Change the depth, then switch to the residual stream. These are measurements at initialization (PyTorch encoder blocks, width 64, Xavier init, random data, mean of 5 seeds). The post-LN gradient of the last block stays near 0.35 for every depth. The pre-LN one shrinks like : the column "pre " stays near 0.5. In the second view, the pre-LN residual stream grows about linearly with depth, while post-LN keeps it at 1.
Answers to section 1. (1) Zero. BatchNorm gives the same output for and , so for every . Differentiating in gives . (2) The variance doubles in every block: . Residual connections protect the identity path of the gradient, not the forward scale; normalization or branch scaling is still needed.
4. Worked example #
(a) BatchNorm forward. One channel, training mode, batch , , , , momentum 0.1, running mean 0 and running variance 1 before the step.
- Mean: . Deviations: .
- Biased variance: , so .
- Output: .
- Running mean: .
- Running variance: the unbiased estimate is , so .
- Eval mode now maps to . Training mode mapped it to 0. After one step the running statistics are still far from the data.
(b) BatchNorm backward. Take , so (with ).
- and .
- and .
- The third term is .
- So the bracket is :
- : ;
- : ;
- : ;
- : .
- .
- Checks: the entries sum to 0, and .
- Meaning: raising lowers . It raises the mean and shrinks the spread, and both push further below zero.
(c) The same vector as one token. LayerNorm over its features gives the same . RMSNorm: , so . It is not centered.
(d) Residual vs plain, 50 scalar layers. Let the layer derivatives ( for the plain stack, for the residual branches) be for 25 layers and for the other 25.
- Plain: .
- Residual: .
A batch goes through BatchNorm in training mode with , , . What is ?
; the deviations are ; the biased variance is . So . The third option divides by the unbiased variance , which is what the running estimate uses, not the normalization.
5. In code #
PyTorch and autograd check the worked example. PyTorch refuses in training, so we use .
import torch
import torch.nn.functional as F
x = torch.tensor([[0.], [2.], [4.], [6.]], dtype=torch.float64, requires_grad=True)
bn = torch.nn.BatchNorm1d(1, eps=1e-12, momentum=0.1, dtype=torch.float64)
y = bn(x) # training mode: batch statistics
print(y.detach().ravel().numpy().round(4)) # [-1.3416 -0.4472 0.4472 1.3416]
print(round(bn.running_mean.item(), 4), round(bn.running_var.item(), 4)) # 0.3 1.5667
y[0, 0].backward() # upstream gradient (1, 0, 0, 0)
dx = x.grad.ravel()
print((dx * 5 ** 0.5).numpy().round(4)) # [ 0.3 -0.4 -0.1 0.2]
xhat = y.detach().ravel()
print(abs(dx.sum().item()) < 1e-12, abs((dx @ xhat).item()) < 1e-12) # True True
bn.eval() # now uses the running statistics
print(round(bn(torch.tensor([[3.]], dtype=torch.float64)).item(), 3)) # 2.157
t = torch.tensor([0., 2., 4., 6.], dtype=torch.float64)
print(F.rms_norm(t, (4,), eps=1e-12).numpy().round(4)) # [0. 0.5345 1.069 1.6036]
The theorem of Xiong et al. with PyTorch's own blocks (norm_first=True is pre-LN):
import torch
import torch.nn as nn
def last_ffn_grad(norm_first, L, d=64, seed=0):
"""At init: gradient norm of the last block's second FFN weight, and mean |x_L|^2 / d."""
torch.manual_seed(seed)
blocks = nn.ModuleList(
nn.TransformerEncoderLayer(d, 4, 4 * d, dropout=0.0, batch_first=True, norm_first=norm_first)
for _ in range(L))
for p in blocks.parameters():
if p.dim() == 2:
nn.init.xavier_normal_(p)
final = nn.LayerNorm(d) if norm_first else nn.Identity() # pre-LN needs a final norm
head = nn.Linear(d, 100)
x, target = torch.randn(8, 16, d), torch.randint(0, 100, (8, 16))
for blk in blocks:
x = blk(x)
loss = nn.functional.cross_entropy(head(final(x)).flatten(0, 1), target.flatten())
loss.backward()
return blocks[-1].linear2.weight.grad.norm().item(), (x.detach() ** 2).sum(-1).mean().item() / d
for L in (6, 12, 24, 48):
g_post, _ = last_ffn_grad(False, L)
g_pre, stream = last_ffn_grad(True, L)
print(f"L={L:2d} post-LN {g_post:.3f} pre-LN {g_pre:.3f} pre-LN |x_L|^2/d {stream:.1f}")
# L= 6 post-LN 0.363 pre-LN 0.213 pre-LN |x_L|^2/d 3.8
# L=12 post-LN 0.371 pre-LN 0.162 pre-LN |x_L|^2/d 6.8
# L=24 post-LN 0.373 pre-LN 0.096 pre-LN |x_L|^2/d 13.6
# L=48 post-LN 0.398 pre-LN 0.074 pre-LN |x_L|^2/d 34.1
Post-LN stays between 0.36 and 0.40 at every depth. Pre-LN falls by a factor from to , and its residual stream grows about linearly with depth. These are single random draws, so the last digit is noise (the figure in section 3 averages 5 seeds). The exact constants of Xiong et al. hold in their simplified setting, not exactly here.
6. Where it shows up #
- CNNs. Conv → BN → ReLU is the standard unit. For inference, BN is folded into the conv weights, also before post-training quantization. Fine-tuning with small batches often freezes BN (keeps it in eval mode).
- Small batches. GroupNorm (Wu and He, 2018) computes statistics over groups of channels within one example. It replaces BN when the batch per device is tiny: detection, segmentation, diffusion U-Nets.
- Transformers. Pre-LN with RMSNorm (LLaMA), a final norm, and extra norms for stability such as QK-norm (lesson 12).
- ML for science. Global weather and climate fields are large, so the batch per GPU is often 1 or 2 and BN statistics are useless. ViT-style Earth-system models use LayerNorm; diffusion U-Nets use GroupNorm. DiT's adaptive LayerNorm (adaLN-Zero) computes and from the condition (the diffusion time step, a class label, or a physical input) and initializes the branch scale at zero, so each block starts as the identity.
- Distributed training. With data parallelism, BN computes its statistics on each GPU's local
batch.
SyncBatchNormgives global statistics at the cost of one all-reduce per BN layer; LayerNorm needs no communication.
7. Common mistakes #
- Forgetting
model.eval(). BN then normalizes test data with the test batch statistics. With one value per channel, and the output is (PyTorch'sBatchNorm1draises an error in that case). - Wrong variance. Normalize with the biased variance (divide by ); the running estimate uses .
- Momentum convention. PyTorch's
momentum=0.1is the weight of the new batch value; Keras'smomentum=0.99is the weight of the old running value. - Wrong LayerNorm axis. LayerNorm in a transformer normalizes over the features of each token, not over the batch and not over the sequence.
- Pre-LN without a final norm. The output head then sees a stream whose scale grows with depth.
- "Residual connections prevent all instability." The forward variance still grows (doubles per block for unscaled branches) unless the branches are normalized or scaled.
8. Check yourself #
- BatchNorm2d on a tensor of shape : how many values enter each mean, how many
learned parameters are there, and how many running values?
Answer
Each channel's statistics use values. There are learned parameters () and running values (mean and variance). - Show in one line, without the backward formula, that for LayerNorm.
Answer
LayerNorm gives the same output for and , so . - Which component of does the RMSNorm backward pass remove, and why only one?
Answer
The component along (the radial direction): . RMSNorm is invariant only to , not to shifts, so the mean direction stays. - An eval-mode BN follows a linear layer (row vector ). Fold the two into one affine layer .
Answer
With (one entry per output channel): (column of times ) and . - 24 residual blocks without normalization. Each branch output has variance and is uncorrelated with . What is ?
Answer
Each block multiplies the variance by , so (close to ), instead of for unscaled branches. - In a pre-LN block, set the output projection of attention and the second FFN matrix (weights and biases) to zero. What does the block compute? And a post-LN block?
Answer
Pre-LN: both branches output 0, so the block is the identity and its Jacobian is . Post-LN: , which is not the identity; the gradient still passes through two LayerNorm Jacobians.
Write the BatchNorm forward pass for one group .
, , , .
Over which numbers does BatchNorm compute one mean for an tensor?
One channel over all examples and all positions: numbers per mean, means.
Over which numbers does LayerNorm compute one mean in a transformer with input ?
The features of one token. There are means.
Write the RMSNorm formula.
with . No mean subtraction and no .
What are BatchNorm's running statistics, and when are they used?
Moving averages of the batch mean and (unbiased) variance, updated with momentum 0.1 in training. They replace the batch statistics in eval mode.
Which two components of does the BatchNorm/LayerNorm backward pass remove?
The component along (shift) and the component along (scale): .
Why is through BatchNorm in training mode?
The output does not change when every is shifted by the same ; differentiating in gives the sum.
For , what is ?
with ; it equals plus terms with Jacobians, so the gradient has an identity path.
Why does BatchNorm not suit transformers? Give one reason.
It depends on the other examples in the batch: statistics are noisy for small batches, there is no batch when decoding one token, pad tokens and future tokens leak into the statistics.
What is the difference between post-LN and pre-LN?
Post-LN: . Pre-LN: , with a pure identity path and a final LayerNorm.
9. Interview angle #
- "BatchNorm at train and test time?" — Batch statistics in training, running averages in eval (unbiased variance in the update). In eval it is a fixed affine map that can be folded into the previous layer.
- "Derive the BatchNorm backward pass." — The five steps of section 3. The result projects out and ; check it with .
- "Why LayerNorm in transformers?" — Batch dependence: small batches, batch-1 decoding, padding, causal leakage. LayerNorm is per token and the same in training and inference.
- "Why do ResNets train with 1000 layers?" — The identity term in , plus normalization or branch scaling to control the forward variance.
- "Why do post-LN transformers need warmup?" — Their gradients near the output are large and do not shrink with depth at initialization (Xiong et al.); pre-LN avoids this but needs a final norm.
10. Practice #
The notebook for this lesson runs in Colab. Each exercise has a check cell and a folded solution.
- BatchNorm forward with running statistics, in training and eval mode, checked against
nn.BatchNorm1d. - LayerNorm and RMSNorm forward, checked against PyTorch.
- The closed-form BatchNorm backward pass, checked against autograd.
- Gradient norm per layer of a 30-layer plain MLP and of the same MLP with residual connections, at initialization.
- Pre-LN and post-LN stacks: gradient norm per block at initialization.
11. Watch and read #
- d2l.ai, §8.5 Batch normalization, including layer normalization and prediction mode.
- Simon Prince, Understanding Deep Learning, free PDF: chapter 11 (§11.2 residual blocks, §11.3 exploding gradients in residual networks, §11.4 batch normalization).
- Chris Bishop and Hugh Bishop, Deep Learning: Foundations and Concepts, §7.4 Normalization.
- Ioffe and Szegedy, Batch Normalization (2015); Ba, Kiros and Hinton, Layer Normalization (2016); Zhang and Sennrich, Root Mean Square Layer Normalization (2019); Wu and He, Group Normalization (2018).
- He et al., Deep Residual Learning for Image Recognition and Identity Mappings in Deep Residual Networks (2016).
- Xiong et al., On Layer Normalization in the Transformer Architecture (2020), §3.3–3.4.
Runs in your browser with nothing to install. The notebook repeats the key demos and has exercises with checks; solutions are inside, folded.