Gradient descent
Derive gradient descent from a local model and analyze its descent guarantee.
The negative gradient is the steepest-descent direction; gradient descent simply takes repeated steps along it. It is the algorithm under nearly every model trained today. This lesson derives the update from a local quadratic model, explains the step-size tradeoff, and proves it makes progress on smooth functions.
The update
Gradient descent generates iterates by
where is the step size (learning rate). Each step moves opposite the gradient, downhill, by an amount scaled by [1].
Derivation from a local model
The update is the minimizer of a local model of . Approximate near by its first-order Taylor expansion plus a quadratic proximity penalty that distrusts large steps:
Setting gives , the gradient step. The step size is the inverse weight on the trust penalty: small trusts the linear model only nearby, large ventures further.
The descent lemma
Progress is guaranteed when the gradient does not change too fast. is -smooth if its gradient is -Lipschitz, , which for twice- differentiable means . Smoothness gives a quadratic upper bound:
Descent lemma. Take a gradient step and substitute:
For any step size the bracket is positive, so whenever the gradient is nonzero: the objective strictly decreases. The largest guaranteed per-step decrease is at , giving [2].
The step-size tradeoff
The descent lemma makes the tradeoff concrete. Too small an wastes the allowed decrease and crawls. Too large () breaks the guarantee: the quadratic penalty term dominates and the iterates can oscillate or diverge. On a quadratic with curvature , the threshold is exactly where the update starts to amplify the error instead of shrinking it.
Numbers fix the scale. The quadratic in the code block below has Hessian with rows and , whose eigenvalues are and , so and the safe range is . Reading the bracket across that range:
- (well below the optimum): the step decreases but barely, throwing away most of the allowed per-step drop.
- : the bracket is , the largest guaranteed decrease, clean convergence.
- (just under ): still convergent, but the bracket has shrunk to , so each step buys almost nothing and the path oscillates.
- : past the threshold, the bracket is negative, and the iterates blow up to infinity.
Check it on arrays
import numpy as np
# Minimize f(x) = 0.5 x^T A x - b^T x, gradient A x - b, minimum at x* = A^{-1} b.
A = np.array([[3.0, 1.0], [1.0, 2.0]]) # symmetric PD
b = np.array([1.0, -1.0])
f = lambda x: 0.5 * x @ A @ x - b @ x
grad = lambda x: A @ x - b
x_star = np.linalg.solve(A, b)
L = np.linalg.eigvalsh(A).max() # smoothness = largest eigenvalue
eta = 1.0 / L
x = np.zeros(2)
for t in range(60):
x = x - eta * grad(x)
print("GD solution:", np.round(x, 6), " exact:", np.round(x_star, 6))
# Monotone decrease at eta = 1/L
x, prev = np.array([5.0, 5.0]), np.inf
decreasing = True
for t in range(50):
fx = f(x)
decreasing &= (fx <= prev + 1e-12)
prev = fx
x = x - eta * grad(x)
print("monotone decrease at eta=1/L:", decreasing)
# Divergence when eta > 2/L
eta_big = 2.5 / L
x = np.array([5.0, 5.0])
for t in range(40):
x = x - eta_big * grad(x)
print("eta>2/L diverges:", not np.all(np.isfinite(x)) or np.linalg.norm(x) > 1e6)Gradient descent at converges to the exact minimizer with a monotone decrease, and pushing past diverges, matching the descent lemma.
Exercises
Exercise 1. Derive the gradient-descent update by minimizing the local model .
Solution
Step 1. Take the gradient of in . The constant and the linear term contribute ; the penalty contributes :
Step 2. Set it to zero and solve for :
Step 3. The model is convex (its Hessian is ), so this stationary point is the global minimizer. It is the gradient step.
Exercise 2. Starting from the descent lemma , find the step size that maximizes the guaranteed per-step decrease, and state that decrease.
Solution
Step 1. The guaranteed decrease is . Holding the gradient fixed, maximize the scalar coefficient over .
Step 2. Differentiate and set to zero: , so . Since , this is a maximum.
Step 3. Evaluate the bracket at : , so the coefficient is . The largest guaranteed decrease is
Exercise 3 (code). For the quadratic with having rows and and , scan step sizes across the safe range and verify empirically that the descent lemma bracket predicts whether keeps decreasing: descent for , divergence past it.
Solution
import numpy as np
A = np.array([[3.0, 1.0], [1.0, 2.0]])
b = np.array([1.0, -1.0])
f = lambda x: 0.5 * x @ A @ x - b @ x
grad = lambda x: A @ x - b
L = np.linalg.eigvalsh(A).max()
def converges(eta, steps=400):
x = np.array([5.0, 5.0])
for _ in range(steps):
x = x - eta * grad(x)
return np.all(np.isfinite(x)) and np.linalg.norm(x) < 1e6
# Bracket positive (eta < 2/L) predicts descent; negative predicts blow-up.
for eta in [0.05, 1.0 / L, 0.50, 2.5 / L]:
bracket = 1 - L * eta / 2
assert converges(eta) == (bracket > 0)
print(f"eta={eta:.3f} bracket={bracket:+.3f} converges={converges(eta)}")
print("2/L threshold =", round(2 / L, 4))The bracket sign matches convergence at every tested step size: positive bracket gives a decreasing, convergent run, and the negative bracket past diverges.
The next lesson quantifies how fast convergence is, using smoothness and strong convexity.
References
- [1]Nocedal, J., & Wright, S. J. (2006). Numerical Optimization (2nd ed.), §2.2 and ch. 3. Springer. Steepest descent and the line-search framework (doi:10.1007/978-0-387-40065-5).↩
- [2]Boyd, S., & Vandenberghe, L. (2004). Convex Optimization, §9.3. Cambridge University Press. The descent lemma and the step-size analysis for gradient descent.↩