Optimizers and learning-rate schedules
From SGD to AdamW, and the schedule that matters more than the optimizer
The optimizer and its learning-rate schedule are the knobs that most often decide whether a run converges, diverges or quietly underperforms. Adam's update rule, its bias correction and the decoupled weight decay of AdamW are used verbatim to train every modern language model (7.8 Build GPT from scratch (nanoGPT walkthrough), 8.1 The LLM lifecycle and model landscape); warmup, cosine decay and gradient clipping are in every training config you will ever read. Knowing why each term exists lets you tune in minutes instead of days (4.8 Training dynamics and debugging).
You'll be able to
- Write the update equations of SGD with momentum, Nesterov, AdaGrad, RMSProp, Adam and AdamW
- Derive Adam's bias correction and explain what goes wrong without it
- Explain why decoupled weight decay differs from L2 regularization under Adam
- Choose a learning-rate schedule (warmup, cosine, one-cycle, WSD) and run a learning-rate range test
- Apply gradient clipping and the linear scaling rule when changing batch size
Key ideas
- Momentum averages past gradients: consistent directions amplify up to 1/(1−β) ×, oscillating ones cancel; Nesterov evaluates the gradient at the look-ahead point.
- Adam normalizes each coordinate's step by its recent RMS gradient, so η sets the per-step movement independent of loss scale.
- Adam's bias correction divides m_t and v_t by (1−β₁ᵗ) and (1−β₂ᵗ); without it the first steps are ~3× too large.
Show 4 more key ideas
- AdamW decouples weight decay from the adaptive step; coupled L2 under Adam decays large-gradient weights less — use AdamW.
- Warmup protects the early, poorly-estimated adaptive steps; cosine (or WSD) decay delivers the final loss drop.
- Clip the global gradient norm (c ≈ 1) to prevent loss spikes; log the pre-clip norm as a diagnostic.
- When batch size grows k×, scale the LR (≈k for SGD, ≈√k for Adam) up to the critical batch size; beyond it, bigger batches stop helping.
Suggested path through this lesson
Mini-batch SGD, the baseline
With a mini-batch of examples, the gradient estimate is unbiased for the full gradient and has variance proportional to (1.10 Optimization: from gradient descent to Adam). The update is . Two problems drive everything else in this lesson: the gradient is noisy, and its scale differs wildly between parameters and over training — a single global step size is a poor fit for both.
Momentum and Nesterov
Momentum accumulates an exponentially-weighted average of past gradients and moves along it:
with (this is PyTorch's convention; some texts write , which just rescales ). If the gradient points the same way for many steps, grows toward — a larger effective step for — while components that flip sign cancel. In a narrow ravine, momentum damps the side-to-side oscillation and accelerates along the valley floor, which is why it converges in far fewer steps on ill-conditioned problems.
Nesterov momentum evaluates the gradient at the look-ahead point instead of at : it peeks where momentum is about to carry it and corrects early. In practice it is a small, consistent improvement for SGD on vision models.
Adaptive per-parameter step sizes
AdaGrad divides each coordinate's step by the root of its accumulated squared gradients, , (all operations element-wise). Rarely-updated parameters (rare words in an embedding table) get large steps, frequent ones get small steps — but only grows, so the learning rate decays to zero whether or not you are done.
RMSProp fixes that with an exponential average, , so the denominator tracks the recent gradient magnitude.
Adam: momentum + RMSProp + bias correction
Adam (Kingma & Ba, 2015) keeps both a first-moment and a second-moment estimate and corrects their start-up bias:
with defaults , , (LLM recipes use for stability).
Deriving the bias correction. Unroll the first-moment recursion from : . If the gradients have a common mean , then by the geometric series. So underestimates the true mean by the factor , which starts at and tends to ; dividing by it removes the bias. The same argument gives for the second moment.
Math check — why it matters. At : and . Without correction the first step would be — three times the intended step, in every coordinate, on the very first iteration. With correction, , and the step is as designed.
Two consequences of the form of the update. First, each coordinate's step is roughly , a number of order regardless of the gradient's scale — Adam is invariant to rescaling the loss, and directly controls how far parameters move per step (typical: to ; is the folk default). Second, the update is close to a sign-based method in early training, which is why warmup is needed (below).
Weight decay: AdamW versus L2
Adding an L2 penalty to the loss adds to the gradient. Under plain SGD that is identical to weight decay, : shrink, then step. Under Adam it is not identical: the penalty term enters and and gets divided by , so parameters with large gradients are decayed less — the opposite of what you want. AdamW (Loshchilov & Hutter, 2019) decouples the two:
It is the default optimizer for transformers; typical is for fine-tuning and for LLM pretraining, usually not applied to biases and normalization parameters. Note that PyTorch's torch.optim.Adam(weight_decay=…) implements the coupled L2 version — use AdamW.
Learning-rate schedules
The schedule usually matters more than the choice between Adam variants.
- Warmup. Increase linearly from ~0 to over the first few hundred to few thousand steps. Early on, Adam's is a poor estimate from few samples and the loss landscape near a random initialization is sharp; a full-size step there can throw the model into a bad region it never recovers from. Warmup is near-universal for transformers.
- Cosine decay to a floor: . Smooth, one hyperparameter, the standard for pretraining; the related warm restarts (SGDR) repeat the cosine cycle.
- Step decay (divide by 10 at fixed epochs) is the classic vision recipe; one-cycle (Smith) warms up to a high peak then anneals, often converging in far fewer epochs; warmup–stable–decay (WSD) holds constant then decays sharply at the end, letting you stop training at any time by running the decay phase — popular for LLMs since 2024.
- Learning-rate range test. Increase geometrically each step for a few hundred iterations and plot loss against : the loss falls, flattens, then explodes. Pick roughly an order of magnitude below the explosion, where the curve is steepest. Five minutes of compute replaces a grid search.
Gradient clipping
Clip the global norm of the gradient: with typical. It leaves most steps untouched and caps the rare huge gradient from a bad batch that would otherwise produce a loss spike or NaN (4.8 Training dynamics and debugging). Logging the pre-clip norm is one of the most useful training diagnostics.
Batch size and the linear scaling rule
Doubling the batch halves the gradient variance, so you can afford a bigger step. For SGD the linear scaling rule (Goyal et al., 2017) — multiply by when the batch grows by , with warmup — reproduces small-batch results up to a critical batch size, beyond which extra samples per step stop buying faster convergence (McCandlish et al.'s gradient noise scale predicts where). For Adam the empirical scaling is closer to . Rule of thumb: tune at your actual batch size, and when you must change the batch, re-tune or scale, never keep fixed blindly.
Landscape intuition
In millions of dimensions, bad local minima are rare; saddle points and long flat plateaus are the obstacles, and momentum and adaptive steps are what get you through them. SGD noise tends to settle in flat minima, which generalize better than sharp ones — one reason small-batch SGD sometimes beats large-batch training at equal steps and why the LR decay phase (noise reduction) produces the final drop in loss (4.7 Regularization in deep nets).
Practical defaults
| setting | CNN / tabular MLP | transformer / LLM |
|---|---|---|
| optimizer | SGD + momentum 0.9 (or AdamW) | AdamW, |
| peak LR | 0.1 (SGD) / (AdamW) | (small) → (large) |
| schedule | step or cosine, 5-epoch warmup | linear warmup (1–2%) + cosine to 10% |
| weight decay | (SGD) / 0.01 | 0.1, excluding biases and norms |
| clipping | rarely | global norm 1.0 |
Where this shows up later
- The schedule and clipping values you will see in nanoGPT and GPT-2 reproduction — 7.8 Build GPT from scratch (nanoGPT walkthrough); AdamW's memory cost (two extra copies of every parameter) in the VRAM budget — 4.12 Compute, hardware and scale basics.
- Reading loss curves to tell "LR too high" from "LR too low" — 4.8 Training dynamics and debugging; weight decay as a regularizer — 4.7 Regularization in deep nets.
- Discriminative learning rates for fine-tuning pretrained backbones — 5.4 Transfer learning and training CNNs in practice; much smaller learning rates for LoRA and full fine-tuning of LLMs — 8.2 Fine-tuning and PEFT; PPO's clipped objective as a trust-region idea — 4.11 Reinforcement learning essentials.
Common pitfalls (5)
- Using torch.optim.Adam(weight_decay=…) expecting AdamW behaviour — it is coupled L2 regularization.
- Skipping warmup with Adam on a transformer and getting a loss spike or divergence in the first few hundred steps.
- Keeping the same learning rate after changing the batch size.
- Calling scheduler.step() once per epoch when the schedule was defined per iteration (or vice versa) — a silent 100× timing error.
- Applying weight decay to LayerNorm gains and biases, which slowly erodes the model's ability to scale activations.
Recap: all 7 key points
- Momentum averages past gradients: consistent directions amplify up to 1/(1−β) ×, oscillating ones cancel; Nesterov evaluates the gradient at the look-ahead point.
- Adam normalizes each coordinate's step by its recent RMS gradient, so η sets the per-step movement independent of loss scale.
- Adam's bias correction divides m_t and v_t by (1−β₁ᵗ) and (1−β₂ᵗ); without it the first steps are ~3× too large.
- AdamW decouples weight decay from the adaptive step; coupled L2 under Adam decays large-gradient weights less — use AdamW.
- Warmup protects the early, poorly-estimated adaptive steps; cosine (or WSD) decay delivers the final loss drop.
- Clip the global gradient norm (c ≈ 1) to prevent loss spikes; log the pre-clip norm as a diagnostic.
- When batch size grows k×, scale the LR (≈k for SGD, ≈√k for Adam) up to the critical batch size; beyond it, bigger batches stop helping.
Watch
The loss landscape picture that momentum and adaptive methods refine.
Read
Try it live
Interactive demo
runs in your browserExercises
- By hand: three steps of Adam
With , defaults , and gradients on a single parameter starting at , compute and for , with and without bias correction. Expected: corrected steps have magnitude ≈ 0.1; the uncorrected first step is ≈ 0.316.
- NumPy: optimizer zoo on the Rosenbrock function
Implement SGD, momentum, Nesterov, RMSProp and Adam as functions and run each for 2,000 steps from on . Plot the paths over the contour map. Expected: plain SGD crawls along the valley; momentum overshoots then settles; Adam reaches the minimum fastest at a well-chosen η — and diverges if η is 10× larger.
- PyTorch: LR range test and schedule comparison
On a small CNN or MLP, run a learning-rate range test (geometric increase over 300 iterations), pick , then train for 5 epochs with (a) constant LR, (b) warmup + cosine, (c) one-cycle. Log the pre-clip gradient norm. Expected: (b) and (c) end with lower loss than (a); the gradient norm spikes near the LR-test explosion point.
- Experiment: AdamW vs Adam + L2
Train the same model with
AdamW(weight_decay=0.1)and withAdam(weight_decay=0.1). Compare validation accuracy and the final weight norms. Expected: the coupled version behaves differently (often worse) because its decay is scaled by the adaptive denominator.
Quiz
Flashcards
These cards are now in your spaced-repetition queue. Review all due cards →