Batch Normalization
Batch Normalization (BN) is a technique introduced by Ioffe and Szegedy (2015) that normalizes the inputs to each layer across a mini-batch during training, stabilizing and dramatically accelerating the training of deep neural networks. It matters because it reduces sensitivity to weight initialization, allows much higher learning rates, and acts as a mild regularizer — often reducing or eliminating the need for Dropout. The counterintuitive fact: despite being universally used and empirically indispensable, the original explanation of why it works (reducing "internal covariate shift") has since been largely debunked — it appears to work primarily by smoothing the optimization landscape.
Every layer in a deep network sees inputs whose distribution shifts as the weights in earlier layers update — a moving target that forces each layer to constantly re-adapt. Batch Normalization breaks this dependency by forcibly re-centering and re-scaling each layer's pre-activations to have zero mean and unit variance, computed over the current mini-batch, then letting the network learn its own preferred scale and shift via trainable parameters. This gives the optimizer a much smoother, more well-conditioned loss surface to navigate. The "aha moment": BN doesn't remove the network's expressive freedom — it just decouples what the distribution looks like from what the network needs to learn, handing both responsibilities to different, better-behaved mechanisms.
The most common mistake is treating Batch Normalization identically during training and inference. During training, BN normalizes using the mean and variance of the current mini-batch. During inference, however, there is no batch — so PyTorch (and every other framework) switches to running statistics (exponential moving averages of the batch statistics accumulated during training). Practitioners get burned when they: (1) call `model.train()` during evaluation, causing stochastic per-batch normalization that makes results non-reproducible; (2) fine-tune with a batch size of 1, where the batch variance is identically zero and training collapses; or (3) freeze BN layers without also switching them to eval mode, so the running statistics stop being reliable. The correct mental model is: BN has two operating modes that are fundamentally different, controlled explicitly by `model.eval()` vs `model.train()`.
How It Works
Given a mini-batch of scalar activations (one feature, one layer), Batch Normalization proceeds in four steps.
Step 1 — Compute batch mean:
Explanation: This is the ordinary arithmetic mean of the current feature's activations across all examples in the mini-batch. Here is the mini-batch size and is the pre-activation value for the -th example.
Step 2 — Compute batch variance:
Explanation: The variance measures how spread out the activations are around the batch mean, using the biased (population) estimator rather than the unbiased one.
Step 3 — Normalize:
Explanation: Each activation is shifted to zero mean and scaled to unit variance, with a small constant (typically ) added for numerical stability when .
Step 4 — Scale and shift (affine transform):
Explanation: Two learnable parameters — scale and shift — restore the network's ability to represent any mean and variance it needs; without these, BN would permanently constrain every layer to unit Gaussians. Here and are per-feature scalar parameters learned by backpropagation, initialized to and .
Inference — running statistics:
Explanation: During training, a running exponential moving average of the batch mean (and analogously the variance) is maintained with momentum (default 0.1 in PyTorch) so that fixed statistics are available at inference time without any batch. At inference, the normalization uses and in place of the batch statistics, making the transform a pure deterministic affine map per feature. ```python import torch import torch.nn as nn
BN in a typical Conv block — applied BEFORE activation
class ConvBNReLU(nn.Module): def __init__(self, in_ch, out_ch, kernel_size=3, padding=1): super().__init__() self.conv = nn.Conv2d(in_ch, out_ch, kernel_size, padding=padding, bias=False)
# bias=False because BN's beta subsumes the bias term self.bn = nn.BatchNorm2d(out_ch) # one (gamma, beta) pair per channel self.relu = nn.ReLU(inplace=True) def forward(self, x): return self.relu(self.bn(self.conv(x)))
Correct eval/train switching
model = ConvBNReLU(3, 64) model.train() # BN uses batch statistics, updates running stats model.eval() # BN uses frozen running stats — deterministic
Inspecting BN parameters
bn = nn.BatchNorm1d(32) print(bn.weight.shape) # gamma: torch.Size([32]) print(bn.bias.shape) # beta: torch.Size([32]) print(bn.running_mean.shape) # torch.Size([32]) — not a Parameter print(bn.momentum) # 0.1 (alpha in eq [5]) ```
Sergey Ioffe is a research scientist at Google who has worked extensively on machine learning and computer vision. In 2015, together with Christian Szegedy, he introduced Batch Normalization in the paper "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift," which became one of the most cited papers in the entire deep learning literature with over 60,000 citations. A surprising fact: the paper was originally motivated by frustration with training Inception networks — Ioffe and Szegedy needed to train deeper, faster versions and found that simply normalizing mid-network activations unlocked training speeds that had seemed unreachable with careful initialization alone.
Christian Szegedy is a research scientist at Google Brain and the primary architect of the Inception family of convolutional networks. His co-authorship of the Batch Normalization paper arose directly from his work on Inception v2 and v3, where BN was a key ingredient enabling the removal of Dropout from most layers. Somewhat ironically, Szegedy's earlier AlexNet-era observation that deep networks were extremely sensitive to initialization was a direct precursor to the problem BN was designed to solve.
Intuition
Imagine you're running a relay race where each runner (layer) receives the baton from the previous one. But suppose between every handoff, the previous runner randomly changes whether they throw the baton high or low, fast or slow — the next runner can never settle into a reliable stance. They spend all their energy adapting to the incoming throw rather than just running fast. Batch Normalization is like installing a standardized baton-passing zone: no matter how wildly the upstream runner is changing, the baton always arrives at the same height with the same speed. The downstream runner can now focus entirely on their own leg of the race. The trainable and parameters are like letting the standardized zone adjust its height slightly once the runners have learned to cooperate — they provide the escape hatch if a non-standard hand-off truly is optimal. Meanwhile, the "running statistics" at inference are like the race officials measuring the average hand-off conditions across all training runs and codifying them as the official procedure for race day.
Historical Development
Why It Matters
Prerequisites
What This Leads To
See Also
Layer Normalization · Instance Normalization · Group Normalization · Weight Initialization · Gradient Vanishing and Exploding