Deep Learning
/2 min read

The Geometry of Gradient Descent

Why the negative gradient is the direction of steepest descent — and what the learning rate is really controlling.

  • optimization
  • calculus
  • machine-learning

Gradient descent is the workhorse of modern machine learning, and yet the reason it works is a single, elegant fact from multivariable calculus. This note walks through that fact from the ground up.

The setup

We want to minimize a differentiable loss L(θ)L(\theta) over parameters θRn\theta \in \mathbb{R}^n. Starting from a point θt\theta_t, we take a small step Δ\Delta and ask how the loss changes. A first-order Taylor expansion gives

L(θt+Δ)L(θt)+L(θt)Δ,L(\theta_t + \Delta) \approx L(\theta_t) + \nabla L(\theta_t)^\top \Delta,

where L(θt)\nabla L(\theta_t) is the gradient — the vector of partial derivatives (Lθ1,,Lθn)\left( \tfrac{\partial L}{\partial \theta_1}, \dots, \tfrac{\partial L}{\partial \theta_n} \right).

Why the negative gradient?

We want to choose the step direction Δ\Delta that decreases LL as fast as possible. Restricting to unit-length directions Δ=1\lVert \Delta \rVert = 1, the change in loss is the inner product L(θt)Δ\nabla L(\theta_t)^\top \Delta. By the Cauchy–Schwarz inequality,

L(θt)Δ    L(θt),\nabla L(\theta_t)^\top \Delta \;\geq\; -\lVert \nabla L(\theta_t) \rVert,

with equality exactly when Δ\Delta points opposite the gradient:

Δ=L(θt)L(θt).\Delta^\star = -\frac{\nabla L(\theta_t)}{\lVert \nabla L(\theta_t) \rVert}.

Steepest descent

Among all unit-norm directions, the negative gradient produces the most negative first-order change in the loss. That is the whole justification for the update rule below.

Scaling that direction by a step size η>0\eta > 0 gives the familiar update:

θt+1=θtηL(θt).\theta_{t+1} = \theta_t - \eta \, \nabla L(\theta_t).

The update loop

In code

The whole algorithm is a handful of lines. Everything interesting lives in the grad function you pass in.

import numpy as np
 
def gradient_descent(grad, theta, lr=0.1, steps=100):
    """Minimize a function given its gradient."""
    history = [theta]
    for _ in range(steps):
        theta = theta - lr * grad(theta)
        history.append(theta)
    return theta, np.array(history)

Choosing the learning rate

The step size η\eta is the one knob that decides whether the iteration converges at all.

RegimeBehaviour
η\eta too smallslow, creeping convergence
η\eta well-tunedsteady, monotone descent
η\eta too largeovershoot, oscillation, blow-up

The learning rate is the single hyperparameter that can turn a clean convergence proof into a divergence in practice.

For a convex, β\beta-smooth loss, any fixed η1β\eta \leq \tfrac{1}{\beta} guarantees the loss never increases — a rare and reassuring guarantee in a field with few of them.