On this page
The loss, one number for how wrong the model is, forms the loss landscape over all the weights; the gradient is its slope at the current weights, pointing where the loss rises fastest. Gradient descent steps the weights against the gradient until it is near zero, leaving weights with low training loss.
A local minimum is lowest only nearby, the global minimum lowest everywhere. Gradient descent cannot tell them apart; it moves downhill from wherever it starts.
Figure 1. The start decides which side of the peak gradient descent goes down.
Each step costs one forward and backward pass over the whole training set and carries only the weights θ to the next. One random minibatch per step instead is stochastic gradient descent (SGD), on SGD, Momentum and Nesterov.
SGD defaults to α = 0.001.The update on the toy loss, the sum of θ²; the loop stops once the slope is shorter than tol. A smaller tol stops later, closer to the minimum; at 0 it runs all 1000 steps.
import numpy as np
from numpy.linalg import norm
def grad_L(theta):
return 2 * theta # sum θ²
theta = np.array([3.0, -2.0])
alpha, tol = 0.1, 1e-6
for t in range(1000):
grad = grad_L(theta)
if norm(grad) < tol:
break # flat: done
move = alpha * grad
theta = theta - move
Figure 2. Steps shrink as the slope flattens.
The start picks the minimum and the learning rate decides whether it arrives: a small rate crawls, a medium one converges, a larger one oscillates across the minimum and one too large diverges. Ending in the local minimum is optimization error: better weights exist and training missed them.
Figure 3. One run at the slider's rate; the ring marks the minimum it reaches.
torch.optim.SGD without momentum is exactly the update; the snippet fits a 3-weight linear model to 512 points with α = 0.1.
import torch
X = torch.randn(512, 3)
y = X @ torch.randn(3, 1)
net = torch.nn.Linear(3, 1)
ps = net.parameters()
opt = torch.optim.SGD(ps, 0.1)
for step in range(100):
opt.zero_grad()
err = net(X) - y
loss = (err ** 2).mean()
loss.backward() # grads
opt.step() # θ ← θ − α·g
Each step is a forward and backward pass over all 512 points, so time grows with the data and memory holds every point's activations; plain SGD keeps no other state. The rate has no universal value; the torch.optim documentation example uses 0.01.
opt.zero_grad() adds every old gradient into each new step.w -= lr * w.grad must run inside torch.no_grad(), or autograd records it.With one rate for every weight and only the current gradient, plain gradient descent oscillates across steep directions and crawls along gentle ones; momentum fixes this on SGD, Momentum and Nesterov, and per-weight step sizes and Adam on Adaptive Optimizers: AdaGrad, RMSProp and Adam. Weight decay, learning rate schedules and gradient clipping around the step are on Optimizers in Practice: AdamW, SAM, Schedules and Clipping.
The usual classifier loss is Cross-Entropy Loss.