Skip to main content
© 2026 ePowerAI. All rights reserved.
CoursesContact
eAI
CoursesContact
Week 8: Adaptive First-Order Methods
Optimization
01Week 1: Optimization for Learning
02Week 2: Convex Sets and Functions
03Week 3: Gradient Descent Theory
04Week 4: Least Squares and Conditioning
05Week 5: Classification Losses
06Week 6: Stochastic Gradient Descent
07Week 7: Momentum and Acceleration
08Week 8: Adaptive First-Order Methods
09Week 9: Practical Training Dynamics
10Week 10: Nonsmooth and Composite Objectives
11Week 11: Constraints: Projections and Penalties
12Week 12: Duality for Practitioners
13Week 13: Nonconvex Deep Learning
14Week 14: Capstone — Theory Meets Training
Week 08· Optimization· First-order stack24 min read

Week 8: Adaptive First-Order Methods

✦Learning Outcomes
  • Write AdaGrad: accumulate squared gradients and divide learning rate per-parameter
  • Explain AdaGrad's failure mode (accumulator grows forever) and how RMSProp fixes it with EMA
  • Write AdamAdaptive Moment Estimation (optimizer) as RMSProp + momentum + bias correction
  • Describe what adaptivity buys (sparse gradients, varying scales) and what it hides
◆Prerequisites

Background: Week 6 (SGDStochastic Gradient Descent) and Week 7 (momentum). Adam combines the momentum idea (Week 7) with adaptive per-parameter step sizes introduced here.

Later weeks: Adam is the go-to optimiser for nonconvex deep learning (Week 13) and appears in both RL (policy gradient) and generative model training (Week 14).

One Learning Rate Doesn't Fit All#

In deep networks, different parameters have vastly different scales:

  • Weights in early layers might have typical gradient magnitudes of 10−210^{-2}10−2
  • Weights in later layers might have gradients of 10−510^{-5}10−5
  • Embedding parameters for rare words get updated very infrequently (sparse gradients)

A single global learning rate η\etaη cannot serve all parameters well. If η\etaη is tuned for the fastest-changing parameters, the slow-changing ones barely move. If η\etaη is large enough for the slow parameters, the fast ones oscillate or diverge.

Adaptive methods solve this by maintaining a per-parameter effective learning rate, automatically scaled by the historical magnitude of that parameter's gradient.

AdaGrad#

AdaGrad (Duchi et al., 2011) was the first widely-used adaptive method. It accumulates the squares of all past gradients:

Gk=∑t=1kgt⊙gt(elementwise square and sum)G_k = \sum_{t=1}^k g_t \odot g_t \quad \text{(elementwise square and sum)}Gk​=∑t=1k​gt​⊙gt​(elementwise square and sum)

wk+1=wk−ηGk+ε⊙gkw_{k+1} = w_k - \frac{\eta}{\sqrt{G_k + \varepsilon}} \odot g_kwk+1​=wk​−Gk​+ε​η​⊙gk​

where ⊙\odot⊙ is elementwise multiplication and ε=10−8\varepsilon = 10^{-8}ε=10−8 prevents division by zero.

How it works: Parameters that have historically received large gradients get their effective learning rate divided by a large number (slowing them down). Parameters with historically small gradients get divided by a small number (speeding them up).

The failure mode: GkG_kGk​ is monotonically increasing — it never forgets. In the limit k→∞k \to \inftyk→∞, Gk→∞G_k \to \inftyGk​→∞ and the effective learning rate η/Gk\eta/\sqrt{G_k}η/Gk​​ goes to zero for all parameters. Training stalls. This is why AdaGrad fell out of favour for deep learning.

RMSProp#

RMSProp (Tieleman & Hinton, 2012) fixes AdaGrad's accumulating-divisor problem by replacing the sum with an exponential moving average (EMA):

vk=β2 vk−1+(1−β2) gk⊙gkv_k = \beta_2 \, v_{k-1} + (1 - \beta_2) \, g_k \odot g_kvk​=β2​vk−1​+(1−β2​)gk​⊙gk​

wk+1=wk−ηvk+ε⊙gkw_{k+1} = w_k - \frac{\eta}{\sqrt{v_k + \varepsilon}} \odot g_kwk+1​=wk​−vk​+ε​η​⊙gk​

where β2∈[0,1)\beta_2 \in [0, 1)β2​∈[0,1) (typically 0.9990.9990.999) controls the memory length. The EMA forgets old gradients — approximately the last 1/(1−β2)≈10001/(1-\beta_2) \approx 10001/(1−β2​)≈1000 steps influence vkv_kvk​.

RMSProp maintains adaptivity (each parameter gets its own scale) without the monotonic-stall problem. It became the default in many frameworks before Adam took over.

Adam#

AdamAdaptive Moment Estimation (optimizer) (Kingma & Ba, 2015) combines the best of both worlds:

  1. Momentum (Week 7) for the gradient direction: mk=β1mk−1+(1−β1)gkm_k = \beta_1 m_{k-1} + (1-\beta_1) g_kmk​=β1​mk−1​+(1−β1​)gk​
  2. RMSProp for per-parameter scaling: vk=β2vk−1+(1−β2)gk⊙gkv_k = \beta_2 v_{k-1} + (1-\beta_2) g_k \odot g_kvk​=β2​vk−1​+(1−β2​)gk​⊙gk​

The full Adam update:

