Deep LearningBackprop & Optimizers

Backpropagation & Optimizers

Background
  • Rumelhart, Hinton, and Williams (1986) popularized backpropagation for training multi-layer networks
  • Based on the chain rule of calculus applied to computational graphs
  • Adam optimizer (Kingma & Ba, 2014) is now the default optimizer for most deep learning tasks
The Concept

Backpropagation computes gradients of the loss with respect to every weight in the network by applying the chain rule backward from the output. Optimizers (SGD, Adam, AdaGrad) use these gradients to update weights, iteratively reducing the loss.

How it Links
  • Chain rule is the mathematical backbone - connects to automatic differentiation in PyTorch/JAX
  • Learning rate scheduling (cosine annealing, warmup) builds on the basic optimizer framework
  • Vanishing/exploding gradients connect to initialization strategies, batch norm, and residual connections
Concrete Example
Training a network to classify photos: forward pass computes a wrong prediction (cat predicted as dog). Backprop asks: by how much did each weight in each layer contribute to that error? Each weight gets a gradient proportional to its blame. Adam updates each weight scaled by its recent gradient history.
Learning outcomes
  • Derive reverse-mode automatic differentiation as the chain rule applied right-to-left on a DAG, with cost equal to one forward pass per output.
  • State the SGD, momentum, Nesterov, Adagrad, RMSprop, Adam, and AdamW updates and explain when each is preferred.
  • Prove (sketch) Adam's bias correction and explain why Adam + L2 differs from AdamW.
  • Pick a learning-rate schedule (cosine annealing, warmup) and explain why warmup helps with Adam in transformers.
  • Apply gradient clipping and mixed-precision training, and recognize when L-BFGS or K-FAC is the right choice.

TL;DR

TL;DR

Backpropagation is reverse-mode automatic differentiation specialized to feed-forward DAGs [1]. It computes every parameter gradient in time proportional to one forward pass. The optimizer family layered on top is a tower of corrections: SGD is the baseline; momentum (Polyak 1964) [9] averages gradients to dampen noise; Nesterov (1983) [8] evaluates the gradient at the look-ahead point; Adagrad [6] rescales each parameter by its accumulated squared gradient; RMSprop [10] replaces the sum with an exponential moving average; Adam [2] combines momentum with RMSprop and adds bias correction; AdamW [3] decouples weight decay from the adaptive update. Cosine annealing [4] and warmup [5] are the standard schedules; gradient clipping and mixed precision [11] are the standard production fixes.

Intuition

Training a neural network is gradient descent on a high-dimensional, non-convex loss surface. Two pieces have to work together:

  1. A way to compute the gradient. Backprop computes ∇θL\nabla_\theta \mathcal{L} for all parameters at once in a single sweep over the network, reusing intermediate results.
  2. A rule for taking the next step. SGD steps directly along the negative gradient. Everything fancier (momentum, Adam, AdamW) is a different way to pick the step size and direction given a stream of stochastic gradient estimates.

The optimizers in this section all share the same skeleton: maintain some running statistics of past gradients (mean, second moment, both, or neither), use those statistics to scale the next step, and trade more state for faster convergence and more stable training.

The key insight behind reverse-mode autodiff

For a scalar loss L\mathcal{L} that depends on parameters through a DAG, reverse-mode autodiff (backprop) computes all ∂L/∂θi\partial \mathcal{L} / \partial \theta_i in a single backward pass over the graph. The cost is at most a small constant (typically 2 to 4) times the forward pass, regardless of the number of parameters. This is what makes training networks with billions of parameters feasible.

Forward-mode autodiff has the opposite trade-off: cheap per-input partial ∂f/∂xj\partial f / \partial x_j, but you need one pass per input. For ML where there is one scalar loss and millions of parameters, reverse mode wins by orders of magnitude.

Math

Reverse-mode autodiff: chain rule on a DAG

Setup

Model the computation as a directed acyclic graph G=(V,E)G = (V, E) where each node vv stores a value ava_v computed from its parents pa(v)\mathrm{pa}(v) via some elementary operation:

av=fv ⁣(au1,au2,… ),ui∈pa(v).a_v = f_v\!\bigl( a_{u_1}, a_{u_2}, \dots \bigr), \qquad u_i \in \mathrm{pa}(v).

Designate one node as the loss L=av⋆\mathcal{L} = a_{v^\star} and a subset of nodes as parameters θ\theta. We want ∂L/∂aθ\partial \mathcal{L} / \partial a_\theta for every parameter.

Forward pass

Evaluate nodes in topological order. Cache every ava_v. Time and memory are both O(∣V∣)O(|V|).

Backward pass

Define the adjoint aˉv=∂L/∂av\bar{a}_v = \partial \mathcal{L} / \partial a_v. The adjoint of the loss node is aˉv⋆=1\bar{a}_{v^\star} = 1. For every other node vv, by the chain rule applied to the children ch(v)\mathrm{ch}(v):

aˉv=∑u∈ch(v)aˉu⋅∂fu∂av(apa(u)).\bar{a}_v = \sum_{u \in \mathrm{ch}(v)} \bar{a}_u \cdot \frac{\partial f_u}{\partial a_v}\bigl( a_{\mathrm{pa}(u)} \bigr).

Process nodes in reverse topological order. For each node, fetch the adjoints of its children (already computed) and multiply by the local Jacobian. The local Jacobian is computed at the cached forward values, which is why the forward pass had to run first.

Cost

Every edge in EE is touched once in the forward pass and once in the backward pass. So time is Θ(∣E∣)\Theta(|E|) for both passes, and the backward pass is at most a small constant times the forward pass (typically 2-4× because of cache pressure and slightly more arithmetic per local Jacobian).

Memory

The naive implementation stores every intermediate activation (O(∣V∣)O(|V|) memory). For very deep networks this is the dominant cost; checkpointing recomputes activations in segments to trade compute for memory, taking the cost from O(L)O(L) memory and O(L)O(L) time to O(L)O(\sqrt{L}) memory and O(L)O(L) time. [14]

Why it works for arbitrary DAGs

Backprop is just reverse-mode autodiff specialized to feed-forward networks. For arbitrary DAGs (residual connections, attention, weight tying), the same algorithm applies: process in reverse topological order and accumulate the adjoint contributions from every child. Modern frameworks (PyTorch’s autograd, JAX’s grad, TensorFlow’s GradientTape) all implement this.

From SGD to Nesterov: the first-order family

Notation: parameters θ∈Rp\theta \in \mathbb{R}^p, stochastic gradient at step tt is gt=∇θLt(θt)g_t = \nabla_\theta \mathcal{L}_t(\theta_t), learning rate η\eta, momentum coefficient ρ∈[0,1)\rho \in [0, 1).

SGD

θt+1=θt−η gt.\theta_{t+1} = \theta_t - \eta\, g_t.

Pros: cheap (no extra state), well-understood theory. Cons: noisy on stochastic gradients, slow on ill-conditioned problems (the step size must be at most 1/L1/L where LL is the largest curvature, but the slowest direction has curvature μ\mu, so convergence rate is O((1−μ/L)t)O((1 - \mu/L)^t)).

Polyak’s heavy-ball momentum

vt+1=ρ vt+gt,θt+1=θt−η vt+1.v_{t+1} = \rho\, v_t + g_t, \qquad \theta_{t+1} = \theta_t - \eta\, v_{t+1}.

The velocity vv is an exponential moving average of past gradients with effective horizon 1/(1−ρ)1/(1-\rho). This dampens oscillations across narrow valleys (the perpendicular components average to zero) while accumulating progress along the valley floor.

Convergence rate on quadratic objectives: O((κ−1)2/(κ+1)2)O((\sqrt{\kappa} - 1)^2 / (\sqrt{\kappa} + 1)^2) where κ=L/μ\kappa = L/\mu is the condition number, roughly κ\sqrt{\kappa} times faster than vanilla GD on ill-conditioned quadratics [9]. Typical ρ\rho for deep learning: 0.9 (effective horizon 10 steps).

Nesterov’s accelerated gradient

Evaluate the gradient at the look-ahead point θt−ηρvt\theta_t - \eta \rho v_t rather than at θt\theta_t:

