Optimization Using Gradient Descent
Every training loop in the second half of this book is a variation on one line:
That is it. Chapter 5 built the ; this page is about the , and about what the line can and cannot promise. The short version: it always goes downhill, it never tells you which valley you are in, and there are two ways to choose badly for every one way to choose it well.
What you’ll learn
Section titled “What you’ll learn”- Why setting the derivative to zero is the definition of a stationary point but almost never a usable method (§7.1, and the Abel–Ruffini theorem).
- Equation 7.6, the gradient descent update, and why it carries a transpose.
- Example 7.1 reproduced to the digit — including why the book’s is exactly right.
- The step-size ceiling and the fastest step size , both measured against a sweep of 136 values.
- Why a step size a hair past the ceiling can look like it is working for 30,000 iterations before it visibly fails.
- The condition number (§4.5), and why it — not the step size — is the thing that makes gradient descent hopeless.
- That the famous “zigzag” is a transient: successive steps start almost reversed and end almost parallel.
Intuition: a ball, a hill, and no map
Section titled “Intuition: a ball, a hill, and no map”You are somewhere on a landscape in fog. You can feel the slope under your feet — that is the gradient — and nothing else. You cannot see the horizon, you do not know how many valleys there are, and you have no idea how far away the bottom is.
Two decisions follow, and they are the whole of this page:
- Which way? Downhill. The gradient points uphill (§5.2), so you go the other way. This decision is free and always correct.
- How far? Nobody tells you. Step too short and you are still walking at sunset. Step too long and you cross the valley and land higher up than you started.
The first decision is a one-liner. The second is a research field.
flowchart TD F["objective f, differentiable
Chapter 5"] --> G["gradient at the current point
Eq 5.40"] G --> D["descent direction
the negative gradient"] D --> U["update, Eq 7.6
x - gamma * grad"] S["step size gamma
Section 7.1.1"] --> U U --> U U --> R["a LOCAL minimum"] C["curvature: mu and L
the Hessian, Eq 5.147"] -.->|"caps gamma at 2/L"| S C -.->|"kappa = L/mu sets the speed"| R X["which valley?"] -.->|"decided by x0 alone"| R style X stroke-dasharray: 4 3 style C stroke-dasharray: 4 3
The two dashed inputs are the honest part of the diagram. Curvature governs both the largest step you may take and how fast you converge, but the algorithm never looks at it. And the starting point alone decides which minimum you end up in, with nothing in the gradient to warn you.
The math
Section titled “The math”The problem
Section titled “The problem”for a differentiable . By convention, machine learning minimises — if you have something to maximise, negate it. The book is explicit that this chapter assumes differentiability so that a gradient exists everywhere; §7.4 points at subgradient methods for when that fails.
Why not just set the derivative to zero?
Section titled “Why not just set the derivative to zero?”Because you usually cannot solve the resulting equation. Take the book’s opening example:
This is a cubic, so it has three real roots here and they are findable. Go one degree higher and you are stuck: by the Abel–Ruffini theorem there is no general algebraic solution for polynomials of degree five or more. And a neural network’s stationarity condition is not a polynomial at all.
So: stationary points are the definition of where an optimum can be, and root-finding is not a general method for locating them. That gap is why the rest of the chapter exists.
Classifying what you find
Section titled “Classifying what you find”Positive at a stationary point means a minimum, negative means a maximum. Solving exactly rather than reading the plot gives
| verdict | |||
|---|---|---|---|
| global minimum | |||
| maximum | |||
| local minimum |
The book quotes the visual estimates , , and a minimum value of “approximately ”. Those are good reads: the exact values are , , and .
The update
Section titled “The update”Gradient descent is a first-order method: it uses and , nothing else. Starting from ,
and in general
Why the transpose. This module follows the book’s convention that a gradient is a row vector (§5.2, Equation 5.40). is a column. You cannot subtract a row from a column, so the gradient is transposed back into a column first. It is a bookkeeping detail with no mathematical content, and it is exactly the kind of thing the typeface convention of §0.2 exists to catch.
For a small enough we get , and the sequence converges to a local minimum. Note what is not claimed: not the global minimum, and not quickly.
Contour lines
Section titled “Contour lines”A useful second picture. The set where is a contour line, and the gradient is everywhere orthogonal to it. So the descent direction always crosses the contours at right angles. On a circular bowl that points straight at the centre. On a long thin valley it points at the near wall, not at the bottom — which is the entire reason gradient descent zigzags, and the subject of the next page.
Worked example by hand
Section titled “Worked example by hand”This is the book’s Example 7.1, done in full. The objective is a quadratic in two variables:
with gradient
Write for the matrix and for the vector, so and, transposed into a column, .
Step 0. Start at with .
Both components are negative, so the negative gradient points north and east — exactly as the book says.
Step 1.
Matching the book exactly, and note it is exact rather than rounded: and .
Step 2. Repeat at :
which rounds to the book’s . Notice that has swung from to in a single step: the iterate crossed the valley. That is the zigzag, visible after two steps.
Where it is going. This particular objective can be solved exactly, by setting the gradient to zero:
using . So and . After three gradient steps we are at , still more than above the answer.
The two numbers that decide the speed. The eigenvalues of satisfy , so
giving and , hence . Everything on the rest of this page is a consequence of those two numbers.
See it move
Section titled “See it move”Start with the book’s own configuration. The trajectory below is Example 7.1, and the frames step through the same arithmetic you just did by hand.
Frames 1 to 3 are the hand calculation above. Watch the second component swing from positive to negative: that is the iterate crossing the valley, and it is what the condition number of 10.3 buys you.
Now the pathological case. Same algorithm, same step-size rule, a surface whose curvature ratio is far worse:
The gradient is nearly orthogonal to the direction of the minimum for almost the whole run, so the iterates hop between the walls and creep along the floor. Nothing is wrong with the step size; the geometry is the problem.
And the case that gradient descent cannot detect at all:
The gradient vanishes at the origin, so the update stalls there even though the point is a maximum in one direction. First-order information cannot distinguish this from a minimum — that takes the Hessian, from Section 5.7.
The step-size question deserves a sketch of its own. Below, the ceiling is a real number you can walk up to:
From scratch
Section titled “From scratch”Twenty lines of NumPy, no optimiser library, reproducing every number claimed above.
import numpy as np
# Example 7.1's quadratic: f(x) = 1/2 x^T A x - b^T x
A = np.array([[2.0, 1.0], [1.0, 20.0]])
b = np.array([5.0, 3.0])
def f(x):
return 0.5 * x @ A @ x - b @ x
def grad(x):
# Equation 7.8. The book writes the gradient as a ROW; NumPy hands back a
# flat array, which we treat as the column that Equation 7.6 subtracts.
return A @ x - b
def descend(x0, gamma, steps):
x = np.asarray(x0, dtype=float)
path = [x.copy()]
for _ in range(steps):
x = x - gamma * grad(x)
path.append(x.copy())
return np.array(path)
# --- the book's own iterates, Example 7.1 -----------------------------------
path = descend([-3.0, -1.0], gamma=0.085, steps=3)
for i, x in enumerate(path):
print(f"x{i} = [{x[0]:+.4f}, {x[1]:+.4f}] f = {f(x):+.6f}")
# --- where it is heading ----------------------------------------------------
xstar = np.linalg.solve(A, b) # setting the gradient to zero
print(f"\nx* = [{xstar[0]:.6f}, {xstar[1]:.6f}] f(x*) = {f(xstar):.6f}")
print("gradient at x* is zero:", np.allclose(grad(xstar), 0))
# --- the two numbers that govern everything --------------------------------
mu, L = np.linalg.eigvalsh(A)
print(f"\nmu = {mu:.6f} L = {L:.6f} kappa = {L / mu:.6f}")
print(f"divergence threshold 2/L = {2 / L:.6f}")
print(f"fastest step size 2/(mu+L) = {2 / (mu + L):.6f}")
# --- so does the step size actually matter? --------------------------------
def steps_to_tolerance(gamma, tol=1e-8, cap=200_000):
x = np.array([-3.0, -1.0])
for k in range(cap):
if np.linalg.norm(x - xstar) < tol:
return k
x = x - gamma * grad(x)
if not np.all(np.isfinite(x)) or np.linalg.norm(x) > 1e12:
return -1
return cap
print("\n gamma steps to 1e-8")
for gamma in (0.0100, 0.0850, 0.0909, 0.0990, 0.0997, 0.0998, 0.1050):
k = steps_to_tolerance(gamma)
print(f" {gamma:.4f} {'diverged' if k < 0 else k}")x0 = [-3.0000, -1.0000] f = +40.000000
x1 = [-1.9800, +1.2100] f = +22.435600
x2 = [-1.3213, -0.4237] f = +11.978082
x3 = [-0.6356, +0.6639] f = +5.576037
x* = [2.487179, 0.025641] f(x*) = -6.256410
gradient at x* is zero: True
mu = 1.944615 L = 20.055385 kappa = 10.313294
divergence threshold 2/L = 0.099724
fastest step size 2/(mu+L) = 0.090909
gamma steps to 1e-8
0.0100 1025
0.0850 112
0.0909 104
0.0990 1280
0.0997 39113
0.0998 diverged
0.1050 divergedRead the last block carefully, because it is the point of the page. Going from to — a increase — makes the method 376 times slower. One more nudge and it does not converge at all.
§7.1.1 Step-size
Section titled “§7.1.1 Step-size”The book’s framing: the step size, also called the learning rate, is a genuine design choice, and both failure modes are real.
- Too small: every step is a descent step, so nothing goes wrong. It is simply slow, and “slow” here can mean thousands of iterations for a two-variable problem.
- Too large: gradient descent overshoots, fails to converge, or diverges outright.
For the quadratic the threshold is exact and worth knowing. Along the eigendirection with eigenvalue , one step multiplies the error by . Every mode must contract, so we need for all , which gives
and the choice that makes the worst mode contract fastest balances the extremes:
For Example 7.1 that is with a rate of . The measured best over sampled step sizes is iterations, at exactly that value.
Adaptive step sizes
Section titled “Adaptive step sizes”Rather than guessing, rescale as you go. The book gives two heuristics (Toussaint, 2012):
- If the function value increased after a step, the step was too large. Undo the step and decrease .
- If the function value decreased, the step could have been bigger. Increase .
Example 7.2: solving a linear system by descent
Section titled “Example 7.2: solving a linear system by descent”Given , minimise the squared error
whose gradient with respect to is
Feed that straight into Equation 7.6 and you have an iterative linear solver. For this particular
problem you should not: setting the gradient to zero gives the normal equations, which lstsq
solves exactly in one call. Chapter 9 develops that properly. The example is here because it is the
smallest honest instance of the pattern, and because it exposes the real bottleneck.
The condition number is the bottleneck
Section titled “The condition number is the bottleneck”the ratio of largest to smallest singular value (§4.5). The book’s reading of it is the one to keep: measures the ratio of the most curved direction to the least curved one. A large is a long thin valley — steep across, nearly flat along.
The fix is preconditioning: instead of , solve
choosing so that has a better condition number while stays cheap to apply. Those two goals pull against each other — gives and is exactly as hard as the original problem — and navigating that trade-off is its own literature.
On real data
Section titled “On real data”Reading the plot
Section titled “Reading the plot”The first figure separates two things the word “minimum” runs together. In the left panel the exact stationary points sit where the calculus puts them, and the shading is the measured basin of attraction: descend from anywhere green and you land on ; descend from anywhere amber and you stop at , which is higher. The right panel makes the boundary explicit by running gradient descent from starting points and recording where each ends up. The split is perfectly clean and it falls at the maximum, .
That is worth comparing against the book, which says the negative gradient leads to the right-hand minimum “for ”. True — but conservative. The real watershed is , so the interval is a strip the book gives away: start at and you do reach the global minimum. Nothing hinges on it, but it is the kind of statement worth checking rather than absorbing, and the sharp version has a reason behind it: a watershed between two basins must be a stationary point, since the gradient has to change sign there.
The second figure is the step-size story and its shape is the surprise. The instinct is that step size trades safety for speed monotonically — bigger is faster until it breaks. It is not. The curve is a U. Iteration count falls as grows, bottoms out at iterations at , and then climbs again, steeply, before the cliff at . At the method needs iterations. So the region just below the ceiling is almost as bad as a step size ten times too small, and it is the region a “keep increasing it while it still converges” search walks straight into.
The right panel shows why. Past the optimum the fast eigendirection is being overshot every step — its error factor is heading back up toward from below — so it stops contracting while the slow direction is still crawling. Both modes must contract, and the binding constraint switches from the slow mode to the fast one exactly at .
The third figure corrects a piece of received wisdom. Everyone learns that gradient descent zigzags in a valley, and the left panel shows it doing so. But the middle panel measures the angle between successive steps, and it decays: between the first two steps (nearly a reversal), crossing around the sixth, and averaging over the last twenty. The steps end up almost parallel.
Both facts are real and they describe different phases. Early on, the error has a large component in the steep direction, that component is what the gradient mostly sees, and overshooting it flips the step — zigzag. That component dies quickly, at rate per step. What survives is the error along the flattest direction, which contracts at only , and there the steps all point the same way. So the correct summary is: gradient descent zigzags briefly, then crawls. The crawl is the expensive part, and it is what momentum on the next page attacks.
The right panel prices conditioning. At the answer is exact after iterations. At
it is exact by . At , after one hundred thousand iterations
the relative error is still — the method has barely started, on a problem lstsq closes
exactly and instantly. That gap is the motivation for everything in §7.4: momentum, conjugate
gradients, quasi-Newton methods, and preconditioning all exist to break the dependence on .
Pitfalls
Section titled “Pitfalls”Compare
Section titled “Compare”| gradient descent | Newton’s method (§5.7) | direct solve | |
|---|---|---|---|
| information used | , | , , | the whole problem structure |
| cost per step | one gradient | a gradient, a Hessian, a linear solve | one factorisation |
| step size | must be chosen, | none needed | not applicable |
| on a quadratic | steps | one step, exactly | exact |
| sensitive to | yes, severely | no | via numerical stability only |
| scales to millions of parameters | yes | no, the Hessian is | rarely |
| step size | behaviour on Example 7.1 | iterations to |
|---|---|---|
| safe, every step descends | ||
| the book’s choice | ||
| , provably fastest | ||
| past the optimum, fast mode overshooting | ||
| just inside the ceiling | ||
| past | diverges | |
| well past | diverges |
-
Gradient descent converges and reports a point where the gradient is zero. What have you learned?
A zero gradient holds at minima, maxima and saddle points alike. Distinguishing them needs the second derivative, and ruling out a better minimum elsewhere needs either convexity or many restarts. In the chapter's opening example one of the three stationary points is a maximum and one of the two minima is 43.23 worse than the other.
pch.quizShowAnswer
B — That the point is a stationary point, and without more information not even that it is a minimum — A zero gradient holds at minima, maxima and saddle points alike. Distinguishing them needs the second derivative, and ruling out a better minimum elsewhere needs either convexity or many restarts. In the chapter's opening example one of the three stationary points is a maximum and one of the two minima is 43.23 worse than the other.
-
On Example 7.1's quadratic, the fastest step size is 0.090909 and the divergence threshold is 0.099724. What happens at 0.0997?
Iteration count against step size is a U, not a slide. Past 2/(mu+L) the fast eigendirection is overshot and stops contracting, so the count climbs steeply well before the cliff. Converging and converging usefully are different questions.
pch.quizShowAnswer
C — It converges, but takes 39113 iterations — hundreds of times slower than the optimum — Iteration count against step size is a U, not a slide. Past 2/(mu+L) the fast eigendirection is overshot and stops contracting, so the count climbs steeply well before the cliff. Converging and converging usefully are different questions.
-
Why does gradient descent slow down as it approaches a minimum in a long thin valley?
Each eigendirection contracts by its own factor |1 - gamma*lambda|. The steep ones vanish fast, so what remains is the flattest, contracting at the rate set by the smallest eigenvalue. The measured angle between successive steps falls from 133 degrees to about 3 degrees, confirming the iterates end up crawling in one direction rather than zigzagging.
pch.quizShowAnswer
B — The steep error component dies quickly, leaving the flattest direction, which contracts at only |1 - gamma*mu| per step — Each eigendirection contracts by its own factor |1 - gamma*lambda|. The steep ones vanish fast, so what remains is the flattest, contracting at the rate set by the smallest eigenvalue. The measured angle between successive steps falls from 133 degrees to about 3 degrees, confirming the iterates end up crawling in one direction rather than zigzagging.
-
A run uses gamma = 0.09973 on Example 7.1, just past the 0.099724 ceiling. What does it look like?
The growth factor is |1 - gamma*L| = 1.000124: greater than one, so the fast mode grows, but only by 0.012 percent a step. Meanwhile the slow mode still contracts, so the total error falls at first. It took 30262 iterations after the turn to reach ten times the starting error. A healthy-looking first few hundred steps prove nothing.
pch.quizShowAnswer
B — It improves for about 24 iterations, then takes tens of thousands more to visibly blow up — The growth factor is |1 - gamma*L| = 1.000124: greater than one, so the fast mode grows, but only by 0.012 percent a step. Meanwhile the slow mode still contracts, so the total error falls at first. It took 30262 iterations after the turn to reach ten times the starting error. A healthy-looking first few hundred steps prove nothing.
-
What does the condition number kappa tell you about a gradient descent problem?
kappa is the ratio of largest to smallest singular value, and geometrically it is exactly the aspect ratio of the valley. The ceiling 2/L depends on L alone; the iteration count depends on the ratio. At kappa = 1000 gradient descent was still 84 percent wrong after 100000 iterations on a problem lstsq solves exactly.
pch.quizShowAnswer
B — The ratio of the most curved direction to the least curved one, which sets how many iterations you need — kappa is the ratio of largest to smallest singular value, and geometrically it is exactly the aspect ratio of the valley. The ceiling 2/L depends on L alone; the iteration count depends on the ratio. At kappa = 1000 gradient descent was still 84 percent wrong after 100000 iterations on a problem lstsq solves exactly.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Find and classify the stationary points
Section titled “Exercise 1 – Find and classify the stationary points”Exercise 2 – One gradient step, reproducing the book
Section titled “Exercise 2 – One gradient step, reproducing the book”Exercise 3 – The ceiling is 2/L and nothing else
Section titled “Exercise 3 – The ceiling is 2/L and nothing else”Exercise 4 – Does f actually decrease every step?
Section titled “Exercise 4 – Does f actually decrease every step?”Exercise 5 – Gradient descent against a direct solver
Section titled “Exercise 5 – Gradient descent against a direct solver”Recall card
Section titled “Recall card”- Equation 7.6 is the whole algorithm: x minus gamma times the gradient, transposed. Everything else in this chapter is a repair to it.
- The transpose is bookkeeping, not mathematics. Gradients are rows in this book, iterates are columns, and in NumPy the distinction is invisible until you reshape — at which point the subtraction silently broadcasts.
- Setting the derivative to zero is a definition, not a method. By Abel-Ruffini there is no general algebraic root for degree five or more, and a neural network’s stationarity condition is not even a polynomial.
- Example 7.1 exactly: A is [[2,1],[1,20]], b is [5,3], gamma is 0.085 from x0 = [-3,-1]. Then x1 = [-1.98, 1.21] and x2 = [-1.32, -0.42], and the exact minimiser is (97/39, 1/39) = [2.487179, 0.025641].
- Two numbers govern everything: mu = 1.944615 and L = 20.055385, so kappa = 10.313294. The ceiling is 2/L = 0.099724 and the fastest step is 2/(mu+L) = 0.090909.
- Iteration count against step size is a U, not a slide. 104 steps at the optimum, 39113 at 0.0997, divergence at 0.0998. Tune on iterations to tolerance, never on “did it converge”.
- A step past the ceiling can look healthy for a long time. At gamma = 0.09973 the error improves for 24 steps and needs 30262 more to grow tenfold. The reliable test is the growth factor |1 - gamma*L| against 1.
- The zigzag is a transient. The angle between successive steps falls from 133.26 degrees to a mean of 3.31 over the last twenty: gradient descent zigzags briefly, then crawls along the flattest eigendirection.
- The basin boundary is a stationary point. In Equation 7.1 it is the maximum at -1.432112, and the two minima differ by 43.23. The gradient never reveals which basin you are in.
- Conditioning, not step size, is the wall. At kappa = 1000 gradient descent is still 84 percent wrong after 100000 iterations of a problem lstsq closes exactly. Momentum, conjugate gradients and preconditioning all exist to break that dependence.
- Adaptive step sizes, two rules: if f went up the step was too large, so undo it and shrink gamma; if f went down, try a bigger one. The undo is what buys monotonic convergence.
Next: the first repair to Equation 7.6 — give it a memory. Momentum and Stochastic Gradient Descent
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading