Part II — Neural networks and training · Chapter 4

Backpropagation

~45 min read5 interactive widgets5 plates

In this chapter

  1. What backpropagation is
  2. One neuron: the forward pass
  3. One neuron: the backward pass
  4. Collapsing the sigmoid into one operator
  5. The neuron as a black box
  6. A full network, one training step
  7. Matrix based backpropagation
  8. The matrix example, worked
  9. Vanishing and exploding gradients
  10. Gradient clipping
  11. Check your understanding

1. What backpropagation is

The backpropagation algorithm is probably the most fundamental component of an Artificial Neural Network. After each forward pass through a network, backpropagation performs a backward pass while adjusting the model parameters (weights and biases). The parameters are updated by computing gradients of expressions through automatic differentiation and the recursive application of the chain rule — the two machines built in Chapter 2.

The whole algorithm serves one formula:

w′ = w − η · ∂E/∂w
SymbolMeaning
wThe old weight.
w′The new weight.
ηThe learning rate.
∂E/∂wThe partial derivative of the loss function with respect to the weight w.

Geometrically, the slides picture an error surface E(w) over the weight vector w = [w1, w2]. The gradient ∇E(w) = [∂E/∂w1, ∂E/∂w2] points along the direction of steepest increase, so the minus sign in the update sends every weight downhill.

2. One neuron: the forward pass

The slides work the algorithm on the smallest interesting object: a neuron with two inputs in1 and in2, a bias, and the sigmoid as activation function. The trick that makes the mechanics visible is to expand the sigmoid into its primitive operators, so that the neuron becomes a computational graph of seven simple nodes:

in₁ × w₁  ─┐
              ├─ + ─ + w₀ ─ −x ─ eˣ ─ 1+x ─ 1/x ─ out
in₂ × w₂  ─┘

The last four nodes are just the sigmoid written out: σ(net) = 1 / (1 + e−net).

Now instantiate the example: w₀ = −3, in1 = −1, w1 = 2, in2 = −2, w2 = −3. During the forward pass, from inputs to outputs, each operator computes its output value:

#OperatorComputationValue
1×in1 · w1 = (−1) · 2−2
2×in2 · w2 = (−2) · (−3)6
3+−2 + 64
4+ (bias)4 + (−3)1   (this is net)
5−x−1−1
6exe−10.37
71 + x1 + 0.371.37
81 / x1 / 1.37out = 0.73

3. One neuron: the backward pass

During the backward pass, from the outputs to the inputs, each operator i does two things:

  1. it learns the sensibility ∂out/∂ρi of the neuron final output with respect to its own output variation, using the chain rule;
  2. it computes the local gradient ∂ρi/∂x of its output with respect to its inputs.

The upstream sensibility multiplied by the local gradient is the sensibility handed to the node below. Here is the entire descent, in the order the slides walk it:

NodeLocal gradientChain ruleSensibility passed down
1/x∂ρ1/∂x = −1/x² = −1/1.37² = −0.531 · (−0.53)∂out/∂ρ2 = −0.53
1 + x∂ρ2/∂x = 1−0.53 · 1∂out/∂ρ3 = −0.53
ex∂ρ3/∂x = ex = e−1 = 0.37−0.53 · 0.37∂out/∂ρ4 = −0.20
−x∂ρ4/∂x = −1−0.20 · (−1)∂out/∂ρ5 = 0.20
+ (bias)∂ρ5/∂w₀ = 1, ∂ρ5/∂x1 = 10.20 · 1∂out/∂w₀ = 0.20 and ∂out/∂ρ6 = 0.20
+∂ρ6/∂x1 = 1, ∂ρ6/∂x2 = 10.20 · 1∂out/∂ρ7 = 0.20 and ∂out/∂ρ8 = 0.20
× (top)∂ρ7/∂in1 = w1 = 2, ∂ρ7/∂w1 = in1 = −10.20 · 2 and 0.20 · (−1)∂out/∂in1 = 0.40, ∂out/∂w1 = −0.20
× (bottom)∂ρ8/∂in2 = w2 = −3, ∂ρ8/∂w2 = in2 = −20.20 · (−3) and 0.20 · (−2)∂out/∂in2 = −0.60, ∂out/∂w2 = −0.40