gt=∇L(θt−ηρvt),vt+1=ρvt+gt,θt+1=θt−ηvt+1.g_t = \nabla \mathcal{L}\bigl(\theta_t - \eta \rho v_t\bigr), \qquad v_{t+1} = \rho v_t + g_t, \qquad \theta_{t+1} = \theta_t - \eta v_{t+1}.

The look-ahead lets the algorithm “see” the curvature ahead and correct earlier. On smooth convex problems Nesterov’s method achieves the optimal O(1/t2)O(1/t^2) rate (vs O(1/t)O(1/t) for plain GD) [8]. In practice on deep nets the win over plain momentum is small but consistent and free [7].

Adaptive optimizers: Adagrad, RMSprop, Adam, AdamW

The first-order family scales every parameter with the same learning rate η\eta. Adaptive methods give each parameter its own learning rate, scaled by past gradient magnitude.

Adagrad

Gt=Gt−1+gt⊙gt,θt+1=θt−ηGt+ϵ⊙gt.G_t = G_{t-1} + g_t \odot g_t, \qquad \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} \odot g_t.

(Element-wise.) Each parameter’s effective learning rate decays like 1/t1/\sqrt{t} along directions that have seen large gradients. Strong on sparse gradients (NLP with bag-of-words features, recommender embeddings) where rare features get to keep a large step. Convergence guarantees on online convex problems [6]. Weakness: GtG_t grows unboundedly and the effective LR eventually dies.

RMSprop

Replace the sum with an exponential moving average:

vt=β2 vt−1+(1−β2) gt⊙gt,θt+1=θt−ηvt+ϵ⊙gt.v_t = \beta_2\, v_{t-1} + (1 - \beta_2)\, g_t \odot g_t, \qquad \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{v_t + \epsilon}} \odot g_t.

This fixes the dying-LR problem. Hinton introduced it in lecture 6.5 of his 2012 Coursera class [10]; it never had a paper but is widely used.

Adam

Combine RMSprop (second-moment EMA) with momentum (first-moment EMA):

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

with bias correction (see below):

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

and update

θt+1=θt−ηv^t+ϵ m^t.\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon}\, \hat{m}_t.

Defaults from Kingma & Ba [2]: β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, ϵ=10−8\epsilon = 10^{-8}, η=10−3\eta = 10^{-3}. Adam is the default optimizer for most deep-learning workloads outside CV.

Why bias correction

At t=1t = 1, m1=(1−β1)g1≈0.1 g1m_1 = (1 - \beta_1) g_1 \approx 0.1\, g_1, biased toward zero. Computing m^t=mt/(1−β1t)\hat{m}_t = m_t / (1 - \beta_1^t) undoes the bias. Without it, the early steps would be a factor of 1−β1t≈0.11 - \beta_1^t \approx 0.1 smaller than intended, making the network train too slowly for the first few hundred steps; over-time it self-corrects but the warm-up period is significant. The same logic applies to v^t\hat{v}_t.

AdamW: decoupled weight decay

The original Adam paper recommended adding L2 regularization through the gradient: gt+=λθtg_t \mathrel{+}= \lambda \theta_t before the moment updates. Loshchilov & Hutter showed this is wrong: the L2 term gets divided by v^t\sqrt{\hat{v}_t} along with everything else, so parameters with large second moments end up with effectively less weight decay than parameters with small second moments. The fix is to decouple weight decay from the gradient:

θt+1=θt−η (m^tv^t+ϵ  +  λ θt).\theta_{t+1} = \theta_t - \eta\, \Bigl( \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} \;+\; \lambda\, \theta_t \Bigr).

The decay term is a uniform pull toward the origin, applied directly to θ\theta without going through the adaptive scaling. AdamW [3] is the default for transformer training (GPT, BERT, ViT).

Schedules and tricks

A lot of “what makes training work” lives in the learning-rate schedule and a small set of stability tricks.

Cosine annealing with warm restarts

The cosine schedule (Loshchilov & Hutter 2016) [4] decays the learning rate from ηmax⁡\eta_{\max} to ηmin⁡\eta_{\min} as

ηt=ηmin⁡+12(ηmax⁡−ηmin⁡) (1+cos⁡(πt/T)).\eta_t = \eta_{\min} + \tfrac{1}{2}(\eta_{\max} - \eta_{\min})\,\bigl(1 + \cos(\pi t / T)\bigr).

The variant SGDR resets tt at the end of each cycle (warm restart); each restart escapes flat regions and explores new local minima. Now the de facto schedule for image classification.

Linear warmup

Start at η=0\eta = 0 and linearly ramp to ηmax⁡\eta_{\max} over the first ~5% of training. Standard for transformers and large-batch CV (Goyal et al. 2017 trained ImageNet in 1 hour on 256 GPUs by carefully scheduling warmup with batch size; see “linear scaling rule”) [5]. Empirically the largest stability benefit is for Adam in transformers, where the second-moment estimate v^t\hat{v}_t is noisy at the start and a too-large learning rate quickly diverges.

Gradient clipping

When a single batch produces a huge gradient (a step into a non-smooth region of the loss), clip its norm:

g  ←  g⋅min⁡ ⁣(1,  τ/∥g∥).g \;\leftarrow\; g \cdot \min\!\Bigl( 1,\; \tau / \lVert g \rVert \Bigr).

Standard for RNNs (where exploding gradients are routine) and for transformers under mixed precision. Typical τ\tau: 1.0 or 5.0.

Mixed precision

Store activations and gradients in fp16 (or bf16) but keep a master copy of weights and the optimizer state in fp32. Use a dynamic loss-scaling factor to keep small gradients out of the fp16 subnormal range. The result: ~2× memory and ~1.5-2× throughput at no measurable accuracy loss [11]. Implemented in torch.cuda.amp and tf.keras.mixed_precision.

Second-order methods

Briefly: when batch size is large enough that one batch is essentially the full gradient, second-order methods can win. Two examples:

  • L-BFGS [12]: maintains an implicit low-rank approximation of the inverse Hessian using the last mm pairs of (Δθ,Δg)(\Delta \theta, \Delta g). Memory O(mp)O(mp). Used for batch optimization in scientific computing and sometimes for fine-tuning small models.
  • K-FAC [13]: approximates the Fisher information matrix as a Kronecker product of layer-local matrices, then preconditions the gradient. Sometimes useful on small networks but adds significant complexity. Mostly displaced in practice by Adam + careful schedules.

For most modern training, the first-order family (Adam/AdamW with cosine + warmup) is the right default; second-order methods are a niche tool.

Optimizer comparison

OptimizerState per parameterAdaptive LR?Typical hyperparametersWhen to useWatch out for
SGD0nolr 0.01-0.1CV from scratch with momentumSlow on ill-conditioned problems.
SGD + momentum1 (velocity)nolr 0.01-0.1, momentum 0.9ResNet-style CNNs, ImageNet baselinesTune lr per architecture.
Nesterov1nolr 0.01-0.1, momentum 0.9Slight upgrade over momentumSame as momentum.
Adagrad1 (G_t)yes (per-param)lr 0.01-0.1Sparse gradients (NLP bag-of-words)LR dies as G_t grows; rarely used today.
RMSprop1 (v_t EMA)yeslr 1e-3, beta2 0.99RNNs and pre-Adam workloadsMostly displaced by Adam.
Adam2 (m_t, v_t)yeslr 1e-3, beta1 0.9, beta2 0.999Default for transformers and most non-CVAdam + L2 is not weight decay; use AdamW.
AdamW2yeslr 1e-3 to 5e-4, weight_decay 0.01-0.1Default for transformers, ViT, LLM finetuningDecay weight is decoupled, not part of the gradient.

Implementations

NumPy implementations of SGD, momentum, and Adam side-by-side, then PyTorch’s torch.optim and JAX/Optax. Each version trains the same tiny MLP on XOR.

python
import numpy as np

# Each optimizer is a (state -> next state) pure function.

def sgd_step(params, grads, state, lr=0.1):
  return [p - lr*g for p, g in zip(params, grads)], state

def momentum_step(params, grads, state, lr=0.1, rho=0.9):
  if state is None: state = [np.zeros_like(p) for p in params]
  new_v = [rho*v + g for v, g in zip(state, grads)]
  new_p = [p - lr*v for p, v in zip(params, new_v)]
  return new_p, new_v

