Layer Normalization - Transformer Architecture
Implement Layer Normalization in Python with NumPy. easy exercise with examples, hints, and instant feedback. No setup or signup required.
Layer Normalization
What you'll build
LayerNorm over the last (feature) axis, with a learned scale gamma and shift beta. Every Transformer block uses it.
Background
BatchNorm normalizes each feature over the batch, which couples examples together and breaks down for variable-length sequences and batch size 1 at inference. LayerNorm normalizes each token's feature vector on its own, so it behaves identically in training and inference and needs no running statistics. In pre-norm Transformers (GPT-2 onwards) it sits at the start of every residual branch: x = x + Attn(LN(x)). LLaMA-style models use the cheaper RMSNorm (see SwiGLU Feed-Forward Block in LLM Internals).
Formula
For each position (each vector x along the last axis, length D):
mu = mean(x) # scalar per position
sigma^2 = mean((x - mu)^2) # population (biased) variance
y = gamma * (x - mu) / sqrt(sigma^2 + eps) + beta
Inputs
X: float array(..., D), e.g.(seq_len, D)or(batch, seq_len, D)gamma,beta: float arrays(D,)eps: small float, default1e-5
Expected Return Format
Array with the same shape as X.
Edge cases and conventions
- Statistics are over axis=-1 only, never over the batch or sequence axes.
- Use the biased variance (
np.vardefault,ddof=0), as PyTorch'snn.LayerNormdoes. epsgoes inside the square root. A constant row givesx - mu = 0, so the output is justbeta.
Constraints
NumPy only; use keepdims=True so the statistics broadcast for any number of leading axes.
Follow-up
- Write the backward pass. Why do the gradients with respect to
xsum to zero along the feature axis? - Pre-norm vs post-norm: which one trains stably without learning-rate warmup, and why?
Examples
Input: layer_norm(np.array([[1.0, 2.0, 3.0, 4.0]]), np.ones(4), np.zeros(4)) Output: [[-1.3416, -0.4472, 0.4472, 1.3416]]
mean = 2.5, variance = 1.25, so (x - 2.5) / sqrt(1.25).
Input: layer_norm(np.array([[2.0, 2.0, 2.0, 2.0]]), np.ones(4), np.array([0.0, 0.0, 0.0, 1.0])) Output: [[0.0, 0.0, 0.0, 1.0]]
A constant row normalizes to 0 (eps avoids division by zero), leaving only beta.
Practice in Python
import numpy as np
def layer_norm(X, gamma, beta, eps=1e-5):
"""
Apply layer normalization over the last axis.
Args:
X: Input (..., D), e.g. (batch, seq_len, D) or (seq_len, D)
gamma: Scale (D,)
beta: Shift (D,)
eps: Small constant added to the variance
Returns:
Array with the same shape as X
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 5 tests, or use the progressive hints.