A network can have millions of weights. After each mistake, how does it know which ones to change, and by how much? The answer is backpropagation: an efficient way to send the error backwards through the network so every weight gets its fair share of the blame. It's the algorithm that makes deep learning possible.
- Level: Intermediate
- Time: about 45 minutes
- Needs: Modules 1 to 4
By the end of this module you will be able to
- Explain the problem backpropagation solves
- Explain the chain rule using a simple everyday picture
- Work through a forward pass and a backward pass by hand
- Describe how the error signal flows back through layers
- Explain how deep learning libraries automate backpropagation
- Connect backpropagation to vanishing and exploding gradients
- Train a network on XOR from scratch using backpropagation in NumPy
5.1The blame problem
In Module 4 you learned the rule for improving a weight: new weight = weight − learning rate × gradient. The gradient tells us how much the loss changes when that one weight changes. For a single neuron, that's easy to work out. But in a deep network, a weight in the first layer affects the output only indirectly, through every layer after it.
A football team loses 3–0
Whose fault was it? The striker who missed chances? The midfielder whose poor pass started the counter-attack? The defender who was out of position? A good coach traces the mistake backwards from the goal: who contributed, and how much. Backpropagation does exactly this for every weight in the network.
A slow way to find each gradient would be to nudge every weight one at a time and re-run the network to see what changes. With a million weights, that's a million extra runs per step. Backpropagation gets all the gradients in roughly the time of one extra run. That efficiency is why it transformed the field when it was popularised in 1986.
5.2The chain rule, without the scary maths
Backpropagation is built on one idea from calculus called the chain rule. Here it is with gears:
A neural network is a chain too: weight → neuron → next layer → … → output → loss. To find how much a weight affects the loss, we multiply the effects of every link in its chain. And because the later links are shared by many weights, we can calculate them once and reuse them. That's the trick that makes backpropagation fast.
5.3A worked example
Let's follow the smallest possible network: 1 input, 1 hidden neuron and 1 output neuron, both using sigmoid. The input is x = 1, the correct answer is y = 1, and the weights start at w₁ = 0.5 and w₂ = 0.8 (biases are 0 to keep it simple). The loss is ½ × (y − prediction)².
Step 1: forward pass
| Calculation | Value |
|---|---|
| Hidden total: w₁ × x = 0.5 × 1 | 0.500 |
| Hidden output: sigmoid(0.5) | h = 0.622 |
| Output total: w₂ × h = 0.8 × 0.622 | 0.498 |
| Prediction: sigmoid(0.498) | ŷ = 0.622 |
| Loss: ½ × (1 − 0.622)² | 0.0714 |
Step 2: backward pass
Now we go backwards, multiplying the effect of each link (the chain rule):
| Link in the chain | Effect | Running product |
|---|---|---|
| How the loss changes with the prediction: −(y − ŷ) | −0.378 | −0.378 |
| How the prediction changes with its total (sigmoid slope): ŷ × (1 − ŷ) | 0.235 | −0.0889 (the output's "blame") |
| Gradient for w₂: × h | 0.622 | −0.0553 |
| Pass the blame back to the hidden neuron: × w₂ | 0.8 | −0.0711 |
| Hidden neuron's sigmoid slope: h × (1 − h) | 0.235 | −0.0167 (the hidden neuron's "blame") |
| Gradient for w₁: × x | 1 | −0.0167 |
Step 3: update the weights
With a learning rate of 1: w₂ = 0.8 − 1 × (−0.0553) = 0.855 and w₁ = 0.5 − 1 × (−0.0167) = 0.517. Run the forward pass again and the prediction rises from 0.622 to 0.652, and the loss drops from 0.0714 to 0.0605. One step closer to the right answer.
Notice
The gradient for w₁ (−0.017) is much smaller than for w₂ (−0.055), because the blame was multiplied by another sigmoid slope (0.235) on its way back. That shrinking is the vanishing gradient problem from Module 3, happening right in front of you.
5.4Try it: step through backpropagation
Here's the same tiny network. Press the buttons in order to run the forward pass, the backward pass and the update, and watch the loss fall with every round.
Try it: one neuron at a time
5.5Backpropagation in a full network
In a real network, the same three steps happen for every layer at once:
| Step | What happens | Direction |
|---|---|---|
| 1. Forward pass | Compute every neuron's output and remember them all | Input → output |
| 2. Compute the loss | Compare the prediction with the correct answer | At the end |
| 3. Backward pass | Starting at the loss, work out each layer's blame and each weight's gradient, reusing the results from the layer after it | Output → input |
| 4. Update | Every weight takes a gradient descent step (or an Adam step) | Everywhere at once |
A neuron that sends its output to several neurons in the next layer collects blame from all of them, added together. That's why, in the Python example, the hidden layer's blame is calculated with the whole weight matrix: d_out @ W2.T.
5.6You'll never do this by hand again
Working out gradients by hand is great for understanding, but impractical for real networks. Deep learning libraries do it automatically, using a technique called automatic differentiation (autodiff).
As your network runs forward, the library quietly records every calculation in a computational graph. It knows the slope of every simple operation (multiply, add, sigmoid, ReLU…), so it can run the chain rule backwards through the whole graph for you.
| Library | How backpropagation is triggered |
|---|---|
| Keras (Module 6) | Completely hidden inside model.fit() |
| TensorFlow | tf.GradientTape() records the operations, then tape.gradient(loss, weights) |
| PyTorch | loss.backward() fills in the gradient of every weight |
So why learn it? Because understanding backpropagation explains why training sometimes fails: vanishing gradients, exploding gradients, dying ReLUs and learning rates that are too high all come from how blame flows backwards. You'll use that understanding in Module 7.
5.7Vanishing and exploding gradients, revisited
In the worked example, the blame was multiplied by a weight and an activation slope at every layer on its way back. In a deep network, that happens many times over:
| If each layer multiplies the blame by… | After 20 layers | Problem |
|---|---|---|
| 0.5 | 0.5²⁰ ≈ 0.000001 | Vanishing gradients: early layers barely learn |
| 1.0 | 1 | Healthy: the signal arrives intact |
| 1.5 | 1.5²⁰ ≈ 3,300 | Exploding gradients: huge, unstable updates |
Fixes include ReLU activations (Module 3), careful starting weights, batch normalisation, gradient clipping and skip connections. You'll meet all of them in Module 7.
5.8Backpropagation from scratch in Python
In Module 2, a single perceptron could never learn XOR. Let's build a 2-4-1 network in NumPy, write the forward pass, backward pass and update ourselves, and watch it solve XOR.
import numpy as np
# XOR: the problem a single perceptron could never solve (Module 2)
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])
sigmoid = lambda z: 1 / (1 + np.exp(-z))
rng = np.random.default_rng(1)
# A 2-4-1 network: 2 inputs, 4 hidden neurons, 1 output
W1, b1 = rng.normal(0, 1, (2, 4)), np.zeros((1, 4))
W2, b2 = rng.normal(0, 1, (4, 1)), np.zeros((1, 1))
lr = 1.0
for epoch in range(5001):
# ---- Forward pass ----
h = sigmoid(X @ W1 + b1) # hidden layer
y_hat = sigmoid(h @ W2 + b2) # output layer
loss = np.mean((y - y_hat) ** 2)
# ---- Backward pass (backpropagation) ----
d_out = (y_hat - y) * y_hat * (1 - y_hat) # blame at the output
d_hidden = (d_out @ W2.T) * h * (1 - h) # blame passed back to hidden
# ---- Update every weight (gradient descent) ----
W2 -= lr * h.T @ d_out
b2 -= lr * d_out.sum(axis=0, keepdims=True)
W1 -= lr * X.T @ d_hidden
b1 -= lr * d_hidden.sum(axis=0, keepdims=True)
if epoch in (0, 500, 1000, 2000, 5000):
print(f"Epoch {epoch:4}: loss = {loss:.4f}")
print("\nPredictions after training:")
for inputs, pred in zip(X, y_hat):
print(f" {inputs} -> {pred[0]:.3f} (rounded: {round(pred[0])})")
Epoch 0: loss = 0.2749 Epoch 500: loss = 0.0135 Epoch 1000: loss = 0.0023 Epoch 2000: loss = 0.0008 Epoch 5000: loss = 0.0002 Predictions after training: [0 0] -> 0.012 (rounded: 0) [0 1] -> 0.984 (rounded: 1) [1 0] -> 0.986 (rounded: 1) [1 1] -> 0.019 (rounded: 0)
What the output tells us
- The loss falls from 0.275 to 0.0002 over 5,000 epochs, as backpropagation steadily improves all 17 weights and biases.
- XOR is solved: the predictions are 0.012, 0.984, 0.986 and 0.019, which round to exactly 0, 1, 1, 0. A hidden layer, trained by backpropagation, did what the perceptron never could.
d_outis the output's blame, the same "loss slope × sigmoid slope" as in the worked example.d_hiddenpasses that blame back throughW2and multiplies by the hidden sigmoid slope: the chain rule, for 4 neurons and 4 examples at once.
Try this yourself
Change the hidden layer from 4 neurons to 2 ((2, 2) and (2, 1)) and run it with a few different seeds. Sometimes it solves XOR and sometimes it gets stuck. That's a local minimum in action, and one reason bigger networks are often easier to train.
SummaryKey takeaways
- Backpropagation works out every weight's share of the blame for the error, efficiently.
- It uses the chain rule: the effect along a chain is the product of the effects of each link.
- Training repeats: forward pass → loss → backward pass → update.
- Blame flows backwards, layer by layer, reusing the results from the layer after.
- Libraries do it automatically with autodiff: Keras inside
fit(), PyTorch withloss.backward(). - Repeated multiplying causes vanishing (too small) or exploding (too big) gradients.
- A hidden layer trained with backpropagation solves XOR.
Check your understanding
Your neural networks roadmap
- 01Introduction to Neural NetworksWhat neural networks are and how they work
- 02The Perceptron and the Artificial NeuronBuild a single neuron from scratch
- 03Activation FunctionsSigmoid, tanh, ReLU and softmax
- 04Loss Functions and Gradient DescentHow networks measure and reduce error
- 05BackpropagationYou are here
- 06Building Networks with KerasYour first deep learning library
- 07Training Better NetworksOverfitting, dropout, batch normalisation, early stopping
- 08Convolutional Neural NetworksTeaching computers to see
- 09Recurrent Neural Networks and LSTMsWorking with sequences
- 10Transformers and AttentionThe architecture behind ChatGPT and Claude
- 11Transfer Learning and Pretrained ModelsStanding on the shoulders of giants
- 12Capstone ProjectBuild and present a deep learning project
Next module
Building Networks with Keras