Deep Learning Slides 📂 Introduction · 8 of 18 33 min read

The Backpropagation Algorithm: How Neural Networks Learn

Backpropagation is how a network turns one wrong guess into thousands of precise corrections. This visual guide builds it from the chain rule up — the error signal δ, the four key equations, a fully worked forward-and-backward numerical example, NumPy and PyTorch code, and the pitfalls to avoid.

The Backpropagation Algorithm

Forward propagation makes a guess; the loss measures how wrong it is. Backpropagation is the missing third act — it walks the error backward through every layer, handing each weight its exact share of the blame.
The Chain Rule Error Signal δ Worked Gradients Autograd

Press Next → or use ← → arrow keys

Section 01

The Story: Assigning the Blame

An arrow misses the bullseye by 30 cm. A bad coach shouts generic blame at the whole team. A great coach walks backward down the line — the stance nudged the aim a little, the grip a lot, the release most of all — handing each person a personalised correction sized to their contribution. Backpropagation is that great coach: it distributes the error to every weight in exact proportion to how much it caused the miss.
🎯
The credit-assignment problem

A network has thousands of weights. Which ones, and by how much, should change to reduce the loss? Backpropagation answers that for every weight at once — in a single backward sweep.

Section 01

The Third Act of Training

Training is a loop of three moves. You've met the first two — backprop is what closes the circle and makes learning possible:

➡️
1 · Forward pass
Push the input through the layers to get a prediction ŷ. Pure arithmetic, one direction.
📏
2 · Compute loss
Measure how far ŷ sits from the true target y with a loss function — a single number.
🔁
3 · Backward pass
Send the error back through the layers, computing how each weight should change. This is backprop.
One forward + one backward — at any depth

Backprop computes every gradient in a single backward sweep, no matter how deep the network. That efficiency — O(forward + backward), not O(weights) — is what made modern deep learning possible.

Section 02

The Four Core Concepts

Backprop rests on four ideas. Nail these and the equations write themselves:

ConceptSymbolWhat it is
Computation graphThe network as a graph of operations — traversed forward to predict, backward to learn
Error signal (delta)δ = ∂L/∂zThe "blame" at a neuron: how much the loss changes with its pre-activation z
Weight gradient∂L/∂W = δ · aᵀHow to nudge each weight — the error signal times the incoming activation
Bias gradient∂L/∂b = δThe error signal itself — the bias's input is always 1
🧩
Everything hangs on δ

Once you know the error signal δ at a neuron, both its weight and bias gradients follow immediately. The whole algorithm is really about computing δ at every neuron.

Section 03

The Chain Rule — The Engine

Turn a gear that spins the next one faster, which spins a third faster. How fast does the last one turn per turn of the first? You multiply: 2 × 3 = . The chain rule is exactly this — to get a distant gradient, multiply the local gradients along the path.
x z L ∂z/∂x ∂L/∂z ∂L/∂x = (∂L/∂z) × (∂z/∂x) — multiply along the chain
🌊
Through an activation

Crossing a neuron's activation adds one more factor: ∂L/∂z = (∂L/∂a) × (∂a/∂z) = δ × σ′(z). That σ′(z) term is where saturation quietly kills gradients.

Section 04

Two Passes Through One Graph

The same graph runs both ways: values flow forward (cyan) to make a prediction, then gradients flow backward (amber) to assign blame.

x ŷ L forward: values → ← backward: gradients δ
💾
Save the forward values

The backward pass reuses every activation a and pre-activation z from the forward pass. That's why frameworks cache them — you can't compute gradients without them.

Section 04

The Four Key Equations

Backprop is really just these four formulas, applied layer by layer from the output backward:

δᴸ = (ŷ − y) σ′(zᴸ)
1 · error at the output layer
δˡ = (Wˡ⁺¹ᵀ δˡ⁺¹) σ′()
2 · error at a hidden layer
∂L/∂Wˡ = δˡ aˡ⁻¹ᵀ
3 · gradient for the weights
∂L/∂bˡ = δˡ
4 · gradient for the biases
🔑
Read them as a recipe

