Nesterov Accelerated Gradient
Plain gradient steps where you stand; Nesterov steps where you are headed. That lookahead kills zigzags and squares the convergence speed.
Why Does This Exist?
Ill-conditioned valleys (condition number ) make gradient descent bounce wall to wall: each step overshoots across the narrow axis while crawling along the long one. Heavy-ball momentum helps but still evaluates gradients at stale positions and overshoots.
Nesterov fixes the staleness: step to the lookahead , evaluate , then correct. Smooth convex rate jumps , optimal for first-order methods. It accelerates the descents in gradient descent variants toward the rates in convergence rates.
The one idea you need first
Momentum carries velocity . Heavy-ball steps : gradient at the old spot plus inertia. Nesterov moves the gradient to the predicted spot.
Think of It Like This
Steering where the car will be
Heavy-ball steers from the rear-view mirror: gradient where you were, plus speed. Nesterov looks through the windshield at the predicted position and steers there.
On straight highways both work; in slaloms the mirror steers late and oscillates. Where it stops: lookahead assumes smooth roads. Potholes (nonsmooth kinks, stochastic noise) punish prediction and need smaller bets or proximal variants.
How It Actually Works
For -smooth convex : , , giving f(x_k) - f^\\star \\leq 2L\\|x_0-x^\\star\\|^2/(k+1)^2. Strongly convex variant restarts or fixes for linear rate. Practical Nesterov (PyTorch NAG, AdamW+Nesterov) adapts the schedule; FISTA ports it to proximal (Lasso).
Worked example
Quadratic (). Gradient descent with : crawls ( per step, ~114 steps per digit) while bounces at the edge. Nesterov with : lookahead damps the overshoot first, then rides down, reaching in about 30 steps versus 200+ plain. Two arcs replace six zigzags: the diagram's count is the mechanism, not decoration.
Code
import numpy as np
def f_grad(p): return np.array([2 * p[0], 50 * p[1]]) # grad of x^2+25y^2
x, v = np.array([5.0, 5.0]), np.zeros(2)for k in range(1, 31): y = x + 0.8 * v # lookahead x_new = y - (1 / 50) * f_grad(y) v, x = x_new - x, x_newprint(np.round(x, 3)) # -> ~[0. 0.]Thirty lookahead steps park near zero where plain descent still bounces. The only change from heavy-ball is where the gradient is read.
Watch Out For
Running textbook schedule on noisy stochastic gradients
The proof needs exact gradients; noisy SGD estimates make lookahead chase sampling noise and diverge at aggressive . Loss explodes where deterministic Nesterov glides.
Dampen (), grow batches, or use accelerated-variance-reduced variants. If Nesterov-SGD oscillates worse than SGD, noise is driving the wheel.
Skipping restarts on locally-strong bowls
Global with local strong convexity wastes speed: scheduled Nesterov overshoots near the bottom and ripples. Gradient restarts (reset momentum when ) recover linear tail speed.
Restart adaptively. If the loss sawtooths late, momentum is stale, not small.
The Quick Version
- Nesterov reads the gradient at the lookahead, not the position.
- Smooth convex jumps to , optimal for first-order oracles.
- Example : ~30 steps versus 200+ plain.
- Heavy-ball carries the past; Nesterov aims the gradient at the future.
- FISTA extends it to proximal nonsmooth splits.
- Dampen or restart under noise and local strong convexity.