Run it yourself, one node at a time. The forward thread must finish before the backward thread can move, because the chain rule needs the stored node values.

Key idea

Read the multiplication node carefully: its local gradient with respect to one input is the other input. That is why ∂out/∂w1 = 0.20 · in1 — a weight receives a gradient proportional to the activation that fed it. A neuron whose input was zero receives no update on that weight, no matter how wrong the output was.

Two structural facts fall out of the table, and both are worth memorising:

4. Collapsing the sigmoid into one operator

Expanding the sigmoid was a teaching device. In practice, grouping the simple operators composing the sigmoid function into a single operator makes the derivation more efficient. The four nodes −x, ex, 1+x, 1/x collapse into one node computing 1/(1 + e−x), whose derivative is known in closed form:

∂σ/∂x = (1 − σ(x)) · σ(x)

Evaluated at the same point: (1 − σ(1)) · σ(1) = 0.20. That is exactly the value that four chained multiplications produced in the previous section, obtained in a single step.

The rest of the backward pass is unchanged. The collapsed graph gives, in three moves:

StepResult
sigmoid node∂σ/∂x = 0.20
the two add nodes (local gradients all 1)∂out/∂ρ1 = 1 · 0.20 = 0.20, then ∂out/∂w₀ = 0.20
the two multiply nodes∂out/∂in1 = 0.40, ∂out/∂w1 = −0.20, ∂out/∂in2 = −0.60, ∂out/∂w2 = −0.40
Editor’s note

This is exactly what a framework does when it registers a “fused” operator: it trades a longer graph for a hand-derived closed-form derivative. The numerical answer is identical; only the number of nodes traversed changes.

5. The neuron as a black box

Once the internal mechanics are understood, the neuron can be treated as a black box with a clean contract:

The two formulas that define the contract, both obtained by one application of the chain rule through net:

∂out/∂wi  = (∂out/∂net) · (∂net/∂wi)  = (∂f/∂net) · ini

∂out/∂ini = (∂out/∂net) · (∂net/∂ini) = (∂f/∂net) · wi
For the exam

These two lines are the compressed form of the whole chapter, and they explain the division of labour in a network. The derivative with respect to a weight is scaled by the incoming activation ini — that is what updates the parameters. The derivative with respect to an input is scaled by the weight wi — that is what gets handed to the previous layer so the recursion can continue.

6. A full network, one training step

The slides now assemble a network: 2 input neurons (I1, I2), 2 hidden neurons (H1, H2) and 1 output neuron (O1), with the identity function as activation. Inputs x1 = 2, x2 = 3, desired output y = 1, learning rate η = 0.05.

WeightValueWeightValue
wI1,H10.11wI2,H10.21
wI1,H20.12wI2,H20.08
wH1,O10.14wH2,O10.15

Forward pass

The input is transformed layer by layer into an output and a loss, while all intermediate activations are computed and stored — storing them is not an implementation detail, it is what makes the backward pass possible.

outH1 = 2 · 0.11 + 3 · 0.21 = 0.85
outH2 = 2 · 0.12 + 3 · 0.08 = 0.48
outO1 = 0.85 · 0.14 + 0.48 · 0.15 = 0.191

The most used loss function for the regression task is the Square Error:

SE = (y − ŷ)² = (1 − 0.191)² = 0.654

To reduce the error, the predicted output needs to be increased.

Backward pass: the output neuron

We want to know how much each specific weight affects the error, so we need ∂E/∂w, computed with the chain rule. For an output neuron Oj:

∂E/∂wHi,Oj = (∂E/∂outOj) · (∂outOj/∂wHi,Oj)

with the two factors:

∂E/∂wH1,O1 = −1.62 · 1 · 0.85 = −1.38
  ⇒ w′H1,O1 = 0.14 − 0.05 · (−1.38) = 0.14 + 0.07 = 0.21

∂E/∂wH2,O1 = −1.62 · 1 · 0.48 = −0.78
  ⇒ w′H2,O1 = 0.15 − 0.05 · (−0.78) = 0.15 + 0.04 = 0.19

Backward pass: the hidden neurons

For a hidden neuron Hj the same decomposition applies, but the first factor now has to be gathered from every output neuron the hidden neuron feeds:

∂E/∂wIi,Hj = (∂E/∂outHj) · (∂outHj/∂wIi,Hj)

∂E/∂outHj = ∑k=1nO ∂EOk/∂outHj

and each term of that sum is itself a chain:

∂EOk/∂outHj = ∂EOk/∂inHj,Ok = (∂EOk/∂outOk) · (∂outOk/∂inHj,Ok)
                        = (−2(yk − outOk)) · (1 · wHj,Ok)

The second factor is again simple because the activation is the identity and inIi,Hj = outIi = xi:

∂outHj/∂wIi,Hj = (∂fH/∂netHj) · inIi,Hj = 1 · xi

Putting the four numbers together:

∂E/∂wI1,H1 = −1.62 · 0.14 · 1 · 2 = −0.45  ⇒ w′ = 0.11 + 0.02 = 0.13
∂E/∂wI2,H1 = −1.62 · 0.14 · 1 · 3 = −0.68  ⇒ w′ = 0.21 + 0.03 = 0.24
∂E/∂wI1,H2 = −1.62 · 0.15 · 1 · 2 = −0.49  ⇒ w′ = 0.12 + 0.02 = 0.14
∂E/∂wI2,H2 = −1.62 · 0.15 · 1 · 3 = −0.73  ⇒ w′ = 0.08 + 0.04 = 0.12

Did it work?

Run the forward pass again with the updated weights:

outH1 = 2 · 0.13 + 3 · 0.24 = 0.98
outH2 = 2 · 0.14 + 3 · 0.12 = 0.64
outO1 = 0.98 · 0.21 + 0.64 · 0.19 = 0.327

SE = (1 − 0.327)² = 0.453     (< 0.654)

One step of backpropagation moved the prediction from 0.191 to 0.327 and the error from 0.654 to 0.453. Do it yourself below, repeatedly, and watch the loss curve fall.

7. Matrix based backpropagation

To take advantage of fast matrix libraries and powerful GPU-based computing systems, backpropagation is implemented in matrix form. This is the version every framework actually runs.

Given an ANN with L layers, let Wl (for l ∈ {2, …, L}) be the weight matrix of layer l, where the element wij contains the weight from the j-th neuron of layer l−1 to the i-th neuron of layer l; and let x be the input vector.

Forward pass, from layer 2 up to layer L

sl = Wl al−1

al = x          if l = 1
al = fl(sl)   otherwise

where sl is the vector of weighted sums (the pre-activations) of all neurons in layer l and al−1 is the vector of activations of the previous layer.

Backward pass, from layer L down to layer 2

The gradient of the error with respect to the pre-activation vector of layer l is the quantity everything else is built from:

δl = ∂E/∂sl = (∂E/∂al) ⊙ (∂al/∂sl)          if l = L
δl = (Wl+1T δl+1) ⊙ (∂al/∂sl)              otherwise

Then the gradient with respect to the weights between layer l−1 and l, and the update:

∂E/∂Wl = δl al−1T

ΔWl = −η · ∂E/∂Wl
W′l = Wl + ΔWl
Key idea

Read the recursion for δl structurally. Wl+1Tδl+1 is the “derivative with respect to an input, scaled by the weight” rule from Section 5, written for a whole layer at once. The Hadamard product with ∂al/∂sl is the activation function derivative applied element by element. Those two operations, repeated, are backpropagation — and they are also, as Section 9 shows, exactly where the gradient goes to die.

8. The matrix example, worked

The slides re-run the same network in matrix form, which is a good way to check that the two formulations agree.

Forward pass, l = 2

aI = x = [2, 3]T

WH = | 0.11  0.21 |
     | 0.12  0.08 |

