Pick a starting point. Watch SGD struggle.
The navigator below has presets; start with the narrow ravine. Drag the start point off-axis, set around , and run plain SGD (no momentum). Watch the trajectory zig-zag.
Then crank higher. It gets worse, not better: the iterates start oscillating across the ravine. Try above the divergence threshold and the trajectory shoots off the edge.
This is the ravine pathology, the defining failure mode of vanilla gradient descent. The rest of the module is the toolkit for fixing it. But first, the basics.
The gradient-descent update rule
From Module 6: the gradient points in the direction of steepest ascent. To descend, walk against it:
Read it aloud: new equals old minus a step-size times the gradient. The scalar (eta) is called the learning rate. Everything interesting about optimization is buried in how you pick that one number.
One step by hand
For with and , compute .
Why against the gradient, not along it
If you walk along the gradient, you’re walking toward the direction of fastest increase (the worst direction). If you walk against it, you’re walking toward the direction of fastest decrease. The sign in the update rule is load-bearing. Get it wrong and the loss explodes.
A common confusion: “gradient descent moves toward the minimum.” It doesn’t. It moves against the gradient, which in curved landscapes is almost never the same thing as “toward the minimum.” In a narrow valley that direction is roughly perpendicular to the valley’s axis. You end up zig-zagging, as you saw above. We’ll fix that with momentum in Lesson 10.3.
The three regimes of step size
On the 1D parabola , GD is . Three behaviors, depending on :
- : monotone decay. Small, steady steps. Slow but safe.
- : one-shot convergence to zero. Best possible for this problem.
- : oscillation. The iterates bounce across the minimum but still shrink in magnitude.
- : divergence. Iterates blow up.
The general rule: the biggest stable learning rate is , where is the largest eigenvalue of the Hessian (which, for our parabola, equals ). Above , GD diverges. Below it but close, you oscillate. Well below, you crawl.
Threshold of divergence
For , what is the largest learning rate above which GD diverges?
Another curvature
For , what is the divergence threshold ?
(Hint: for , the second derivative is , so .)
2D: when the curvature isn't the same in every direction
Make the function . The Hessian has eigenvalues and . A learning rate safe for (below ) is painfully slow for (which would like near ). Along the -axis GD bounces; along the -axis it crawls. The trajectory becomes a tight zig-zag hugging the long axis of the valley.
This is the ravine problem you saw in step 1. Nearly every real loss landscape has one axis that is 100× or 10,000× steeper than another. Vanilla GD cannot handle that ratio well.
Switch the navigator above to SGD + momentum and run the same starting point. Watch it rocket down the ravine. Momentum is the fix for this exact failure mode.
Condition number
A 2D quadratic’s Hessian has eigenvalues and . Compute the condition number .
(The bigger this number, the more GD struggles. A transformer’s loss-landscape condition number is typically astronomical.)
The whole module, in one sentence
Gradient descent has two problems:
- Vanilla GD is bad at anisotropic landscapes. Fix: momentum (Lesson 10.3), adaptive per-parameter step sizes (Lesson 10.4).
- You can’t afford to compute the full gradient. Fix: stochastic/mini-batch gradient estimation (Lesson 10.2).
Everything else in this module (Adam, AdamW, cosine schedules, warmup, gradient clipping) is engineering on top of those two fixes. By the end you’ll read nanoGPT’s configure_optimizers and understand every line.
Lesson complete