def adam_step(params, grads, state, lr=1e-3, b1=0.9, b2=0.999, eps=1e-8):
  if state is None:
      state = ([np.zeros_like(p) for p in params],
               [np.zeros_like(p) for p in params],
               0)
  m, v, t = state
  t += 1
  new_m = [b1*mi + (1-b1)*g  for mi, g in zip(m, grads)]
  new_v = [b2*vi + (1-b2)*g*g for vi, g in zip(v, grads)]
  bc1 = 1 - b1**t
  bc2 = 1 - b2**t
  new_p = [p - lr * (mi/bc1) / (np.sqrt(vi/bc2) + eps)
           for p, mi, vi in zip(params, new_m, new_v)]
  return new_p, (new_m, new_v, t)
About 25 lines of pure NumPy. Each optimizer is a state transformer; this is essentially what Optax does in JAX.

Run a head-to-head numpy comparison of SGD, momentum, and Adam on a tiny MLP for XOR. Watch how Adam reaches a low loss in far fewer steps:

Idle (packages: numpy)
(no output yet)

Interactive demo

A two-dimensional toy: gradient descent on f(x)=x2f(x) = x^2. Step the ball forward, change the learning rate, and watch the algorithm converge or oscillate. The same dynamics generalize to high-dimensional loss surfaces.

Gradient descent on f(x) = x²
x = 2.5000 f(x) = 6.2500 ∇f(x) = 5.0000

Real-world uses

DomainOptimizerScheduleWhy
Modern image classification (ResNet, EfficientNet)SGD + momentum 0.9Cosine annealing, warmup ~5%Tradition + good generalization on CV; many papers report SGD beats Adam at final test accuracy.
Transformers (BERT, GPT)AdamWLinear warmup + linear or cosine decayAdaptive LR is essential for the wide range of gradient scales across attention vs FFN layers; AdamW because plain Adam mishandles weight decay.
Vision Transformers and hybridsAdamWWarmup + cosineSame reasoning as transformers; CNN tricks (SGD + momentum) do not transfer to ViTs.
LLM fine-tuning (full-parameter)AdamWConstant or short cosineFine-tuning is short enough that fancy schedules do not matter much; stability does.
LLM fine-tuning (LoRA / PEFT)AdamW (only on adapter weights)ConstantMost parameters are frozen; the optimizer state is small enough to ignore.
Reinforcement learning (policy gradient)AdamConstantNoisy gradients benefit from adaptive scaling; weight decay is rarely needed when episodes provide implicit regularization.

Pitfalls

Adam + L2 is NOT what you want; use AdamW

Adding λθ\lambda \theta to the gradient before the moment update means the decay term gets divided by v^t\sqrt{\hat{v}_t} along with the rest. Parameters with large second moments end up with effectively less decay than parameters with small ones, the opposite of what L2 regularization is supposed to do. AdamW decouples the decay so it acts uniformly on θ\theta. For BERT/GPT/ViT-class models the gap between Adam+L2 and AdamW is several percent on downstream tasks [3].

Learning-rate cliff

Too small a learning rate stalls (you watch the loss plateau, then declare the model “doesn’t work”); too large diverges (loss to infinity in a few steps). The sweet spot can span an order of magnitude or two. The cheapest way to find it: an LR-finder sweep (Smith 2017): exponentially ramp the LR from very small to very large over a single epoch, plot the loss, pick the largest stable LR.

Warmup is not optional for Adam in transformers

Without warmup, the first few hundred Adam steps have a noisy second-moment estimate v^t\hat{v}_t, and the resulting per-parameter step sizes are wildly miscalibrated. Combined with the typical large LR of 10−310^{-3} for transformers, this makes early training diverge frequently. A linear warmup from 0 to peak over the first ~5% of steps fixes this [5]. CNNs trained with SGD+momentum do not need warmup nearly as urgently because momentum is much less sensitive to early-step noise.

Momentum interacts with batch size