sH = WH aI = [0.85, 0.48]T
aH = fH(sH) = [0.85, 0.48]T

Forward pass, l = 3

WO = [ 0.14  0.15 ]

sO = WO aH = 0.191
aO = fO(sO) = 0.191

Backward pass, l = 3

∂E/∂aO = −2 · (y₁ − outO1) = −1.62
∂aO/∂sO = f′O(sO) = 1

δO = (−1.62) ⊙ 1 = −1.62

∂E/∂WO = δO aHT = −1.62 · [0.85  0.48] = [−1.38  −0.78]
ΔWO = −0.05 · [−1.38  −0.78] = [0.07  0.04]
W′O = [0.14  0.15] + [0.07  0.04] = [0.21  0.19]

Backward pass, l = 2

∂aH/∂sH = f′H(sH) = [1, 1]T

δH = (WOT δO) ⊙ [1, 1]T
   = [0.14, 0.15]T · (−1.62) ⊙ [1, 1]T = [−0.23, −0.24]T

∂E/∂WH = δH aIT = | −0.46  −0.69 |
                          | −0.48  −0.72 |

ΔWH = −0.05 · ∂E/∂WH = | 0.02  0.03 |
                            | 0.02  0.04 |

W′H = | 0.11  0.21 | + | 0.02  0.03 | = | 0.13  0.24 |
     | 0.12  0.08 |   | 0.02  0.04 |   | 0.14  0.12 |

The updated weights are identical to those obtained scalar by scalar in Section 6: 0.13, 0.24, 0.14, 0.12 for the hidden layer and 0.21, 0.19 for the output layer. Same algorithm, one written for a person and one written for a GPU.

For the exam

Being able to run this example end to end — forward to 0.191, error 0.654, backward with ∂E/∂out = −1.62, updates with η = 0.05, forward again to 0.327, error 0.453 — is the single most reliable way to demonstrate you understand backpropagation. Know it in both forms and know that they agree.

9. Vanishing and exploding gradients

When training an ANN with backpropagation, the partial derivatives are computed backward through the network using the chain rule, by applying consecutive matrix multiplications. If the derivatives are small or large, then the gradient will decrease or increase exponentially through the model.

For shallow networks with only a few layers this is not a big problem. In deep neural networks it causes the gradient to be:

More generally, the gradient in deep neural networks is unstable, tending to either vanish or explode in earlier layers. Because this instability is a fundamental problem for deep neural networks, it is very important to detect and solve it.

  • The model improves very slowly, or the training stops very early.
  • The model weights do not significantly change, especially those closer to the input layer.
  • The model is not learning much, or it has large changes in loss on each update.
  • The model weights become very large or even NaN, especially those closer to the input layer.

Various solutions have been proposed in the literature:

  • the simplest solution to reduce the vanishing gradient problem is to use ReLU as activation function, because it only saturates in one direction;
  • a careful weight initialization tends to be a partial solution, but it does not solve the problem completely;
  • the exploding gradient problem can be controlled by applying a gradient clipping method;
  • batch normalization can reduce the vanishing gradient problem (Chapter 7);
  • apply ad-hoc solutions, for example residual neural networks (Chapter 7) or truncated backpropagation (Chapter 8).

Watch the exponential behaviour directly: set a per-layer derivative and a depth, and see what survives the trip back to the first layer.

10. Gradient clipping

Gradient clipping limits the gradient to ensure optimization performs more reasonably near sharp areas of the loss surface. A common solution is to clip the gradient g by projecting it back to a ball of a given radius θ:

g′ = min(1, θ / ‖g‖) · g

Read the two cases:

ConditionFactorEffect
‖g‖ ≤ θmin(1, ≥1) = 1The gradient passes through untouched.
‖g‖ > θθ / ‖g‖ < 1The gradient is rescaled so that its norm is exactly θ.

Two guarantees follow, and both matter: the gradient norm will never exceed θ, and the updated gradient g′ remains entirely aligned with the original direction of g. Clipping changes the step length, never the step direction.

11. Chapter summary

Check your understanding

State the weight update rule and name every symbol.

