Gradient Descent

Gradient descent is how almost every modern model learns: compute which way the loss goes up, step the other way, repeat. This page builds the update rule from intuition, shows why the learning rate matters, compares batch, stochastic and mini-batch variants, and implements it in NumPy.


1. What Gradient Descent Is

Most machine learning models are trained by choosing parameters that make a loss function small. The loss measures how wrong the model is on the training data: mean squared error for regression, cross-entropy for classification. Gradient descent is the simplest general method for finding parameters with low loss.

The idea is the one in the picture: if you stand on a hillside in fog, you can feel which way the ground slopes under your feet. Take a step downhill, feel again, repeat. The gradient is the multi-dimensional version of slope; it points in the direction the loss increases fastest, so we step the other way.

Walking Downhill on a Loss Curve minimum start w0 w_new = w - lr * dL/dw steps shrink as the slope flattens Loss L(w) = w^2, learning rate 0.3: each step moves 60% of the way to the minimum.

The yellow dots are successive parameter values. Because the slope gets smaller near the bottom, the steps naturally get shorter even though the learning rate stays the same.

2. Why It Matters

Linear and logistic regression, neural networks of every size, matrix factorisation for recommenders, and gradient-boosted trees (in function space) all rely on gradient-based optimisation. Large language models are trained with variants of the same update rule described here. Understanding the learning rate, batch size and convergence behaviour explains most training failures you will meet: loss that explodes to NaN, loss that plateaus, or a model that fits training data but generalises poorly.

3. How It Works

Write the parameters as a vector θ and the loss as L(θ). The gradient ∇L(θ) is the vector of partial derivatives ∂L/∂θj. One step of gradient descent is:

θt+1 = θt − η · ∇L(θt)

where η (eta) is the learning rate. For training data of n examples, the loss is usually an average, L(θ) = (1/n) Σi ℓ(θ; xi, yi), so the gradient is also an average of per-example gradients. In neural networks, those per-example gradients are computed with backpropagation (reverse-mode automatic differentiation).

Effect of the Learning Rate Too small (0.05) About right (0.3) Too large (1.05) Small steps waste compute; overly large steps overshoot and the loss grows.

The learning rate controls everything. Too small and training crawls. Too large and each step overshoots the valley, landing higher up the opposite side, and the loss grows without bound. For a simple quadratic loss with curvature c the update is stable only when η < 2/c; in many dimensions the steepest direction sets the limit, while the flattest direction sets how slowly you converge. The ratio between them (the condition number) is why feature scaling matters so much: standardising features makes the valley rounder and lets one learning rate suit every direction.

4. Batch, Stochastic and Mini-Batch

Batch, Stochastic and Mini-Batch Paths Batch GD all n examples per step: smooth, slow per step SGD 1 example per step: noisy, cheap Mini-batch 32 to 1024 examples: the usual choice

Batch gradient descent computes the exact gradient over all n examples each step. It is smooth but each step costs a full pass over the data. Stochastic gradient descent (SGD) uses one random example per step: the gradient is a noisy but unbiased estimate, and you take n steps per pass instead of one. Mini-batch gradient descent averages over a small random batch, reducing noise while using vectorised hardware efficiently. In practice "SGD" in libraries almost always means mini-batch.

The noise in SGD is not only a cost. It can help escape shallow local minima and saddle points, and there is evidence that it biases training toward solutions that generalise well. Because the noise never fully disappears, SGD with a fixed learning rate bounces around the minimum; a decaying learning rate schedule (step decay, cosine, or linear warmup then decay) lets it settle.

Popular refinements: momentum accumulates a running average of gradients so consistent directions speed up and oscillations cancel; Nesterov momentum looks ahead before computing the gradient; AdaGrad, RMSProp and Adam keep per-parameter scales from past squared gradients, which effectively gives each parameter its own learning rate. AdamW decouples weight decay from the adaptive step and is a common default for transformers.

5. Worked Example

Fit a line y = w x + b by mini-batch gradient descent in plain NumPy. The loss is mean squared error, whose gradients are ∂L/∂w = (2/m) Σ (ŷ − y) x and ∂L/∂b = (2/m) Σ (ŷ − y).

import numpy as np

rng = np.random.default_rng(0)
n = 1_000
X = rng.uniform(-3, 3, n)
y = 2.5 * X - 1.0 + rng.normal(0, 0.5, n)      # true w=2.5, b=-1.0

w, b = 0.0, 0.0
lr, batch, epochs = 0.05, 32, 20

for epoch in range(epochs):
    idx = rng.permutation(n)                   # shuffle each epoch
    for start in range(0, n, batch):
        j = idx[start:start + batch]
        err = (w * X[j] + b) - y[j]
        grad_w = 2 * np.mean(err * X[j])
        grad_b = 2 * np.mean(err)
        w -= lr * grad_w
        b -= lr * grad_b
    loss = np.mean((w * X + b - y) ** 2)
    if epoch % 5 == 0 or epoch == epochs - 1:
        print(f"epoch {epoch:2d}  loss {loss:.4f}  w {w:.3f}  b {b:.3f}")

The printed w and b approach 2.5 and −1.0 and the loss approaches the noise variance (about 0.25). Try lr = 0.5: with features spread over [−3, 3] the curvature in w is large enough that the updates diverge, a direct demonstration of the stability limit above. In PyTorch the same loop is loss.backward() followed by optimizer.step() with torch.optim.SGD or torch.optim.AdamW.

6. Convergence

For convex, smooth losses (linear regression, logistic regression) gradient descent with a small enough fixed learning rate converges to the global minimum. Neural network losses are non-convex: there is no guarantee of a global minimum, but in high dimensions most critical points are saddle points rather than poor local minima, and momentum plus noise move through them. In practice you judge convergence empirically: plot training and validation loss per epoch, stop when validation loss stops improving (early stopping), and keep the best checkpoint.

7. Comparison of Optimisers

MethodPer-step costTuningTypical use
Batch GDFull datasetLearning rateSmall convex problems, teaching
SGD / mini-batchOne batchLearning rate, schedule, batch sizeLarge data, CNNs (often with momentum)
SGD + momentumOne batchPlus momentum (about 0.9)Vision models, well-tuned pipelines
Adam / AdamWOne batch, extra memory for two moment estimatesLearning rate, betas, weight decayTransformers, quick strong baselines
L-BFGSFull or large batch, curvature historyFew knobsSmall to medium smooth problems, scikit-learn logistic regression
Newton's methodHessian solve, expensive in high dimensionsFew knobsSmall models, GLMs

8. Pitfalls

10. Further Reading