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
| Symbol | Meaning |
|---|---|
w | The old weight. |
w′ | The new weight. |
η | The learning rate. |
∂E/∂w | The 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.
E(w). The gradient points uphill, so the update w′ = w − η·∂E/∂w steps in the opposite direction. Backpropagation exists to compute that gradient efficiently for every weight at once.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:
| # | Operator | Computation | Value |
|---|---|---|---|
| 1 | × | in1 · w1 = (−1) · 2 | −2 |
| 2 | × | in2 · w2 = (−2) · (−3) | 6 |
| 3 | + | −2 + 6 | 4 |
| 4 | + (bias) | 4 + (−3) | 1 (this is net) |
| 5 | −x | −1 | −1 |
| 6 | ex | e−1 | 0.37 |
| 7 | 1 + x | 1 + 0.37 | 1.37 |
| 8 | 1 / x | 1 / 1.37 | out = 0.73 |
During the backward pass, from the outputs to the inputs, each operator i does two things:
∂out/∂ρi of the neuron final output with respect to its own output variation, using the chain rule;∂ρ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:
| Node | Local gradient | Chain rule | Sensibility passed down |
|---|---|---|---|
1/x | ∂ρ1/∂x = −1/x² = −1/1.37² = −0.53 | 1 · (−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 = 1 | 0.20 · 1 | ∂out/∂w₀ = 0.20 and ∂out/∂ρ6 = 0.20 |
+ | ∂ρ6/∂x1 = 1, ∂ρ6/∂x2 = 1 | 0.20 · 1 | ∂out/∂ρ7 = 0.20 and ∂out/∂ρ8 = 0.20 |
× (top) | ∂ρ7/∂in1 = w1 = 2, ∂ρ7/∂w1 = in1 = −1 | 0.20 · 2 and 0.20 · (−1) | ∂out/∂in1 = 0.40, ∂out/∂w1 = −0.20 |
× (bottom) | ∂ρ8/∂in2 = w2 = −3, ∂ρ8/∂w2 = in2 = −2 | 0.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.
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:
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:
| Step | Result |
|---|---|
| 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 |
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.
Once the internal mechanics are understood, the neuron can be treated as a black box with a clean contract:
out;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
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.
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.
| Weight | Value | Weight | Value |
|---|---|---|---|
wI1,H1 | 0.11 | wI2,H1 | 0.21 |
wI1,H2 | 0.12 | wI2,H2 | 0.08 |
wH1,O1 | 0.14 | wH2,O1 | 0.15 |
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.
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/∂outOj = −2 · (y − outO1) = −2 · (1 − 0.191) = −1.62∂outOj/∂wHi,Oj = (∂fO/∂netOj) · inHi,Oj = 1 · outHi, because fO is the identity function∂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
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
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.
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.
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.
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
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.
The slides re-run the same network in matrix form, which is a good way to check that the two formulations agree.
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
WO = [ 0.14 0.15 ]
sO = WO aH = 0.191
aO = fO(sO) = 0.191
∂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]
∂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.
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.
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.
Various solutions have been proposed in the literature:
Watch the exponential behaviour directly: set a per-layer derivative and a depth, and see what survives the trip back to the first layer.
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:
| Condition | Factor | Effect |
|---|---|---|
‖g‖ ≤ θ | min(1, ≥1) = 1 | The gradient passes through untouched. |
‖g‖ > θ | θ / ‖g‖ < 1 | The 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.
θ. A gradient already inside the ball is untouched; one outside is pulled back onto the boundary along its own direction, so only the length changes.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.
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.
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.
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.
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.
∂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.
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.
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.
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.
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.
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.
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.