ML Systems Lab Open interactive version →
Intermediate 31 min read optimisersAdamSGDlearning ratemomentum

Deep Learning Optimisers

SGD, momentum, RMSProp, Adam, AdaGrad — convergence and learning rate schedules

Batch norm and layer norm stabilise what each layer's *inputs* look like — they say nothing about how a weight should actually *move* once its gradient is known. That's the next question. You are training a ResNet on ImageNet with plain SGD at a fixed learning rate. After 30 epochs the loss flattens out. Switch to an optimiser that gives every weight its own effective step size, and in the next 5 epochs the loss drops more than it did in the previous 30. What just happened — and why might plain SGD still win in the end?

The core issue with one fixed learning rate is that different weights have wildly different lives. A weight in the embedding for a rare word might get a gradient once in a thousand steps; a weight in the final layer gets one every step. The same step size cannot suit both — the rare one needs a big push to make progress from its infrequent updates, the busy one needs small steps to avoid bouncing around its target.

Think of each weight as a hiker descending its own private mountain, carrying a cane that feels out how rough the terrain underfoot has been lately. A hiker on a long, gentle, featureless stretch takes big confident strides. A hiker on jagged, jumpy terrain takes small, careful ones. Force every hiker, on every terrain, to use the same fixed stride, and the gentle-terrain hiker crawls while the jagged-terrain one stumbles. Adaptive optimisers give each weight its own cane — its own effective learning rate, derived from its own gradient history.


The lineage: AdaGrad → RMSProp → Adam

AdaGrad keeps, for every weight, a running *sum* of its squared gradients — v_t = v_{t-1} + g_t² — and scales its step by 1/√v_t: w_t = w_{t-1} − (η / √(v_t + ε))·g_t. A weight whose gradient has been small or rare keeps a small v_t and gets a comparatively large step; a weight updated every step accumulates a large v_t and its step shrinks.

Walk two weights through a few steps. A *busy* weight sees a gradient of magnitude 1 every step: v_1 = 1 (step scaled by 1/√1 = 1), v_2 = 1 + 1 = 2 (1/√2 ≈ 0.71), v_3 = 3 (1/√3 ≈ 0.58) — its effective step keeps shrinking. A *rare* weight sees nothing for 99 steps, then a single gradient of the same magnitude 1: its v_t is still just 1 at that point, giving it a step scaled by 1/√1 = 1 — noticeably bigger than the busy weight's step at that same moment (v_t ≈ 100, scale ≈ 1/√100 = 0.1). That is exactly the behaviour the earlier crisis called for: the busy weight self-throttles, the rare one still gets a real push the moment it fires.

But AdaGrad's sum only ever grows — on *dense* problems, where every weight behaves like the busy one, the step size keeps shrinking toward zero and learning eventually stalls entirely. RMSProp fixes this with a *decaying* running average instead of a running sum: v_t = β·v_{t-1} + (1 − β)·g_t² (β typically 0.9–0.99) — old squared gradients fade out geometrically instead of piling up forever, so the step size never collapses.

Adam adds one more piece on top of RMSProp: a momentum term that smooths the *direction*, not just the scale. It keeps m_t = β1·m_{t-1} + (1 − β1)·g_t (a running average of the gradient itself) alongside v_t = β2·v_{t-1} + (1 − β2)·g_t² (RMSProp's adaptive scale), bias-corrects both — m̂_t = m_t/(1 − β1^t), v̂_t = v_t/(1 − β2^t) — and updates with w_t = w_{t-1} − η · m̂_t / (√v̂_t + ε).

That bias correction matters more than it looks. Take the very first step, default β1=0.9, β2=0.999, and a gradient g_1 = 1.0: m_1 = 0.9·0 + 0.1·1.0 = 0.1, and v_1 = 0.999·0 + 0.001·1.0² = 0.001 — both estimates are still mostly their zero initialisation, badly underestimating the true gradient. Bias correction divides each by (1 − β^1): m̂_1 = 0.1/0.1 = 1.0, v̂_1 = 0.001/0.001 = 1.0 — the correction exactly cancels the initialisation bias on step 1, so Adam's first update is already essentially full-size (≈ −η), computed from a single noisy gradient with no history behind it yet. That is exactly why Transformer training needs a learning-rate *warmup*: those first, history-free steps are the least trustworthy ones Adam ever takes, and a full-size η at that moment is the single most common cause of an early training blow-up. The combination converges fast on almost anything, which is why Adam is the default for most deep learning.


So why does SGD sometimes win?

Here is the twist. On image-classification benchmarks, a well-tuned SGD with momentum — which keeps a running *velocity* v_t = β·v_{t-1} + g_t (β typically 0.9, no bias correction, no per-parameter rescaling — just gradient history rolling into the update direction like a ball gathering speed downhill) and updates w_t = w_{t-1} − η·v_t — often *beats* Adam on validation accuracy, even though Adam trains faster. The reason ties back to the sharp-versus-flat-minima idea: Adam's per-weight rescaling lets it slide neatly into the *nearest* minimum, which tends to be a sharp, narrow one. Plain SGD keeps more of its gradient noise, which jostles it toward *flatter, wider* minima — and flat minima generalise better, especially when the test data drifts from training. So the trade is real: Adam gives you speed, SGD (tuned, with patience) can give a slightly better final model. Whichever you use, never leave the learning rate fixed for the whole run — decay it over time, and for Transformers *warm it up* first (start tiny and ramp up over the first few thousand steps), or the early, unreliable gradients will blow training up.

Key points

Takeaway

Adam converges faster but lands in sharper minima; SGD+momentum is slower but finds flatter optima that generalise better under distribution shift — the choice is training speed versus the generalisation ceiling, and neither should ever run at a fixed learning rate for the full training run.

Recap

Check your understanding

Q1. Adam is used with default parameters (β1=0.9, β2=0.999, ε=1e-8). Training loss decreases but validation loss starts increasing after epoch 10. Should you change the optimizer or change regularisation? Select the TWO correct actions.

Q2. Compare Adam and SGD+momentum on the training loss curves: Adam converges faster in early epochs, SGD is slower but eventually matches or beats Adam on validation. Why?

Q3. You are training a Transformer from scratch with Adam (default β1=0.9, β2=0.999) at a target learning rate of 3e-4, no warmup. Training loss spikes and diverges within the first 200 steps. What is the most likely fix?

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →