Level 7 · BuilderLesson 7.2 42 min read 10 question quiz 12 marks of written practice

Backpropagation: How a Network Learns

The credit assignment problem

A network makes a wrong prediction. It has a million weights. Which ones were to blame, and by how much should each change?

Gradient descent (Level 5) answers in principle: move every weight against its partial derivative of the loss. The question is how to get a million partial derivatives cheaply. Nudging each weight in turn (Level 5) would take a million and more forward passes per step.

Backpropagation gets every one of them in a single backward sweep through the network, costing about twice a forward pass. It is the reason deep learning is possible.

The chain rule, in gears

If y depends on u, and u depends on x, then a small change in x passes through u to y:

The chain rule

dy/dx = dy/du × du/dx

The chain rule as gearsA gear train. A large violet gear A, standing for x, turns a small gear on the axle of gear B, standing for u, so B turns 3 times for each turn of A. A larger gear on B's axle turns a small orange gear C, standing for y, 2 times for each turn of B, so C turns 6 times for each turn of A. Beside it: du by dx equals 3, dy by du equals 2, so dy by dx equals 3 times 2 equals 6. When motion is allowed, the gears turn at those speeds.The chain rule as gears: rates multiply along the chainA: xturns onceB: uturns 3 timesC: yturns 6 timesdu/dx = 3B turns 3 times for each turn of Ady/du = 2C turns 2 times for each turn of Bdy/dx = 3 × 2 = 6so C turns 6 times for each turn of A
Backpropagation multiplies the local rates along every path from a weight to the loss.

Think of gears. If gear B turns 3 times for every turn of gear A, and gear C turns 2 times for every turn of B, then C turns 3 × 2 = 6 times for every turn of A. Rates of change multiply along a chain. A neural network is a long chain of simple steps, so the effect of any weight on the loss is a product of local rates of change along the path from that weight to the loss.

Computational graphs: forward then backward

Write any calculation as a computational graph: a diagram of simple operations, each node taking inputs and producing an output.

Forward and backward through a computational graphA computational graph for the loss L equals w times x plus b minus y, all squared. Inputs x equals 2 and w equals 0.5 feed a multiply node giving u equals 1; u and b equals 0.1 feed an add node giving z equals 1.1; z and the target y equals 1 feed a subtract node giving e equals 0.1; a square node gives L equals 0.01. Stepping through, forward values appear in blue left to right, then gradients appear in orange right to left: 1 at L, 0.2 at e, z, b and u, 0.4 at w and 0.1 at x.Forward values (blue), then backward gradients (orange)xw×b+y−( )²loss20.50.1120.50.11u = 120.50.11u = 1z = 1.120.50.11u = 1z = 1.1e = 0.120.50.11u = 1z = 1.1e = 0.1L = 0.0120.50.11u = 1z = 1.1e = 0.1L = 0.01120.50.11u = 1z = 1.1e = 0.1L = 0.0110.220.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.220.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.20.220.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.20.20.220.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.20.20.20.420.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.20.20.20.40.120.50.11u = 1z = 1.1e = 0.1L = 0.0110.20.20.20.20.40.1−0.2
Each node multiplies the gradient it receives by its own local derivative.

The example computes the squared error of a one-weight model, L = (w × x + b − y)², with x = 2, w = 0.5, b = 0.1 and target y = 1.

  1. Forward pass, left to right: compute each node's value. u = w × x = 1, z = u + b = 1.1, e = z − y = 0.1, L = e² = 0.01.
  2. Backward pass, right to left: start with dL/dL = 1, and at each node multiply the incoming gradient by the node's local derivative.
NodeLocal derivativeGradient arrivingGradient passed on
L = e²dL/de = 2e = 0.210.2 to e
e = z − yde/dz = 10.20.2 to z
z = u + b1 to each input0.20.2 to u, 0.2 to b
u = w × xdu/dw = x = 20.20.4 to w

So dL/dw = 0.4 and dL/db = 0.2. Every node only needs to know its own simple derivative; the chain rule does the rest. That locality is what makes backpropagation easy to program and fast to run.

Through a neuron, with real numbers

Now add a sigmoid: z = w × x + b, a = σ(z), L = (a − y)². With x = 2, w = 0.5, b = 0.1, y = 1:

StepForwardBackward
z = 0.5 × 2 + 0.11.1dL/dz = dL/da × da/dz = −0.4995 × 0.1874 = −0.0936
a = σ(1.1)0.7503dL/da = 2(a − y) = −0.4995
L = (0.7503 − 1)²0.0624start: dL/dL = 1

The sigmoid's local derivative is a(1 − a) = 0.7503 × 0.2497 = 0.1874. Then dL/dw = dL/dz × x = −0.1872 and dL/db = dL/dz × 1 = −0.0936.

Both gradients are negative, so gradient descent increases w and b. With a learning rate of 0.5: w becomes 0.5936 and b becomes 0.1468. Run forward again: a = 0.7915 and the loss falls from 0.0624 to 0.0435. One step of learning, done by hand.

Backpropagation through a whole network

For a deep network the same process runs layer by layer, from the output back to the input:

  1. Forward pass: compute and remember every layer's activations.
  2. Compute the loss and its gradient with respect to the output.
  3. For each layer, from last to first, turn the gradient arriving at its outputs into gradients for its weights, its biases and its inputs.
  4. Update every weight with gradient descent.

Inside each layer, step 3 does three small things:

  • multiply the incoming gradient by the activation's derivative, the local slope;
  • combine that with the layer's inputs to get the gradients of its weights and biases;
  • multiply by the layer's weights, transposed, to get the gradient to pass to the layer below.

Each layer receives "how much the loss would change if my outputs changed" and passes back "how much the loss would change if my inputs changed". That is why it is called back propagation of errors.

The training loopA circular loop with four stations: 1, forward pass, predict for a batch; 2, loss, how wrong; 3, backward pass, backpropagation computes the gradients; 4, update, the optimiser steps the weights; then back to 1. Arrows run clockwise between them, and when motion is allowed the dashes flow round the loop. In the centre: repeat for every batch and epoch. Beside it, a noisy loss curve falls steeply and then levels off over the training steps.The training loop: thousands of times, for every batch1 Forward passpredict for a batch2 Losshow wrong?3 Backward passbackprop: the gradients4 Updatethe optimiser stepsrepeat for everybatch and epochand the loss fallstraining steps
Every model in this course, from a line to a transformer, is trained by this loop.

The training loop

Forward pass, loss, backward pass, update, repeated for every batch and every epoch. In a framework such as PyTorch, the backward pass is a single call, because the framework records the computational graph during the forward pass automatically:

Python (on your computer)
import torch

model = torch.nn.Sequential(torch.nn.Linear(2, 3), torch.nn.Tanh(), torch.nn.Linear(3, 1))
optimiser = torch.optim.SGD(model.parameters(), lr=0.5)
loss_fn = torch.nn.BCEWithLogitsLoss()

X = torch.tensor([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
y = torch.tensor([[0.], [1.], [1.], [0.]])

for epoch in range(2000):
    loss = loss_fn(model(X), y)     # forward pass and loss
    optimiser.zero_grad()
    loss.backward()                 # backpropagation: every gradient at once
    optimiser.step()                # gradient descent update

This is called automatic differentiation, or autograd. The hands-on section builds a tiny version from scratch.

When gradients misbehave

Backpropagation multiplies many local derivatives together, so the gradient can shrink or grow exponentially with depth.

ProblemSymptomFixes
Vanishing gradientsEarly layers barely change; training stallsReLU-family activations, careful initialisation, normalisation layers, residual connections
Exploding gradientsLoss suddenly jumps to huge values or NaNGradient clipping (cap the gradient's size), lower learning rate, normalisation
Bad initialisationAll neurons learn the same thing, or the network is stuck from the startRandom starting weights scaled to the layer size (Xavier or He initialisation)
Never start with all weights equal

If every weight starts at the same value, every hidden neuron computes the same output and receives the same gradient, so they stay identical forever. Random initialisation breaks the symmetry so each neuron can learn something different.

Neural network playground

Train a small neural network in your browser and watch its decision boundary form, weight by weight. This tool needs JavaScript, so turn it on to use it. Everything you need to answer the exam question is explained in the text above.

Use the playground above to watch backpropagation shape a network's decision boundary in real time. Try a spiral with one hidden neuron, then with eight, then with two layers.

Hands-on: backpropagation from scratch

A tiny automatic differentiation engine

Each Value remembers how it was made. Calling backward() walks the graph in reverse and applies the chain rule at every node.

Python
import math

class Value:
    def __init__(self, data, parents=()):
        self.data, self.grad = data, 0.0
        self.parents = parents
        self.backward_step = lambda: None

    def __add__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data + other.data, (self, other))
        def step():
            self.grad += out.grad
            other.grad += out.grad
        out.backward_step = step
        return out

    def __mul__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data * other.data, (self, other))
        def step():
            self.grad += other.data * out.grad
            other.grad += self.data * out.grad
        out.backward_step = step
        return out

    def __sub__(self, other):
        return self + (-1 * other if isinstance(other, Value) else Value(-other))

    def __rmul__(self, other):
        return self * other

    def sigmoid(self):
        s = 1 / (1 + math.exp(-self.data))
        out = Value(s, (self,))
        def step():
            self.grad += s * (1 - s) * out.grad
        out.backward_step = step
        return out

    def backward(self):
        order, seen = [], set()
        def visit(v):
            if id(v) not in seen:
                seen.add(id(v))
                for p in v.parents:
                    visit(p)
                order.append(v)
        visit(self)
        self.grad = 1.0
        for v in reversed(order):
            v.backward_step()

# the worked example: z = w*x + b, a = sigmoid(z), L = (a - y)^2
w, b = Value(0.5), Value(0.1)
a = (w * 2 + b).sigmoid()
error = a - 1
L = error * error
L.backward()
print(f"loss {L.data:.4f}, dL/dw = {w.grad:.4f}, dL/db = {b.grad:.4f}")

The engine reproduces the hand calculation: dL/dw = −0.1872 and dL/db = −0.0936.

Training a network to learn XOR

Now the real thing: a network with 2 inputs, 3 tanh hidden neurons and a sigmoid output, trained by backpropagation and gradient descent, written out by hand.

Python
import math, random

random.seed(2)
H = 3
w1 = [[random.uniform(-1, 1) for _ in range(2)] for _ in range(H)]
b1 = [random.uniform(-1, 1) for _ in range(H)]
w2 = [random.uniform(-1, 1) for _ in range(H)]
b2 = 0.0
data = [((0, 0), 0), ((0, 1), 1), ((1, 0), 1), ((1, 1), 0)]
lr = 0.5

def forward(x):
    h = [math.tanh(w1[j][0] * x[0] + w1[j][1] * x[1] + b1[j]) for j in range(H)]
    p = 1 / (1 + math.exp(-(sum(w2[j] * h[j] for j in range(H)) + b2)))
    return h, p

for epoch in range(1, 1001):
    loss = 0.0
    for x, y in data:
        h, p = forward(x)                                   # forward pass
        loss -= y * math.log(p) + (1 - y) * math.log(1 - p)
        d_out = p - y                                       # gradient at the output
        for j in range(H):
            d_h = d_out * w2[j] * (1 - h[j] ** 2)           # back through w2 and tanh
            w2[j] -= lr * d_out * h[j]                      # update output weights
            w1[j][0] -= lr * d_h * x[0]                     # update hidden weights
            w1[j][1] -= lr * d_h * x[1]
            b1[j] -= lr * d_h
        b2 -= lr * d_out
    if epoch in (1, 50, 100, 200, 500, 1000):
        print(f"epoch {epoch:4}: average loss {loss / 4:.4f}")

for x, y in data:
    print(x, "->", round(forward(x)[1], 3), " target", y)

With sigmoid outputs and cross-entropy loss, the gradient at the output is simply p − y, one of the neat results that makes this pairing so popular.

Try this

  1. Change random.seed(2) to random.seed(3). Training gets stuck with every output near 0.5. A bad start can trap a network on a flat region; try H = 4 with the same seed.
  2. Set lr = 5. What happens to the loss?
  3. Use the Value engine to check one of the XOR network's gradients.

Level check

  • Explain the credit assignment problem and why nudging each weight is too slow
  • State the chain rule and explain it with an example
  • Carry out a forward and backward pass on a small computational graph
  • Calculate the gradients of a single sigmoid neuron and one gradient descent update
  • Describe how backpropagation passes gradients through the layers of a network
  • Describe vanishing and exploding gradients and why weights start random

Key terms you must be able to define

Backpropagation
Computing the gradient of the loss with respect to every weight by applying the chain rule backwards through the network.
Credit assignment problem
Working out how much each weight contributed to an error.
Chain rule
The rate of change through a chain of functions is the product of each link's rate of change.
Computational graph
A calculation drawn as a network of simple operations, used for forward and backward passes.
Local derivative
The derivative of one node's output with respect to its inputs, which is all each node needs for backpropagation.
Automatic differentiation
Software that records a calculation and computes its derivatives automatically; also called autograd.
Training loop
Forward pass, loss, backward pass and update, repeated over batches and epochs.
Exploding gradient
A gradient that grows exponentially through the layers, making training unstable.
Gradient clipping
Capping the size of a gradient to stop exploding gradients.
Weight initialisation
Choosing random starting weights, scaled to each layer's size, so that every neuron can learn something different.

Flashcards

DefineBackpropagation
AnswerComputing the gradient of the loss with respect to every weight by applying the chain rule backwards through the network.
DefineCredit assignment problem
AnswerWorking out how much each weight contributed to an error.
DefineChain rule
AnswerThe rate of change through a chain of functions is the product of each link's rate of change.
DefineComputational graph
AnswerA calculation drawn as a network of simple operations, used for forward and backward passes.
DefineLocal derivative
AnswerThe derivative of one node's output with respect to its inputs, which is all each node needs for backpropagation.
DefineAutomatic differentiation
AnswerSoftware that records a calculation and computes its derivatives automatically; also called autograd.
DefineTraining loop
AnswerForward pass, loss, backward pass and update, repeated over batches and epochs.
DefineExploding gradient
AnswerA gradient that grows exponentially through the layers, making training unstable.

Go further: notes for future AI engineers

Reverse mode versus forward mode

There are two ways to apply the chain rule to a graph. Forward mode pushes derivatives from one input to all the outputs; reverse mode pulls derivatives from one output back to all the inputs. A network has millions of inputs (its weights) and one output (the loss), so reverse mode gets every gradient in one sweep, while forward mode would need one sweep per weight. Backpropagation is reverse-mode automatic differentiation.

Memory is the price

Reverse mode needs the activations from the forward pass, so training stores every layer's activations for the whole batch. That is often what limits batch size and model size on a GPU. Gradient checkpointing saves memory by storing only some activations and recomputing the rest during the backward pass, trading extra compute for less memory, and is standard practice for training large models.

Misconceptions to avoid

Avoid these
  • Thinking backpropagation is a different learning method from gradient descent. It only computes the gradients; gradient descent uses them.
  • Starting every weight at zero or the same value. Every neuron then stays identical.
  • Forgetting the activation's derivative when passing a gradient back through a layer.
  • Believing backpropagation is slow. It costs about twice a forward pass, whatever the number of weights.
  • Thinking the backward pass changes the weights. It computes gradients; the optimiser's step changes the weights.
Fun fact

Backpropagation was discovered several times: Seppo Linnainmaa described its core idea in 1970 and Paul Werbos proposed it for neural networks in 1974, but it took the 1986 paper by Rumelhart, Hinton and Williams to make the field pay attention.

Knowledge check

0 of 10 answered
1What does backpropagation compute?
2If dy/du = 3 and du/dx = 4, what is dy/dx?
3In which direction does the backward pass travel through the network?
4In the graph u = w × x with x = 2, a gradient of 0.2 arrives at u. What is dL/dw?
5What is the local derivative of a sigmoid node whose output is a?
6Roughly how much does a backward pass cost compared with a forward pass?
7Why do networks start with random weights rather than all zeros?
8A training run's loss suddenly jumps to NaN. What is a likely cause?
9What does loss.backward() do in PyTorch?
10With a sigmoid output and cross-entropy loss, what is the gradient of the loss with respect to the output's z?
Score 0 / 10

Check you could explain this

12 marks
1Explain what backpropagation is and why it is needed to train neural networks.4 marks
Explain

Mark scheme0 / 4
  • LTraining needs the gradient of the loss for every weight
  • LCalculating each separately / by nudging would be far too slow
  • LBackpropagation applies the chain rule backwards from the loss
  • LIt gets all the gradients in one backward pass, about twice the cost of a forward pass
Model answer

Gradient descent needs the gradient of the loss with respect to every weight in the network, and a network can have millions of weights. Estimating each gradient separately, by nudging one weight at a time, would need millions of forward passes per step and be far too slow. Backpropagation applies the chain rule backwards through the network, from the loss towards the inputs, multiplying local derivatives at each layer. It produces every gradient in a single backward pass, costing only about twice a forward pass, which makes training large networks practical.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

2A neuron computes z = wx + b and a = σ(z), with loss L = (a − y)². Given dL/da = −0.5 and a = 0.75, calculate dL/dw when x = 2.4 marks
Calculate

Mark scheme0 / 4
  • Lda/dz = a(1 − a) = 0.75 × 0.25 = 0.1875
  • LdL/dz = −0.5 × 0.1875 = −0.09375
  • Ldz/dw = x = 2
  • LdL/dw = −0.1875
Model answer

The sigmoid's local derivative is da/dz = a(1 − a) = 0.75 × 0.25 = 0.1875. By the chain rule, dL/dz = dL/da × da/dz = −0.5 × 0.1875 = −0.09375. Since z = wx + b, dz/dw = x = 2, so dL/dw = −0.09375 × 2 = −0.1875.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

3Describe the steps of the training loop used to train a neural network.4 marks
Describe

Mark scheme0 / 4
  • LForward pass: compute the outputs for a batch
  • LCompute the loss by comparing outputs with targets
  • LBackward pass: backpropagation computes the gradients
  • LUpdate the weights with gradient descent / an optimiser, then repeat for batches and epochs
Model answer

For each batch of training examples, the network first does a forward pass, computing its outputs layer by layer. The loss is calculated by comparing those outputs with the targets. A backward pass then uses backpropagation to compute the gradient of the loss with respect to every weight and bias. Finally an optimiser such as gradient descent or Adam updates every parameter a small step against its gradient. The loop repeats for every batch, and the whole dataset is passed through many times, once per epoch.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

Machine mark 0 / 12