mk=β1mk−1+(1−β1)gkm_k = \beta_1 m_{k-1} + (1 - \beta_1) g_kmk​=β1​mk−1​+(1−β1​)gk​ vk=β2vk−1+(1−β2)gk⊙gkv_k = \beta_2 v_{k-1} + (1 - \beta_2) g_k \odot g_kvk​=β2​vk−1​+(1−β2​)gk​⊙gk​ m^k=mk1−β1k,v^k=vk1−β2k(bias correction)\hat m_k = \frac{m_k}{1 - \beta_1^k}, \quad \hat v_k = \frac{v_k}{1 - \beta_2^k} \quad \text{(bias correction)}m^k​=1−β1k​mk​​,v^k​=1−β2k​vk​​(bias correction) wk+1=wk−ηv^k+ε⊙m^kw_{k+1} = w_k - \frac{\eta}{\sqrt{\hat v_k} + \varepsilon} \odot \hat m_kwk+1​=wk​−v^k​​+εη​⊙m^k​

Bias correction: At initialisation, m0=0m_0 = 0m0​=0 and v0=0v_0 = 0v0​=0. The EMA estimates start biased toward zero. Dividing by 1−βk1 - \beta^k1−βk corrects this — at k=1k=1k=1, m1/(1−β1)=g1m_1/(1-\beta_1) = g_1m1​/(1−β1​)=g1​ exactly, and as k→∞k \to \inftyk→∞ the correction factor approaches 1.

Standard hyperparameters:

  • η=0.001\eta = 0.001η=0.001 (or 1×10−31 \times 10^{-3}1×10−3)
  • β1=0.9\beta_1 = 0.9β1​=0.9 (momentum decay)
  • β2=0.999\beta_2 = 0.999β2​=0.999 (RMSProp decay)
  • ε=10−8\varepsilon = 10^{-8}ε=10−8

These defaults work remarkably well across a wide range of deep learning problems — one reason Adam became the de facto standard.

Practice Defaults and What Adaptivity Hides#

When Adam works well:

  • Sparse gradients (embeddings, NLP)
  • Non-stationary objectives (GANs, RL — though care is needed)
  • Default choice when you don't want to tune η\etaη extensively

When to fall back to SGD + momentum:

  • When you need the best possible generalisation on a well-understood problem
  • Computer vision benchmarks (where SGD + momentum with careful tuning often beats Adam)
  • When theoretical convergence guarantees matter

What adaptivity hides: Adam's per-parameter scaling can mask poor conditioning, obscuring the diagnosis. The theoretical convergence guarantees for Adam are weaker than for SGD — under certain adversarial constructions, Adam can diverge even on convex problems. The β2\beta_2β2​ parameter needs to be close enough to 1 for the RMSProp term to provide a stable denominator.

Exercise · Multiple choice

Which component of Adam prevents the effective learning rate from vanishing for parameters with historically small gradients?

The RMSProp-style denominator √(v_k) with β₂ < 1
The momentum term m_k with β₁ = 0.9
The bias correction 1/(1−β₂^k)
The learning rate η itself
Question 1 of 4

AdaGrad's main failure mode in deep learning is:

The gradient accumulator G_k grows forever, driving the effective LR to zero
It requires second derivatives
It doesn't use momentum
It only works for convex functions

Browser lab#

Compare AdamAdaptive Moment Estimation (optimizer) vs SGD + momentum on a toy nonconvex 2D loss with an elongated valley. Plot iterate paths to visualise the adaptivity.

python · runs in browser
import numpy as np
import matplotlib.pyplot as plt

def f(w):
    x, y = w[0], w[1]
    return 0.5 * x**2 + 5.0 * y**2 + 2.0 * np.sin(x) * np.sin(y)

def grad(w):
    x, y = w[0], w[1]
    return np.array([x + 2.0 * np.cos(x) * np.sin(y),
                     10.0 * y + 2.0 * np.sin(x) * np.cos(y)])

def adam(w0, K, eta=0.1, beta1=0.9, beta2=0.999, eps=1e-8):
    w = w0.copy(); m = np.zeros(2); v = np.zeros(2)
    path = [w.copy()]; losses = [f(w)]
    for t in range(1, K+1):
        g = grad(w)
        m = beta1 * m + (1 - beta1) * g
        v = beta2 * v + (1 - beta2) * g**2
        m_hat = m / (1 - beta1**t)
        v_hat = v / (1 - beta2**t)
        w = w - eta * m_hat / (np.sqrt(v_hat) + eps)
        path.append(w.copy()); losses.append(f(w))
    return np.array(path), np.array(losses)

def sgd_momentum(w0, K, eta=0.05, beta=0.9):
    w = w0.copy(); v = np.zeros(2)
    path = [w.copy()]; losses = [f(w)]
    for _ in range(K):
        v = beta * v - eta * grad(w)
        w = w + v
        path.append(w.copy()); losses.append(f(w))
    return np.array(path), np.array(losses)

w0 = np.array([3.0, 2.0])
K = 150
path_adam, loss_adam = adam(w0, K)
path_sgd, loss_sgd = sgd_momentum(w0, K)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
ax1.plot(path_adam[:, 0], path_adam[:, 1], label="Adam", linewidth=1.5)
ax1.plot(path_sgd[:, 0], path_sgd[:, 1], label="SGD+momentum", linewidth=1.5)
ax1.plot(w0[0], w0[1], "ko", markersize=6, label="Start")
ax1.set_xlabel("w1"); ax1.set_ylabel("w2")
ax1.set_title("Iterate Paths"); ax1.legend()
ax2.plot(loss_adam, label="Adam"); ax2.plot(loss_sgd, label="SGD+momentum")
ax2.set_xlabel("Iteration"); ax2.set_ylabel("Loss")
ax2.set_title("Loss vs Iteration")
ax2.set_yscale("log"); ax2.legend(); ax2.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()
← Previous
Week 7: Momentum and Acceleration
Next →
Week 9: Practical Training Dynamics
On this page
  • One Learning Rate Doesn't Fit All
  • AdaGrad
  • RMSProp
  • Adam
  • Practice Defaults and What Adaptivity Hides
  • Browser lab