Training neural networks
Choose an objective (squared error for numbers, cross-entropy for classes), then walk downhill with gradient descent. Backpropagation is just the chain rule run backwards. The landscape has many valleys, and the network can overfit, so start small, go slow and stop early.
2025 Q2 asked for “an objective function for a neural network”. This stage gives you that, backprop by hand, the classification version with softmax, and the PyTorch training loop from the lab (including the lab’s double-softmax bug).
Book: what to read for this stage
ISLP 10.7 Fitting a neural network (non-convexity, Fig. 10.17), 10.7.1 Backpropagation and 10.7.2 Regularization and SGD: read. 10.2 p. 405 (softmax, one-hot, cross-entropy, eqs. 10.13 and 10.14): read. 10.7.3 dropout and 10.7.4 tuning: skim. 10.8 double descent: skip. Note the book puts \(\frac12\) in front of the RSS, so its gradients have \(-(y - f)\) where the slides have \(-2(y - f)\): same direction, different step size.
Lab bug
ShallowNet.forward ends with nn.Softmax, then the loss is nn.CrossEntropyLoss, which applies softmax again internally. Predictions are still right (softmax keeps the order), but the loss can’t go below \(\log(1 + 9/e) \approx 1.461\), which is why the lab’s losses flatten at about 1.465, and the gradients are weakened. Fix: return the raw scores (logits) from forward; call torch.softmax only when you want probabilities.
1. The objective, and why it’s hard to minimise
For a numeric output, minimise the squared error over all parameters \(\theta\) (all the \(w\)’s and \(\beta\)’s):
\[\mathrm{RSS}(\theta) = \sum_{i=1}^{n}\big(y_i - f(x_i)\big)^2 \quad\text{or the MSE}\quad \frac1N\sum_{n=1}^{N}\big(Y_n - f(X_n)\big)^2.\]
The \(w\)’s sit inside \(g\), so there is no closed-form solution: use gradient descent. And unlike linear regression’s single bowl, the objective is non-convex: many valleys, so gradient descent can stop in a local minimum, and where it ends depends on where it starts. One reason: swapping two hidden units gives exactly the same \(f\), so every solution has copies.
Starting point: small random weights, not zeros. With all-zero weights every hidden unit computes the same thing and gets the same gradient, so they stay identical forever. Small, because large weights push sigmoids into their flat tails, where the slope is nearly 0.
Why do networks start training from small random weights rather than all zeros?
Random starts break the symmetry; they don’t guarantee the global minimum (the landscape is non-convex).
2. Backpropagation by hand
The gradient of a sum is the sum of gradients, so work with one point, \(R_i = (y_i - f(x_i))^2\). Write \(z_{ik}\) for unit \(k\)’s weighted sum and \(A_{ik} = g(z_{ik})\). Chain rule:
\[\frac{\partial R_i}{\partial\beta_k} = -2(y_i - f(x_i))\,A_{ik},\qquad \frac{\partial R_i}{\partial w_{kj}} = -2(y_i - f(x_i))\cdot\beta_k\cdot g'(z_{ik})\cdot x_{ij}\]
(with \(A_{i0} = 1\) for \(\beta_0\), and \(x_{i0} = 1\) for the bias \(w_{k0}\)). Both start with the residual: a forward pass computes the \(z\)’s, \(A\)’s and \(f\); then the error is sent backwards, to each output weight scaled by its unit’s activation, and further back to each hidden weight scaled by \(\beta_k\), the slope \(g'\) and the input. Hence backpropagation. With ReLU, \(g'(z) = 1\) if \(z > 0\) and 0 otherwise: a unit that’s off gets no gradient (a unit off for every point is dead).
Worked example. One input, two ReLU units: \((w_{10}, w_{11}) = (0, 1)\), \((w_{20}, w_{21}) = (1, -1)\); \(\beta = (0.5, 1, 2)\). One point \(x = 2\), \(y = 3\).
Forward: \(z_1 = 2\), \(A_1 = 2\); \(z_2 = -1\), \(A_2 = 0\); \(f = 0.5 + 2 + 0 = 2.5\); residual \(0.5\); \(-2e = -1\).
Backward: \(\partial R/\partial\beta_0 = -1\), \(\partial R/\partial\beta_1 = -1 \times 2 = -2\), \(\partial R/\partial\beta_2 = 0\); \(\partial R/\partial w_{11} = -1 \times 1 \times 1 \times 2 = -2\); unit 2’s weights get 0 (it’s off).
One step with \(\eta = 0.05\): \(\beta_0 = 0.55\), \(\beta_1 = 1.1\), \(w_{10} = 0.05\), \(w_{11} = 1.1\). New \(f = 0.55 + 1.1 \times 2.25 = 3.025\): the loss fell from 0.25 to 0.0006.
3. Classification networks: softmax and cross-entropy
Two classes: \(P(Y = 1 \mid X) = \sigma(f(X))\), trained with week 3’s negative log-likelihood. Logistic regression is this with no hidden layer.
\(M\) classes: \(M\) outputs \(f_0, \dots, f_{M - 1}\) sharing the hidden layer, turned into probabilities by softmax, \(p_m = e^{f_m}/\sum_{m'}e^{f_{m'}}\). Encode the label as one-hot (\(y_{im} = 1\) for the true class). The loss is the cross-entropy:
\[\ell(\theta) = -\sum_{i=1}^{n}\sum_{m}y_{im}\log p_m(x_i) = -\sum_i\log p_{y_i}(x_i):\]
minus the log-probability of the true class. Its gradient at the outputs is simply \(p_m - y_{im}\) (predicted minus truth), then backprop as before. Predict the argmax. Parameters: \(K(p + 1) + M(K + 1)\).
Worked example. Logits \((2, 1, 0)\), true class 0. Softmax \((0.665, 0.245, 0.090)\), loss \(-\log 0.665 = 0.408\). Output gradient \((0.665 - 1, 0.245, 0.090) = (-0.335, 0.245, 0.090)\): raise the true class’s score, lower the others.
4. Fighting overfitting, and the PyTorch loop
A network has many parameters, so it can memorise a small training set. Two strategies (slide 43):
- Regularisation: add a ridge penalty, \(R(\theta) = \mathrm{RSS} + \lambda\|\theta\|^2\) (not on the biases), \(\lambda\) chosen on validation data. PyTorch’s
weight_decay. - Slow learning + early stopping: small random start, small steps, watch the validation loss each epoch and stop (keep the best epoch’s weights) when it starts rising while the training loss keeps falling.
Also: fewer hidden units. Minibatches: each step uses a random batch (e.g. 32 points) instead of all \(n\): stochastic gradient descent; one epoch is one pass through the data (\(\lceil n/\text{batch}\rceil\) steps). The lab’s Adam is gradient descent with automatic per-parameter step sizes.
for X_b, y_b in train_loader: # one epoch, minibatch by minibatch
out = model(X_b) # forward pass (logits!)
loss = criterion(out, y_b) # CrossEntropyLoss for classes, MSELoss for numbers
optimizer.zero_grad() # clear old gradients (they accumulate otherwise)
loss.backward() # backpropagation
optimizer.step() # theta <- theta - eta * grad (or Adam's version)1,257 training images, batch size 32, 60 epochs. How many optimiser steps?
\(\lceil 1257/32\rceil = 40\) steps per epoch, so \(40 \times 60 = 2400\).
The validation loss falls for 30 epochs, then rises while the training loss keeps falling. What’s happening and what do you do?
Training loss down, validation loss up: the classic overfitting signature.
Stage test
(4 marks, exam format 2; 2025 Q2.) Write an objective function for training a neural network \(f\) with parameters \(\lambda\) on \(D = \{(X_n, Y_n)\}_{n=1}^{N}\) with \(Y_n \in \mathbb R\), and explain how it is minimised.
For a real-valued output, a standard objective is the mean squared error \(L(\lambda) = \frac1N\sum_{n=1}^{N}\big(Y_n - f(X_n; \lambda)\big)^2\), possibly plus a ridge penalty \(\mu\|\lambda\|^2\) on the weights to limit overfitting. Because the parameters sit inside the non-linear activations there is no closed-form minimiser and \(L\) is non-convex, so we start from small random parameters and use gradient descent, \(\lambda^{(t+1)} = \lambda^{(t)} - \eta\nabla L(\lambda^{(t)})\), often on minibatches, with the gradient computed by backpropagation (the chain rule applied from the output back to the inputs). We monitor the loss on a validation set and stop when it starts to rise (early stopping). (For classification, the objective would instead be the cross-entropy \(-\sum_n\log p_{Y_n}(X_n)\) with a softmax output.)
A digits classifier has \(p = 64\) inputs, \(K = 32\) hidden units and \(M = 10\) outputs. How many parameters?
\(32 \times 65 + 10 \times 33 = 2080 + 330 = 2410\).
What goes wrong if you forget optimizer.zero_grad() in the training loop?
Gradients accumulate by design (useful for some tricks), so they must be cleared each step.
Why does applying softmax twice (the lab bug) still give the right predicted class?
\(e^z\) is increasing and every score is divided by the same total.