3.5 — Adam: momentum + RMSProp + bias correction
Goal: Derive the full Adam update from momentum, RMSProp, and the EMA bias correction; recover the equations of the Adam paper.
1. Why this session exists
Adam (Kingma & Ba, 2014)[1] is the optimiser you will see in 95% of deep-learning code written since 2015 — every GPT, every BERT, every diffusion model. It is also the most assembled optimiser in this chapter: a clean stack of the three ideas we just built. Once you see it this way, the paper's equations stop looking like magic and become inevitable.
The recipe:
- Maintain an EMA of the gradient. (Momentum, 3.3.)
- Maintain an EMA of the squared gradient. (RMSProp, 3.4.)
- Bias-correct both. (3.2.)
- Take a step in .
That's the entire algorithm. The rest of this session is one derivation, one careful look at the bias correction, and a handful of practical notes.
2. The Adam update, derived
At step , compute the (mini-batch) gradient . Then:
(a) First-moment EMA — a velocity, mean of recent gradients:
(b) Second-moment EMA — element-wise mean of squared gradients:
where denotes the element-wise square.
(c) Bias correction (from 3.2): both EMAs are initialised at zero, so they are biased toward zero in early steps. Correct:
(d) Update:
All operations after the gradient are element-wise. for numerical stability of the division.
That's Adam, equation (1) of the paper. Read it: each parameter coordinate gets its own direction (sign of the momentum) and its own step size (inversely proportional to its recent gradient magnitude).
3. What each piece does
Re-cast each component in terms of what it solves:
- The term smooths the gradient over time, providing variance reduction (cf. 3.1's batch-noise argument) and acceleration in consistent directions (cf. 3.3's valley argument).
- The term rescales per coordinate, equalising the magnitude of steps across parameters with very different gradient statistics (cf. 3.4).
- The bias correction fixes the cold-start bias of zero-initialised EMAs (cf. 3.2). Without it, the first few hundred steps of training would be artificially small.
The combined unit analysis is clean: has the units of per unit time; has the units of per unit time; their ratio is dimensionless; supplies the units. The effective per-coordinate learning rate is
bounded above by (which is huge — for ) but in practice always much smaller because dominates after the first few steps.
A more useful bound: the signal-to-noise form. Note
with equality iff the gradient is constant in sign. So at each coordinate, Adam's step is at most in absolute value (modulo the correction). The per-parameter LR is bounded by , which is one of the reasons Adam is so easy to tune — you almost never have to worry about it blowing up a coordinate.
4. The bias correction in practice
To see the correction at work, take , , a constant gradient , and start from .
At :
So , and the first Adam step is exactly . Without bias correction, the first step would be — overshooting in the other direction by a factor of 3, because is underestimating the variance more than is underestimating the mean.
The correction is what makes early steps well-scaled.
5. Compute and memory
Per parameter, Adam stores two extra scalars ( and ) and performs float operations per step (two EMA updates, two corrections, one division, one , one step). Memory cost: 3× the model parameters (). For a 7B-parameter model in float32, that is GB of optimiser state alone, plus the model and gradients. This is why "Adam optimiser state" is the dominant memory cost in LLM training and why much of the recent work (Shazeer's Adafactor, bitsandbytes 8-bit Adam, ZeRO sharding) is about compressing or sharding .
For convolutional and small-scale models the memory is rarely an issue. For LLMs it is a first-class concern.
6. Default hyperparameters and convergence intuition
The Adam paper's recommended defaults — used unchanged by nearly every major model — are
For LLMs, is usually scaled down to or , and is sometimes pushed to (more reactive second moment, helps with noisy LM gradients).
Adam does not have a convergence guarantee for non-convex losses. Reddi et al. (2018, "On the Convergence of Adam") even showed it can diverge in some convex problems where AdaGrad converges. The fix in that paper (AMSGrad) keeps monotonically non-decreasing. It is correct theory and almost no-one uses it in practice — vanilla Adam works empirically.
7. Pseudocode (PyTorch-equivalent)
# state per parameter: m, v
for t, batch in enumerate(loader, start=1):
g = compute_gradient(theta, batch)
m = beta1 * m + (1 - beta1) * g
v = beta2 * v + (1 - beta2) * (g * g)
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
theta = theta - alpha * m_hat / (np.sqrt(v_hat) + eps)
Ten lines. Drop in and you have the most-used optimiser of the last decade.
8. Takeaways
- Adam = momentum () + RMSProp () + bias correction.
- Update: , element-wise.
- Per-coordinate step is bounded above by , which makes the LR robust to tune.
- Memory cost: 3× model parameters. The main bottleneck in LLM training optimiser state.
- Defaults that work: , , (or for LLMs), .
- Adam has no general non-convex convergence guarantee; we use it because it works.
(a) With , , and a constant gradient , compute and at . Confirm that exactly at every (so the first 100 Adam steps are exactly each).
(b) Show the per-coordinate-bound: . (Hint: Cauchy–Schwarz, or directly the inequality applied to the weighted-average defining the EMAs.)
(c) Implement Adam in 10 lines of NumPy and train it on the two-moons code from 2.12 (replace the SGD update with the Adam update). Report the final loss after 200 epochs and compare to plain SGD.
(d) A common "Adam pathology" is huge gradients during the first few steps (e.g., from a poorly-initialised classifier head). Argue that the per-coordinate bound makes Adam immune to gradient explosions in a way that plain SGD is not.
References
- [1]
Kingma, D. P., & Ba, J. (2014). "Adam: A method for stochastic optimization". arXiv:1412.6980 — Adam optimiser (3.5).
↩