AI Knowledge Tree

AdaGrad, RMSProp, and Adam

Build per-coordinate adaptive step sizes and assemble the Adam update.

A single global step size is a poor fit when coordinates have wildly different curvatures or gradient scales, as they do across the layers of a deep network. Adaptive methods give each coordinate its own effective step size, derived from the history of its gradients. Adam, the default optimizer for most deep learning, combines this idea with momentum.

AdaGrad: accumulate squared gradients

AdaGrad divides each coordinate's step by the square root of its accumulated squared gradient. With gt=∇f(xt)g_t = \nabla f(x_t) and elementwise operations:

Gt=Gt−1+gt2,xt+1=xt−ηGt+ϵ gt.G_t = G_{t-1} + g_t^2, \qquad x_{t+1} = x_t - \frac{\eta}{\sqrt{G_t} + \epsilon}\,g_t. [1]

Coordinates with large or frequent gradients get a shrinking step; rarely updated coordinates keep a large one. The ϵ\epsilon (a tiny constant like 10−810^{-8}) prevents division by zero. The flaw: GtG_t only grows, so the effective step decays monotonically to zero and learning stalls on long runs.

RMSProp: forget the distant past

RMSProp fixes the monotone decay by replacing the running sum with an exponentially weighted moving average of squared gradients, decaying old information with rate ρ\rho (typically 0.9):

St=ρ St−1+(1−ρ) gt2,xt+1=xt−ηSt+ϵ gt.S_t = \rho\, S_{t-1} + (1 - \rho)\, g_t^2, \qquad x_{t+1} = x_t - \frac{\eta}{\sqrt{S_t} + \epsilon}\, g_t.

Because StS_t tracks a window rather than all history, the effective step size adapts to the recent gradient scale and does not collapse. This makes RMSProp suitable for nonstationary objectives where the right scale changes during training.

Adam: momentum plus RMSProp

Adam maintains two moving averages: a first moment mtm_t (momentum, the mean gradient) and a second moment vtv_t (the RMSProp scale), with decay rates β1\beta_1 (0.9) and β2\beta_2 (0.999):

mt=β1mt−1+(1−β1)gt,vt=β2vt−1+(1−β2)gt2.m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t, \qquad v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2.

Initialized at zero, both averages are biased toward zero early on. Adam corrects this with bias correction, dividing by 1−βt1 - \beta^t:

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}.

Why the correction. With m0=0m_0 = 0, the average mt=(1−β1)∑k=1tβ1t−kgkm_t = (1-\beta_1)\sum_{k=1}^t \beta_1^{t-k}g_k has expectation about (1−β1t) E[g](1 - \beta_1^t)\,\mathbb{E}[g], underestimating by the factor 1−β1t1 - \beta_1^t. Dividing by it restores an unbiased estimate, important in the first few steps when β1t\beta_1^t is not yet negligible. The final update is

xt+1=xt−ηv^t+ϵ m^t.x_{t+1} = x_t - \frac{\eta}{\sqrt{\hat v_t} + \epsilon}\, \hat m_t. [2, 3]

AdamW decouples weight decay from the adaptive step, applying the L2 penalty directly to the weights (xt+1−=ηλxtx_{t+1} \mathrel{-}= \eta\lambda x_t) rather than folding it into the gradient, which interacts badly with the v^t\sqrt{\hat v_t} rescaling. AdamW is the standard for training transformers.

On a badly-scaled quadratic, per-coordinate adaptive methods (AdaGrad, RMSProp, Adam) drive the loss down by many orders of magnitude while plain gradient descent, capped by the steepest direction, barely moves.
On a badly-scaled quadratic, per-coordinate adaptive methods (AdaGrad, RMSProp, Adam) drive the loss down by many orders of magnitude while plain gradient descent, capped by the steepest direction, barely moves.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(0)

# Badly scaled quadratic: coordinate curvatures span 3 orders of magnitude.
scales = np.array([1.0, 10.0, 100.0, 1000.0])
f = lambda x: 0.5 * np.sum(scales * x**2)
grad = lambda x: scales * x

def adam(steps=500, eta=0.1, b1=0.9, b2=0.999, eps=1e-8):
    x = np.ones(4); m = np.zeros(4); v = np.zeros(4)
    for t in range(1, steps + 1):
        g = grad(x)
        m = b1 * m + (1 - b1) * g
        v = b2 * v + (1 - b2) * g**2
        mhat = m / (1 - b1**t)
        vhat = v / (1 - b2**t)
        x = x - eta * mhat / (np.sqrt(vhat) + eps)
    return f(x)

def plain_gd(steps=500, eta=1.0 / 1000):           # eta limited by largest curvature
    x = np.ones(4)
    for _ in range(steps):
        x = x - eta * grad(x)
    return f(x)

print("Adam final f:    ", f"{adam():.3e}")
print("plain GD final f:", f"{plain_gd():.3e}")   # crawls on the flat coordinate

