Lesson 5 of 7 · 20 min
Backpropagation & Gradient Descent in Depth
The actual training machinery: forward passes, loss functions, the slope of the loss, the update rule with worked iterations, blame assignment by the chain rule, and vanishing gradients.
Recap: the forward pass
Before a network can learn, it must make a guess. That guess-making trip is the forward pass: numbers enter on the left and flow right.
A tiny 2-2-1 network. Inputs x₁ = 1.0, x₂ = 0.5.
Hidden neuron 1: 0.5 × 1.0 + 0.8 × 0.5 + 0.1 = 0.5 + 0.4 + 0.1 = 1.0. ReLU keeps it: h₁ = 1.0.
Hidden neuron 2: −0.4 × 1.0 + 0.2 × 0.5 + 0.2 = −0.4 + 0.1 + 0.2 = −0.1. ReLU flattens it: h₂ = 0.
Output: 0.7 × 1.0 + (−0.5) × 0 + 0.0 = 0.7. Sigmoid turns that into 1 / (1 + e^(−0.7)) = 0.668.
So the network guesses about 0.67.
ŷ = sigmoid(v₁h₁ + v₂h₂ + b₀)
Hidden layers pass through the activation (here ReLU); the output passes through sigmoid so it behaves like a probability.
Loss functions: scoring the guess
A loss function turns the gap between guess and target into a number. The classic is mean squared error (MSE).
Worked MSE with 2 data points:
Point 1: guess 0.8, target 1.0 → (0.8 − 1.0)² = 0.04.
Point 2: guess 0.4, target 0.5 → (0.4 − 0.5)² = 0.01.
MSE = (0.04 + 0.01) / 2 = 0.025. Smaller is better; zero would be perfect.
MSE = (1/N) Σ (guessᵢ − targetᵢ)²
Square each error (so signs don't cancel), then average across all N examples.
The derivative idea: which way is downhill?
To improve a weight, we need its slope: if I nudge this knob up, does the loss go up or down, and how fast?
No calculus required — we can just measure it. Take the loss L(w) = (w − 1)² (the guess is w, the target is 1).
At w = 0.5: L = (0.5 − 1)² = 0.25.
At w = 0.51: L = (0.51 − 1)² = (−0.49)² = 0.2401.
Slope ≈ (0.2401 − 0.25) / (0.51 − 0.5) = −0.0099 / 0.01 = −0.99.
The slope is about −1.0. Negative means nudging w up makes the loss go down — so we should nudge up.
The exact calculus answer is 2(w − 1) = 2(0.5 − 1) = −1.0, matching our finite-difference estimate.
Gradient descent: take a step
The update rule says: move each weight a little against the slope.
w_new = w_old − α × slope, where α is the learning rate.
Let us run it on L(w) = (w − 1)², whose slope is 2(w − 1). Start at w = 3.0, use α = 0.2.
Iteration 1: slope 2(3.0 − 1) = 4.0; w = 3.0 − 0.2 × 4.0 = 2.20; loss (1.20)² = 1.44.
Iteration 2: slope 2(2.20 − 1) = 2.40; w = 2.20 − 0.48 = 1.72; loss (0.72)² = 0.5184.
Iteration 3: slope 2(1.72 − 1) = 1.44; w = 1.72 − 0.288 = 1.432; loss (0.432)² ≈ 0.187.
Iteration 4: slope 2(1.432 − 1) = 0.864; w = 1.432 − 0.1728 = 1.259; loss (0.259)² ≈ 0.067.
The steps shrink as the slope flattens: 3.0 → 2.2 → 1.72 → 1.43 → 1.26 → …, marching toward the minimum at w = 1.
w_new = w_old − α × (slope of the loss)
Subtract the slope so you always step downhill. α controls how big each step is.
Try it: drop the ball
Move the start slider and the learning-rate slider. At α = 0.5 the ball reaches the bottom in a single step; past α = 1.0 it bounces between mirror positions and then flies off the curve.
Gradient Descent Playground
Drop a ball on a loss curve and watch it roll downhill. Crank the learning rate and it overshoots the bottom; crank it further and it flies away.
The blue curve is the loss for every possible weight w: loss = w². The red ball takes gradient-descent steps. Start position and learning rate are yours to tune. The minimum sits at w = 0.
Control knobs
Backpropagation: the chain rule, organized
A deep network has many weights, each with its own slope. Backpropagation computes all of them efficiently with one backward sweep.
Blame assignment analogy. When a project fails, you trace the mistake back up the chain to see who contributed. Backprop does the same: start with the output error, then ask every weight 'how much did you push the output?'
The tool is the chain rule: to find how a change travels through a chain, multiply the small effects along the way.
∂Loss/∂w = (∂Loss/∂ŷ) × (∂ŷ/∂z) × (∂z/∂w).
Backprop by hand: continuing the network
Target y = 1.0, guess ŷ = 0.668, and loss ½(ŷ − y)².
Output signal: ∂Loss/∂ŷ = ŷ − y = 0.668 − 1.0 = −0.332.
Through the sigmoid: ∂ŷ/∂z = ŷ(1 − ŷ) = 0.668 × 0.332 = 0.222. Multiply: δ_out = −0.332 × 0.222 = −0.0736.
Output weight `v₁`: the output was z = v₁h₁ + v₂h₂ + b₀ and h₁ = 1.0, so ∂z/∂v₁ = 1.0. Thus ∂Loss/∂v₁ = −0.0736. With α = 0.5: v₁ ← 0.7 − 0.5 × (−0.0736) = 0.7368.
Hidden weight `w₁`: blame keeps flowing back. ∂z/∂h₁ = v₁ = 0.7; ReLU's slope is 1 because h₁ > 0; and ∂z₁/∂w₁ = x₁ = 1.0.
So ∂Loss/∂w₁ = −0.0736 × 0.7 × 1 × 1.0 = −0.0515, and w₁ ← 0.5 − 0.5 × (−0.0515) = 0.5258.
One backward sweep gave every weight its own update — that is the whole trick.
Why deep networks were hard: vanishing gradients
Backprop multiplies slopes layer by layer. If each slope is smaller than 1, the product shrinks fast.
Sigmoid's slope is at most 0.25 (at x = 0).
Five sigmoid layers: 0.25⁵ ≈ 0.001.
Ten sigmoid layers: 0.25¹⁰ ≈ 0.000001.
By the time the blame reaches the early layers it is almost zero, so those weights barely move — the vanishing-gradient problem. That is why ReLU (slope 1 for positives) and shortcuts like skip connections made deep networks practical.
Slopes above 1, on the other hand, can compound into exploding gradients — which is one reason huge learning rates blow a deep network up.
Check yourself
Backpropagation & Gradient Descent
What does backpropagation compute?