ML from Scratch · Lecture 4 of 5
Neural Networks II: Backpropagation from Scratch
The chain rule, organised so that one backward sweep gives every gradient. Worked by hand on a tiny network, then vectorised.
The problem
A network is a composition $L=\ell(f_K(\cdots f_2(f_1(x;\theta_1);\theta_2)\cdots))$. Gradient descent needs $\partial L/\partial\theta$ for every parameter. Doing each by finite differences costs one forward pass per parameter, millions of passes. Backpropagation gets all of them for the cost of about two passes.
The chain rule on a computational graph
Write the forward pass as a graph of simple operations. For our network from Lecture 3:
Going backwards, each node multiplies the incoming gradient by its local derivative:
Worked example
Below is a 2-input, 2-hidden-unit, 1-output network with fixed weights $W=\begin{pmatrix}0.5&-0.3\\0.8&0.2\end{pmatrix}$, $b=(0.1,-0.1)$, $u=(1.2,-0.7)$, $c=0.05$. Move the inputs and target and every forward value and gradient updates. Try checking one entry by hand, e.g. $\partial L/\partial W_{11}=\frac{\partial L}{\partial z_1}x_1$.
Vectorised for a mini-batch
def backward(X, y, Z1, H, p, w2):
n = len(y)
ds = (p - y) / n # (n,)
dw2 = H.T @ ds # (H,)
db2 = ds.sum()
dH = np.outer(ds, w2) # (n, H)
dZ1 = dH * (1 - H ** 2) # tanh'
dW1 = dZ1.T @ X # (H, d)
db1 = dZ1.sum(axis=0)
return dW1, db1, dw2, db2
for epoch in range(5000):
Z1, H, p = forward(X, W1, b1, w2, b2)
dW1, db1, dw2, db2 = backward(X, y, Z1, H, p, w2)
W1 -= lr * dW1; b1 -= lr * db1; w2 -= lr * dw2; b2 -= lr * db2
This is exactly what runs inside the training widget of Lecture 3.
Always gradient-check
Compare backprop with a centred finite difference on a few random weights:
Vanishing and exploding gradients
Backprop multiplies one local derivative per layer. Sigmoid's derivative is at most ¼, so through 20 layers gradients shrink like $4^{-20}$ and early layers stop learning. Large weights do the opposite and gradients explode. Remedies (next lecture): ReLU activations, careful initialisation, normalisation layers, residual connections and gradient clipping.
loss.backward() as magic.Key takeaways
- Backprop is the chain rule applied right to left on the computational graph: all gradients in ~2 passes.
- Sigmoid/softmax + cross-entropy give the clean output gradient p − y.
- Gradient-check with centred differences; watch for vanishing/exploding gradients in deep nets.
Exercises
- Extend the backward pass to a second hidden layer and verify it with a gradient check.
- Derive the gradient of softmax + categorical cross-entropy and show it equals p − y (one-hot y).
- Why does initialising all weights to zero break learning in a hidden layer? (Think about symmetry.)