A constant learning rate is rarely optimal
Learning rate decay — starting high, ending low — is one of the simplest free wins in deep learning. PyTorch ships several schedulers; you'll use three regularly:
StepLR— multiplies LR by gamma everystep_sizeepochs. Simple, predictable, the historical default.CosineAnnealingLR— smooth cosine curve from initial LR toeta_minoverT_maxsteps. The modern Transformer / vision favorite.OneCycleLR— warmup-then-cooldown in one cycle. Excellent for fast training (Leslie Smith's 1cycle policy).
Warmup — why and how
Modern Transformer training nearly always starts with a few hundred steps of warmup: linearly increasing LR from 0 to the target, then decaying. Without it, the early gradients can be huge (parameters are random, loss is high, derivatives are wild) and the optimizer takes catastrophic steps. With warmup, you ease the optimizer into the regime where the loss landscape is well-behaved.
For warmup + cosine, you can either compose two schedulers (SequentialLR) or write a custom LambdaLR. Hugging Face's get_cosine_schedule_with_warmup is the canonical implementation if you want to skip writing it.
Step the scheduler at the right granularity
- Per-epoch schedulers (StepLR, CosineAnnealingLR with epoch units) — call
scheduler.step()once per epoch. - Per-step schedulers (OneCycleLR, LambdaLR sized in steps) — call
scheduler.step()once per batch.