Start with the output error (1). Push it back a layer with the transposed weights (2). At each layer, the two gradients (3) and (4) drop straight out of δ. Repeat to the input.

Section 05

A Full Worked Example

A tiny network — 2 inputs → 1 hidden → 1 output, sigmoid activations, MSE loss — small enough to do entirely by hand.

x = [0.5, 0.8]
y = 1.0
σ(z) = 1 / (1 + e⁻ᶻ)
L = ½(ŷ − y)²
W₁₁ = 0.4, W₁₂ = 0.6
b₁ = 0.1
W₂ = 0.9
b₂ = 0.1
🎬
Forward, then backward

First we run the input forward to get ŷ and the loss. Then we push the error backward to find every gradient — and finally update the weights.

Section 05

Step 1 — The Forward Pass

1
Hidden pre-activation: z₁ = W₁₁x₁ + W₁₂x₂ + b₁ = 0.68
2
Hidden activation: a₁ = σ(0.68) = 0.6637
3
Output pre-activation: z₂ = W₂·a₁ + b₂ = (0.9)(0.6637) + 0.1 = 0.6973
4
Prediction: ŷ = σ(0.6973) = 0.6682
5
Loss: L = ½(0.6682 − 1.0)² = 0.0550
📊
We predicted 0.67, the truth was 1.0

A loss of 0.0550 says "clearly wrong, but not wildly." Now backprop finds exactly which knobs to turn — and how far — to shrink that number.

Section 05

Step 2 — The Backward Pass

1
Output error: ∂L/∂ŷ = ŷ − y = −0.3318,   σ′(z₂) = 0.6682(1−0.6682) = 0.2217 → δ₂ = −0.0736
2
Output gradients: ∂L/∂W₂ = δ₂·a₁ = −0.0488,   ∂L/∂b₂ = δ₂ = −0.0736
3
Push back to hidden: ∂L/∂a₁ = δ₂·W₂ = −0.0662,   σ′(z₁) = 0.2232 → δ₁ = −0.0148
4
Hidden gradients: ∂L/∂W₁₁ = δ₁·x₁ = −0.0074,   ∂L/∂W₁₂ = δ₁·x₂ = −0.0118,   ∂L/∂b₁ = −0.0148
🔍
Notice the gradients shrink going back

δ₂ = −0.0736 at the output becomes δ₁ = −0.0148 one layer back — multiplied down by W₂ and σ′(z₁). Stack many layers and this shrinking becomes the vanishing-gradient problem.

Section 05

Step 3 — Update the Weights

Apply gradient descent with learning rate η = 0.5: θ ← θ − η · ∂L/∂θ. Every parameter takes one small step downhill:

ParameterOld valueGradientNew value
W₂0.9000−0.04880.9244
b₂0.1000−0.07360.1368
W₁₁0.4000−0.00740.4037
W₁₂0.6000−0.01180.6059
b₁0.1000−0.01480.1074
🔁
That's one training step

Because every gradient was negative, every weight nudged up — pushing the next prediction closer to 1.0. Repeat this loop thousands of times and the network learns.

Section 06

Activations & Their Derivatives

Backprop needs the derivative of each activation — the σ′(z) factor in every δ. Here are the ones you'll use:

ActivationFormulaDerivativeIssue
Sigmoid1/(1+e⁻ᶻ)σ(z)(1−σ(z))Vanishing
Tanh(eᶻ−e⁻ᶻ)/(eᶻ+e⁻ᶻ)1 − tanh²(z)Less vanishing
ReLUmax(0, z)1 if z>0 else 0Dead neurons
Leaky ReLUz if z>0 else 0.01z1 if z>0 else 0.01None
📉
The derivative is the whole story

Sigmoid's derivative peaks at just 0.25, so gradients shrink fast in deep nets. ReLU's is exactly 1 for positive inputs — which is precisely why it trains deep networks so much better.