Adam drives every coordinate down despite the 1000x curvature spread, while plain gradient descent, capped by the steepest coordinate, barely moves the flattest one.

Exercises

Exercise 1. Adam runs with β1=0.9\beta_1 = 0.9 and β2=0.999\beta_2 = 0.999. Compute the bias-correction factors 1−β1t1 - \beta_1^t and 1−β2t1 - \beta_2^t at t=1t = 1 and at t=10t = 10, and explain why the second-moment correction matters for longer than the first.

Solution

Step 1. At t=1t = 1: 1−β11=1−0.9=0.11 - \beta_1^1 = 1 - 0.9 = 0.1 and 1−β21=1−0.999=0.0011 - \beta_2^1 = 1 - 0.999 = 0.001. The raw moments m1,v1m_1, v_1 are only 0.10.1 and 0.0010.001 of the gradient and its square, so without correction the first step would be tiny and badly scaled.

Step 2. At t=10t = 10: β110=0.910≈0.3487\beta_1^{10} = 0.9^{10} \approx 0.3487, so 1−β110≈0.65131 - \beta_1^{10} \approx 0.6513. And β210=0.99910≈0.9900\beta_2^{10} = 0.999^{10} \approx 0.9900, so 1−β210≈0.01001 - \beta_2^{10} \approx 0.0100.

Step 3. The first-moment factor has climbed from 0.10.1 to 0.650.65 in ten steps: the correction is nearly done. The second-moment factor is still only 0.010.01, because β2=0.999\beta_2 = 0.999 decays far more slowly. The second moment stays biased toward zero for hundreds of steps, which is why dividing by 1−β2t1 - \beta_2^t matters over a much longer horizon than dividing by 1−β1t1 - \beta_1^t.

Exercise 2. Consider one coordinate where the gradient is the constant gg at every step. Show that AdaGrad's effective step size η/(Gt+ϵ)\eta / (\sqrt{G_t} + \epsilon) decays like 1/t1/\sqrt{t}, while RMSProp's reaches a finite steady value. Ignore ϵ\epsilon.

Solution

Step 1 (AdaGrad). With gk=gg_k = g for all kk, the accumulator is Gt=∑k=1tg2=t g2G_t = \sum_{k=1}^t g^2 = t\,g^2. The effective step is

ηGt=ηt ∣g∣,\frac{\eta}{\sqrt{G_t}} = \frac{\eta}{\sqrt{t}\,\lvert g\rvert},

which decays as 1/t1/\sqrt{t} and goes to zero, the stalling flaw of AdaGrad.

Step 2 (RMSProp). The moving average obeys St=ρSt−1+(1−ρ)g2S_t = \rho S_{t-1} + (1-\rho)g^2. With constant g2g^2 this is a fixed-point iteration whose limit S∞S_\infty solves S∞=ρS∞+(1−ρ)g2S_\infty = \rho S_\infty + (1-\rho)g^2, giving S∞=g2S_\infty = g^2.

Step 3. The steady effective step is therefore η/S∞=η/∣g∣\eta / \sqrt{S_\infty} = \eta / \lvert g\rvert, a finite constant. Replacing the growing sum with a decaying average is exactly what stops the step from collapsing.

Exercise 3 (code). Implement RMSProp and confirm Step 2 of Exercise 2 numerically: with a constant gradient g=2g = 2 and ρ=0.9\rho = 0.9, the running average StS_t converges to g2=4g^2 = 4 and the effective step converges to η/∣g∣\eta / |g|.

Solution
python · numpy
import numpy as np

g = 2.0
rho = 0.9
eta = 0.1
eps = 1e-8

S = 0.0
for _ in range(500):
    S = rho * S + (1 - rho) * g**2     # constant gradient each step

eff_step = eta / (np.sqrt(S) + eps)

assert np.isclose(S, g**2, atol=1e-6)             # S_inf = g^2 = 4
assert np.isclose(eff_step, eta / abs(g), atol=1e-6)
print("S_inf:", round(S, 6), " effective step:", round(eff_step, 6))

The average settles at 4=g24 = g^2 and the effective step at 0.1/2=0.050.1 / 2 = 0.05, matching the fixed point derived by hand.

The next lesson turns from unconstrained methods to optimization under constraints.


References

  1. [1]Duchi, J., Hazan, E., & Singer, Y. (2011). "Adaptive subgradient methods for online learning and stochastic optimization (AdaGrad)". JMLR, 12, 2121-2159. The per-coordinate accumulated-gradient step size.↩
  2. [2]Kingma, D. P., & Ba, J. (2015). "Adam: A method for stochastic optimization". ICLR. The first/second-moment update with bias correction (arXiv:1412.6980).↩
  3. [3]Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning, ch. 8. MIT Press. A comparative treatment of RMSProp, Adam, and related adaptive optimizers.↩