Gradient Descent from Scratch in Python
One idea, three variants
Almost every model you will train is fitted by the same procedure: measure the error, work out which direction increases it, and step the other way. That is gradient descent. The three variants people argue about — batch, stochastic and mini-batch — differ only in how much data they look at before taking each step.
θ are the parameters, J is the loss, ∇J is the gradient, and η is the learning rate — the single most important number in the whole procedure.
1. The loss and its gradient
For linear regression with mean squared error the gradient is easy enough to derive by hand, which makes it the perfect place to start.
2. Batch gradient descent
Compute the gradient over the whole dataset, then take one step. Smooth, deterministic, and slow — every step costs a full pass over the data.
3. Stochastic gradient descent
Update after every single example. The path to the minimum is noisy, but each step is cheap and the noise itself helps escape shallow local minima in non-convex problems.
4. Mini-batch, momentum and decay
Mini-batch is the practical compromise: average the gradient over 32–256 examples. Momentum accumulates a velocity so consistent directions accelerate and oscillations cancel. A decaying learning rate lets you move fast early and settle precisely later.
5. Diagnosing a bad run
- Loss increases or becomes nan: learning rate too high, or features unscaled. Divide η by 10 and standardize.
- Loss falls then plateaus far above zero: model too simple, or learning rate now too large for fine-tuning — decay it.
- Loss falls agonisingly slowly: learning rate too small, or features on wildly different scales.
- Loss oscillates every epoch with SGD: normal. Watch a moving average, not the raw value.
- Train loss falls, validation loss rises: you are past the sweet spot. Stop early.
Plot the loss curve every single time. It is the cheapest diagnostic in machine learning and it catches most bugs before the metrics do.