Section 07

Backprop From Scratch in NumPy

The forward and backward passes, side by side — the code mirrors the four equations exactly:

def sigmoid(z):        return 1 / (1 + np.exp(-z))
def sigmoid_deriv(z):  s = sigmoid(z); return s * (1 - s)

# Forward
z1 = W1 @ x + b1
a1 = sigmoid(z1)
z2 = W2 @ a1 + b2
y_hat = sigmoid(z2)
loss = 0.5 * (y_hat - y)**2

# Backward
delta2 = (y_hat - y) * sigmoid_deriv(z2)   # δ₂ = -0.0736
dL_dW2 = delta2 @ a1.T                      # -0.0488
dL_db2 = delta2

dL_da1 = W2.T @ delta2
delta1 = dL_da1 * sigmoid_deriv(z1)         # δ₁ = -0.0148
dL_dW1 = delta1 @ x.T                        # [-0.0074, -0.0118]
dL_db1 = delta1

# Update (lr = 0.5)
W2 -= 0.5 * dL_dW2; b2 -= 0.5 * dL_db2
W1 -= 0.5 * dL_dW1; b1 -= 0.5 * dL_db1
The comments are our hand numbers

δ₂ = −0.0736, δ₁ = −0.0148, and the weight gradients all match the worked example exactly.

Section 08

Autograd — Backprop for Free

In practice you never hand-derive gradients. Frameworks record the forward operations and replay them backward automatically. One call does it all:

import torch

x  = torch.tensor([[0.5], [0.8]])
y  = torch.tensor([[1.0]])
W1 = torch.tensor([[0.4, 0.6]], requires_grad=True)
b1 = torch.tensor([[0.1]], requires_grad=True)
W2 = torch.tensor([[0.9]], requires_grad=True)
b2 = torch.tensor([[0.1]], requires_grad=True)

z1 = W1 @ x + b1
a1 = torch.sigmoid(z1)
z2 = W2 @ a1 + b2
y_hat = torch.sigmoid(z2)
loss = 0.5 * (y_hat - y)**2

loss.backward()          # chain rule, applied automatically
print(W2.grad)          # matches -0.0488 exactly
🤖
Same algorithm, automated

loss.backward() runs the exact four equations you just did by hand — and PyTorch's .grad values match the manual gradients to the last digit.

Section 09

Common Pitfalls

Three things go wrong when gradients flow back through many layers. Each has a known fix:

🫥
Vanishing gradients
Each layer multiplies by σ′(z) ≤ 0.25. Over 10 layers, 0.25¹⁰ ≈ 0.000001 — early layers stop learning. Fix: ReLU, BatchNorm, residuals.
💥
Exploding gradients
The opposite — gradients blow up and training diverges. Fix: gradient clipping and careful (Xavier / He) initialisation.
💀
Dead ReLU neurons
A neuron stuck at negative pre-activation outputs 0 forever — gradient 0, no updates. Fix: Leaky ReLU or ELU.
🔬
Watch your activation derivatives

Most training failures trace back to σ′(z) — either shrinking to nothing or zeroing out. Monitoring activation statistics catches these problems early.

Section 10

7 Golden Rules

  Backpropagation cheat-sheet
1Store the forward values. Every activation a and pre-activation z is needed for the backward pass.
2Apply the chain rule in reverse order, layer by layer from the output back to the input.
3Bias gradient = the error signal: ∂L/∂b = δ. No extra work.
4Check your gradients numerically when implementing by hand — compare against a finite-difference estimate.
5Zero the gradients before each batch, or they accumulate across steps.
6Watch for saturation and dead neurons — monitor activation derivatives during training.
7Cost is O(forward + backward), not O(weights) — one backward sweep gives every gradient.
🚀
The algorithm that trains everything

Forward to predict, loss to score, backward to learn. Backpropagation is the engine inside every deep network — from a 2-neuron toy to a trillion-parameter transformer.