Batch Normalization - Neural Networks
Implement Batch Normalization in Python with NumPy. medium exercise with examples, hints, and instant feedback. No setup or signup required.
Batch Normalization (training-mode forward)
What you'll build
The forward pass of batch normalization for a 2-D batch of activations, in training mode: normalise every feature with the mean and variance of the current batch, then apply a learned scale and shift. Return the output and a cache for the backward pass.
Background
BatchNorm (Ioffe & Szegedy, 2015) keeps each layer's inputs at a stable scale, which allows higher learning rates and makes deep CNNs much easier to train. Because the statistics come from the batch, every example's output depends slightly on the others, which also acts as a mild regulariser.
Formula
For X of shape (batch, features), per feature j:
1. mu_j = mean(X[:, j]) # batch mean, shape (features,)
2. var_j = mean((X[:, j] - mu_j)^2) # biased batch variance, shape (features,)
3. X_norm = (X - mu) / sqrt(var + eps) # shape (batch, features)
4. out = gamma * X_norm + beta # learned scale and shift, broadcast over rows
Inputs
X:(batch, features)float arraygamma,beta:(features,)eps: small constant, default1e-5
Expected return format
Return the tuple (out, cache):
out:(batch, features), the normalised, scaled and shifted outputcache: the tuple(X, X_norm, mu, var, gamma, eps), in this order, for the backward pass
Edge cases and conventions
- Use the biased variance (divide by
batch, NumPy's defaultnp.var), as BatchNorm does in training. epsgoes inside the square root.- A constant feature has
var = 0;epskeeps the division finite andX_normbecomes 0.
Constraints
NumPy only; vectorised over the batch (reduce with axis=0).
Follow-up
- At inference time BatchNorm uses running averages of
muandvarcollected during training. What goes wrong if you forgetmodel.eval()? - Derive the backward pass from the cache; the compact form is
dX = (gamma / (batch * std)) * (batch * dout - dout.sum(0) - X_norm * (dout * X_norm).sum(0)). - LayerNorm normalises each example across its features instead (Transformers section). Why does it suit variable-length sequences better?
Examples
Input: batch_norm_forward(np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), np.ones(2), np.zeros(2))[0] Output: [[-1.2247, -1.2247], [0.0, 0.0], [1.2247, 1.2247]] (rounded)
mu = [3, 4] and var = [8/3, 8/3] (biased), so the rows map to -2, 0, 2 divided by sqrt(8/3) = 1.633.
Input: batch_norm_forward(np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), np.array([2.0, 2.0]), np.array([0.5, -1.0]))[0] Output: [[-1.9495, -3.4495], [0.5, -1.0], [2.9495, 1.4495]] (rounded)
gamma = 2 doubles the normalised values and beta shifts each column.
Practice in Python
import numpy as np
def batch_norm_forward(X, gamma, beta, eps=1e-5):
"""
Batch normalization forward pass (training mode, batch statistics).
Args:
X: Input (batch, features)
gamma: Scale (features,)
beta: Shift (features,)
eps: Added to the variance inside the square root
Returns:
out: Normalized, scaled and shifted output (batch, features)
cache: Tuple (X, X_norm, mu, var, gamma, eps) for the backward pass
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 8 tests, or use the progressive hints.