w′ = w − η · ∂E/∂w: the new weight equals the old weight minus the learning rate η times the partial derivative of the loss function with respect to that weight. The minus sign is what turns the uphill gradient into a downhill step on the error surface.

What are the two things each operator does during the backward pass?

It learns the sensibility ∂out/∂ρi of the final output with respect to its own output variation, using the chain rule; and it computes the local gradient ∂ρi/∂x of its output with respect to its inputs. The product of the two is what it passes further back.

Why is the add operator described as a gradient router?

Because its local gradient is a constant 1 with respect to every input, so the gradients on all its inputs are equal to the gradient on its output. It copies the incoming gradient unchanged to each branch. The multiply operator, by contrast, scales: its local gradient with respect to one input is the value of the other input.

Run the backward pass on the example neuron and give ∂out/∂w₁.

With w₀ = −3, in1 = −1, w1 = 2, in2 = −2, w2 = −3 the forward pass gives net = 1 and out = 0.73. Going back: −1/1.37² = −0.53, then ×1 = −0.53, then ×e−1 = −0.20, then ×(−1) = 0.20. The two add nodes pass 0.20 through unchanged, and the multiply node gives ∂out/∂w1 = 0.20 · in1 = 0.20 · (−1) = −0.20.

What is gained by collapsing the sigmoid into a single operator?

Efficiency, at no cost in accuracy. Instead of chaining four local gradients (−x, ex, 1+x, 1/x), one closed-form derivative ∂σ/∂x = (1 − σ(x)) · σ(x) is evaluated in a single step. At net = 1 it gives 0.20, exactly the value the four chained multiplications produced.

Give the black-box formulas for the gradients of a neuron and explain the difference between them.

∂out/∂wi = (∂f/∂net) · ini and ∂out/∂ini = (∂f/∂net) · wi. The first is scaled by the incoming activation and is used to update that weight; the second is scaled by the weight and is what is handed back to the previous layer so the recursion can continue.

In the 2-2-1 example, why is ∂E/∂outO1 equal to −1.62?

Because the loss is the square error E = (y − out)², whose derivative with respect to the output is −2 · (y − out). With y = 1 and outO1 = 0.191 this is −2 · 0.809 = −1.62. Its negative sign says the predicted output must be increased to reduce the error.

Why does the hidden-layer gradient involve a sum, while the output-layer gradient does not?

Because a hidden neuron influences the error through every output neuron it feeds. Its contribution must therefore be gathered across all of them: ∂E/∂outHj = ∑k ∂EOk/∂outHj, with each term equal to (−2(yk − outOk)) · (∂fO/∂netOk) · wHj,Ok. An output neuron has no such fan-out.

Write the matrix form of backpropagation.

Forward: sl = Wl al−1 and al = fl(sl) (with a1 = x). Backward: δL = (∂E/∂aL) ⊙ (∂aL/∂sL) and, for the other layers, δl = (Wl+1T δl+1) ⊙ (∂al/∂sl). Then ∂E/∂Wl = δl al−1T, ΔWl = −η ∂E/∂Wl and W′l = Wl + ΔWl.

Why do gradients vanish or explode, and where does it hurt most?

Because the backward pass applies consecutive matrix multiplications through the chain rule. If the derivatives involved are consistently small or large, the gradient decreases or increases exponentially with depth. The instability is worst in the earlier layers, the ones closest to the input, since their gradient has passed through the most multiplications.

List the proposed remedies for gradient instability.

ReLU as activation (the simplest fix for vanishing, since it saturates in only one direction); careful weight initialization (a partial solution only); gradient clipping for exploding gradients; batch normalization, which reduces vanishing; and ad-hoc architectures such as residual networks or truncated backpropagation.

What exactly does gradient clipping preserve, and what does it change?

g′ = min(1, θ/‖g‖) · g. It changes the magnitude: the gradient norm will never exceed θ. It preserves the direction: g′ remains entirely aligned with the original direction of g, because the clipping factor is a non-negative scalar. Gradients already inside the ball are left completely untouched.