The “linear scaling rule” (Goyal et al. 2017) [5]: when you multiply batch size by kk, multiply the learning rate by kk and re-tune the warmup. Otherwise momentum overshoots the new gradient regime. This is the trick that lets large-batch training keep pace with small-batch baselines.

Gradient clipping after, not before, the LR schedule

The conventional order in PyTorch is: loss.backward() -> clip_grad_norm_ -> optimizer.step() -> scheduler.step(). Swapping clip_grad_norm_ with optimizer.step() clips the wrong quantity. This is a common bug in tutorial code.

Pop quiz

Check yourself
Score: 0 / 5(0 answered)
  1. 1. What is the asymptotic cost of one backward pass relative to one forward pass?
  2. 2. Why do we apply the bias correction m_hat = m / (1 - beta1^t) in Adam?
  3. 3. Why is AdamW preferred over Adam + L2 regularization?
  4. 4. Why does linear warmup help Adam in transformer training?
  5. 5. Which technique is most effective at preventing gradient explosion in RNNs?

Interview Q&A

Derive backprop on a 2-layer MLP from first principles.onsite

Forward pass. For input x∈Rd0x \in \mathbb{R}^{d_0}, weights W1∈Rd1×d0W_1 \in \mathbb{R}^{d_1 \times d_0}, W2∈Rd2×d1W_2 \in \mathbb{R}^{d_2 \times d_1}, and biases b1,b2b_1, b_2:

z1=W1x+b1,h1=σ(z1),z2=W2h1+b2,z_1 = W_1 x + b_1, \qquad h_1 = \sigma(z_1), \qquad z_2 = W_2 h_1 + b_2,

with loss L(z2,y)\mathcal{L}(z_2, y).

Adjoints. Let δ2=∂L/∂z2\delta_2 = \partial \mathcal{L} / \partial z_2. Then by the chain rule

∂L∂W2=δ2 h1⊤,∂L∂b2=δ2,∂L∂h1=W2⊤δ2.\frac{\partial \mathcal{L}}{\partial W_2} = \delta_2\, h_1^\top, \qquad \frac{\partial \mathcal{L}}{\partial b_2} = \delta_2, \qquad \frac{\partial \mathcal{L}}{\partial h_1} = W_2^\top \delta_2.

For the hidden layer, δ1=∂L/∂z1=(W2⊤δ2)⊙σ′(z1)\delta_1 = \partial \mathcal{L} / \partial z_1 = (W_2^\top \delta_2) \odot \sigma'(z_1), and

∂L∂W1=δ1 x⊤,∂L∂b1=δ1.\frac{\partial \mathcal{L}}{\partial W_1} = \delta_1\, x^\top, \qquad \frac{\partial \mathcal{L}}{\partial b_1} = \delta_1.

Why this is reverse-mode autodiff. Each adjoint δℓ\delta_\ell is computed from the next-layer adjoint δℓ+1\delta_{\ell+1} and a local Jacobian (the activation derivative and the weight matrix transpose). Processed in reverse topological order, the cost is one local Jacobian-vector product per node, equal to the forward pass cost up to a constant. [1]

Derive Adam's update rule with bias correction.onsite

Maintain two EMAs:

mt=β1mt−1+(1−β1)gt,vt=β2vt−1+(1−β2)gt⊙gt,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 \odot g_t,

with m0=v0=0m_0 = v_0 = 0.

Bias derivation. Unroll: mt=(1−β1)∑s=1tβ1t−sgsm_t = (1 - \beta_1) \sum_{s=1}^{t} \beta_1^{t-s} g_s. If gsg_s is approximately constant at value gˉ\bar{g}, then E[mt]=(1−β1t)gˉ\mathbb{E}[m_t] = (1 - \beta_1^t) \bar{g}, biased toward zero by the factor 1−β1t1 - \beta_1^t. Define m^t=mt/(1−β1t)\hat{m}_t = m_t / (1 - \beta_1^t) to undo this; analogously v^t=vt/(1−β2t)\hat{v}_t = v_t / (1 - \beta_2^t).

Update.

θt+1=θt−ηv^t+ϵ m^t.\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon}\, \hat{m}_t.

