Skip to content
AriadneTechnology

The Middle Ring · Chamber 4 of 8

Backpropagation: The Chain Rule at Scale

How a network assigns blame to millions of weights, computed first by hand and then by machine.

30 min 60 XP + 5 questions + 1 challengeVideoMathLabCodeHistory

In this chamber you will

  • Apply the chain rule to nested functions
  • Run forward and backward passes on a computational graph
  • Derive the gradients of a two-layer network
  • Verify gradients numerically like a careful researcher

The credit assignment problem

A network with a million weights makes a bad prediction. Which weights are to blame, and how much should each one change? Gradient descent needs ∂L/∂w\partial L / \partial w for every weight, at every step.

The naive approach is to nudge one weight, rerun the network, see how the loss changes, and repeat for the next weight. That costs one forward pass per weight: a million passes for a single update. Hopeless.

Backpropagation computes every one of those derivatives in a single backward sweep, at a cost comparable to one extra forward pass. It is the reason deep learning is possible at all, and it rests entirely on one idea from calculus.

The chain rule

If yy depends on xx, and LL depends on yy, then

dLdx=dLdy⋅dydx\frac{dL}{dx} = \frac{dL}{dy} \cdot \frac{dy}{dx}

Rates of change multiply along a chain. The mathematician George Simmons put it memorably: if a car travels twice as fast as a bicycle, and the bicycle four times as fast as a walking man, then the car travels 2×4=82 \times 4 = 8 times as fast as the man.

A quick example: L=(2x−1)2L = (2x - 1)^2 at x=2x = 2. Let y=2x−1=3y = 2x - 1 = 3. Then dLdy=2y=6\frac{dL}{dy} = 2y = 6 and dydx=2\frac{dy}{dx} = 2, so dLdx=6×2=12\frac{dL}{dx} = 6 \times 2 = 12.

Computational graphs

To apply the chain rule to a whole network, break the computation into a graph of primitive operations. Then run it twice:

  1. 1

    Forward pass

    Compute every node's value from left to right, and remember them: the backward pass needs them.

  2. 2

    Backward pass

    Start at the loss with ∂L∂L=1\frac{\partial L}{\partial L} = 1 and walk right to left. At every node, apply the chain rule locally:

    ∂L∂ input⏟downstream=∂L∂ output⏟upstream×∂ output∂ input⏟local\underbrace{\frac{\partial L}{\partial\, \text{input}}}_{\text{downstream}} = \underbrace{\frac{\partial L}{\partial\, \text{output}}}_{\text{upstream}} \times \underbrace{\frac{\partial\, \text{output}}{\partial\, \text{input}}}_{\text{local}}

Each node only ever needs its own local derivative. A handful of patterns covers most of what you'll meet:

GateForwardBackward rule
addz=x+yz = x + ycopies the incoming gradient to both inputs
multiplyz=x⋅yz = x \cdot yeach input gets the incoming gradient times the other input
ReLUa=max⁡(0,z)a = \max(0, z)passes the gradient if z>0z > 0, blocks it otherwise
squareL=u2L = u^2multiplies the incoming gradient by 2u2u
Quick check +20 XP

For z=x⋅wz = x \cdot w, the gradient arriving at zz is ∂L∂z=2\frac{\partial L}{\partial z} = 2. If x=3x = 3 and w=−4w = -4, what is ∂L∂w\frac{\partial L}{\partial w}?

Backprop by hand

Enough reading: it's your turn to be the backward pass. The lab gives you one neuron, L=(ReLU(wx+b)−y)2L = \big(\mathrm{ReLU}(wx + b) - y\big)^2, with real numbers. Compute every gradient yourself. Get them all right on the first try and something shiny happens.

Interactive lab

Backprop by hand

One neuron, one example: L = (ReLU(w·x + b) − y)². First run the forward pass. Then walk backwards and compute each gradient yourself. Teal numbers are values, gold numbers are gradients.
x=3w=2b=−1y=4xwby×+ReLUL

Forward pass · step 1 of 4

  • Next: compute m
Challenge: Backprop by handCompute every gradient in the computational graph.+50 XP

Backprop for a whole network

The same bookkeeping scales to entire networks, where it's written with vectors and matrices. Take a two-layer network on one example:

z1=W1x+b1,h=ϕ(z1),y^=w2⊤h+b2,L=12(y^−y)2\mathbf{z}_1 = W_1\mathbf{x} + \mathbf{b}_1, \qquad \mathbf{h} = \phi(\mathbf{z}_1), \qquad \hat{y} = \mathbf{w}_2^\top\mathbf{h} + b_2, \qquad L = \tfrac{1}{2}(\hat{y} - y)^2
Derive the gradients (try on paper first!)

Walk backwards, one line per node:

δ=∂L∂y^=y^−y∂L∂w2=δ h,∂L∂b2=δ∂L∂h=δ w2∂L∂z1=∂L∂h⊙ϕ′(z1)∂L∂W1=∂L∂z1 x⊤,∂L∂b1=∂L∂z1\begin{aligned} \delta &= \frac{\partial L}{\partial \hat{y}} = \hat{y} - y \\[4pt] \frac{\partial L}{\partial \mathbf{w}_2} &= \delta\,\mathbf{h}, \qquad \frac{\partial L}{\partial b_2} = \delta \\[4pt] \frac{\partial L}{\partial \mathbf{h}} &= \delta\,\mathbf{w}_2 \\[4pt] \frac{\partial L}{\partial \mathbf{z}_1} &= \frac{\partial L}{\partial \mathbf{h}} \odot \phi'(\mathbf{z}_1) \\[4pt] \frac{\partial L}{\partial W_1} &= \frac{\partial L}{\partial \mathbf{z}_1}\,\mathbf{x}^\top, \qquad \frac{\partial L}{\partial \mathbf{b}_1} = \frac{\partial L}{\partial \mathbf{z}_1} \end{aligned}

The pattern repeats in every layer. An error signal arrives, gets multiplied elementwise (⊙\odot) by the activation's slope, and is passed back through the transpose of the layer's weights. (The 12\tfrac12 in the loss is a convenience: it cancels the 2 from differentiating the square.)

That elementwise multiply by ϕ′\phi' explains the vanishing gradient problem you were warned about. Each sigmoid layer multiplies the signal by at most 0.250.25, so after ten layers it can shrink by a factor of 0.2510≈10−60.25^{10} \approx 10^{-6}. That's one reason ReLU (slope 1), careful initialisation, normalisation layers and residual connections became standard.

Trust, but verify: gradient checking

Hand-derived gradients are easy to get slightly wrong. Worse, a buggy gradient often still reduces the loss, just badly. Careful researchers check their gradients numerically with the centred difference:

∂L∂θ≈L(θ+ϵ)−L(θ−ϵ)2ϵ\frac{\partial L}{\partial \theta} \approx \frac{L(\theta + \epsilon) - L(\theta - \epsilon)}{2\epsilon}
Python
import numpy as np

def grad_check(f, grad_f, theta, eps=1e-5):
    """Relative error between an analytic gradient and centred finite differences."""
    analytic = grad_f(theta)
    numeric = np.zeros_like(theta)
    for i in range(theta.size):
        step = np.zeros_like(theta)
        step.flat[i] = eps
        numeric.flat[i] = (f(theta + step) - f(theta - step)) / (2 * eps)
    diff = np.linalg.norm(analytic - numeric)
    return diff / (np.linalg.norm(analytic) + np.linalg.norm(numeric))

# Around 1e-7 or smaller: great. Around 1e-2 or larger: almost certainly a bug.

Autograd: let the machine do it

In practice nobody writes backward passes by hand any more. Frameworks such as PyTorch and JAX record the computational graph as your code runs and apply the chain rule for you. Here is the lab's neuron in PyTorch. It prints exactly the gradients you just computed:

Python
import torch

x, y = torch.tensor(3.0), torch.tensor(4.0)
w = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(-1.0, requires_grad=True)

a = torch.relu(w * x + b)   # forward pass: a = 5
loss = (a - y) ** 2         # loss = 1

loss.backward()             # backward pass
print(w.grad, b.grad)       # tensor(6.) tensor(2.)

Understanding what loss.backward() does, and when it can silently go wrong, is what separates someone who uses deep learning from someone who can research it.

Learning representations by back-propagating errorsDavid E. Rumelhart, Geoffrey E. Hinton, Ronald J. Williams · Nature 323, 533–536, 1986

Four pages that changed the field. A perfect candidate for your first three-pass read in Chamber 6.

Watch: backpropagation, intuitively

Backpropagation, intuitively3Blue1Brown · 13 min

Want to go deeper? Build a tiny autograd engine yourself, line by line, with Andrej Karpathy:

The spelled-out intro to neural networks and backpropagation: building microgradAndrej Karpathy · 146 min · Go deeper

Key takeaways

  • Backpropagation is the chain rule applied systematically on a computational graph: forward to compute values, backward to compute gradients.
  • Each node multiplies the upstream gradient by its local derivative. Add gates copy, multiply gates swap, ReLU gates route.
  • One backward sweep yields every gradient, which makes training networks with millions of weights feasible.
  • Check hand-written gradients with centred finite differences. Beware vanishing gradients in deep stacks of saturating activations.

Checkpoint

Prove it to the labyrinth

Answer every question to clear this chamber. First-try answers earn the most XP.

0/4
Question 1 of 4 +20 XP

Let y=3x+1y = 3x + 1 and L=y2L = y^2. What is dLdx\frac{dL}{dx} at x=1x = 1?

Question 2 of 4 +20 XP

During the backward pass, what does each node in a computational graph compute?

Question 3 of 4 +20 XP

Why is backpropagation so much faster than estimating each weight's gradient separately (e.g. by nudging weights one at a time)?

Question 4 of 4 +20 XP

You implemented backprop by hand. What is the standard way to check it's correct?

End of the chamber

Clear this chamber

  • Questions in this chamber (0/5 solved)Next unsolved
  • Bonus: Backprop by hand (+50 XP)
+60 XPChain RuleComputational GraphBackpropagationGradient Check