AI / ML Theory
Chapter 3 · 22 min

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:

  1. Maintain an EMA mtm_t of the gradient. (Momentum, 3.3.)
  2. Maintain an EMA vtv_t of the squared gradient. (RMSProp, 3.4.)
  3. Bias-correct both. (3.2.)
  4. Take a step in −α⋅m^t/(v^t+ε)-\alpha \cdot \hat m_t / (\sqrt{\hat v_t} + \varepsilon).

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 tt, compute the (mini-batch) gradient gt=∇L(θt)g_t = \nabla L(\theta_t). Then:

(a) First-moment EMA — a velocity, mean of recent gradients:

mt=β1 mt−1+(1−β1) gt,m0=0.m_t = \beta_1\, m_{t-1} + (1 - \beta_1)\, g_t, \qquad m_0 = 0.

(b) Second-moment EMA — element-wise mean of squared gradients:

vt=β2 vt−1+(1−β2) gt⊙2,v0=0,v_t = \beta_2\, v_{t-1} + (1 - \beta_2)\, g_t^{\odot 2}, \qquad v_0 = 0,

where gt⊙2g_t^{\odot 2} 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:

m^t=mt1−β1t,v^t=vt1−β2t.\hat m_t = \frac{m_t}{1 - \beta_1^t}, \qquad \hat v_t = \frac{v_t}{1 - \beta_2^t}.

(d) Update:

 θt+1=θt−α m^tv^t+ε. \boxed{\ \theta_{t+1} = \theta_t - \alpha\, \frac{\hat m_t}{\sqrt{\hat v_t} + \varepsilon}.\ }

All operations after the gradient are element-wise. ε∼10−8\varepsilon \sim 10^{-8} 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 mtm_t 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 v^t\sqrt{\hat v_t} term rescales per coordinate, equalising the magnitude of steps across parameters with very different gradient statistics (cf. 3.4).
  • The bias correction 1/(1−βit)1/(1 - \beta_i^t) 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: m^t\hat m_t has the units of θ\theta per unit time; v^t\sqrt{\hat v_t} has the units of ∣θ∣|\theta| per unit time; their ratio is dimensionless; α\alpha supplies the units. The effective per-coordinate learning rate is

αt(i)=α⋅∣m^t(i)∣v^t(i)+ε⋅1∣m^t(i)∣=αv^t(i)+ε,\alpha_t^{(i)} = \alpha \cdot \frac{|\hat m_t^{(i)}|}{\sqrt{\hat v_t^{(i)}} + \varepsilon} \cdot \frac{1}{|\hat m_t^{(i)}|} = \frac{\alpha}{\sqrt{\hat v_t^{(i)}} + \varepsilon},

bounded above by α/ε\alpha / \varepsilon (which is huge — 10510^5 for α=10−3,ε=10−8\alpha = 10^{-3}, \varepsilon = 10^{-8}) but in practice always much smaller because v^t(i)\hat v_t^{(i)} dominates ε\varepsilon after the first few steps.

A more useful bound: the signal-to-noise form. Note

∣m^t(i)∣v^t(i)=∣mean(g)∣mean(g2)≤1,\frac{|\hat m_t^{(i)}|}{\sqrt{\hat v_t^{(i)}}} = \frac{|\mathrm{mean}(g)|}{\sqrt{\mathrm{mean}(g^2)}} \le 1,

with equality iff the gradient is constant in sign. So at each coordinate, Adam's step is at most α\alpha in absolute value (modulo the ε\varepsilon correction). The per-parameter LR is bounded by α\alpha, 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 β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, a constant gradient g=1g = 1, and start from m0=v0=0m_0 = v_0 = 0.

At t=1t = 1:

m1=0.1,v1=0.001,m^1=0.10.1=1,v^1=0.0010.001=1.m_1 = 0.1, \quad v_1 = 0.001, \quad \hat m_1 = \frac{0.1}{0.1} = 1, \quad \hat v_1 = \frac{0.001}{0.001} = 1.

So m^1/v^1=1\hat m_1 / \sqrt{\hat v_1} = 1, and the first Adam step is exactly −α⋅1-\alpha \cdot 1. Without bias correction, the first step would be −α⋅0.1/0.001=−α⋅3.16-\alpha \cdot 0.1 / \sqrt{0.001} = -\alpha \cdot 3.16 — overshooting in the other direction by a factor of 3, because v1v_1 is underestimating the variance more than m1m_1 is underestimating the mean.

The correction is what makes early steps well-scaled.


5. Compute and memory

Per parameter, Adam stores two extra scalars (mm and vv) and performs ∼10\sim 10 float operations per step (two EMA updates, two corrections, one division, one \sqrt{}, one step). Memory cost: 3× the model parameters (θ,m,v\theta, m, v). For a 7B-parameter model in float32, that is ∼84\sim 84 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 (m,v)(m, v).

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

α=10−3,β1=0.9,β2=0.999,ε=10−8.\alpha = 10^{-3}, \qquad \beta_1 = 0.9, \qquad \beta_2 = 0.999, \qquad \varepsilon = 10^{-8}.

For LLMs, α\alpha is usually scaled down to 10−410^{-4} or 3×10−43 \times 10^{-4}, and β2\beta_2 is sometimes pushed to 0.950.95 (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 v^t\hat v_t 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 β1=0.9,β2=0.999,α=10−3,ε=10−8\beta_1 = 0.9, \beta_2 = 0.999, \alpha = 10^{-3}, \varepsilon = 10^{-8} and you have the most-used optimiser of the last decade.


8. Takeaways

  • Adam = momentum (mtm_t) + RMSProp (vtv_t) + bias correction.
  • Update: θ←θ−α m^t/(v^t+ε)\theta \leftarrow \theta - \alpha\, \hat m_t / (\sqrt{\hat v_t} + \varepsilon), element-wise.
  • Per-coordinate step is bounded above by α\alpha, which makes the LR robust to tune.
  • Memory cost: 3× model parameters. The main bottleneck in LLM training optimiser state.
  • Defaults that work: β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, α=10−3\alpha = 10^{-3} (or 10−410^{-4} for LLMs), ε=10−8\varepsilon = 10^{-8}.
  • Adam has no general non-convex convergence guarantee; we use it because it works.

Self-test

(a) With β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, and a constant gradient g=1g = 1, compute m^t\hat m_t and v^t\hat v_t at t=1,2,10,100t = 1, 2, 10, 100. Confirm that m^t/v^t=1\hat m_t / \sqrt{\hat v_t} = 1 exactly at every tt (so the first 100 Adam steps are exactly −α-\alpha each).

(b) Show the per-coordinate-bound: ∣m^t(i)∣/v^t(i)≤1|\hat m_t^{(i)}| / \sqrt{\hat v_t^{(i)}} \le 1. (Hint: Cauchy–Schwarz, or directly the inequality ∣E[X]∣≤E[X2]|\mathbb{E}[X]| \le \sqrt{\mathbb{E}[X^2]} 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 ≤1\le 1 makes Adam immune to gradient explosions in a way that plain SGD is not.


References

  1. [1]

    Kingma, D. P., & Ba, J. (2014). "Adam: A method for stochastic optimization". arXiv:1412.6980 — Adam optimiser (3.5).

    ↩