Gradient Descent and Numerical Optimization › Example

Michael BrenndoerferOctober 25, 20265 min read
Machine Learning from ScratchAll chapters
Part 3 of 15Model Fundamentals and Diagnostics
Chapter 30Gradient Descent and Numerical Optimization › Example
Reading section 6 of 10 · Example · 5 minSection 6 of 10 · 5 min

Section 6 of 10 · 5 min read

Gradient Descent and Numerical Optimization › ExampleSection view · Sections in this chapter6 / 10 ↓
Reading tools Definitions, links & notes

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Notes & comments

We can check the recurrence by hand, then compare its predicted iterates with the update loop.

Take the well-conditioned curvature a=(2,1)a = (2, 1), start at θ0=(1,1)\theta_0 = (1, 1), and use η=0.25\eta = 0.25. The gradient at the start is (2⋅1,1⋅1)=(2,1)(2\cdot 1, 1\cdot 1) = (2, 1), so the first update is

θ1=(1,1)−0.25 (2,1)=(0.5, 0.75)\theta_1 = (1,1) - 0.25\,(2,1) = (0.5,\, 0.75)

The objective falls from J(θ0)=12(2⋅12+1⋅12)=12(2+1)=1.5J(\theta_0) = \tfrac12(2\cdot 1^2 + 1\cdot 1^2) = \tfrac12(2 + 1) = 1.5 to J(θ1)=12(2⋅0.52+1⋅0.752)=12(0.5+0.5625)=12(1.0625)=0.53125J(\theta_1) = \tfrac12(2\cdot 0.5^2 + 1\cdot 0.75^2) = \tfrac12(0.5 + 0.5625) = \tfrac12(1.0625) = 0.53125, where θ0\theta_0 and θ1\theta_1 are the parameters before and after the first update, each term is 12ajθj2\tfrac12 a_j \theta_{j}^2 with aja_j the curvature and θj\theta_j the parameter along coordinate jj, and the intermediate sums make the arithmetic explicit. Continuing, we can factor the update per coordinate. For coordinate one the multiplier is 1−ηa1=1−0.25⋅2=1−0.5=0.51 - \eta a_1 = 1 - 0.25 \cdot 2 = 1 - 0.5 = 0.5; for coordinate two it is 1−ηa2=1−0.25⋅1=1−0.25=0.751 - \eta a_2 = 1 - 0.25 \cdot 1 = 1 - 0.25 = 0.75, where η\eta is the learning rate and a1,a2a_1, a_2 are the curvatures along the two coordinates. So after kk steps

θ1,k=0.5k,θ2,k=0.75k\begin{aligned} \theta_{1,k} &= 0.5^k, \\ \theta_{2,k} &= 0.75^k \end{aligned}

where θ1,k\theta_{1,k} and θ2,k\theta_{2,k} are the two coordinates at iteration kk. With this starting point and rate, coordinate two decays more slowly. After ten steps it is 0.7510≈0.05630.75^{10} \approx 0.0563, while coordinate one is 0.510≈0.000980.5^{10} \approx 0.00098. Here the smaller-curvature direction has the larger contraction multiplier. For another rate, we must compare both absolute multipliers rather than assuming that the smallest curvature always controls the error.

Now verify the Newton prediction. The Hessian is constant, so the step from θ0\theta_0 is

d=−H−1∇J(θ0)=−[0.5001][21]=−[0.5⋅2+0⋅10⋅2+1⋅1]=−[11]=[−1−1]\begin{aligned} d &= -H^{-1}\nabla J(\theta_0) \\ &= -\begin{bmatrix} 0.5 & 0 \\ 0 & 1 \end{bmatrix}\begin{bmatrix} 2 \\ 1 \end{bmatrix} \\ &= -\begin{bmatrix} 0.5 \cdot 2 + 0 \cdot 1 \\ 0 \cdot 2 + 1 \cdot 1 \end{bmatrix} \\ &= -\begin{bmatrix} 1 \\ 1 \end{bmatrix} \\ &= \begin{bmatrix} -1 \\ -1 \end{bmatrix} \end{aligned}

