Momentum and Stochastic Gradient Descent
Page 701 left gradient descent with two specific injuries. It crawls when the problem is badly conditioned — at it was still wrong after a hundred thousand iterations. And it is expensive, because Equation 7.15 sums a gradient over every training example before taking one step.
This page is the two standard repairs. They are unrelated fixes to unrelated problems, and both are one extra term.
What you’ll learn
Section titled “What you’ll learn”- Equations 7.11 and 7.12, the momentum update, and why the book calls it “a gradient update with memory”.
- That momentum is not a tweak: it changes the iteration count from to — measured at 71× fewer iterations at .
- The heavy-ball parameters a quadratic hands you for free, and the surprise that the resulting step size sits 16.3% above the where plain descent diverges.
- That momentum has two failure modes at a fixed step size: too little diverges, too much oscillates.
- Equations 7.13–7.15 and the one property SGD actually needs: the mini-batch gradient must be unbiased, not accurate.
- Measured: at a single-example gradient has a typical error 2.34× the norm of the true gradient — and its mean over 3000 draws is right to .
- Why the noise falls as only once you include the finite-population correction, and why that forces it to hit exactly zero at .
Intuition: a heavy ball, and a cheap compass
Section titled “Intuition: a heavy ball, and a cheap compass”Momentum. Roll a marble down the ravine of page 701 and it does what gradient descent does: hits a wall, bounces back, hits the other wall. Now roll a bowling ball. It is reluctant to change direction, so the sideways bounces partly cancel while the consistent downhill component accumulates. The oscillation is averaged away and the useful signal survives. That is the whole idea, and the book states it exactly this way — a heavy ball, and a moving average.
Stochastic gradients. You are still in fog, but now measuring the slope precisely means polling a million sensors. Instead you poll thirty-two of them at random. The reading is noisy — genuinely, badly noisy — but it is not systematically wrong, and it costs one thirty-thousandth as much. Take thirty thousand cheap noisy steps instead of one careful step and you are further downhill.
flowchart TD GD["Eq 7.6: x - gamma * grad"] GD --> P1["problem 1: crawls when kappa is large"] GD --> P2["problem 2: one step costs a full pass over N examples"] P1 --> M["Eq 7.11: add alpha * delta-x
a memory of the last update"] P2 --> S["Eq 7.15 with a subset
an unbiased estimate of the sum"] M --> MR["iterations drop from
O(kappa) to O(sqrt kappa)"] S --> SR["cost per step drops from
N gradients to B"] MR --> C["both, together:
the workhorse of large-scale ML"] SR --> C S -.->|"the noise also helps
escape bad local optima"| C
The dashed edge is the accidental benefit. SGD’s noise was introduced to save money, and it turned out to help the optimisation escape shallow bad minima. The book is careful to present this as an observed advantage rather than the design goal.
§7.1.2 Momentum
Section titled “§7.1.2 Momentum”The update
Section titled “The update”with . Read the second line carefully, because it is where the memory lives: is literally the previous step taken, and it is itself built from the step before that. Unrolling gives
an exponentially weighted moving average of every gradient seen so far, most recent weighted heaviest. At the sum collapses to the current gradient and you are back to Equation 7.6. At the effective averaging window is about gradients.
Two things follow immediately:
- Consistent directions accumulate. If the gradient keeps pointing the same way, the geometric sum multiplies the effective step by up to .
- Alternating directions cancel. If the gradient flips sign every step — exactly the zigzag of page 701 — consecutive terms subtract.
So momentum does not damp everything equally. It damps oscillation and amplifies drift, which is precisely the decomposition the eigendirections of a quadratic hand you.
What it costs and what it buys
Section titled “What it costs and what it buys”On a quadratic with eigenvalues in the optimal heavy-ball parameters are closed-form:
giving a convergence rate of against gradient descent’s . The became a . That is the entire value proposition, and on a problem with it is the difference between iterations and .
For Example 7.1’s matrix, and , so and .
This also means momentum has a lower stability bound, which is not how it is usually taught. At a fixed step size above , too little momentum diverges.
§7.1.3 Stochastic Gradient Descent
Section titled “§7.1.3 Stochastic Gradient Descent”The objective is a sum
Section titled “The objective is a sum”In machine learning the loss is almost always a sum over examples:
The canonical instance is a negative log-likelihood, where independence across examples turns a product into a sum (§0.3 — this is the same identity, doing real work):
so the batch update is
Every single step requires gradient evaluations. At that is a poor deal, since the step you buy is no better than a step bought with .
The one property that matters
Section titled “The one property that matters”Here is the book’s key insight, and it is worth stating precisely because it is narrower than people assume:
for gradient descent to converge, we only require that the gradient is an unbiased estimate of the true gradient.
The sum is itself an empirical estimate of an expectation (§6.4.1). Any other unbiased estimate of that expectation will do — including one computed from a random subset. Scaled correctly,
Note what is not required: that be close to . It can be enormously wrong on any given draw. Bias breaks convergence; variance only slows it down. That asymmetry is the whole design space of mini-batching.
Batch size, honestly
Section titled “Batch size, honestly”The book lays out both directions and neither dominates:
| large mini-batch | small mini-batch | |
|---|---|---|
| gradient accuracy | high, low variance in the update | low, noisy |
| convergence | more stable | noisier |
| cost per step | higher | cheap |
| hardware | exploits vectorised matrix operations | leaves the GPU idle |
| bad local optima | can get stuck | noise can escape them |
| memory | may not fit in CPU/GPU | fits |
And the framing the book adds, which is the one worth remembering: the goal is generalisation (Chapter 8), not a precise minimum of the training objective. If you do not need the exact minimiser, paying for an exact gradient is waste.
Worked example by hand
Section titled “Worked example by hand”Take Example 7.1’s quadratic and run three momentum steps at , , from . The gradient is as before.
Step 1. , since there is no previous step. So
Identical to plain gradient descent. The first momentum step is always the plain step, because there is nothing to remember yet. Useful as a sanity check on any implementation.
Step 2. The gradient at is from page 701. Now the memory contributes:
Compare against plain descent’s . Two differences, both diagnostic:
- In — the direction that needs to travel far — momentum has moved to where plain descent reached only . The consistent component was amplified.
- In — the oscillating direction — momentum sits at where plain descent overshot to . The memory of the previous partially cancelled this step’s , so the crossing was damped rather than completed.
That is the mechanism in two numbers: amplify the drift, damp the oscillation.
See it move
Section titled “See it move”Plain descent first, as the baseline, on the badly conditioned surface:
The iterates hop between the walls and creep along the floor. This is the run momentum is trying to beat.
Now the same surface with a memory term:
Watch the velocity readout rather than the position. The sideways component keeps reversing sign and averages toward zero; the along-the-valley component keeps its sign and accumulates.
And the stochastic version, where the gradient itself is unreliable:
Each step uses a corrupted gradient, so the path wanders and never quite settles. It still gets there, because the corruption has mean zero — that is the only property the convergence argument uses.
The trade-off between the two knobs is worth playing with directly. In the sketch below, both the step size and the momentum have their own stability limits, and the interesting region is where they interact:
From scratch
Section titled “From scratch”import numpy as np
# Example 7.1's quadratic again, so momentum can be compared against page 701.
A = np.array([[2.0, 1.0], [1.0, 20.0]])
b = np.array([5.0, 3.0])
xstar = np.linalg.solve(A, b)
mu, L = np.linalg.eigvalsh(A)
x0 = np.array([-3.0, -1.0])
def grad(x):
return A @ x - b
def run(gamma, alpha, tol=1e-8, cap=200_000):
"""Equations 7.11 and 7.12. alpha = 0 is plain gradient descent."""
x, dx = x0.copy(), np.zeros_like(x0)
for k in range(cap):
if np.linalg.norm(x - xstar) < tol:
return k
dx = alpha * dx - gamma * grad(x) # the remembered update
x = x + dx
if not np.all(np.isfinite(x)) or np.linalg.norm(x) > 1e12:
return -1
return cap
# --- the heavy-ball parameters a quadratic hands you -----------------------
g_hb = 4 / (np.sqrt(mu) + np.sqrt(L)) ** 2
a_hb = ((np.sqrt(L) - np.sqrt(mu)) / (np.sqrt(L) + np.sqrt(mu))) ** 2
print(f"kappa = {L / mu:.6f} sqrt(kappa) = {np.sqrt(L / mu):.6f}")
print(f"2/L ceiling = {2 / L:.6f} <- plain descent dies above this")
print(f"heavy-ball gamma = {g_hb:.6f} alpha = {a_hb:.6f}")
print(f"that gamma is {100 * (g_hb / (2 / L) - 1):.1f}% ABOVE the plain ceiling")
print(f"\nplain descent at its own best step 2/(mu+L) = {2 / (mu + L):.6f}: "
f"{run(2 / (mu + L), 0.0)} steps")
print(f"plain descent at the heavy-ball step : "
f"{'DIVERGES' if run(g_hb, 0.0) < 0 else run(g_hb, 0.0)}")
print(f"heavy ball at the heavy-ball step : {run(g_hb, a_hb)} steps")
# --- so how much momentum is right? ---------------------------------------
print("\n alpha steps")
for alpha in (0.0, 0.10, 0.1650, 0.28, a_hb, 0.5, 0.9, 0.99, 1.0):
k = run(g_hb, alpha)
print(f" {alpha:.4f} {'diverged' if k < 0 else k}")
# --- the scaling law, which is the real point ------------------------------
print("\n kappa GD HB ratio sqrt(kappa)")
for kappa in (10.0, 100.0, 1000.0, 10000.0):
Ak, bk = np.diag([1.0, kappa]), np.zeros(2)
zk, sk = np.zeros(2), np.array([1.0, 1.0])
def go(gamma, alpha):
x, dx = sk.copy(), np.zeros(2)
for k in range(200_000):
if np.linalg.norm(x - zk) < 1e-8:
return k
dx = alpha * dx - gamma * (Ak @ x - bk)
x = x + dx
return 200_000
gk = 4 / (1.0 + np.sqrt(kappa)) ** 2
ak = ((np.sqrt(kappa) - 1) / (np.sqrt(kappa) + 1)) ** 2
n1, n2 = go(2 / (1 + kappa), 0.0), go(gk, ak)
print(f" {kappa:>7.0f} {n1:>7} {n2:>6} {n1 / n2:>7.2f} {np.sqrt(kappa):>8.2f}")kappa = 10.313294 sqrt(kappa) = 3.211432
2/L ceiling = 0.099724 <- plain descent dies above this
heavy-ball gamma = 0.115976 alpha = 0.275732
that gamma is 16.3% ABOVE the plain ceiling
plain descent at its own best step 2/(mu+L) = 0.090909: 104 steps
plain descent at the heavy-ball step : DIVERGES
heavy ball at the heavy-ball step : 37 steps
alpha steps
0.0000 diverged
0.1000 diverged
0.1650 3900
0.2800 33
0.2757 37
0.5000 59
0.9000 343
0.9900 3421
1.0000 200000
kappa GD HB ratio sqrt(kappa)
10 94 35 2.69 3.16
100 939 119 7.89 10.00
1000 9384 397 23.64 31.62
10000 93837 1315 71.36 100.00On real data
Section titled “On real data”Reading the plot
Section titled “Reading the plot”The first figure is the argument for momentum, and it is an argument about exponents. Both curves are straight on log-log axes, which means both are power laws in , and the dotted reference lines are pure slopes and anchored at the leftmost measured point. Neither line is fitted. Gradient descent tracks the first, heavy ball the second, across four decades.
The right panel converts that into the number you would care about in practice. The speedup is a modest at — momentum is nearly pointless on a well-conditioned problem — and grows to at . So momentum is not a general accelerator. It is a conditioning fix, and its value is precisely proportional to how badly conditioned your problem is. That is the honest way to decide whether to reach for it.
The second figure has the fact I did not expect. The middle panel’s step size, , is above the that page 701 established as the hard ceiling for plain gradient descent. Plain descent diverges there — measured, not asserted. Yet with the same step size converges in iterations.
The reason is that the heavy-ball recursion has a different characteristic polynomial. For plain descent, mode contracts by , a single factor that must be under one. For momentum the mode satisfies a second-order recursion whose two roots multiply to , and when they are complex their common modulus is exactly — independent of . So the whole spectrum contracts at one rate, and is free to be large enough to push every mode into the complex regime. That is what “the memory keeps the overshoot bounded” means mechanically: the overshoot is still there, it is just rotating rather than growing.
The right panel is the consequence, and it is the part usually taught backwards. At that step size, momentum has a lower bound as well as an upper one: below the run diverges, because without enough memory the too-large step is simply a too-large step. Above it converges but slowly — iterations at , at — because the ball is now so heavy it takes hundreds of steps to stop. The optimum is . The default that everyone reaches for is worse than the optimum on this problem, and it is better than ; it is a reasonable compromise for problems whose you do not know, not a good value for one you do.
The third figure is the load-bearing claim of SGD, isolated. Look at on the right panel and read the two curves against each other. The red point says the typical error of a single-example gradient is times the norm of the entire true gradient — the estimate is not slightly noisy, it points a substantially different direction with a substantially different length. The green point says that averaging of those estimates lands within , and the residual is sampling error in my draws, not bias. These two facts coexist, and only the second one is required.
The left panel shows how the noise falls, and it needed a correction I initially got wrong. A naive line does not fit: at out of the measured spread is smaller than at , where predicts only . The dashed curve includes the finite-population factor , which arises because sampling is without replacement, and it fits. That factor is also what makes the endpoint sensible: at the noise is exactly , not merely small, because a full batch is not a very good sample of the population — it is the population. Any account of mini-batch noise that predicts small-but-nonzero error at has the wrong sampling model.
Pitfalls
Section titled “Pitfalls”Compare
Section titled “Compare”| plain descent | momentum | stochastic descent | |
|---|---|---|---|
| equation | 7.6 | 7.11 and 7.12 | 7.15 with a subset |
| extra state | none | one vector, | none |
| extra hyperparameter | none | batch size | |
| fixes | — | slow convergence when is large | cost per step |
| iterations on a quadratic | , but cheap ones | ||
| gradient per step | exact, evaluations | exact, evaluations | noisy, evaluations |
| step-size limit | can exceed | needs | |
| can escape a bad local minimum | no | rarely | sometimes, via the noise |
| at | iterations | reading |
|---|---|---|
| diverges | this is plain descent above its ceiling | |
| diverges | not enough memory to bound the overshoot | |
| just inside the lower stability bound | ||
| measured optimum | ||
| the closed-form | ||
| still fine | ||
| the popular default, off here | ||
| far too heavy | ||
| never | must be strictly below |
-
What does momentum change about gradient descent's convergence on a quadratic?
Measured across four decades: at kappa = 3 the speedup is only 1.65 times, at kappa = 10000 it is 71 times. Momentum is a conditioning fix, and its value is proportional to how badly conditioned the problem is — which is also why it is nearly pointless on a well-conditioned one.
pch.quizShowAnswer
B — The exponent: iterations go from proportional to kappa to proportional to the square root of kappa — Measured across four decades: at kappa = 3 the speedup is only 1.65 times, at kappa = 10000 it is 71 times. Momentum is a conditioning fix, and its value is proportional to how badly conditioned the problem is — which is also why it is nearly pointless on a well-conditioned one.
-
The heavy-ball step size for Example 7.1 is 0.115976, which is above the 2/L = 0.099724 ceiling from page 701. What happens?
Plain descent does diverge at that step size — measured. Momentum's modes satisfy a second-order recursion whose two roots multiply to alpha, so when they go complex their modulus is root alpha regardless of the eigenvalue. The overshoot is still there; it rotates instead of growing.
pch.quizShowAnswer
B — It converges in 37 iterations, because the momentum recursion has a different stability condition — Plain descent does diverge at that step size — measured. Momentum's modes satisfy a second-order recursion whose two roots multiply to alpha, so when they go complex their modulus is root alpha regardless of the eigenvalue. The overshoot is still there; it rotates instead of growing.
-
At gamma = 0.115976, what happens if you set alpha = 0.10?
Momentum has a stability interval at a fixed step size, not just an upper bound. This makes 'turn momentum down until it stabilises' precisely the wrong move above 2/L, which is the regime momentum exists to let you use.
pch.quizShowAnswer
B — It diverges: below about 0.17 there is not enough memory to bound the overshoot at that step size — Momentum has a stability interval at a fixed step size, not just an upper bound. This makes 'turn momentum down until it stabilises' precisely the wrong move above 2/L, which is the regime momentum exists to let you use.
-
What property must a mini-batch gradient have for SGD to converge?
At a batch size of one, the measured typical error is 2.34 times the norm of the whole true gradient — the estimate is badly wrong on any given draw — while its mean over 3000 draws is within 4.8 percent. Bias breaks convergence; variance only slows it. A low-variance biased estimator is the more dangerous failure, since it converges smoothly to the wrong answer.
pch.quizShowAnswer
B — It must be an unbiased estimate of the true gradient; accuracy is not required — At a batch size of one, the measured typical error is 2.34 times the norm of the whole true gradient — the estimate is badly wrong on any given draw — while its mean over 3000 draws is within 4.8 percent. Bias breaks convergence; variance only slows it. A low-variance biased estimator is the more dangerous failure, since it converges smoothly to the wrong answer.
-
Why does mini-batch gradient noise reach exactly zero at B = N rather than merely getting small?
A full batch is not a very good sample of the population, it IS the population, so there is nothing left to be uncertain about. The same factor explains why the measured improvement at B = 1024 of N = 2000 was 45 times rather than the 32 times that 1/root-B alone predicts.
pch.quizShowAnswer
B — Because sampling is without replacement, so the finite-population factor root of (N-B)/(N-1) is zero there — A full batch is not a very good sample of the population, it IS the population, so there is nothing left to be uncertain about. The same factor explains why the measured improvement at B = 1024 of N = 2000 was 45 times rather than the 32 times that 1/root-B alone predicts.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Equations 7.11 and 7.12, from scratch
Section titled “Exercise 1 – Equations 7.11 and 7.12, from scratch”Exercise 2 – The parameters a quadratic hands you
Section titled “Exercise 2 – The parameters a quadratic hands you”Exercise 3 – Too little momentum is also fatal
Section titled “Exercise 3 – Too little momentum is also fatal”Exercise 4 – Unbiased, and yet enormous
Section titled “Exercise 4 – Unbiased, and yet enormous”Exercise 5 – O(kappa) against O(root kappa)
Section titled “Exercise 5 – O(kappa) against O(root kappa)”Recall card
Section titled “Recall card”- Equation 7.11 adds one term: plus alpha times the previous increment. Equation 7.12 says that increment is itself alpha times the one before, minus gamma times the old gradient — so it is an exponentially weighted moving average of every gradient so far.
- The first momentum step is always the plain step, since there is nothing remembered yet. Cheapest possible sanity check on an implementation.
- Momentum amplifies drift and damps oscillation. A consistent gradient gets multiplied by up to 1/(1-alpha); an alternating one partly cancels. It is not a uniform step-size increase.
- The complexity changes, not the constant: O(kappa) becomes O(sqrt kappa). Measured speedup 1.65x at kappa = 3, 71x at kappa = 10000. Momentum is a conditioning fix, worth reaching for exactly in proportion to how bad kappa is.
- Closed forms on a quadratic: gamma = 4/(sqrt(mu)+sqrt(L))^2 and alpha = ((sqrt(L)-sqrt(mu))/(sqrt(L)+sqrt(mu)))^2, giving rate (sqrt(kappa)-1)/(sqrt(kappa)+1).
- That gamma is ILLEGAL for plain descent. On Example 7.1 it is 0.115976, which is 16.3 percent above 2/L = 0.099724, where plain descent diverges. Momentum needs the bigger step, it does not merely survive it.
- Momentum has a lower stability bound too. At that gamma, alpha = 0.10 diverges, 0.165 takes 3900 iterations, 0.28 takes 33, 0.9 takes 343, and alpha = 1 never converges. “Turn momentum down to stabilise” is backwards above 2/L.
- alpha = 0.9 is a hedge, not an optimum. On Example 7.1 the best is 0.28 and 0.9 is ten times worse. The right value depends on kappa.
- SGD needs UNBIASED, not accurate. At B = 1 the typical single gradient is 2.34 times the norm of the true gradient; its mean over 3000 draws is right to 4.8 percent. Bias breaks convergence, variance only slows it.
- Keep the N/|B| scaling. Without it you minimise |B|/N times the loss: same minimiser, but the effective step size is now tied to the batch size, which is why changing the batch size appears to break training.
- Mini-batch noise falls as 1/sqrt(B) times sqrt((N-B)/(N-1)). The second factor is why noise hits exactly zero at B = N, and why the measured gain at B = 1024 of 2000 was 45x rather than 32x.
- Small batches have a bonus and a cost: the noise can escape shallow bad optima, and it leaves vectorised hardware idle. The book’s framing is that generalisation, not a precise training minimum, is the goal — so an exact gradient is often waste.
Next: constraints. What changes when the answer is not allowed to be anywhere it likes. Constrained Optimization and Lagrange Multipliers
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading