The gradient says which way is downhill. How far to step is a separate question, and chapter 6 showed why it matters: two thousand rounds for four observations. This chapter is about the three answers that matter in practice — the plain step you have used since chapter 2, momentum, and Adam, the optimizer the GPT at the end of this course actually trains with.
The working file is
examples/ArtOfTheStep
— run it with make -C examples/ArtOfTheStep run.
The unfair valley
The example digs one loss surface: (p−2)² + 256·(q−1)² — a canyon
whose wall is two hundred and fifty-six times steeper than its floor.
Every real loss has this shape somewhere; the exaggeration just makes
it printable.
steep wall (q): one grain too bold and the step overshoots,
═╗ lands higher on the far side, overshoots harder — explosion
║
╚═══════════════════════▶ shallow floor (p): safe steps are
tiny steps only grains, progress is a crawlThe cruelty is the coupling: one rate serves both directions. The wall demands a tiny step or the descent explodes across it; the floor then inherits that tiny step and crawls. The wall prices the floor.
Three descents, printed
The plain step, capped by the wall at a rate of about 1/512:
the plain step
round 1: loss 260.000000
round 50: loss 2.725719
round 200: loss 0.842453
round 800: loss 0.007688Eight hundred rounds and still not done — all of it spent crawling the floor, since the wall direction was finished almost immediately.
Momentum, at the same capped rate, gives each knob a velocity that accumulates blame. Where the slope keeps agreeing — a long floor — the stride grows toward a multiple of the plain one; where the slope alternates — across a wall — the contributions cancel. A rolling ball, not a cautious walker:
vp := Beta1 * vp + Gradient(p);
p := p - WallRate * vp;momentum
round 50: loss 1.872343
round 200: loss 0.000000Adam repeals the wall’s pricing altogether. Beside the momentum
average m, each knob keeps a running average s of its blame’s
square, and steps by m / √s: a knob whose blame is habitually large
takes modest strides, one whose blame is habitually small takes
confident ones. Each direction is normalized to its own scale — so the
example runs Adam at a rate thirty-two times bolder than the wall
permits the plain step, safely:
mp := Beta1 * mp + (1.0 - Beta1) * gp;
sp := Beta2 * sp + (1.0 - Beta2) * gp * gp;
p := p - AdamRate * mHat / (Sqrt(sHat) + Epsilon);adam
round 50: loss 0.213920
round 200: loss 0.000000Ahead of both at every milestone. The β₁ = 0.9 and β₂ = 0.999 here
are the values almost every published model uses unchanged, and the ones
gpt.mica trains with; the mHat/sHat corrections repair the running
averages’ early rounds, when they still remember their zero start. The
example spells all of it out on two scalar knobs so you can read every
line — and the math unit’s AdamStep verb is this exact arithmetic over
whole tensors at once, which is how the GPT will consume it.
What to take forward
One honest caution, then the summary. Adam is not magically better — on this convex canyon everything converges eventually, and there are problems where plain momentum generalizes better; what Adam buys is robustness to scale differences between knobs, which a 786944-parameter model has in abundance. The summary: direction comes from the backward walk, pace from the optimizer, and both are now in hand. What the machine still cannot do is read — the GPT’s raw material is text, and text is not numbers. Turning one into the other is next: tokens and embeddings.