The numerator m^t\hat{m}_t is a momentum-style EMA of past gradients; the denominator v^t\sqrt{\hat{v}_t} is a per-parameter normalization (RMSprop-style). Defaults: β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, η=10−3\eta = 10^{-3}, ϵ=10−8\epsilon = 10^{-8}. [2]

Why does AdamW outperform Adam + L2 on transformer training?research

In Adam + L2, the regularizer is added to the gradient: gt+=λθtg_t \mathrel{+}= \lambda \theta_t. Then the moment updates and the divide by v^t\sqrt{\hat{v}_t} are applied. The effect of the decay on parameter ii is

−η λθt,iv^t,i+ϵ.- \eta\, \frac{\lambda \theta_{t,i}}{\sqrt{\hat{v}_{t,i}} + \epsilon}.

Parameters with large v^t,i\hat{v}_{t,i} (typically large outgoing weights, or attention queries/keys) see effectively smaller decay; parameters with small v^t,i\hat{v}_{t,i} see effectively larger decay. The intended uniform pull toward zero is corrupted into a non-uniform pull weighted by gradient magnitudes.

AdamW decouples by writing

θt+1,i=θt,i−η m^t,iv^t,i+ϵ−η λ θt,i.\theta_{t+1,i} = \theta_{t,i} - \eta\, \frac{\hat{m}_{t,i}}{\sqrt{\hat{v}_{t,i}} + \epsilon} - \eta\, \lambda\, \theta_{t,i}.

The decay term is the same −ηλθ- \eta \lambda \theta for every parameter. Empirically this matters: Loshchilov & Hutter and many subsequent papers show 0.5-2% absolute accuracy gains on ImageNet ViTs and language model perplexity. AdamW is the default for every transformer optimizer in HuggingFace and PyTorch out of the box [3].

Sketch SGD's convergence proof on a smooth convex objective.research

Assume L:Rp→R\mathcal{L}: \mathbb{R}^p \to \mathbb{R} is convex, LL-smooth (∥∇L(x)−∇L(y)∥≤L∥x−y∥\lVert \nabla \mathcal{L}(x) - \nabla \mathcal{L}(y) \rVert \le L \lVert x - y \rVert), and the stochastic gradient gtg_t satisfies E[gt]=∇L(θt)\mathbb{E}[g_t] = \nabla \mathcal{L}(\theta_t) and E∥gt∥2≤G2\mathbb{E}\lVert g_t \rVert^2 \le G^2.

For SGD with step size ηt\eta_t:

E∥θt+1−θ⋆∥2=E∥θt−θ⋆∥2−2ηt E⟨gt,θt−θ⋆⟩+ηt2E∥gt∥2.\mathbb{E}\lVert \theta_{t+1} - \theta^\star \rVert^2 = \mathbb{E}\lVert \theta_t - \theta^\star \rVert^2 - 2 \eta_t\, \mathbb{E}\langle g_t, \theta_t - \theta^\star \rangle + \eta_t^2 \mathbb{E}\lVert g_t \rVert^2.

By convexity, ⟨∇L(θt),θt−θ⋆⟩≥L(θt)−L(θ⋆)\langle \nabla \mathcal{L}(\theta_t), \theta_t - \theta^\star \rangle \ge \mathcal{L}(\theta_t) - \mathcal{L}(\theta^\star). Take expectations and rearrange:

E[L(θt)−L(θ⋆)]≤E∥θt−θ⋆∥2−E∥θt+1−θ⋆∥22ηt+ηtG22.\mathbb{E}[\mathcal{L}(\theta_t) - \mathcal{L}(\theta^\star)] \le \frac{\mathbb{E}\lVert \theta_t - \theta^\star \rVert^2 - \mathbb{E}\lVert \theta_{t+1} - \theta^\star \rVert^2}{2 \eta_t} + \frac{\eta_t G^2}{2}.

Sum from t=1t = 1 to TT with ηt=c/t\eta_t = c / \sqrt{t} and apply Jensen on the average iterate:

E[L(θˉT)−L(θ⋆)]=O ⁣(G∥θ1−θ⋆∥/T).\mathbb{E}[\mathcal{L}(\bar{\theta}_T) - \mathcal{L}(\theta^\star)] = O\!\bigl( G \lVert \theta_1 - \theta^\star \rVert / \sqrt{T} \bigr).

