Key ideas
- Minibatch SGD: each update uses the gradient averaged over a random minibatch of examples. One epoch is one pass over the data, i.e. updates if the last partial batch is kept. Reshuffle every epoch: data sorted by label give biased minibatch gradients, and the fit drifts towards whatever part of the data came last.
- Learning rate : too large makes the loss oscillate, spike or grow (divergence); too small makes progress slow. Small batches give cheap but noisy updates; large batches give smoother gradients that tolerate a larger , but fewer updates per epoch. Neither has a universal best value; tune them together.
- Momentum keeps a velocity and steps . PyTorch's
SGDstores exactly this buffer (its docs call the coefficient ; dampening defaults to 0) and starts it at 1. A constant gradient builds the velocity up to , while a gradient alternating keeps it near : with , steady directions move 10 times further per step and zig-zags are damped 19-fold relative to them. The moving-average form , which Adam uses for its first moment2, multiplies every gradient by , so the same gives steps times as large. - Adam keeps moving averages of the gradient (, the momentum part) and of its elementwise square (, as in RMSProp), bias-corrects both because they start at 0, and divides one by the square root of the other, giving every parameter its own step size; defaults , , 2. Rescaling all gradients by a constant leaves the steps unchanged, and they stay roughly below 2; the first step is up to , since and . RMSProp and AdaGrad divide by a running average or a sum of 2, which carries no sign, so unlike momentum and Adam they do not damp sign oscillations. Training with Adam holds weights, gradients and two moments: four numbers per parameter.
- L2 penalty vs weight decay: with plain SGD, adding to the loss shrinks every weight by the constant factor per step, i.e. it is weight decay; with Adam it is not, because the penalty's gradient is divided by each parameter's adaptive scale, so weights with large past gradients are decayed less. AdamW therefore decouples the decay from the gradient step3.
- Initialization: gradients are products of many layer Jacobians, so they can vanish (e.g. saturated sigmoids) or explode4. If all hidden weights start equal, the hidden units compute the same activations and receive the same gradients, and gradient descent alone never makes them differ, so weights are drawn at random to break the symmetry4. Xavier (Glorot) initialization uses variance , a compromise between keeping activation variances () and gradient variances () stable4.
- Batch normalization standardizes each feature with the minibatch mean and variance, then applies a learned scale (initially 1) and shift (initially 0)5,6. For conv layers there is one mean, variance, and per channel, computed over the batch and all positions5. In evaluation mode it uses fixed statistics gathered in training, so a prediction does not depend on the rest of the batch5. PyTorch normalizes with the biased batch variance (divide by ) but keeps running averages with momentum 0.1 and the unbiased variance (divide by )6. A bias right before BN is redundant: subtracting the batch mean cancels any constant, and takes its role. In a fully connected layer with batch size 1 each feature equals its own mean, so every normalized value is 0 and nothing can be learned5 (PyTorch refuses this in training mode); a conv BN layer still has the positions to average over. Layer normalization instead normalizes each example over its own features, independently of the batch5.
- Regularization limits effective complexity: L2 shrinks all weights; L1 pushes each nonzero weight towards 0 with the constant force and makes many exactly zero; elastic net adds both penalties; a max-norm constraint caps the L2 norm of each unit's incoming weight vector at , dividing the vector back to length whenever an update pushes it beyond7; data augmentation adds label-preserving variations of the training inputs.
- Dropout sets each hidden activation to 0 with probability and to otherwise, so its expectation is unchanged, and is switched off at test time8. The original version did not rescale during training and instead scaled the outgoing weights by the keep probability at test time (halved for ); either way it trains a huge number of weight-sharing subnetworks at once7.
- Early stopping checks the validation loss after every epoch, stops once it has not improved by at least
min_deltaforpatienceepochs in a row, and usually restores the best epoch's weights: it regularizes and saves compute. - Learning curves: training loss falling while validation loss rises means overfitting (more data, regularization, early stopping, smaller model); both losses high and flat means underfitting (bigger model, longer training, less regularization, a pretrained model); a rising training loss means the step is too large.
Formulas
PyTorch's weight_decay=λ adds to the gradient1: that is the penalty , with factor .
PyTorch's running estimates: and 6.
Inverted dropout on a value : and , so . Two dropout layers with rates and act like one with rate .
Worked example
Minimize from with and momentum in the PyTorch form:
- Step 1: , , .
- Step 2: , , .
The moving-average form (with ) gives , , then , : the same direction with smaller steps, so a learning rate tuned for one convention does not carry over to the other.
In code
import torch
from torch import nn
bn = nn.BatchNorm1d(1) # gamma = 1, beta = 0, momentum = 0.1
x = torch.tensor([[2.0], [4.0], [4.0], [6.0]])
with torch.no_grad():
print(bn(x).flatten()) # tensor([-1.4142, 0.0000, 0.0000, 1.4142]) batch stats, var/n
print(bn.running_mean, bn.running_var) # tensor([0.4000]) tensor([1.1667]) 0.9*0 + 0.1*4, 0.9*1 + 0.1*8/3
bn.eval()
print(bn(x).flatten()) # tensor([1.4813, 3.3329, 3.3329, 5.1846]) running stats
try: nn.BatchNorm1d(1)(torch.ones(1, 1)) # a single example in training mode
except ValueError as e: print(type(e).__name__) # ValueError
torch.manual_seed(0)
drop = nn.Dropout(p=0.25) # training mode: kept values become 1/(1 - 0.25)
print(drop(torch.ones(6))) # tensor([0.0000, 1.3333, 1.3333, 0.0000, 1.3333, 0.0000])
print(drop.eval()(torch.ones(3))) # tensor([1., 1., 1.]) dropout is off in eval mode
Common pitfalls
- An epoch is not one update: 6,000 examples in minibatches of 64 give updates per epoch, the last one on 48 examples.
- Batch normalization does not make activations Gaussian: it only shifts and rescales each feature, which fixes the mean and variance but keeps the shape of the distribution, and then and rescale it again.
- Dropout and BN behave differently in training and evaluation mode; forgetting
model.eval()gives random, batch-dependent predictions. - Inverted dropout already rescales during training, so nothing is multiplied by at test time.
- adds to the gradient, while (PyTorch's
weight_decay) adds : check the convention before comparing decay factors. - Regularization fights overfitting; on an underfitting model it hurts, and a far too large makes training and validation losses both rise.
- With all weights and biases at zero, a tanh MLP's hidden outputs stay 0 and only the output bias ever learns; softmax regression, which has no hidden units, can start from zero.
- Tune hyperparameters on a validation split, never on the test set.
References
- PyTorch documentation: torch.optim.SGD — Algorithm box (weight decay λ, momentum μ, dampening τ, first step b_1 = g_1) and the note on Sutskever et al.
- Kingma & Ba (2015), Adam: A Method for Stochastic Optimization — Algorithm 1 and Sec. 2 Algorithm (2.1 Adam's Update Rule); Sec. 5 Related Work (RMSProp, AdaGrad)
- Loshchilov & Hutter (2019), Decoupled Weight Decay Regularization — Abstract; Sec. 1 Introduction
- Dive into Deep Learning — Sec. 5.4.1 Vanishing and Exploding Gradients (5.4.1.3 Breaking the Symmetry), 5.4.2.2 Xavier Initialization
- Dive into Deep Learning — Sec. 8.5.1 Training Deep Networks, 8.5.2.2 Convolutional Layers, 8.5.2.3 Layer Normalization, 8.5.2.4 Batch Normalization During Prediction
- PyTorch documentation: torch.nn.BatchNorm1d — Class description and note (biased variance in training, unbiased running variance, momentum 0.1, γ = 1 and β = 0 at start)
- Hinton et al. (2012), Improving neural networks by preventing co-adaptation of feature detectors — Main text, p. 2 (upper bound on each hidden unit's incoming weight norm; mean network with halved outgoing weights)
- Dive into Deep Learning — Sec. 5.6 Dropout (Eq. 5.6.1), 5.6.1 Dropout in Practice