Skip to content
AI360XpertBeta

SGD and Optimization Variants

Full-batch gradients cost a pass over every example per step, so training estimates them from small random mini-batches and steadies the noise with momentum and per-coordinate scaling.

On a stretched loss valley, plain SGD bounces across the walls, momentum damps the bounce and glides forward, and Adam rescales a lopsided gradient of 0.2 and 4.0 into equal steps of 0.1
On a stretched loss valley, plain SGD bounces across the walls, momentum damps the bounce and glides forward, and Adam rescales a lopsided gradient of 0.2 and 4.0 into equal steps of 0.1

Why Does This Exist?

Batch gradient descent needs a pass over every training example for a single parameter update. With N=10,000,000N = 10{,}000{,}000 images, one step costs ten million forward passes and ten million backward passes. Training would grind to a halt.

Stochastic gradient descent replaces the true gradient with an estimate from a small random mini-batch of BB samples, typically 32 to 512. Throughput rises by factors of thousands. The price is noise: each estimate wobbles around the truth, and on stretched loss surfaces the wobble has structure. Where curvature is steep along one axis and shallow along another, plain SGD ricochets between the steep walls while barely advancing along the valley floor.

The variants exist to fix those two complaints. Momentum averages out the oscillation. RMSprop rescales each coordinate by its history. Adam does both, with a correction for the early steps. For the object they all consume, see gradients; for the loop around them, see gradient descent variants.

The one idea you need first

A gradient is one slope per parameter, pointing the steepest uphill way, so training steps against it. A mini-batch gradient is that object estimated on BB random examples: unbiased, meaning right on average, with variance that shrinks as 1/B1/B. Everything below is machinery for living with that variance.

Think of It Like This

A ping-pong ball, then a bowling ball with smart treads

Roll a ball down a steep icy half-pipe to reach the exit at the far end. The walls rise sharply left and right, but the floor tilts only gently forward.

Plain SGD is a lightweight ping-pong ball in gusty wind. Released on a wall, it shoots across the floor and ricochets up the opposite side, travelling miles sideways while advancing inches forward.

Momentum makes it a heavy bowling ball. Sideways bounces cancel across steps while the steady forward pull accumulates, so it barrels down the centre. Adam adds motorised treads per direction: brakes across the steep bouncing axis, gas along the gentle forward one.

Where it stops: bowling balls obey continuous physics in three dimensions, while optimizers take discrete steps across parameter spaces with millions of axes. And no tread pattern fixes a learning rate that is simply wrong.

How It Actually Works

Given data {(xi,yi)}i=1N\{(\mathbf{x}_i, y_i)\}_{i=1}^N and parameters θ\mathbf{\theta}, the true loss is the average L(θ)=1N∑iℓi(θ)\mathcal{L}(\mathbf{\theta}) = \frac{1}{N} \sum_i \ell_i(\mathbf{\theta}). At step tt a random mini-batch Bt\mathcal{B}_t of size BB gives the estimate gt=1B∑i∈Bt∇ℓi(θt)\mathbf{g}_t = \frac{1}{B} \sum_{i \in \mathcal{B}_t} \nabla \ell_i(\mathbf{\theta}_t), and the update is θt+1=θt−ηgt\mathbf{\theta}_{t+1} = \mathbf{\theta}_t - \eta \mathbf{g}_t. Because batches are uniform samples, E[gt]=∇L(θt)\mathbb{E}[\mathbf{g}_t] = \nabla \mathcal{L}(\mathbf{\theta}_t), with covariance scaling as 1/B1/B.

Momentum

Polyak momentum keeps a velocity vt=βvt−1+gt\mathbf{v}_t = \beta \mathbf{v}_{t-1} + \mathbf{g}_t with friction β\beta in [0.9,0.99)[0.9, 0.99), and steps θt+1=θt−ηvt\mathbf{\theta}_{t+1} = \mathbf{\theta}_t - \eta \mathbf{v}_t. Unrolled, the velocity is an exponential moving average ∑τβt−τgτ\sum_{\tau} \beta^{t-\tau} \mathbf{g}_\tau. Gradients that flip sign cancel toward zero; gradients that persist reinforce, multiplying the effective step by 1/(1−β)1/(1 - \beta), which is 1010 times at β=0.9\beta = 0.9.

RMSprop

Different parameters need different step sizes. RMSprop tracks squared gradients st=β2st−1+(1−β2)gt2\mathbf{s}_t = \beta_2 \mathbf{s}_{t-1} + (1 - \beta_2) \mathbf{g}_t^2 element-wise, then steps θt+1=θt−η gt/(st+ϵ)\mathbf{\theta}_{t+1} = \mathbf{\theta}_t - \eta\,\mathbf{g}_t / (\sqrt{\mathbf{s}_t} + \epsilon) with ϵ≈10−8\epsilon \approx 10^{-8}. Loud coordinates get turned down, quiet ones get turned up.

Adam