since H−1=diag(1/2,1)H^{-1} = \mathrm{diag}(1/2, 1) and the gradient is (2,1)(2,1), where:

  • HH: the Hessian with diagonal entries equal to the curvatures a1=2a_1 = 2 and a2=1a_2 = 1
  • H−1H^{-1}: the inverse of the Hessian, here diag(1/2,1)\mathrm{diag}(1/2, 1)
  • ∇J(θ0)\nabla J(\theta_0): the gradient at the starting point, here (2,1)(2, 1)
  • dd: the Newton step

Adding gives θ1=(1,1)+(−1,−1)=(0,0)\theta_1 = (1,1) + (-1,-1) = (0,0), the exact minimum, in a single step. Let us confirm these hand calculations with code so nothing rests on arithmetic we did in our heads.

PythonIn [15]
CollapseExpand
a = np.array([2.0, 1.0])
theta = np.array([1.0, 1.0])
eta = 0.25
first_three = [theta.copy()]
for k in range(3):
    grad = a * theta
    theta = theta - eta * grad
    first_three.append(theta.copy())

# Closed form prediction from the per-coordinate contraction factors
k = 10
pred = np.array([(1 - eta * a[0]) ** k, (1 - eta * a[1]) ** k])
gd_final = run_gd(np.array([1.0, 1.0]), a, eta, k)[0][-1]
assert np.allclose(gd_final, pred, rtol=1e-12, atol=1e-15)
assert np.allclose(first_three[1], [0.5, 0.75])
assert np.isclose(objective(first_three[1], a), 0.53125)
ConsoleOut [16]
CollapseExpand
After 10 steps the closed form gives theta = [0.00097656 0.05631351]
direct run gives theta       = [0.00097656 0.05631351]
Objective there: 1.587e-03
Newton one-step solution: [0. 0.]
iterate k=0: theta=[1. 1.], J=1.50000
iterate k=1: theta=[0.5  0.75], J=0.53125
iterate k=2: theta=[0.25   0.5625], J=0.22070
iterate k=3: theta=[0.125   0.42188], J=0.10461

The example also exposes a subtlety about learning rates. With η=0.25\eta = 0.25 both coordinates contract, and the objective decreases monotonically. Raise η\eta to 0.750.75 and coordinate one has multiplier 1−ηa1=1−0.75⋅2=1−1.5=−0.51 - \eta a_1 = 1 - 0.75 \cdot 2 = 1 - 1.5 = -0.5, so it oscillates in sign while shrinking; the objective still decreases each step but the path zig-zags. The multiplier for coordinate jj is 1−ηaj1 - \eta a_j, with η\eta the learning rate and aja_j the curvature along that coordinate. Raise η\eta to 1.251.25 and the multiplier for coordinate one is 1−1.25⋅2=1−2.5=−1.51 - 1.25 \cdot 2 = 1 - 2.5 = -1.5, magnitude greater than one, and the run diverges even though coordinate two still converges. The next cell confirms the threshold numerically.

PythonIn [17]
CollapseExpand
eta_tests = [0.25, 0.75, 1.25]
eta_results = []
for eta_test in eta_tests:
    p, h = run_gd(np.array([1.0, 1.0]), a, eta_test, 12)
    eta_results.append((eta_test, h[-1], p[-1]))
ConsoleOut [18]
CollapseExpand
eta=0.25: J after 12 steps = 5.018e-04, final theta = [0.    0.032]
eta=0.75: J after 12 steps = 5.960e-08, final theta = [0. 0.]
eta=1.25: J after 12 steps = 1.683e+04, final theta = [129.746   0.   ]
Stability requires eta < 2/2.0 = 1.0 for this problem.

The run with η=1.25\eta = 1.25 diverges because of the steep direction. On a positive-definite quadratic, an unbounded parameter eventually forces an unbounded objective. Over a finite budget, a lightly weighted coordinate can still look small in the objective while its parameter error matters. Inspect the parameter trajectory alongside the loss when that distinction is relevant.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy