Part 3 of 83 min read

Layer Normalization

A technique that stabilizes training by making the next layer's input scale independent of how large the values from earlier layers grew. It runs identically in training and at inference.

LLMGPTlayer normalizationfundamentalsplayground
r50k_baseprivate
5 tokens ·

Layer normalization rewrites one token's vector using nothing but that vector: subtract the vector's own mean, divide by its own standard deviation, then multiply by a learned gain and add a learned bias, one of each per dimension.

what reaches the norm: tok_emb(ids) + pos_emb(positions)−+
X[5, 4]
thex^(1)1.33-0.311.63-0.55mean 0.52var 0.93
matchx^(2)-0.291.031.450.36mean 0.64var 0.44
didx^(3)-0.381.771.04-0.85mean 0.40var 1.11
notx^(4)1.70-0.300.970.84mean 0.80var 0.51
matchx^(5)0.630.991.39-0.37mean 0.66var 0.43
standardized: (x − mean) / sqrt(var + eps)−+
Standardize(X)[5, 4]
the0.83-0.871.15-1.11mean 0.00var 1.00
match-1.400.591.23-0.42mean 0.00var 1.00
did-0.731.300.61-1.18mean 0.00var 1.00
not1.25-1.540.230.05mean 0.00var 1.00
match-0.050.511.12-1.58mean 0.00var 1.00

Values are rounded to 2 decimals.

standardization only
−+
γ (gain)1.001.001.001.00
β (bias)0.000.000.000.00
the layer's output: at 1 and 0 the pair changes nothing
LayerNorm(X)[5, 4]
the0.83-0.871.15-1.11mean 0.00var 1.00
match-1.400.591.23-0.42mean 0.00var 1.00
did-0.731.300.61-1.18mean 0.00var 1.00
not1.25-1.540.230.05mean 0.00var 1.00
match-0.050.511.12-1.58mean 0.00var 1.00
eps1e-5
this step in python: needs torch

What it computes

LayerNorm(x)=γ⊙x−μσ2+ϵ+β\text{LayerNorm}(x) = \gamma \odot \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta

xx is one token's row, d components wide. μ\mu and σ2\sigma^2 are that row's own mean and variance — the variance divided by d rather than by d − 1, which is unbiased=False in PyTorch and what GPT-2 was trained with. The fraction is the row standardized: mean 0 and variance 1, whatever arrived. γ\gamma and β\beta are the learned gain and bias, applied componentwise.

Three details in the arithmetic are worth naming:

  • The statistics are the token's own, so a token's output never depends on what else is in the batch or the sequence, and training and inference compute the same thing. This is the contrast with batch normalization, which normalizes across the batch and is not used in transformers.

  • eps guards the division. A token vector whose components are all equal has variance 0, and without eps the division would be by zero. GPT-2 uses ϵ=10−5\epsilon = 10^{-5}, PyTorch's default.

  • gain and bias are the only trainable parameters, d of each: one pair per dimension, shared by every token in the sequence and every sequence in the batch. They start at 1 and 0, where the layer is standardization and nothing more. Training moves them, so the output rows stop having mean 0 and variance 1. That is intended: standardizing fixes the scale and the offset of what leaves, and the pair then lets training pick a scale and an offset per dimension rather than inheriting whatever the input carried.

Why it matters

input invariance−+
the same rows halved: X_small = 0.5 × X + 0.19
X_small[5, 4]
the0.850.041.00-0.09mean 0.45var 0.23
match0.050.710.920.37mean 0.51var 0.11
did0.001.070.71-0.23mean 0.39var 0.28
not1.040.040.680.61mean 0.59var 0.13
match0.510.690.890.01mean 0.52var 0.11
LayerNorm(X_small)[5, 4]
the0.83-0.871.15-1.11mean 0.00var 1.00
match-1.400.591.23-0.42mean 0.00var 1.00
did-0.731.300.61-1.18mean 0.00var 1.00
not1.25-1.540.230.05mean 0.00var 1.00
match-0.050.511.12-1.58mean 0.00var 1.00

Values are rounded to 2 decimals.

difference between outputs0.0001
this step in python: needs torch

Switching between X_small and X_big changes the layer's input magnitude and offset; the gain and bias from the section above stay at 1 and 0 here.

The two branches are the same rows forty times apart in magnitude, each with its own offset, and layer normalization gives the same output for both — up to the eps under the square root, which does not scale with the rows and whose effect the readout prints. However large or small the values arriving have become, the next layer sees rows at the same scale, so its gradients neither explode nor vanish with them.

GPT-2 places layer normalization in three positions: before the attention sublayer and before the feed-forward network of every transformer block, and once after the last block. With 12 blocks, GPT-2 (124M) has 25 of them. Normalizing only each sublayer's input is called pre-norm: the sublayer's shortcut connection carries the input around it unnormalized. The original transformer normalized after each shortcut connection instead. With pre-norm, the output of the last block's second shortcut connection is not normalized, so the final layer normalization normalizes it before the output layer.

References

Sebastian Raschka, Build a Large Language Model (From Scratch) (Manning, 2024). Section 4.2 implements the layer in PyTorch, which the code exported above follows. It names the two parameters scale and shift; this chapter uses Ba et al.'s gain and bias instead.

Jimmy Lei Ba, Jamie Ryan Kiros and Geoffrey E. Hinton, Layer Normalization (2016). The paper the method comes from.

Alec Radford et al., Language Models are Unsupervised Multitask Learners (2019). Section 2.3 moves the normalization to the input of each sublayer and adds the final one.

Search

Search pages, articles, and resources