Adam (adaptive moment estimation) keeps both moments, mt=β1mt−1+(1−β1)gt\mathbf{m}_t = \beta_1 \mathbf{m}_{t-1} + (1 - \beta_1) \mathbf{g}_t and vt=β2vt−1+(1−β2)gt2\mathbf{v}_t = \beta_2 \mathbf{v}_{t-1} + (1 - \beta_2) \mathbf{g}_t^2, then corrects the zero start-up bias with m^t=mt/(1−β1t)\hat{\mathbf{m}}_t = \mathbf{m}_t/(1 - \beta_1^t) and v^t=vt/(1−β2t)\hat{\mathbf{v}}_t = \mathbf{v}_t/(1 - \beta_2^t), stepping θt+1=θt−η m^t/(v^t+ϵ)\mathbf{\theta}_{t+1} = \mathbf{\theta}_t - \eta\,\hat{\mathbf{m}}_t/(\sqrt{\hat{\mathbf{v}}_t} + \epsilon). Defaults are β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, ϵ=10−8\epsilon = 10^{-8}. Second-order context for why curvature matters lives in the Jacobian and Hessian.

Worked example

One Adam step by hand, t=1t = 1, from θ0=[1.0,2.0]\mathbf{\theta}_0 = [1.0, 2.0] with η=0.1\eta = 0.1 and gradient g1=[0.2,4.0]\mathbf{g}_1 = [0.2, 4.0], twenty times steeper along the second coordinate.

First moment: m1=0.9×0+0.1×[0.2,4.0]=[0.02,0.40]\mathbf{m}_1 = 0.9 \times \mathbf{0} + 0.1 \times [0.2, 4.0] = [0.02, 0.40]. Squared gradient: g12=[0.04,16.00]\mathbf{g}_1^2 = [0.04, 16.00]. Second moment: v1=0.999×0+0.001×[0.04,16.00]=[0.00004,0.01600]\mathbf{v}_1 = 0.999 \times \mathbf{0} + 0.001 \times [0.04, 16.00] = [0.00004, 0.01600].

Bias correction at t=1t = 1 divides out the start-up deficit exactly: m^1=[0.02,0.40]/0.1=[0.2,4.0]\hat{\mathbf{m}}_1 = [0.02, 0.40]/0.1 = [0.2, 4.0], v^1=[0.00004,0.01600]/0.001=[0.04,16.00]\hat{\mathbf{v}}_1 = [0.00004, 0.01600]/0.001 = [0.04, 16.00]. Then v^1=[0.2,4.0]\sqrt{\hat{\mathbf{v}}_1} = [0.2, 4.0], the ratios are [0.2/0.2, 4.0/4.0]=[1.0,1.0][0.2/0.2,\ 4.0/4.0] = [1.0, 1.0], and θ1=[1.0,2.0]−0.1×[1.0,1.0]=[0.9,1.9]\mathbf{\theta}_1 = [1.0, 2.0] - 0.1 \times [1.0, 1.0] = [0.9, 1.9].

The ravine vanished on step one: a 2020-to-11 gradient imbalance became equal 0.10.1 moves per coordinate. That equalisation is the whole point of the second moment.

Code

One Adam step as a small function, matching the worked example exactly. NumPy only.

import numpy as np

def adam_step(theta: np.ndarray, grad: np.ndarray, m: np.ndarray, v: np.ndarray, t: int,              lr: float = 0.1, b1: float = 0.9, b2: float = 0.999, eps: float = 1e-8) -> tuple:    """One Adam update with bias correction; start m and v at zero."""    m = b1 * m + (1 - b1) * grad    v = b2 * v + (1 - b2) * grad ** 2    m_hat = m / (1 - b1 ** t)    v_hat = v / (1 - b2 ** t)    return theta - lr * m_hat / (np.sqrt(v_hat) + eps), m, v

theta = np.array([1.0, 2.0])m = np.zeros(2)v = np.zeros(2)theta, m, v = adam_step(theta, np.array([0.2, 4.0]), m, v, 1)print(np.round(theta, 4))  # -> [0.9 1.9]

Both coordinates move exactly 0.10.1 despite the 2020-to-11 gradient ratio, which is the equalisation the worked example derived by hand.

Watch Out For

Skipping the bias correction during warm-up

Without the 1−βt1 - \beta^t corrections, the second moment at step one is v1=0.001 g12\mathbf{v}_1 = 0.001\,\mathbf{g}_1^2, so dividing by its root scales the step up by 1/0.001≈31.61/\sqrt{0.001} \approx 31.6 times the intended size. Transformers hit with that spike in their opening steps destabilise immediately.

The symptom looks like a learning rate that is far too high, but only for the first few hundred steps. Fix: always apply both corrections, and pair Adam with a learning-rate warm-up over the first 500 to 2,000 steps.

Putting weight decay inside Adam instead of beside it

Adding L2 to the loss feeds the decay gradient through the adaptive rescaling, so weights with large gradients get decayed less and quiet weights get decayed more. That is backwards: decay should shrink every weight equally.

The symptom is weak regularisation exactly where overfitting is worst, on the loudest weights. Fix: use decoupled weight decay (AdamW), which subtracts λθ\lambda \mathbf{\theta} after the adaptive step instead of routing it through the moments.

The Quick Version

  • Mini-batch SGD estimates the true gradient on BB random examples: unbiased, with variance scaling as 1/B1/B.
  • Momentum averages past gradients with decay β\beta, cancelling oscillation and multiplying steady progress by 1/(1−β)1/(1 - \beta).
  • RMSprop divides each coordinate by its root-mean-square history, equalising loud and quiet parameters.
  • Adam keeps both moments with exact bias correction, and is the default starting choice for deep networks.
  • In the worked step, a gradient of [0.2,4.0][0.2, 4.0] becomes equal moves of 0.10.1 per coordinate.
  • Never skip bias correction or warm-up, and use AdamW so weight decay is not rescaled.