So SGD on smooth convex problems converges at rate 1/T1/\sqrt{T} in function value. With strong convexity the rate improves to 1/T1/T; deep learning has neither, but the bound’s intuition (smaller step + decreasing ηt\eta_t implies smaller variance in the limit) carries over and motivates LR decay schedules [14].

Build it yourself

A short project that takes about an evening:

  1. Implement the from-scratch numpy MLP from the previous page, plus the SGD / momentum / Adam optimizers above. Train on MNIST.
  2. Reproduce the loss curves: SGD at η=0.5\eta = 0.5 vs momentum at the same η\eta vs Adam at η=10−3\eta = 10^{-3}. Plot all three on one axis.
  3. Add cosine annealing and a 5% warmup. Compare to constant LR.
  4. Add gradient clipping at τ=1.0\tau = 1.0 and try to find an LR that diverges without it but converges with it.
  5. Stretch: implement L-BFGS with a memory of m=5m = 5 and run it on a small linear regression. Notice it converges in a handful of iterations, but the per-iteration cost is much higher than Adam.

References

  1. Rumelhart, D. E., Hinton, G. E., and Williams, R. J. (1986). Learning representations by back-propagating errors. Nature 323, 533-536. doi:10.1038/323533a0
  2. Kingma, D. P. and Ba, J. (2014). Adam: A Method for Stochastic Optimization. ICLR 2015 (arXiv preprint 2014). arXiv:1412.6980
  3. Loshchilov, I. and Hutter, F. (2017). Decoupled Weight Decay Regularization. ICLR 2019 (arXiv preprint 2017). arXiv:1711.05101
  4. Loshchilov, I. and Hutter, F. (2016). SGDR: Stochastic Gradient Descent with Warm Restarts. ICLR 2017 (arXiv preprint 2016). arXiv:1608.03983
  5. Goyal, P., Dollár, P., Girshick, R., Noordhuis, P., Wesolowski, L., Kyrola, A., Tulloch, A., Jia, Y., and He, K. (2017). Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour. arXiv preprint. arXiv:1706.02677
  6. Duchi, J., Hazan, E., and Singer, Y. (2011). Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. Journal of Machine Learning Research 12, 2121-2159. https://jmlr.org/papers/v12/duchi11a.html
  7. Sutskever, I., Martens, J., Dahl, G., and Hinton, G. (2013). On the importance of initialization and momentum in deep learning. Proc. 30th International Conference on Machine Learning (ICML), 1139-1147. https://proceedings.mlr.press/v28/sutskever13.html
  8. Nesterov, Y. (1983). A method for solving the convex programming problem with convergence rate O(1/k^2). Doklady Akademii Nauk SSSR 269(3), 543-547. https://www.mathnet.ru/eng/dan46009
  9. Polyak, B. T. (1964). Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), 1-17. doi:10.1016/0041-5553(64)90137-5
  10. Tieleman, T. and Hinton, G. (2012). Lecture 6.5 - rmsprop: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural Networks for Machine Learning. https://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf
  11. Micikevicius, P., Narang, S., Alben, J., Diamos, G., Elsen, E., Garcia, D., Ginsburg, B., Houston, M., Kuchaiev, O., Venkatesh, G., and Wu, H. (2017). Mixed Precision Training. ICLR 2018 (arXiv preprint 2017). arXiv:1710.03740
  12. Liu, D. C. and Nocedal, J. (1989). On the limited memory BFGS method for large scale optimization. Mathematical Programming 45(1), 503-528. doi:10.1007/BF01589116
  13. Martens, J. and Grosse, R. (2015). Optimizing Neural Networks with Kronecker-factored Approximate Curvature. ICML 2015. arXiv:1503.05671
  14. Goodfellow, I., Bengio, Y., and Courville, A. (2016). Deep Learning. MIT Press. https://www.deeplearningbook.org

Interactive Demo

Mode:
Click "Step Forward" to begin
InputHidden 1Hidden 2Output
Example weight update: 0.005000
Mode: ForwardActive layer: none

Tip: Click Forward to watch activations propagate, then Backward to see gradients flow in reverse