Unit 4 · Neural Networks · Course Outcomes CO2, CO3
Four points that break everything so far.
Unit 2 fitted six model families to eight students and every one reached 100% accuracy. Here is a dataset with four points and two features that no linear model can fit at all — not with more data, not with a better optimiser, not ever. Fixing that requires one new idea: put a layer in the middle and let the network invent its own features.
Section 4.1
XOR, in full
Two binary inputs, output 1 when they differ.
A (0,0)→0, B (0,1)→1, C (1,0)→1, D (1,1)→0. That is the entire dataset. There is no noise, no missing value, no ambiguity, and no shortage of data — these four rows are the function.
Rose is target 0, teal is target 1, exactly as in Unit 2. Notice that the two teal points sit on opposite corners.
Section 4.1
And no straight line separates them
Not a hard one. An impossible one.
Drag the line anywhere. Every orientation leaves at least one point misclassified, and section 4.1 proves it in four lines of algebra rather than by exhaustion.
This kills the perceptron, logistic regression, and the linear SVM together. It is the reason the field spent the 1970s in the cold, and the reason the next section exists.
Sections 4.1 and 4.4
Two hidden units fold the space
What if the features were different?
The network computes h₁ = ReLU(x₁+x₂) and h₂ = ReLU(x₁+x₂−1), then y = h₁ − 2h₂. Plotted in (h₁, h₂) space, B and C land on the same point and the problem becomes linearly separable.
The hidden layer did not classify anything. It changed the coordinates so that a line would work. That is what every deep network does.
Sections 4.4 to 4.7
Trained from scratch
Can gradient descent find it?
Sometimes. With two hidden units and random initialisation, training succeeds from 16.5% of starting points. With eight units it is 96%.
That is Unit 3's k-means problem again in a new setting: a non-convex objective, local minima you cannot detect from inside, and the same two fixes — more capacity, or more restarts.
The colour contract returns to Unit 2's, with one addition
Labels are back, so rose and teal mean target 0 and target 1 again, as they did in Units 2A and 2B. Amber still marks what the model learns — here the weights and biases.
Violet is new and it means the backward pass. Anywhere you see violet in this unit, information is flowing from the loss back toward the weights. Keeping the two directions in different colours matters more here than anywhere else in the course, because the single most common confusion in backpropagation is losing track of which way a quantity is travelling.
What you need before this chapter
From Unit 1: the chain rule (1.4), gradient descent and its learning rate (1.4), the shape of a loss surface and what a local minimum is (1.2), cross-entropy (1.5), and the activation functions introduced in 1.8. From Unit 2A: logistic regression (2.3.1) — a neural network with no hidden layer is logistic regression, and the identity ∂L/∂z = ŷ − y derived there is reused here without change. From Unit 2B: why a validation split exists (2.4.6). From Unit 3: the softmax that appeared in the Gaussian mixture E-step (3.1.4), and the experience of a non-convex objective with local minima (3.1.1).
If you can differentiate a composition of three functions and you remember why ŷ − y is the gradient of cross-entropy with respect to the logit, you have everything this unit needs.
The spine: XOR, and one exact network that solves it
This unit's running example is not a student dataset. It is four rows, and the reason is that XOR is the smallest, sharpest demonstration of the one idea the unit exists to teach. Every section returns to it: the perceptron fails on it, a hidden layer solves it, backpropagation is worked through it by hand, and every optimiser and regulariser is demonstrated on it.
| Point | x₁ | x₂ | target y | as a sentence |
|---|---|---|---|---|
| A | 0 | 0 | 0 | neither input on |
| B | 0 | 1 | 1 | exactly one on |
| C | 1 | 0 | 1 | exactly one on |
| D | 1 | 1 | 0 | both on |
And here is the network that solves it exactly, with whole-number weights. It is worth checking all four rows now, by hand, because every later section refers back to it.
Read what the two hidden units are doing, because it is the whole idea in miniature. Unit 1 computes "at least one input is on" — it rises as soon as the sum exceeds 0. Unit 2 computes "both inputs are on" — it stays at zero until the sum exceeds 1. The output then says "at least one, but not both", which is XOR stated in English.
Neither hidden unit was told to compute those things. They are features the network invents, and the fact that they are recognisable here is an accident of how small the problem is. In a real network the hidden units compute combinations nobody named, which is exactly why Unit 2B's interpretability material becomes harder from this point on.
The one thing to be clear about before starting
Stacking linear layers without activations is pointless. If h = W₁x + b₁ and y = W₂h + b₂, then substituting gives y = W₂W₁x + (W₂b₁ + b₂), which is W'x + b' — a single linear layer with different numbers. A hundred layers collapse to one.
So the non-linearity is not a refinement or a performance trick. It is the only thing that makes depth mean anything, and it is why section 4.3 is a section rather than a footnote.
The Perceptron, Forward Propagation and Losses
One neuron is a weighted vote with a threshold. It has a learning rule with a convergence guarantee, and a limitation so severe it stalled the field for fifteen years.
The question
Unit 2 fitted models by writing down a functional form and optimising its parameters. Suppose instead you want to build the model out of small identical pieces, each doing something simple, and let the arrangement do the work. What is the simplest useful piece?
The intuition
A committee decision. Each input is a committee member with a vote, some members count more than others, and the chair has a standing bias toward yes or no. Add the weighted votes, compare against the bias, and announce a verdict.
That is a neuron. The weights say how much each input matters and in which direction, the bias says how much evidence is needed before the answer flips, and the activation function turns a number into a decision.
The formal treatment
The geometry is worth stating once and remembering. wᵀx + b = 0 is a hyperplane — a line in two dimensions, a plane in three. w is perpendicular to it and points toward the positive side; b slides it away from the origin. A single neuron is therefore a linear classifier, the same object as logistic regression in 2.3.1 and the linear SVM in 2.3.3, differing only in how the weights are chosen.
The rule is easy to read once you see what the three cases do. If the prediction is correct, e = 0 and nothing moves. If the neuron said 0 when it should have said 1, then e = +1 and the weights move toward x, which raises wᵀx next time. If it said 1 when it should have said 0, the weights move away. It is error-driven and it only ever touches the examples it gets wrong.
The perceptron convergence theorem (Novikoff, 1962) says that if the data is linearly separable, this rule finds a separating hyperplane in a finite number of updates, bounded by (R/γ)² where R is the largest input norm and γ is the margin of the best separator. It is a genuine guarantee and it is worth noticing what it does not promise: nothing about which separator you get, nothing about the margin of the result (that is the SVM's concern in 2.3.3), and nothing at all if the data is not separable, in which case the rule never terminates.
The worked example
Worked 4.1a — the perceptron learns AND
every update shownη = 1 from w = (0,0), b = 0 on the AND function: A(0,0)→0, B(0,1)→0, C(1,0)→0, D(1,1)→1.The step function here fires when z > 0 strictly, so z = 0 predicts 0. Different textbooks put the boundary at z ≥ 0 and get a different trace from the same data. State which you are using; an exam answer that silently switches convention halfway will not reconcile.
| epoch | point | z | ŷ | y | e | w after | b after |
|---|---|---|---|---|---|---|---|
| 1 | A (0,0) | 0 | 0 | 0 | 0 | (0, 0) | 0 |
| 1 | B (0,1) | 0 | 0 | 0 | 0 | (0, 0) | 0 |
| 1 | C (1,0) | 0 | 0 | 0 | 0 | (0, 0) | 0 |
| 1 | D (1,1) | 0 | 0 | 1 | +1 | (1, 1) | +1 |
| 2 | A (0,0) | +1 | 1 | 0 | −1 | (1, 1) | 0 |
| 2 | B (0,1) | +1 | 1 | 0 | −1 | (1, 0) | −1 |
| 2 | C (1,0) | 0 | 0 | 0 | 0 | (1, 0) | −1 |
| 2 | D (1,1) | 0 | 0 | 1 | +1 | (2, 1) | 0 |
| 3 | A ok, B and C fire wrongly, D missed — 3 errors | (2, 1) | −1 | ||||
| 4 | C fires wrongly, D missed — 2 errors | (2, 2) | −1 | ||||
| 5 | B fires wrongly — 1 error | (2, 1) | −2 | ||||
| 6 | no errors — converged | (2, 1) | −2 | ||||
The boundary passes exactly through C, which is legal but fragile — a hair's movement and C flips.
That fragility is precisely what the SVM's margin objective in 2.3.3 was invented to fix.
Notice the error count over the six epochs: 1, 3, 3, 2, 1, 0 — ten updates in total. It is not monotone, and it does not have to be: the rule guarantees termination, not steady improvement. A student who expects the error count to fall every epoch will think something is broken at epoch 2.
Worked 4.1b — the same rule on XOR
the proof, then the failureSuppose weights w₁, w₂ and bias b exist with w₁x₁ + w₂x₂ + b > 0 exactly for B and C.
No weights exist. Not "hard to find", not "needs more data" — the set of functions a perceptron can represent does not contain XOR. This is the argument Minsky and Papert made in 1969, and the reason neural network research largely stopped until backpropagation was popularised in 1986.
The algorithm has no way to detect the impossibility. It runs, and the weights cycle.
Novikoff's theorem is silent here because its one hypothesis — linear separability — is false.
In practice you cap the epochs and return the best weights seen, which is what the "pocket algorithm" does, but the returned answer is still wrong on at least one point.
Two habits follow. A non-terminating perceptron is evidence of non-separability, which is useful diagnostic information rather than a bug. And always cap the iterations, because a training loop with no bound will hang on data you have not checked.
The fix: put a layer in the middle
XOR is not linearly separable in (x₁, x₂). But nothing forces you to classify in the coordinates you were given. Compute two new features first, and classify in those.
Look at B and C: both map to (1, 0). The hidden layer has collapsed the two positive examples onto a single point in its own coordinate system, and once they coincide, a line trivially separates them from A at (0,0) and D at (2,1). The problem did not become easier; the representation changed until the problem became easy.
That sentence is the whole of deep learning in one line, and everything from here to the end of Unit 5 is an elaboration of it. The hidden layer is not a classifier. It is a learned change of coordinates.
Notation and the general forward pass
The notation conflict you will meet
Some books write z = Wx + b with examples as columns; others write z = xW + b with examples as rows. Both are correct and they differ by a transpose everywhere. This file uses weights times column-vector for single examples and rows-as-examples for batches, which is what most library code does. Whichever you use, check the shapes: if W⁽ˡ⁾ is n⁽ˡ⁾ × n⁽ˡ⁻¹⁾ then the multiplication only parses one way, and shape-checking is the fastest way to catch an error in an exam.
Loss functions, and why the choice is not cosmetic
| Task | Output layer | Loss | Gradient at the logit |
|---|---|---|---|
| Regression | 1 linear unit | MSE ½(ŷ − y)² | ŷ − y |
| Binary classification | 1 sigmoid unit | Binary cross-entropy | ŷ − y |
| Multi-class, one label | K softmax units | Categorical cross-entropy | pⱼ − yⱼ |
| Multi-label | K sigmoid units | Sum of binary cross-entropies | ŷⱼ − yⱼ |
That last column is the reason these pairings are standard. Each matched output-and-loss pair gives the same clean gradient, prediction minus target, with the activation's derivative cancelling exactly. It is the identity derived for logistic regression in 2.3.1, and section 4.4 uses it without re-deriving it.
Worked 4.1c — why not just use squared error for classification?
a factor of fiftyŷ when the target is y = 1. Compare MSE and cross-entropy, and their gradients with respect to the logit z.| ŷ | MSE | cross-entropy | dL/dz, MSE | dL/dz, CE | ratio |
|---|---|---|---|---|---|
| 0.99 | 0.00010 | 0.01005 | −0.00020 | −0.01000 | 50.5 |
| 0.90 | 0.01000 | 0.10536 | −0.01800 | −0.10000 | 5.6 |
| 0.50 | 0.25000 | 0.69315 | −0.25000 | −0.50000 | 2.0 |
| 0.10 | 0.81000 | 2.30259 | −0.16200 | −0.90000 | 5.6 |
| 0.01 | 0.98010 | 4.60517 | −0.01960 | −0.99000 | 50.5 |
MSE's gradient is smallest exactly where the model is most wrong, because
ŷ(1−ŷ) collapses at both ends.Cross-entropy's gradient is proportional to the error itself.
So a network trained with MSE on a sigmoid output learns most slowly from the examples it most needs to learn from, and can sit almost motionless on a confidently wrong prediction. That is the whole argument, and it is why "use cross-entropy for classification" is a rule rather than a preference.
Note the symmetry in the ratio column: the factor is also 50 at ŷ = 0.99, where the model is confidently right. There, a small gradient is harmless and arguably desirable. It is the other end that does the damage.
The visualization
The perceptron learning rule, one presentation at a time
interactive — AND converges, XOR does notThe amber line is the current boundary and the shaded side is where the neuron fires. On AND and OR it settles within a few epochs and stops moving. Switch to XOR and run twenty epochs: the line never stops, and it never gets all four points right, because the impossibility proof above says no position exists that would.
The pitfalls
Where marks are lost
- Not stating the step-function convention. Whether
z = 0fires changes the whole trace. Worked 4.1a's answer has C sitting exactly atz = 0, so the convention decides whether it is correct. - Forgetting to update the bias. The bias is a weight on a constant input of 1 and gets the same rule,
b ← b + ηe. Omitting it makes the boundary pass through the origin and AND becomes unlearnable. - Expecting the error count to fall monotonically. It went 1, 3, 3, 2, 1, 0 in Worked 4.1a. Termination is guaranteed; steady progress is not.
- Claiming the perceptron "cannot learn non-linear problems". Too vague to be right. It cannot represent functions that are not linearly separable. It handles a non-linear boundary fine if you hand it non-linear features — which is exactly what the hidden layer does automatically.
- Stacking linear layers and expecting more power. They collapse to one layer. Without an activation function, depth buys nothing.
- Using MSE with a sigmoid output. Worked 4.1c. It trains slowest where it is most wrong.
- Applying softmax to a multi-label problem. Softmax outputs sum to 1, so it enforces "exactly one class". For images that may contain both a cat and a dog, use independent sigmoids.
Practice
P4.1.1 (direct) — Run the perceptron rule with η = 1 from w = (0,0), b = 0 on OR: A(0,0)→0, B(0,1)→1, C(1,0)→1, D(1,1)→1. How many updates?
OR converges faster than AND (4 updates against 10) because its positive region is larger, so a randomly placed boundary is more likely to be nearly right. Verify the answer: x₁ + x₂ > 0 fires for B, C and D and not for A. ✓
P4.1.2 (variation) — Show that NAND is linearly separable by giving explicit weights, then explain why "XOR is impossible but NAND is easy" is not a contradiction.
NAND is A→1, B→1, C→1, D→0 — the negation of AND. Negate AND's solution from Worked 4.1a: w = (−2, −1), b = +2.
C fails on the strict convention, so shift the bias slightly: w = (−2, −1), b = +2.5 gives +0.5 → 1 for C and −0.5 → 0 for D. ✓ The general recipe is that negating a separable function is always separable — flip w and b and adjust for the boundary convention.
Why no contradiction. Separability is a property of a specific labelling of specific points, not of "logic functions" as a class. Of the sixteen Boolean functions of two variables, fourteen are linearly separable; only XOR and XNOR are not. What makes XOR special is that its two positive examples sit on opposite corners of the square, so any line separating them from the other pair would have to bend.
The historically important footnote: NAND is functionally complete, so a network of NAND gates can compute anything, including XOR. Representational power comes from composition, which is the argument for depth and precisely what the perceptron lacked.
P4.1.3 (interpretation) — A colleague reports that their single-layer network reaches 100% training accuracy on a dataset with 200 features and 150 examples, and concludes the problem is easy. Assess.
With 200 features and 150 examples, a linear model can almost always achieve 100% training accuracy on any labelling, so the result carries essentially no information. Points in general position in d dimensions are linearly separable whenever n ≤ d + 1; here n = 150 and d = 200, so separability is close to guaranteed regardless of whether the labels mean anything. Shuffle the labels at random and the same 100% would appear.
This is 1.6's bias-variance material and 2.1's protocol arriving together. Training accuracy measures capacity, not fit, and here the capacity exceeds the data by a wide margin.
What would settle it. The label-permutation test, which is the decisive one: retrain on shuffled labels, and if training accuracy is still 100%, the original result was capacity. A proper validation split or cross-validation, per 2.1. Regularization — ridge or a strong weight penalty — which will collapse a spurious fit and leave a genuine one standing. And more data, since n < d is the root problem.
P4.1.4 (synthesis) — Using 2.3.1 and 2.3.3, state precisely how the perceptron relates to logistic regression and the linear SVM. All three produce a hyperplane; what differs?
The hypothesis space is identical. All three output sign(wᵀx + b) and can represent exactly the linearly separable functions, so all three fail on XOR for the same reason. What differs is entirely the objective.
The perceptron minimises nothing. It has an error-driven update rule with a convergence proof, not a loss function it descends. Consequently it stops at the first separator it finds, and which one that is depends on the data order and the initialisation. On non-separable data it does not terminate.
Logistic regression minimises cross-entropy, a smooth convex loss, so it has a unique optimum (with regularization), it converges on non-separable data, and it produces calibrated probabilities rather than bare decisions. That last property is what made all of 2.4.3 to 2.4.6 possible.
The SVM minimises hinge loss plus a norm penalty, which selects the maximum-margin separator specifically. Worked 4.1a's answer put the boundary exactly through point C, at zero margin; the SVM exists to rule that out. It also gains the kernel trick, which is a different route to non-linearity — a fixed, hand-chosen feature map instead of a learned one.
The relationship to this unit. A network with no hidden layer, one sigmoid output and cross-entropy loss is logistic regression, trained by gradient descent rather than by a dedicated solver. So this unit does not replace 2.3.1; it contains it as the depth-zero case, and everything new comes from what the hidden layers add.
Convolution and Pooling Layers
A dense layer on an image asks what every pixel means individually. A convolutional layer asks what small local patterns look like, and asks it the same way everywhere.
The question
Flatten a 28×28 image into 784 numbers and feed it to a dense layer of 64 units. That costs 50,240 parameters, and it throws away two things you knew for free: that pixel 100 is next to pixel 101, and that a vertical edge in the top-left corner is the same kind of thing as a vertical edge in the bottom-right. The dense layer has to learn an edge detector separately for every position, from scratch, using different weights each time.
The intuition
You are looking for a small pattern — say a vertical edge — in a large photograph. You do not build a separate detector for every location. You build one detector and slide it over the whole image, recording how strongly it responds at each position.
That is convolution, and it buys two properties at once. Parameter sharing: one set of nine weights serves every position, so the cost does not grow with image size. Translation equivariance: move the object in the image and its response moves with it, unchanged, so a pattern learned in one place is recognised everywhere.
The formal treatment
Three consequences fall straight out of those two formulas and they are the ones exams test.
Padding controls whether the image shrinks. With P = 0 ("valid") a k×k kernel removes k−1 from each dimension. With P = (k−1)/2 ("same") and stride 1, the output matches the input — so k = 3 needs P = 1, k = 5 needs P = 2, and even kernel sizes cannot do it symmetrically at all, which is why kernels are almost always odd.
Stride controls downsampling. S = 2 roughly halves each dimension and quarters the number of positions.
The parameter count does not mention I. The same layer costs the same whether the image is 28×28 or 2800×2800. Only the computation scales with image size, not the number of weights, and that single fact is what makes vision networks feasible.
Pooling
Pooling does two jobs. It shrinks the spatial dimensions, which cuts computation and enlarges the receptive field of later layers — the region of the original image that influences one output. And it adds a little translation invariance: shift a feature by one pixel and a max-pooled output often does not change at all.
Be precise about the distinction, because it is a favourite exam question. Convolution is equivariant: shift the input, the output shifts identically. Pooling is what introduces invariance: shift the input a little, the output does not change. A network needs both — equivariance to track where things are, invariance so the final answer does not depend on exact position.
Depth — why 3×3 kernels won, and what a receptive field is
Stack two 3×3 convolutions and an output unit depends on a 5×5 patch of the input; stack three and it is 7×7. In general L stacked 3×3 layers at stride 1 give a receptive field of 2L + 1. So depth buys spatial reach without larger kernels.
And it is cheaper. Two 3×3 layers on C channels in and out cost 2(9C + 1)C parameters; one 5×5 layer with the same 5×5 receptive field costs (25C + 1)C. At C = 64 that is 73,856 against 102,464 — a 28% saving. The stacked version also gets two non-linearities instead of one, so it can represent more. That combination, cheaper and more expressive, is why the VGG architecture used nothing but 3×3 kernels and why almost everything since has followed.
The receptive field is worth computing when a network underperforms. If the object you need to recognise spans 60 pixels and your network's receptive field is 15, no amount of training will fix it — the units that make the decision have never seen the whole object.
The worked example
Worked 4.2 — convolving a 6×6 image by hand
every window integer-valuedBoth kernels sum to zero, which means they respond not to brightness but to change in brightness. A patch of constant value, light or dark, gives exactly 0.
Note that only the first and third columns of the window contribute — the middle column is multiplied by zero. The kernel is literally computing "right side minus left side".
| Kᵛ | Kₕ | |||||||
|---|---|---|---|---|---|---|---|---|
| +1 | 0 | 0 | −1 | +1 | −1 | −1 | +1 | |
| −1 | −2 | +2 | +1 | 0 | −2 | −2 | 0 | |
| −1 | −2 | +2 | +1 | 0 | +2 | +2 | 0 | |
| +1 | 0 | 0 | −1 | −1 | +1 | +1 | −1 |
The structure is exactly what the kernels promise. The vertical map is antisymmetric left to right: strong negative on the ring's left edge where brightness increases rightward, strong positive on the right edge. The horizontal map is antisymmetric top to bottom. Each filter has found its own kind of edge and ignored the other.
ReLU zeroes every negative response, which discards the edges of opposite polarity. Then each 2×2 block of the 4×4 map collapses to its maximum, giving 2×2.
The vertical summary varies left-to-right; the horizontal one varies top-to-bottom.
Eight numbers now encode "there are edges of both orientations, arranged in a ring" — down from 36 pixels.
Count the parameters that produced this: two filters at (3×3×1 + 1) = 10 each, so 20 weights total, and pooling added none. A dense layer mapping the 36 pixels to 8 outputs would have cost (36+1)×8 = 296, and it would have had to learn each position's edge detector independently.
Worked 4.2b — a full parameter count
the standard exam question| Layer | Output shape | Parameter arithmetic | Params |
|---|---|---|---|
| input | 28 × 28 × 1 | — | 0 |
| conv 3×3, 8 filters, same | 28 × 28 × 8 | (3·3·1 + 1) × 8 | 80 |
| maxpool 2×2, stride 2 | 14 × 14 × 8 | no parameters | 0 |
| conv 3×3, 16 filters, same | 14 × 14 × 16 | (3·3·8 + 1) × 16 | 1,168 |
| maxpool 2×2, stride 2 | 7 × 7 × 16 | no parameters | 0 |
| flatten | 784 | 7 · 7 · 16 = 784 | 0 |
| dense 64 | 64 | (784 + 1) × 64 | 50,240 |
| dense 10 | 10 | (64 + 1) × 10 | 650 |
| total | 52,138 |
The two convolutional layers that do all the visual work cost 1,248 between them.
A dense-only network 784 → 64 → 10 costs 50,890, which is almost the same total.
That comparison is more interesting than the usual claim that CNNs have fewer parameters. Here they have slightly more. The difference is where the parameters are and what they can do: the convolutional ones are shared across 784 spatial positions and detect position-independent patterns, while the dense-only network spends everything on position-specific weights that generalise to nothing.
It also shows where to look when a network is too large. The flatten-to-dense transition is almost always the culprit, and replacing it with global average pooling — collapsing each of the 16 channels to its mean, giving a 16-vector — would cut that layer from 50,240 to (16+1)×64 = 1,088 and the whole network to 2,986. That single substitution is why modern architectures rarely flatten.
The visualization
The kernel sliding over the image
interactive — step through every windowThe amber box is the current window and the arithmetic for that one position is printed beside it. Press Next window to walk the kernel across the image and watch the feature map fill in one cell at a time. The same ten numbers produce all sixteen responses — that is parameter sharing, and it is the entire reason this layer scales.
The pitfalls
Where marks are lost
- Forgetting the input channels in the parameter count. A 3×3 filter on an 8-channel input has
3×3×8 = 72weights, not 9. A filter always spans the full depth. - Forgetting the bias. One bias per filter, not per position. That is the
+1in(K²Cᵢₙ + 1)Cᶜᵘᵗ. - Putting the image size into the parameter count. It does not appear. Image size affects computation and activation memory, never the weight count.
- Dropping the floor in the output-size formula. With
I = 7, K = 2, S = 2,(7−2)/2 + 1 = 3.5, and the answer is 3. A non-integer means the last window does not fit and is discarded. - Thinking pooling has parameters. Max and average pooling have none. Only the shape changes.
- Confusing equivariance with invariance. Convolution is equivariant, pooling contributes invariance. They are opposite properties and the words are not interchangeable.
- Assuming convolution makes a network see rotations or scale. It gives translation equivariance only. Rotating an image produces a completely different response, which is why rotation is a standard data augmentation rather than something the architecture handles.
Practice
P4.2.1 (direct) — Give the output size for each: (a) 32×32, 5×5 kernel, no padding, stride 1. (b) the same with padding 2. (c) 64×64, 3×3, padding 1, stride 2. (d) 224×224, 11×11, no padding, stride 4.
Case (d) is AlexNet's first layer and shows the floor doing real work: 213/4 = 53.25, so the final window overhangs the edge and is dropped. Case (c) is the standard "halve the resolution" block — padding 1 with a 3×3 kernel keeps the arithmetic clean so that stride 2 divides exactly.
P4.2.2 (variation) — Convolve the same 6×6 ring with the 3×3 blur kernel of all 1/9, and give the top-left 2×2 of the output. What does the result tell you about what this kernel detects?
The blur kernel averages the nine values in each window, so each output is the count of 1s in the window divided by 9.
Every output is positive and they vary only slightly — between 0.5556 and 0.6667 across these four. The blur kernel's weights sum to 1 rather than 0, so it responds to average brightness rather than to change, and on a roughly uniform image it produces a roughly uniform map.
That is the general rule worth extracting: a kernel whose weights sum to zero is an edge or change detector; a kernel whose weights sum to one is a smoother. Trained networks discover both kinds without being told, and the first convolutional layer of a trained vision model is usually recognisably full of edge detectors at various orientations plus a few colour blobs.
P4.2.3 (interpretation) — A CNN classifies 200×200 medical images at 94% training accuracy and 71% validation accuracy. Its architecture is conv(3×3, 32) → pool → flatten → dense(512) → dense(2). Diagnose and propose a fix.
Count the parameters first, because the diagnosis is in the arithmetic. After one conv and one pool the feature map is 100×100×32, which flattens to 320,000. The dense layer then costs (320,000 + 1) × 512 ≈ 163.8 million parameters, against 320 in the convolutional layer. Over 99.9% of the network is a single dense layer, and medical image datasets are rarely larger than a few thousand images.
So this is 1.6's variance problem in its purest form: a 23-point gap between training and validation, produced by a model with more parameters than it could possibly constrain. It is barely a convolutional network at all — it is a dense network with one convolution glued to the front.
The fixes, in order of impact. Replace flatten with global average pooling, which turns the 100×100×32 map into a 32-vector and cuts that layer from 163.8 million to about 16,900. Add more conv-and-pool stages before flattening, so the spatial dimensions come down properly — four more pooling stages would reduce 100×100 to about 6×6. Then add the standard regularisers of section 4.6 and data augmentation, which for medical images usually means flips and small rotations. And check for leakage: if multiple slices from the same patient appear in both splits, the 71% is optimistic too, and grouped splitting is required.
P4.2.4 (synthesis) — Using 4.1 and 1.7, explain why a convolutional layer can be seen as a dense layer with two constraints, and relate those constraints to regularization.
A convolutional layer is a dense layer with most weights forced to zero and the rest tied together. Write the convolution as a matrix mapping all input pixels to all output positions. The matrix is enormous, but it has two special properties: each output row is non-zero only at the k² pixels inside its window (sparse connectivity), and every row contains the same k² values in shifted positions (weight tying). A dense layer is the same matrix with those constraints removed.
Both constraints are regularization, in exactly 1.7's sense: they restrict the hypothesis space to reduce variance at the cost of some bias. The difference from ridge or lasso is that these are hard constraints derived from a fact about the data — that images have local structure and that patterns mean the same thing wherever they appear — rather than soft penalties tuned by cross-validation. A hard constraint that is true is the most efficient regulariser available, because it costs no bias at all.
And when the assumption is false, the constraint is pure bias. On tabular data where column 5 and column 6 have no relationship, convolution's locality assumption is simply wrong and a dense layer will do better. That is the same trade as everywhere in this course: 3.1.1's Voronoi cells, 2.3.4's axis-aligned splits, and a convolution's locality are all restrictions on shape, useful exactly when they match the data and harmful when they do not.
The connection to 4.2's parameter count now reads differently. The convolutional layers were cheap not because convolution is a clever trick, but because a true assumption let the network stop paying for flexibility it did not need.
Activation Functions and Their Trade-offs
The only reason depth means anything. Four candidates, one of which quietly broke deep learning for twenty years and one of which fixed it.
The question
Section 4.1 established that without a non-linearity, stacked layers collapse into one. So some function must sit between the layers. Which one, and does the choice matter beyond taste?
It matters enormously, and the reason is not about forward computation at all. It is about what the function's derivative does to a gradient travelling backwards through many layers.
The intuition
Backpropagation, which section 4.4 derives in full, multiplies one factor per layer as the gradient travels back toward the input. Each factor includes the activation's derivative at that layer.
If those derivatives are typically less than 1, the product shrinks geometrically and the early layers receive almost nothing — they stop learning. If they are typically greater than 1, the product explodes and training diverges. An activation function is a choice about what happens to a number multiplied by itself ten times.
The sigmoid's derivative maxes out at 0.25. Ten layers of it, in the best possible case, multiply the gradient by 0.25¹⁰ ≈ 10⁻⁶. That is the vanishing gradient problem, and it is why deep networks did not work until the activation changed.
The formal treatment
| Name | Definition | Derivative | Range | Verdict |
|---|---|---|---|---|
| Sigmoid | σ(z) = 1/(1 + e⁻ᵣ) | σ(1 − σ), max 0.25 | (0, 1) | Output layer for binary classification. Never for hidden layers. |
| Tanh | (eᵣ − e⁻ᵣ)/(eᵣ + e⁻ᵣ) | 1 − tanh², max 1.0 | (−1, 1) | Zero-centred, four times better than sigmoid, still saturates. Used in RNNs. |
| ReLU | max(0, z) | 1 if z > 0, else 0 | [0, ∞) | The default. Cheap, no saturation on the positive side, but units can die. |
| Leaky ReLU | z if z > 0, else αz | 1 or α (typically 0.01) | (−∞, ∞) | ReLU with the dying problem removed, at no real cost. |
| Softmax | eᵣʲ/Σᶜ eᵣᶜ | pᵢ(δᵢⱼ − pⱼ) | (0,1), sums to 1 | Output layer only, for one-of-K classification. |
Saturation, quantified
Saturation means the output has flattened, so the derivative is near zero and the unit stops responding to changes in its input. For the sigmoid this happens quickly.
| |z| | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| σ'(z) | 0.2500 | 0.1966 | 0.1050 | 0.0452 | 0.0177 | 0.0066 | 0.0025 |
| % of max | 100% | 79% | 42% | 18% | 7% | 2.7% | 1.0% |
A pre-activation of 6 is not extreme — it is what you get from a dozen inputs with modest weights — and at that point 99% of the gradient is gone. This is also the mechanism behind 2B's calibration material: a saturated sigmoid outputs 0.9975, a confident number the model has no evidence for.
Tanh survives the best case because its derivative reaches exactly 1 at the origin, but only there; away from zero it also shrinks, so tanh delays the problem rather than solving it. ReLU solves it outright: its derivative is exactly 1 on the entire positive half-line, so the product of many such factors is exactly 1 no matter how deep the network.
Depth — the dying ReLU, and why it is a real cost rather than a footnote
ReLU's derivative is 0 for z < 0. Suppose a unit's weights drift so that z < 0 for every training example. Then its output is always 0, its local gradient is always 0, and by the chain rule ∂L/∂w = ∂L/∂a × 0 × x = 0 for every example. The weights receive exactly zero gradient forever. The unit is dead and no amount of further training revives it.
This is not hypothetical, and it already happened in this file. Worked 4.4's single gradient step, coming up in the next section, moves the second hidden unit's pre-activation from +0.5 to −0.5 on the training point — one step, one dead unit, in a network with only two of them.
The usual causes are a learning rate that is too high, so a single large update pushes a unit off the cliff, and a large negative bias. The usual fixes are leaky ReLU, which replaces the zero slope with α = 0.01 so the gradient is small but never exactly zero, and a lower learning rate. The cost of leaky ReLU is essentially nothing, which is why it is a reasonable default when you see dead units in a diagnostic.
Worth knowing that the modern alternatives — GELU and SiLU/swish — are smooth functions that are slightly negative for small negative inputs and approach ReLU for large positive ones. At z = −1, ReLU gives 0, GELU gives −0.159 and SiLU gives −0.269. They are standard in transformers and are worth recognising by name, though the difference from ReLU is a matter of a percent or two of accuracy rather than a change in kind.
Softmax, and one detail that matters in practice
The shift-invariance also explains why softmax outputs are not identified by the logits: adding 5 to every logit changes nothing, so the network has one redundant degree of freedom per softmax layer. It is harmless, but it is why you cannot interpret a single logit's magnitude on its own.
Dividing the logits by a temperature T before the softmax sharpens or softens the distribution. On the same logits (2,1,0): at T = 0.5 the output is (0.867, 0.117, 0.016), at T = 1 it is (0.665, 0.245, 0.090), and at T = 5 it flattens to (0.402, 0.329, 0.269). This is exactly Platt scaling from 2.4.6 under another name — fitting a single T on held-out data is the standard fix for an overconfident network, and it is worth noticing that it cannot change the ranking and therefore cannot change the AUC.
The worked example
Worked 4.3 — the same network, three activations
forward pass on point Dz₁ = (2, 1) for point D (1,1) under the target weights. Compute the hidden activations and the gradient factor each activation contributes, for ReLU, tanh and sigmoid.ReLU passes the gradient through untouched: both factors are exactly 1. It also preserves the magnitude of the pre-activation, so the output layer can still tell that D's sum was 2 rather than 1 — which is precisely what the exact solution y = h₁ − 2h₂ relies on.
Tanh has already squashed 2 to 0.964 and 1 to 0.762, so the difference between them has shrunk from 1.0 to 0.20. Its backward factors are 0.071 and 0.420, so a gradient arriving at this layer is cut to between a quarter and a fourteenth.
Sigmoid squashes to 0.881 and 0.731, a difference of 0.15, and multiplies the backward gradient by around 0.10 to 0.20.
Multiply that across ten layers and the sigmoid network's first layer receives about a millionth of the signal.
Notice this is a two-layer network, where saturation is survivable — the original XOR solutions from the 1980s used sigmoids and worked. The argument for ReLU is not that sigmoid fails here; it is that sigmoid fails as soon as you go deep, and the whole of Unit 5 is about going deep.
The visualization
Each activation and its derivative, with the depth penalty
interactive — function, slope, and the product over layersThe function and its derivative
Gradient surviving to layer 1
The right-hand panel plots the factor raised to the depth, on a logarithmic axis. Choose the sigmoid and slide z away from 0 and watch the bar fall off the bottom of the chart within a few layers. Choose ReLU with z > 0 and it stays flat at 1 for any depth — then push z below zero and it drops to exactly nothing, which is the dying-unit failure in the same picture.
The pitfalls
Where marks are lost
- Using sigmoid in hidden layers. The single most consequential wrong choice in the unit. Sigmoid belongs on a binary output and nowhere else.
- Putting an activation on the output of a regression network. ReLU on the output makes negative predictions impossible; sigmoid caps them at 1. Regression outputs are linear.
- Applying softmax and then a separate cross-entropy that re-exponentiates. Libraries provide a fused "from logits" version for numerical stability. Applying softmax twice is a common and silent bug.
- Saying ReLU is non-differentiable so gradient descent fails. It is non-differentiable at exactly one point,
z = 0, which has probability zero of occurring. Implementations define the derivative there as 0 or 1 by convention and nothing goes wrong. - Believing ReLU cannot vanish. It cannot saturate on the positive side, but a dead unit passes exactly zero gradient, which is worse than a small one because it is permanent.
- Forgetting that tanh is zero-centred and sigmoid is not. Sigmoid outputs are all positive, so all the gradients into a downstream weight vector share a sign, which produces zig-zag updates. It is a real if secondary reason to prefer tanh over sigmoid where a bounded activation is wanted.
- Choosing the activation to fix a problem caused by initialisation. Section 4.7 shows that scale is often the real culprit. Diagnose before substituting.
Practice
P4.3.1 (direct) — Compute σ(z), σ'(z), tanh(z), tanh'(z), ReLU(z) and ReLU'(z) at z = −2 and z = 2.
The derivatives of sigmoid and tanh are symmetric about zero, so saturation is equally bad in both directions. ReLU's are not symmetric at all, which is exactly the asymmetry that makes it both immune to positive-side saturation and vulnerable to dying.
P4.3.2 (variation) — A 15-layer network uses sigmoid activations and its units typically sit near |z| = 2. Estimate the gradient reaching layer 1 relative to the output layer, and say what you would change.
In 32-bit floating point, numbers near 10⁻¹⁴ are still representable, so this does not underflow to zero — it is worse than that in practice, because the gradient is nonzero but utterly swamped by the later layers' updates. The first layer effectively never moves, so the network behaves like a 2- or 3-layer model with a random fixed front end.
What to change, in order. Replace the activations with ReLU or leaky ReLU, which removes the factor entirely on the positive side — this alone fixes it. Add residual connections, Unit 5's mechanism, which give the gradient an additive path that skips the activation. Add batch normalisation (4.6), which keeps pre-activations near zero where σ' is largest, though even the best case of 0.25¹⁴ = 3.7 × 10⁻⁹ is still fatal, so this helps without solving it. And check initialisation (4.7), since |z| = 2 at every layer suggests weights that are too large.
The historical point: this calculation is why deep networks did not work before about 2010. Nothing about the architecture was wrong; the activation function was.
P4.3.3 (interpretation) — After training a ReLU network, you find that 40% of the hidden units output exactly 0 for every example in the validation set. Is this a problem?
Distinguish two things that look identical in that statistic. A ReLU network is supposed to have sparse activations — on any given input, a large fraction of units being zero is normal and is part of why ReLU works. The question is whether the same units are zero for every input.
The diagnostic. Compute, for each unit, the fraction of examples on which it is non-zero. If most units fire on 40–70% of inputs and few are always zero, the network is healthy. If 40% of units have a firing rate of exactly 0.000, those units are dead: they contribute nothing to the forward pass and receive no gradient, so the network's effective width is 60% of what you are paying for.
If they are genuinely dead, the cause is almost always the learning rate. A single oversized update pushes a unit's pre-activation negative everywhere and there is no way back. Check whether the death happened early in training, which the diagnostic will show if you log it per epoch. Reduce the learning rate, add a warmup, or switch to leaky ReLU, which caps the damage because the gradient never reaches exactly zero. Also check the biases: large negative bias initialisation causes this directly.
And a note on severity. Dead units waste capacity but do not corrupt what remains, so a network with 40% dead units still works — it just works like a smaller network that you trained expensively. The reason to fix it is efficiency, not correctness.
P4.3.4 (synthesis) — Using 4.1's loss table and 2.4.6, explain why sigmoid appears on output layers despite being banned from hidden layers, and why its saturation is a problem in one place and not the other.
On the output layer, saturation is cancelled by the loss. Section 4.1 showed that pairing a sigmoid output with cross-entropy gives ∂L/∂z = ŷ − y — the sigmoid's derivative divides out exactly. So a saturated output unit that is confidently wrong still produces a gradient of magnitude near 1. The pathology that makes sigmoid unusable in hidden layers does not arise, because nothing multiplies by σ' there.
In a hidden layer nothing cancels it. The gradient arriving from above is multiplied by σ' and passed down, layer after layer, and the product is what P4.3.2 computed. There is no algebraic partner to remove the factor.
Which is also why MSE with a sigmoid output is wrong. That pairing reintroduces the factor at the output — ∂L/∂z = 2(ŷ−y)ŷ(1−ŷ) — and Worked 4.1c measured the cost as a factor of 50 at ŷ = 0.01. So the rule is not "sigmoid is bad" but "sigmoid is fine wherever its derivative cancels, and nowhere else".
The link to 2.4.6. The sigmoid is on the output because it produces a number in (0,1) that can be read as a probability, which is what made calibration, ROC curves and cost-derived thresholds possible in Unit 2B. But saturation means a hidden layer's large pre-activation becomes an output of 0.9975, and section 2.4.6's warning applies with force: networks are systematically overconfident, and temperature scaling — dividing the logit by a fitted T — is the standard repair. It is Platt scaling, it is fitted on held-out data, and because it is monotone it leaves the AUC untouched.
Backpropagation and Parameter Counting
The chain rule applied to a composition of functions, organised so that nothing is computed twice. One worked example with exact arithmetic, every intermediate value shown.
The question
The network has nine parameters and a loss that depends on all of them. Gradient descent needs ∂L/∂w for each. How do you get nine derivatives — or nine million — without doing nine separate calculations?
The naive approach is finite differences: nudge each weight, re-run the network, see how the loss changed. For P parameters that is P + 1 forward passes. At a million parameters and a millisecond per pass, one gradient takes seventeen minutes. Backpropagation gets the same answer in one forward pass and one backward pass, regardless of P.
The intuition
Blame assignment. The loss is too high; whose fault is it?
The output unit can compute its own share directly — it knows the prediction and the target. It then tells each hidden unit how much its output contributed to the error, weighted by the connection strength between them. Each hidden unit takes that message, adjusts it by how responsive it was at its own operating point (the activation derivative), and passes blame further back.
The key economy: the message a layer sends backwards is computed once and reused by every weight in that layer. That shared quantity is called δ, and organising the computation around it is the entire difference between backpropagation and the chain rule applied naively.
The formal treatment
Read the recurrence carefully, because it contains the whole story of Unit 5. Going back one layer does two things: multiply by Wᵀ, which is the forward weight matrix transposed, and multiply elementwise by the activation derivative. Repeat that L times and you have a product of L weight matrices and L activation derivatives. If those factors are typically below 1 the gradient vanishes; above 1 it explodes. Section 4.3's arithmetic and section 4.7's initialisation rules are both about controlling this product.
Note also what the gradient formula says: ∂L/∂W = δ aᵀ is an outer product of the backward signal with the forward activation. A weight's gradient is large only when both its input was active and its output was blamed. A weight whose input was zero gets zero gradient regardless of the error — which is exactly the dead-ReLU mechanism of 4.3, seen from the other side.
Depth — why this is one pass and not P passes
Backpropagation is reverse-mode automatic differentiation. For a function f: ℝⁿ → ℝᵐ, forward-mode costs one pass per input and reverse-mode costs one pass per output. A loss function has millions of inputs and exactly one output, so reverse mode wins by a factor of millions. Had we wanted the derivative of every output with respect to one input, forward mode would win.
The price is memory. Every a⁽ˡ⁾ computed on the way forward must be kept until the backward pass reaches it, because ∂L/∂W⁽ˡ⁾ needs a⁽ˡ⁻¹⁾. That is why training a network needs several times the memory of running one, why batch size is limited by memory rather than compute, and why gradient checkpointing — discarding activations and recomputing them during the backward pass — is a standard trade of time for space.
Backpropagation is not an approximation and not a learning algorithm. It computes the exact gradient, and it is agnostic about what you do with it; section 4.5's optimisers all consume the same numbers.
The worked example
Worked 4.4 — one full forward and backward pass, exact arithmetic
the central calculation of the unity = 1. Compute every activation, every gradient, and take one step with η = 1.The starting weights were chosen so that z₂ lands exactly on 0. A loss of ln 2 is what any binary classifier scores when it has no opinion, and it is the number to expect from a freshly initialised network — if your first-epoch loss is far from 0.693 on a balanced binary problem, something is wrong before training even starts.
The second column of ∂L/∂W₁ is entirely zero, because x₂ = 0. Those weights receive no gradient from this example at all — not because they are unimportant, but because this particular input never used them. It is the outer-product structure made visible.
Both facts came from the same update.
η = 1 is a large step for this network.This is section 4.3's dying-ReLU warning happening in real time, in a network with two hidden units, after one update. If unit 2's pre-activation is negative for all four training points — and here it would be, since its weights are now (0.5, 0.5) with bias −1.0, giving −1.0, −0.5, −0.5, 0.0 across A, B, C, D — then it is permanently dead and the network has become a one-hidden-unit model, which cannot represent XOR.
The fix is not subtle: use a smaller η. With η = 0.1 the same step gives b₁ = (0.05, −0.55) and unit 2 survives. Section 4.5 is about making this choice less fraught, and section 4.7 about choosing it in the first place.
Gradient checking — the thing to do before trusting any hand-written backward pass
Check ∂L/∂b₂ from the worked example. Perturbing b₂ by ±10⁻⁵ changes z₂ by the same amount, so the loss becomes −ln σ(±10⁻⁵), and the central difference is [0.693142 − 0.693152]/(2 × 10⁻⁵) = −0.5, matching the analytic δ₂ exactly. Do this once on a small network with random weights and you will never again wonder whether a backward pass is right.
Two cautions. Gradient checking is far too slow for training — it is a debugging tool, run once on a tiny model. And it fails spuriously at kinks: ReLU at exactly z = 0 has no derivative, so check at points away from zero or use a smooth activation while checking.
Parameter counting
Count parameters before training anything. If a model has more parameters than training examples, the variance discussion of 1.6 applies immediately and the regularisers of 4.6 are not optional. The spine has 9 parameters and 4 examples, which is why it can fit XOR exactly and why it would be hopeless as a general lesson about generalisation.
The visualization
Forward in amber, backward in violet
interactive — step through the whole calculationThe network is drawn as nodes and edges. Amber values are the forward pass, violet values are the backward pass, and each press of Next stage advances one step of the calculation in the order Worked 4.4 does it. Press Apply update with η = 1 on point C and watch the second hidden unit go dead; drop η to 0.1 first and it survives.
The pitfalls
Where marks are lost
- Forgetting to transpose
Win the backward pass. Forward usesW, backward usesWᵀ. Shape-checking catches this instantly. - Applying the activation derivative at the wrong place. It is evaluated at
z⁽ˡ⁾, the pre-activation of the layer you are computingδfor — not ata⁽ˡ⁾and not at the layer above. - Using
ŷ − ywith a mismatched loss. That identity holds for sigmoid-with-BCE and softmax-with-CE. With MSE on a sigmoid output you must keep theŷ(1−ŷ)factor. - Updating weights during the backward pass. Compute all gradients first, then update. Changing
W₂before using it to computeδ₁gives the wrong answer, and it is a bug that produces plausible-looking training curves. - Forgetting to average over the batch. With
mexamples the gradient is the mean, not the sum, or the effective learning rate scales with batch size. - Missing the bias in a parameter count. The
+1in(nᵢₙ + 1)nᶜᵘᵗ. Easy marks, easily dropped. - Believing backpropagation is the learning algorithm. It computes a gradient. Gradient descent, momentum and Adam are what learn, and section 4.5 shows they behave very differently on the same gradients.
Practice
P4.4.1 (direct) — Repeat Worked 4.4's forward and backward pass for point B (0, 1), target 1, using the same starting weights.
Two things differ from Worked 4.4. The loss starts lower, 0.3133 against 0.6931, because these weights already lean the right way for B. And unit 2 sits exactly at z = 0, where ReLU is not differentiable — the convention ReLU'(0) = 0 is used here, so unit 2 receives no gradient from this example. Using ReLU'(0) = 1 instead would give δ₁ = (−0.2689, +0.2689). Both are defensible; state which you used.
Notice also that the zero column has moved: for point C it was column 2, for point B it is column 1, and in each case the zero sits where the corresponding input was 0.
P4.4.2 (variation) — Count the parameters in a network with input 100, three hidden layers of 50 with ReLU, and a 10-way softmax output. Then say how the count changes if a batch-normalisation layer is added after each hidden layer.
Batch norm adds 2.8% to the parameter count, which is why its cost is never the reason to leave it out. Note also that a BN layer immediately after a dense layer makes that dense layer's bias redundant — BN subtracts the mean, which cancels any constant — so libraries typically set use_bias=False there, saving 150 parameters here.
P4.4.3 (interpretation) — During training the loss is nan from epoch 3 onward. The network is 8 layers, ReLU, learning rate 0.5, no normalisation. Diagnose in order.
nan means an arithmetic operation produced an undefined value, and in a training loop there are only a few ways that happens. The overwhelmingly likely cause here is exploding gradients: the backward recurrence multiplies eight weight matrices, and if their scale is above 1 the product grows geometrically, the update overflows to inf, and inf − inf on the next step gives nan. A learning rate of 0.5 with eight layers and no normalisation is asking for this.
Diagnose in this order, because each step is cheap and rules out a class of cause. First log the gradient norm every step; if it climbs across epochs 1 and 2, the diagnosis is confirmed before epoch 3. Second, check the loss function for log(0) — a cross-entropy implemented as −ln(ŷ) without clipping produces inf the moment a saturated sigmoid returns exactly 0 in floating point, and this is a genuinely common bug independent of the gradient issue. Third, check the data for nan or inf in the inputs, which propagates instantly and is embarrassing to discover late.
The fixes, in order of what to try. Drop the learning rate by a factor of ten — this alone resolves most cases and costs one run. Add gradient clipping, capping the global gradient norm at something like 1.0, which is the standard defence and is one line. Add batch normalisation, which keeps pre-activations in a controlled range. Check initialisation against 4.7's He scaling, since eight layers with weights that are slightly too large is exactly the setup for this. And use the fused "from logits" loss, which avoids the log(0) path entirely.
P4.4.4 (synthesis) — Using 1.4, 2.3.1 and 4.3, explain what backpropagation adds to what you already knew, and what it does not solve.
What was already there. Section 1.4 gave gradient descent and the chain rule. Section 2.3.1 derived ∂L/∂w = (ŷ − y)x for logistic regression, which is exactly this unit's output-layer equation with no hidden layers. So for a network of depth zero, backpropagation and 2.3.1 are the same three lines of algebra.
What backpropagation adds is one idea: reuse. The chain rule alone would let you compute ∂L/∂w for each weight separately, each time re-traversing the network from the loss down to that weight. Backpropagation notices that all weights in a layer share the same upstream factor δ⁽ˡ⁾, computes it once, and reuses it. That converts O(P) passes into O(1) and is the entire reason training deep networks is feasible. It is dynamic programming applied to the chain rule.
What it does not solve, and this is the more useful half of the answer. It gives you the exact gradient at the current point and nothing more. It does not make the loss surface convex — the XOR network succeeds from only 16.5% of random starts with two hidden units, so local minima and saddle points remain, exactly as in 3.1.1's k-means. It does not choose a step size, which is section 4.5's problem and which Worked 4.4 showed can kill a unit in one step. It does not prevent the gradient from vanishing or exploding as it passes through many layers, which is 4.3's problem and 4.7's. And it says nothing about generalisation: a perfect gradient on the training loss will drive you to a solution that may not transfer at all, which is 1.6's material and section 4.6's.
So the honest summary is that backpropagation solved the computational problem completely and left every optimisation and statistical problem untouched. The remaining five sections of this unit are about those.
Optimisers: SGD, Momentum and Adam
Backpropagation hands you a gradient. What you do with it is a separate decision, and on the same gradients these three algorithms behave very differently — including one case where the fanciest is the slowest.
The question
Worked 4.4 took one step with η = 1 and killed a hidden unit. With η = 0.01 it would have taken thousands of steps to get anywhere. Is there something better than picking a number and hoping?
And a second, sharper problem. A loss surface rarely curves equally in all directions. If it is a long narrow valley, the step size that is safe across the valley is far too small along it, and no single η serves both.
The intuition
Plain gradient descent is a walker who looks at the slope underfoot and steps directly downhill. In a narrow valley this is exactly wrong: the steepest direction points at the opposite wall, so the walker zig-zags across the valley making almost no progress along it.
Momentum gives the walker inertia. Steps in a consistent direction accumulate; steps that keep reversing cancel out. The zig-zag damps itself and the slow consistent direction speeds up.
Adam gives each coordinate its own step size, scaled down where gradients have been large and up where they have been small. A direction with tiny gradients is no longer neglected.
The formal treatment
Two details are worth pulling out because exams ask about them and because they explain the behaviour.
Momentum's effective learning rate is η/(1 − β), not η. If the gradient is constant, v converges to g/(1−β), so at β = 0.9 the eventual step is ten times the SGD step. Switching on momentum without reducing η therefore makes every step roughly ten times larger, which is worth knowing before you do it.
It is often said that this makes momentum diverge where SGD would not, and on a quadratic that is false and worth correcting. The stability condition for plain gradient descent on curvature L is η < 2/L; for heavy-ball momentum it is η < 2(1+β)/L, which is larger. At L = 10 and β = 0.9, SGD diverges above 0.2 and momentum survives to 0.38 — both verified numerically for the widget below. Momentum takes bigger effective steps and tolerates a bigger η. The divergence folklore comes from stochastic and non-quadratic settings, where the accumulated velocity can carry you into a region of much higher curvature before the averaging catches up.
Bias correction exists because m and v start at zero. Without it the first few steps would be far too small, since m₁ = 0.1g is only a tenth of the gradient. Dividing by 1 − β₁ᵗ fixes it, and the effect is dramatic at t = 1: m̂₁ = 0.1g/0.1 = g and v̂₁ = 0.001g²/0.001 = g², so the update is −η g/|g|. Adam's very first step is exactly −η times the sign of the gradient, whatever the gradient's magnitude.
Depth — what Adam is actually doing, and why it is not always better
Adam divides each coordinate's step by the running root-mean-square of its own gradients. So it is scale-invariant per coordinate: multiply one weight's gradient by a thousand and its step size is unchanged. That is enormously useful when different parameters have wildly different gradient scales — embedding layers versus output layers, or sparse features that rarely fire.
But scale-invariance cuts both ways. If two directions differ in gradient magnitude because one genuinely matters more, Adam erases that information. On a well-conditioned problem it can be slower than momentum, and it is a documented finding that carefully tuned SGD with momentum still beats Adam on image classification benchmarks, generalising slightly better. Worked 4.5b below shows this happening on an example small enough to check by hand.
The practical rule most people converge on: Adam when you want something that works without tuning — transformers, RNNs, anything new, anything sparse — and SGD with momentum when you have time to tune and want the last point of accuracy. AdamW, which applies weight decay directly to the weights rather than folding it into the gradient, is the version to prefer; the original Adam's interaction with L2 regularization is genuinely broken, because dividing the penalty's gradient by √v̂ makes the effective decay depend on gradient history.
The worked example
Worked 4.5a — four steps of each, by hand
f(w) = w², w₀ = 1, η = 0.1f(w) = w² from w = 1. The gradient is g = 2w. Trace four steps of SGD, momentum and Adam.Momentum is closest in absolute value but has overshot and is now on the far side.
Adam is the slowest here, because normalising by the gradient magnitude throws away the information that the gradient is large and a big step is safe.
On a simple well-scaled bowl, the gradient's magnitude is genuinely useful and Adam discards it. That is the trade in its purest form, and it is worth seeing before meeting the case where the trade pays off.
Worked 4.5b — a narrow valley, where the ranking changes
f = ½w₁² + 5w₂²f(w) = ½w₁² + 5w₂², with gradients g = (w₁, 10w₂). Curvature is 1 in one direction and 10 in the other. Start at (1, 1).Gradient descent on a quadratic of curvature L is stable only while η < 2/L. Here L = 10, so η < 0.2. Heavy-ball momentum is stable while η < 2(1+β)/L = 0.38.
| t | SGD w₁ | SGD w₂ | Mom w₁ | Mom w₂ | Adam w₁ | Adam w₂ |
|---|---|---|---|---|---|---|
| 1 | 0.98200 | 0.82000 | 0.98200 | 0.82000 | 0.98200 | 0.98200 |
| 2 | 0.96432 | 0.67240 | 0.94812 | 0.51040 | 0.96401 | 0.96401 |
| 10 | 0.83390 | 0.13745 | 0.36505 | −0.14942 | 0.82121 | 0.82121 |
| 40 | 0.48357 | 0.00036 | 0.00692 | 0.03070 | 0.35428 | 0.35428 |
Momentum is seventy times closer than SGD on the direction that matters.
The mechanism is exactly the effective learning rate:
0.018/(1 − 0.9) = 0.18, ten times SGD's, applied to the direction where the gradient is consistent.Look at what each optimiser did with the two directions. SGD handled w₂ fine — it reached 0.0004 — and crawled on w₁, because one η has to serve a curvature of 10 and a curvature of 1. Momentum accumulated velocity along the consistent w₁ direction while the w₂ oscillations partly cancelled, and it paid for this with six sign changes in w₂ along the way. Adam moved both coordinates identically, because it normalises each by its own gradient scale and so cannot tell that one direction needs bigger steps.
That last observation is the honest headline: Adam's per-coordinate normalisation is exactly what makes it robust when scales are unknown, and exactly what makes it slower here, where the scale difference was the useful signal. "Adam is better" is not a claim this file will make.
Batch size, epochs and schedules
| Variant | Examples per update | Character |
|---|---|---|
| Batch | all n | Exact gradient, smooth descent, one update per epoch, infeasible on large data. |
| Stochastic | 1 | Very noisy, n updates per epoch, noise can help escape shallow minima, no vectorisation. |
| Mini-batch | 32 to 512 | The default everywhere. Noise enough to help, batches large enough for hardware efficiency. |
The gradient noise from a mini-batch scales as 1/√m, so quadrupling the batch halves the noise. The common heuristic when scaling up is the linear scaling rule: multiply the batch size by k and multiply the learning rate by k, with a warmup period at the start because that rule breaks in the first few hundred steps.
The reason a schedule helps is that early training needs to cross the landscape and late training needs to settle into a minimum, and those want different step sizes. A constant η either wanders forever or never arrives.
The visualization
Three optimisers on the same valley
interactive — watch the paths divergeContours of the loss, with all three paths drawn from the same start. At the default settings momentum wins clearly. Push η past 2/L — 0.2 at the default steepness — and SGD is the one that blows up first, while momentum holds on until 2(1+β)/L = 0.38. Set the valley steepness to 1 and the surface becomes a circular bowl where plain SGD is perfectly adequate and momentum's advantage disappears entirely.
The pitfalls
Where marks are lost
- Turning on momentum without reconsidering
η. The effective rate becomesη/(1−β), a factor of 10 atβ = 0.9. Note the nuance: on a quadratic this is still stable up to2(1+β)/L, so the failure is overshoot and oscillation in non-quadratic regions rather than the immediate divergence often claimed. - Omitting Adam's bias correction. Without it the first steps are roughly
(1−β₁)times too small, so early training stalls and looks like a bad learning rate. - Assuming Adam always beats SGD. Worked 4.5a and 4.5b both show otherwise. Tuned SGD with momentum still wins on many vision tasks.
- Using Adam's default
η = 0.001as though it were universal. It is a reasonable starting point, not a law, and it is 100 times smaller than a typical SGD rate because the update is normalised. - Combining Adam with L2 regularization and expecting weight decay. The penalty's gradient gets divided by
√v̂like everything else, so the decay is not what you asked for. Use AdamW. - Confusing an epoch with a step. One epoch is
n/mupdates. Reporting "trained for 100 iterations" without saying which is meaningless. - Comparing optimisers at a single shared learning rate. Each has a different optimal
η, so a single-rate comparison mostly measures which one happens to like that rate. Sweep each, then compare their best.
Practice
P4.5.1 (direct) — With f(w) = w², w₀ = 2, η = 0.25, take three SGD steps. Then repeat with η = 1.1 and describe what happens.
The stability condition is |1 − 2η| < 1, so 0 < η < 1 here, and the fastest convergence is at η = 0.5 where the factor is exactly 0. In general for curvature L the condition is η < 2/L and the optimal rate is 1/L. The alternating sign is the signature of an overlarge learning rate, and a loss that oscillates upward between epochs is the same symptom in practice.
P4.5.2 (variation) — Show that Adam's first update has magnitude exactly η for any non-zero gradient, and explain what practical property this gives.
The practical property is that η sets a trust region rather than a sensitivity. With SGD, η multiplies the gradient, so you cannot choose it without knowing the gradient's typical magnitude — and that magnitude differs by orders of magnitude between an embedding layer and an output layer, and changes during training. With Adam, η bounds how far any single parameter can move in one step, roughly regardless of its gradient.
That is why Adam's default of 0.001 transfers across architectures while SGD's learning rate must be re-tuned for each one, and it is the main reason Adam is the default for anything new. The cost is the one Worked 4.5b measured: the discarded magnitude information was sometimes worth having.
P4.5.3 (interpretation) — A training loss falls smoothly for 20 epochs, then jumps upward sharply and never recovers. Give three candidate causes and how to distinguish them.
Cause 1: an exploding gradient or a bad batch. A single outlier or a rare large gradient produced an update that threw the weights far from the good region. Distinguish by logging the per-step gradient norm — a single enormous spike at the epoch where the loss jumped confirms it. Fix with gradient clipping, which caps the norm and costs nothing.
Cause 2: the learning rate is marginally too high and it finally bit. Near a minimum the curvature rises, so a rate that was stable in a flat region becomes unstable in a sharper one and the run escapes. Distinguish by whether the loss oscillates before the jump rather than jumping cleanly, and by re-running with η halved. A decay schedule is the proper fix.
Cause 3: numerical, not optimisational. A log(0) in the loss, a division by a near-zero variance in batch norm, or overflow to inf. Distinguish by checking whether the loss is exactly inf or nan rather than merely large, and by checking whether the weights contain non-finite values. This is P4.4.3's territory.
And what to do regardless. Checkpoint every epoch, so a run that destroys itself at epoch 21 does not also destroy epochs 1 to 20 — restarting from the epoch-20 checkpoint with a lower rate usually just works. A run without checkpoints is the actual failure here.
P4.5.4 (synthesis) — Using 1.4 and 3.1.1, place these optimisers in the wider picture. What do they change about the optimisation problem, and what do they leave untouched?
What they change: the path, and therefore the speed. Section 1.4's gradient descent chooses a direction from local information only. Momentum adds a memory of previous directions, and Adam adds a memory of previous magnitudes. Both are ways of using the history of the optimisation to compensate for the fact that a single gradient says nothing about curvature. They are cheap approximations to what a second-order method would do properly, and they are used instead of second-order methods because computing a Hessian for a million parameters is not possible.
What they leave untouched: the landscape. None of them changes the loss surface, so none of them changes where the minima are. If the objective is non-convex, all three can land in a poor local minimum, and the XOR network's 16.5% success rate at two hidden units is the same for all three. This is exactly 3.1.1's k-means situation: Lloyd's algorithm converges reliably to a local optimum, and the fixes were more restarts and better initialisation, not a better descent rule. The same two fixes apply here and are sections 4.7's subject.
And a difference from k-means worth noting. In very high dimensions, the bad stationary points of a neural network loss are mostly saddle points rather than local minima, because a point is a local minimum only if the curvature is positive in every one of a million directions, which is vanishingly unlikely. Saddles are what momentum is particularly good at escaping: the gradient is near zero there, so plain SGD stalls, while accumulated velocity carries momentum through. That is a second, independent reason momentum helps, and it does not appear at all in the two-dimensional example of Worked 4.5b.
Dropout, Batch Normalisation and Early Stopping
A network with more parameters than examples will fit the training set exactly and learn nothing. Three techniques that behave differently at training time and test time, and one of them is not really a regulariser at all.
The question
Section 4.4 counted 52,138 parameters for a small CNN and 10,660 for a modest dense network. Real networks have millions, and datasets are often smaller than that. Section 1.6 says such a model will have low bias and ruinous variance, and section 1.7 offered ridge and lasso as the response.
Those still apply — L2 weight decay is standard practice in networks and is exactly 2.2's ridge penalty. But networks admit three further techniques that have no analogue in linear models, and each is worth understanding as a distinct mechanism rather than as three interchangeable ways to say "regularize".
Dropout
The intuition. A hidden unit can become dependent on one specific other unit: "I fire when unit 7 fires." That is a brittle arrangement, and it is a form of memorisation. If unit 7 might vanish at any moment, no unit can rely on it, and each is forced to be independently useful.
The division by p is the whole reason it is called inverted dropout, and it is the detail that exams test. The older formulation scaled the weights at test time instead; inverted dropout moves the correction into training so that deployment code is simpler and the same saved model works whether or not dropout was used.
Two interpretations, both useful. Ensemble: each mask defines a different sub-network sharing weights, so training with dropout approximately trains 2ᵏ networks at once and testing with the full network approximates averaging them — which is 2.3.5's bagging argument, at no extra cost. Noise injection: dropout adds multiplicative noise of variance (1−p)/p per activation, and noise in the inputs to a layer is a known regulariser.
Typical rates are p = 0.5 for dense layers and p = 0.8 to 0.9 (that is, light dropout) for convolutional layers, which are already regularised by weight sharing. Dropout has fallen out of favour in convolutional networks specifically, because batch normalisation plus data augmentation usually does the job better, but it remains standard in dense and transformer layers.
Batch normalisation
The intuition. Section 4.3 showed that a saturated activation kills the gradient, and section 4.7 will show that the scale of the pre-activations drifts as the layers below change. Batch normalisation fixes the distribution of each unit's pre-activation directly: force it to mean 0 and variance 1 across the batch, then let the network learn where it actually wants it.
Worked 4.6 — batch normalisation on four pre-activations
exact arithmeticz = (2, 4, 6, 8) across a batch of four. Normalise it, then apply γ = 2, β = 1.γ and β then set the spread and centre to whatever the network finds useful.Setting
γ = 2.236 and β = 5 would reproduce the original (2,4,6,8) exactly, so the layer can always learn to do nothing.Now the awkward part. Suppose the next batch is (2, 4, 6, 20) — one outlier. Then μ = 8 and σ = 7.07, and the value 2 normalises to −0.849 instead of −1.342. The same input produced a different output, because of the other examples in its batch. That is a genuine and unusual property: batch normalisation makes a prediction depend on which other examples happen to be batched with it.
It is the source of both the benefit and the problems. The batch-to-batch variation acts as noise, which regularises. But it means training and test behave differently, small batches give unreliable statistics, and batch size becomes a hyperparameter that affects accuracy rather than just speed. Layer normalisation — normalising across features within one example rather than across the batch — avoids all of this and is why transformers use it instead.
Running statistics. At test time there is no batch, so training maintains running estimates, typically running ← 0.9 × running + 0.1 × batch. Note that these converge slowly from their initial value of 0: after batches with means 5, 6 and 4 the running mean is only 1.345, not 5. A model evaluated after very few steps will therefore behave oddly in eval mode, which is a real and confusing bug. These statistics are updated by averaging, not by gradient descent, so they are usually excluded from a parameter count — say which convention you use.
Depth — batch norm probably does not do what it was said to do
The original 2015 paper explained batch normalisation as reducing internal covariate shift — the drift in each layer's input distribution as the layers below it change. The name stuck and is still what most courses teach.
Subsequent work has largely undermined that explanation. Santurkar and colleagues (2018) showed that deliberately injecting covariate shift after batch norm does not remove its benefit, and argued the real mechanism is that it smooths the loss landscape — making the gradients more predictable, so larger learning rates become safe. Others point to a simpler length-direction decoupling effect: normalising makes the loss invariant to the scale of the incoming weights, so weight magnitude stops mattering and only direction does.
The honest position for an exam is to state the original motivation, note that it is disputed, and give the empirical facts, which are not in doubt: batch norm allows substantially higher learning rates, reduces sensitivity to initialisation, acts as a mild regulariser, and usually speeds convergence considerably. A technique can work reliably while the accepted explanation for why is wrong, and this is the clearest example in the course.
Early stopping
The cheapest regulariser and the one most often left out of exam answers. Train, watch validation loss each epoch, and stop when it stops improving.
The restore step is the part people forget, and without it early stopping does nothing useful — you would be keeping the weights from epoch 7, which are worse than epoch 5's. Note also that early stopping needs a validation split, so it consumes data, and that the epoch it selects is a hyperparameter chosen on validation data, which means the validation loss at that epoch is optimistically biased and should not be reported as a final result. That is 2.1's protocol point, unchanged.
| Technique | Mechanism | Train vs test | Params added |
|---|---|---|---|
| L2 / weight decay | Penalises large weights; each step multiplies by (1 − ηλ) | Same | 0 |
| Dropout | Prevents co-adaptation; approximates an ensemble | Different — off at test | 0 |
| Batch norm | Controls pre-activation distribution; smooths the landscape | Different — running stats at test | 2C learned |
| Early stopping | Limits how far the weights travel from initialisation | Same | 0 |
The two marked "different" are the source of the most confusing bug in practical deep learning: a model that scores well during training and badly at evaluation, purely because the framework was left in the wrong mode. In PyTorch this is model.train() against model.eval(), and forgetting it disables nothing and explains a great many mysterious results.
The visualization
Overfitting, and three ways to stop it
interactive — a network with far too many parametersTraining and validation loss for a network fitted to 14 noisy points. Set the capacity to 2 or 4 and both curves sit high and close together — that is bias, and no regulariser will touch it. Raise it to 48 and validation reaches its best around epoch 250 and then drifts upward while training loss keeps falling, so early stopping alone recovers a fifth of the loss for nothing.
Then try the two regularisers and compare them honestly. Dropout at a keep probability of 0.8 genuinely helps here, improving the best validation loss from about 0.077 to about 0.061. L2 raises the training loss at every setting and improves validation at none of them. Both are behaving exactly as designed; only one of them is the right tool for this particular bottleneck, and the only way to find that out was to try it and measure. That is P4.6.3's lesson, and it is worth meeting it here rather than only as a written answer.
The pitfalls
Where marks are lost
- Applying dropout at test time. It is a training-only operation. Leaving it on makes predictions random and degrades accuracy for no reason.
- Forgetting the
1/pscaling. Without it the expected activation entering the next layer isptimes what the network was trained on, so every downstream unit sees the wrong scale. - Confusing
pas keep-probability withpas drop-probability. Different libraries use different conventions and the arithmetic inverts. State which you mean. - Using batch norm with a batch size of 1 or 2. The variance estimate is meaningless and can be exactly zero. Use layer norm or group norm for small batches.
- Keeping the bias in a layer directly followed by batch norm. BN subtracts the mean, which removes any constant, so the bias is exactly redundant and its gradient is wasted.
- Early stopping without restoring the best weights. Stopping at epoch 7 and keeping epoch 7's weights achieves nothing. Restore epoch 5's.
- Reporting the early-stopping validation loss as a final result. The epoch was chosen using that number, so it is a selection statistic, not an estimate. 2.1's rule applies.
- Stacking every regulariser at maximum strength. Dropout 0.5 plus heavy L2 plus batch norm plus aggressive early stopping can underfit badly. Add one at a time and measure.
Practice
P4.6.1 (direct) — A layer outputs a = (2, 4, 6, 8). Inverted dropout with keep probability p = 0.5 drops the second and fourth units. Give the training output and the test output.
The surviving activations are doubled, which looks wrong until you notice that half the units are missing: the expected total is preserved, which is the only thing the next layer can rely on. Note also that the variance is not preserved — it is a²(1−p)/p per unit, which is 16 for the unit with a = 4 — and that injected variance is precisely the regularising effect.
P4.6.2 (variation) — Apply batch normalisation to z = (1, 3, 5, 7, 9) with γ = 1, β = 0. Then state what γ and β would have to be for the layer to output the original values.
That recovery property is why batch norm cannot hurt representational power: the identity transformation is inside its hypothesis space, so the network can always learn to switch it off. Compare this with dropout, which genuinely removes capacity and cannot be undone by learning — a real difference between the two, and the reason batch norm is often described as an optimisation aid that happens to regularise, rather than as a regulariser.
P4.6.3 (interpretation) — A model reaches 99.8% training accuracy and 72% validation. Adding dropout at 0.5 brings training to 94% and validation to 71%. What happened, and what next?
Dropout worked as designed and it did not help, which tells you the diagnosis was wrong. The training accuracy fell by six points, so the regularisation is definitely biting. Validation moved by one point, which is within noise on most validation sets. So the 28-point gap was not caused by the kind of overfitting dropout addresses.
Three more likely explanations. The first is distribution shift: if the validation set differs systematically from training — different hospital, different time period, different collection process — then no amount of regularisation closes the gap, because the model is being asked a different question. Check by evaluating on a held-out slice of the training distribution; if that scores 97%, the problem is shift, not variance.
The second is label noise or leakage. If training labels are cleaner than validation labels, or if a feature leaks the target in training only, this pattern appears exactly.
The third is simply not enough data for the task, in which case dropout at 0.5 is too blunt an instrument and the answer is data augmentation, transfer learning from a pretrained model, or a smaller architecture.
What to do next, in order. Plot a learning curve against training-set size, which section 4.8 covers — if validation accuracy is still rising at the full dataset size, more data is the answer and everything else is a distraction. Check for shift and leakage before tuning anything further. And do not stack more regularisers on top of a diagnosis that has already been falsified.
P4.6.4 (synthesis) — Using 1.7, 2.2 and 2.3.5, relate these techniques to regularisers you already know. Which are genuinely new ideas?
L2 weight decay is ridge regression, unchanged. Section 2.2's penalty λ‖w‖² adds 2λw to the gradient, so each step becomes w ← w(1 − ηλ') − ηg — a multiplicative shrink toward zero before the gradient step. Nothing about networks changes it. The name "weight decay" describes the same operation from the update's point of view rather than the objective's.
Dropout is bagging with shared weights. Section 2.3.5's random forest built many models on resampled data and averaged them, and the variance reduction came from decorrelating the members. Dropout trains 2ᵏ sub-networks that share parameters and approximates the average at test time by using the full network. The novelty is not the ensemble idea — it is doing it for free, inside one model, without training anything extra.
Early stopping is a constraint on distance travelled. For a linear model trained by gradient descent from zero, stopping after T steps is provably close to ridge with a particular λ that decreases as T grows — so it is an implicit L2 penalty applied by the optimiser rather than the objective. Same effect, no penalty term, and it is why "just train less" is a real answer to overfitting.
Batch normalisation is the genuinely new one, and it is not really a regulariser. Nothing in Units 1 to 3 resembles it, because it has no analogue in a model that processes one example at a time: it makes each prediction depend on the other examples in its batch. Its main effect is on optimisation — higher learning rates become safe, initialisation matters less — and its regularising effect is a side-effect of the batch-to-batch noise. Classifying it with dropout and L2 obscures what it does.
The unifying view from 1.7. Every one of these trades bias for variance, and the mechanism is always a restriction: on weight magnitude (L2), on co-adaptation (dropout), on distance from initialisation (early stopping), on activation scale (batch norm). The bias-variance decomposition is the same one that governed 2.2's ridge and 3.1.1's cluster shapes, and it does not become a different subject because the model has layers.
Initialisation and Hyperparameter Tuning
Where the weights start decides whether training works at all. Two of the failure modes are so complete that no optimiser recovers from them, and both are one line to avoid.
The question
Gradient descent needs a starting point. Zero seems natural, and it is catastrophic. Small random numbers seem safe, and the scale turns out to matter enormously. What is the right answer and why?
Two failures that no amount of training fixes
Both runs above are real, not illustrative. They ran the same code that solves XOR from a random start, and neither made any progress in four thousand epochs. Randomness in the initialisation is not a detail; it is what breaks the symmetry and makes the units able to specialise. This is why biases can safely be initialised to zero — the weights already break the symmetry — but weights cannot.
The formal treatment: choosing the scale
Random is necessary but not sufficient. The scale of the random numbers controls whether the signal keeps its size as it passes through layers, forward and backward.
| fan_in | fan_out | Xavier sd | He sd |
|---|---|---|---|
| 2 | 2 | 0.70711 | 1.00000 |
| 784 | 64 | 0.04856 | 0.05051 |
| 64 | 10 | 0.16440 | 0.17678 |
| 256 | 256 | 0.06250 | 0.08839 |
The consequence of getting the scale wrong compounds with depth, and the arithmetic is stark. Take 10 layers of width 256 with ReLU, and write the initialisation as Var(w) = g/nᵢₙ:
Notice how narrow the correct choice is. Halving the variance from He's value costs a factor of a thousand over ten layers; halving it again costs another thousand. There is no "roughly right" here, which is why the rule has a name and a citation rather than being left to judgement.
Depth — the specific reason ReLU needs the factor of two
Xavier's derivation assumes the activation is roughly linear near zero and symmetric, which is true of tanh. ReLU is neither: it sets half of its inputs to exactly zero.
If z is symmetric about zero, then a = ReLU(z) is zero half the time and equals z the other half, so Var(a) = ½ Var(z). Each layer therefore halves the variance on top of whatever the weights do. He initialisation compensates by doubling Var(w), and the two factors cancel exactly.
This also explains why using Xavier with ReLU is a real if survivable mistake: the signal variance falls by a factor of 2 per layer, so after 10 layers it is down by 2¹⁰ ≈ 1000. Shallow networks tolerate it; deep ones do not, which is roughly the point at which residual connections and normalisation became necessary and is where Unit 5 picks up.
The worked example
Worked 4.7 — how often does XOR training actually succeed?
200 random initialisations per setting| hidden units | parameters | successes | rate |
|---|---|---|---|
| 2 | 9 | 33 / 200 | 16.5% |
| 3 | 13 | 104 / 200 | 52.0% |
| 4 | 17 | 131 / 200 | 65.5% |
| 8 | 33 | 192 / 200 | 96.0% |
With four times that capacity it succeeds from 96%.
The extra units are not needed to represent XOR. They are needed to find it.
This is Unit 3's k-means result in a new costume, and it is worth stating the parallel precisely. In 3.1.1, Lloyd's algorithm reached the global optimum from 192 of 220 initialisations, and the fix for the rest was k-means++ seeding plus restarts. Here the objective is also non-convex, the failures are also silent — a failed run reports a converged loss of about 0.477 and predicts three of four points correctly — and the fixes are the same two: better initialisation, or more restarts.
The difference is that in deep learning you rarely restart, because a single run is expensive. Instead you over-provision: use more units than the task strictly needs, because the extra capacity makes the loss surface easier to descend even though it makes the model larger than necessary. That trade — paying capacity to buy optimisation reliability — is one of the reasons real networks are so much bigger than the functions they represent require.
What to tune, and in what order
| Rank | Hyperparameter | Typical range | Notes |
|---|---|---|---|
| 1 | Learning rate | 10−5 to 10−1, log scale | Dominates everything else. Tune first, tune alone. |
| 2 | Architecture: depth and width | problem-dependent | Start with something known to work on a similar task. |
| 3 | Batch size | 32 to 512 | Interacts with the learning rate; scale both together. |
| 4 | Regularisation strength | λ 10−5 to 10−1; dropout 0.1–0.5 | Only once you have confirmed overfitting. |
| 5 | Optimiser and its βs | Adam or SGD+momentum | Defaults are good. Rarely worth tuning β₁, β₂. |
Random search beats grid search, and the reason is worth knowing rather than memorising. With a budget of 25 trials over two hyperparameters, a 5×5 grid tries only 5 distinct values of each. If one of the two barely matters — which is usually the case — you have spent 25 runs to learn about 5 settings of the parameter that does. Random search tries 25 distinct values of both. Bergstra and Bengio (2012) made this argument and it has held up.
The practical procedure that works: sample the learning rate log-uniformly, run a handful of epochs, keep what looks promising, and refine. Successive halving and Hyperband formalise this by starting many configurations cheaply and giving more budget only to survivors, which is usually a better use of compute than running every configuration to completion.
The visualization
Initialisation scale, and what reaches layer 10
interactive — and the two symmetry failuresThe chart plots signal variance layer by layer on a logarithmic axis. At g = 2 the line is flat, which is the whole point of He initialisation. Drag g down to 1 and the line slopes steadily off the bottom; drag it to 4 and it climbs into the exploding regime. Then switch to the zero or all-equal schemes and the picture changes character entirely — the problem there is not scale but that every unit is doing the same thing.
The pitfalls
Where marks are lost
- Initialising weights to zero. No gradient, no learning, ever. The most complete failure in the unit.
- Initialising all weights to the same non-zero value. Every unit in a layer stays identical, so the layer has the power of one unit.
- Initialising biases randomly. Unnecessary — the weights already break symmetry — and a large random bias can start a ReLU unit dead. Zero is correct.
- Using Xavier with ReLU. Missing the factor of 2 costs a factor of
2ᴱin signal variance. Survivable at 3 layers, fatal at 30. - Tuning the architecture before the learning rate. The learning rate dominates. A poor architecture at a good rate usually beats a good architecture at a bad one.
- Grid-searching many hyperparameters at once. The budget is spent on combinations of parameters that do not matter. Random search, or tune in the order of the table above.
- Tuning on the test set. 2.1's rule has not changed. Every number used to choose a setting is a validation number.
- Concluding an architecture "cannot learn" a task from one run. Worked 4.7: the correct architecture fails 83.5% of the time. Always try several seeds before drawing that conclusion.
Practice
P4.7.1 (direct) — Give the He and Xavier standard deviations for a layer with 512 inputs and 128 outputs, and say which to use for ReLU.
The two differ by only 12% here, because nᶜᵘᵗ is small relative to nᵢₙ. They diverge when the fan-in and fan-out are very unequal: for a layer with 10 inputs and 1000 outputs, He gives sd = 0.447 and Xavier gives 0.0445, a factor of ten. That is when the choice actually matters, and it is also when you should think about which direction — forward signal or backward gradient — you most need to preserve.
P4.7.2 (variation) — Prove that a two-unit hidden layer initialised with identical weights and biases can never separate its units, for any number of gradient steps.
Let the two units have identical parameters at step t: w₁ = w₂ and b₁ = b₂. Show they are identical at step t+1.
The empirical confirmation is in section 4.7's opening: 4000 epochs from an all-0.5 start leaves W₁ = [[0.351, 0.351], [0.351, 0.351]] — the values moved, but the two rows stayed identical, so the layer never became more than one unit wide.
Note precisely what the proof needs: the incoming weights equal and the outgoing weights equal. Breaking either one is enough. This is also why dropout incidentally helps — it applies a different random mask to each unit, which breaks the symmetry even from a degenerate start.
P4.7.3 (interpretation) — A 30-layer ReLU network trains to a loss of 2.30 on a 10-class problem and stays there. What is 2.30, and what would you check?
2.30 is ln 10. That is precisely the cross-entropy of a model that outputs a uniform distribution over ten classes — it has learned nothing at all and is predicting 1/10 for everything. This is the ten-class analogue of ln 2 = 0.693 for binary, which appeared in Worked 4.4, and recognising these numbers instantly is worth doing: a loss sitting exactly at ln K means no learning, not slow learning.
With 30 layers and a stuck loss, the first suspect is the signal never reaching the output or the gradient never reaching the input. Check the initialisation scale against He: if Xavier was used with ReLU, the variance falls by 2³⁰ ≈ 10⁹ across the depth, and the output is numerically constant regardless of the input. Verify directly by feeding two very different inputs and comparing the logits — if they are identical to several decimal places, the forward signal has died.
Then check the other candidates. Log the gradient norm per layer; a norm that decays geometrically toward the input confirms vanishing gradients. Check for dead ReLUs, per P4.3.3. Check the learning rate is not so small that nothing moves, and not so large that it diverged immediately to a degenerate solution. And check the labels are actually being passed correctly, because a shuffled or constant label array produces exactly this symptom and is embarrassing to find on day three.
The fixes for a 30-layer network are structural. He initialisation, batch normalisation, and residual connections — the last of which is Unit 5's central mechanism and exists precisely because 30 plain layers do not train no matter how carefully you initialise them.
P4.7.4 (synthesis) — Using 2.1 and 3.1.1, compare hyperparameter tuning here with the same problem in supervised and unsupervised learning. What is genuinely harder?
The protocol is identical to 2.1's and nothing about networks changes it. Split the data, tune on validation, touch the test set once. Every number used to select a setting — including the epoch chosen by early stopping — is a validation number and cannot be reported as a performance estimate.
What is harder than 2.1: cost and interaction. Fitting a logistic regression takes a second, so a 100-point grid search is free. Training a network takes hours, so the search budget is perhaps five or ten configurations, and that scarcity is what makes random search and successive halving necessary rather than merely clever. The hyperparameters also interact strongly — batch size with learning rate, depth with initialisation, regularisation strength with capacity — so tuning one at a time is not reliable in the way it roughly is for ridge's single λ.
What is easier than 3.1.1: there is an answer. Section 3.5's central difficulty was that unsupervised learning has no external referent, so "better" had to be assembled from geometry, stability and a null baseline. Here validation loss is a real measurement against real labels, and a lower one is genuinely better. That is a large advantage and it is worth appreciating after Unit 3.
What is shared with 3.1.1: non-convexity, and the same two fixes. Worked 4.7's 16.5% success rate is the same phenomenon as k-means converging to a poor local optimum from 28 of 220 starts, and the responses are the same — better initialisation (He here, k-means++ there) and more restarts. The distinctively deep-learning addition is a third fix unavailable to k-means: over-provision the capacity, because a wider network has an easier loss surface even though it represents no more than the narrow one needed to.
Learning Curves, Calibration and Fairness
Diagnosing a network from its curves, and the discovery that everything Unit 2B established about honest reporting applies here unchanged — except that networks are worse at one of it.
The question
Training finishes. Validation accuracy is 71%. You have a budget for one more thing: more data, a bigger model, more regularization, or more training. Which?
Guessing is expensive and common. The two learning curves answer it directly, and they answer different questions, so it is worth being precise about which curve is which.
Two different plots, both called a learning curve
Curve 2 is the one that decides the question above, and it is the one people skip because it costs several training runs. Fit the model on 10%, 25%, 50% and 100% of the data, record training and validation loss for each, and plot against n.
| n | high-bias train | high-bias val | gap | high-var train | high-var val | gap |
|---|---|---|---|---|---|---|
| 50 | 0.42 | 0.48 | 0.06 | 0.02 | 0.55 | 0.53 |
| 100 | 0.41 | 0.45 | 0.04 | 0.03 | 0.44 | 0.41 |
| 200 | 0.40 | 0.43 | 0.03 | 0.05 | 0.36 | 0.31 |
| 400 | 0.40 | 0.42 | 0.02 | 0.07 | 0.30 | 0.23 |
| 800 | 0.40 | 0.41 | 0.01 | 0.09 | 0.25 | 0.16 |
Left: high bias. The two curves have met, and they met at a high loss of 0.40. Extra data changes nothing, because the model already fits everything it can and is limited by its own capacity. The fix is a bigger model, better features, or less regularization.
Right: high variance. The gap is still 0.16 and still closing, and validation loss is still falling at n = 800. Extrapolate the trend and more data will keep helping. The fix is more data, more regularization, or a smaller model — in that order of preference, because more data costs nothing in bias.
The rule to carry away: look at where the curves are heading, not where they are. A validation curve still descending at your largest n is the single clearest evidence that collecting more data is worth the money, and a validation curve that flattened at n/4 is the clearest evidence that it is not.
Depth — when the classical picture stops applying
Section 1.6's U-shaped test-error curve says that past some capacity, test error must rise. Very large neural networks routinely violate this. Interpolate the training data perfectly — zero training error, more parameters than examples — and test error often continues to fall as the model grows further. This is double descent: error rises to a peak around the interpolation threshold, where parameters roughly equal examples, and then descends again.
The classical picture is not wrong; it is incomplete. It assumed that among the many models fitting the data perfectly, you get an arbitrary one. In practice gradient descent has an implicit bias toward low-norm solutions, so among all the interpolating networks it finds a smooth one, and smoothness generalises. Capacity measured by parameter count stopped predicting generalisation, which is why the field now talks about implicit regularisation rather than about counting parameters.
For this course the practical implication is narrow but real: do not conclude that a model is too big purely from its parameter count. Measure. And note that this file's own numbers are firmly in the classical regime — nine parameters, four examples — so nothing here demonstrates double descent, and you should not expect to see it on small problems.
Calibration — 2.4.6, and why networks are worse at it
A softmax output is a probability distribution and it is tempting to read it as one. Section 2.4.6 established that a model can rank perfectly and still be badly calibrated, that AUC cannot detect this, and that the repair is a monotone rescaling fitted on held-out data. All of that carries over unchanged.
What is new is the severity. Modern networks are systematically and substantially overconfident, more so than the logistic regressions of Unit 2, and more so than the shallower networks of the 1990s. Guo and colleagues (2017) documented this and traced it to capacity, batch normalisation and reduced weight decay all pushing the same way — a network trained to near-zero training loss has been explicitly optimised to output probabilities near 1.
Worked 4.8 — expected calibration error for a network
the same arithmetic as 2.4.6| mean confidence | n | correct | observed | gap | weight |
|---|---|---|---|---|---|
| 0.95 | 60 | 45 | 0.7500 | 0.2000 | 0.240 |
| 0.85 | 80 | 60 | 0.7500 | 0.1000 | 0.320 |
| 0.65 | 60 | 39 | 0.6500 | 0.0000 | 0.240 |
| 0.55 | 50 | 27 | 0.5400 | 0.0100 | 0.200 |
The 0.95 bin delivers 0.75 — the model claims to be right 19 times in 20 and is right 3 times in 4.
The two low-confidence bins are essentially perfect.
That shape — well calibrated in the middle, badly overconfident at the top — is the characteristic signature of a modern network, and it is the most damaging possible shape, because high-confidence predictions are exactly the ones acted on without review.
The repair is temperature scaling: divide the logits by a single learned T > 1 before the softmax, fitting T on held-out data. On logits (3.0, 1.0, 0.5) the top probability falls from 0.8214 at T = 1 to 0.6885 at T = 1.5 and 0.6045 at T = 2. One parameter, fitted after training, and because the transformation is monotone the ranking never changes — so accuracy and AUC are exactly unaffected. It is Platt scaling from 2.4.6, applied to logits.
Fairness — unchanged, and harder to inspect
Section 2.6's algebra does not depend on the model. Disaggregate the confusion matrix by group and compute the same quantities.
Three points specific to networks, none of which changes the arithmetic.
Fairness through unawareness fails harder. Section 2.6 made this point for linear models; a network makes it worse, because a deep model is far better at reconstructing a removed attribute from correlated features. Dropping the group column does essentially nothing.
Interpretability is genuinely harder, not merely less convenient. A logistic regression has one coefficient per feature and 2.3.4's tree can be read directly. A network's decision is distributed across thousands of weights with no per-feature summary. The available tools — saliency maps, SHAP, integrated gradients, counterfactual probing — are approximations, they frequently disagree with each other, and several have been shown to be insensitive to randomising the model's weights. Treat their output as a hypothesis to test, not an explanation.
And the impossibility result still holds. The proof in 2.6 used only the definitions and the base rates, so it applies to any classifier including this one. You still cannot equalise selection rate, recall and precision simultaneously when base rates differ, and choosing which to equalise is still a decision about values rather than about architecture.
What a network's result should contain
The visualization
Both learning curves, and what each one tells you
interactive — capacity, data size and noiseLoss against epoch
Final loss against training-set size
Set the capacity to 1 and both panels show high bias: the curves meet quickly, at a high loss, and the right-hand panel is flat — more data would buy nothing. Push capacity to 12 and the left panel shows the validation curve turning upward while training loss keeps falling, and the right panel shows validation still descending at the largest n. Same model, two different recommendations, and only the right-hand panel could have told you which.
The pitfalls
Where marks are lost
- Using the epoch curve to decide whether more data would help. It cannot. Only the curve against training-set size answers that.
- Reporting a single run. Worked 4.7's 16.5% makes the point: one seed is a sample of size one from a distribution with enormous variance.
- Reading softmax outputs as calibrated probabilities. They are systematically overconfident. Measure ECE before acting on any confidence threshold.
- Applying temperature scaling on the training set.
Tis fitted on held-out data, exactly like Platt scaling in 2.4.6. Fitting it on training data yieldsT ≈ 1and achieves nothing. - Claiming temperature scaling improved accuracy. It cannot — it is monotone, so the argmax and the ranking are unchanged, and so are accuracy and AUC. It improves the probabilities only.
- Reporting pooled accuracy for a model that will be applied to distinct groups. The example above pools to 0.6467 and conceals a recall gap of 0.525.
- Treating a saliency map as an explanation. It is an approximation whose reliability is contested. Use it to generate a hypothesis and then test the hypothesis.
- Concluding "the model is too big" from a parameter count. Double descent means capacity alone does not predict generalisation. Measure the validation curve.
Practice
P4.8.1 (direct) — A model has training loss 0.08 and validation loss 0.41, and the gap has been widening for 15 epochs. Name three interventions and predict the effect of each on both numbers.
A gap of 0.33 that is still widening is variance, plainly. All three interventions below trade training loss for validation loss, which is the point.
A fourth option worth naming: a smaller model. It reduces variance but adds bias, so it is the least attractive of the four and is worth trying only after the others. And note that all four are 1.6's bias-variance trade-off with different labels — none of them is specific to neural networks.
P4.8.2 (variation) — A network outputs logits (4.0, 1.0, 0.0). Compute the softmax at T = 1 and T = 2, and state the effect on accuracy and on ECE.
Effect on accuracy: none, exactly. Dividing every logit by the same positive T preserves their order, so the argmax is class 1 in both cases. Accuracy, AUC, precision and recall at any rank-based threshold are all unchanged — which is the property that makes temperature scaling safe to apply after training.
Effect on ECE: potentially large. If this model is right about 74% of the time when it outputs 0.936, then T = 1 contributes a gap of about 0.20 and T = 2 contributes about 0.00. Worked 4.8's table had exactly this problem in its top bin.
The value of T is found by minimising negative log-likelihood on a held-out set — a one-dimensional optimisation over a single parameter, which takes seconds and is the cheapest meaningful improvement available to a trained network. T > 1 softens an overconfident model; T < 1 sharpens an underconfident one, which is rarer and is the random-forest failure noted in 2.4.6.
P4.8.3 (interpretation) — A team reports "our CNN achieves 96.2% accuracy, outperforming the previous state of the art of 95.8%." What is missing?
Almost everything needed to evaluate the claim, and the difference itself is probably not real.
No variance estimate. A single run of a single seed. Worked 4.7 showed seed-to-seed variation can be the difference between success and total failure; even on a well-behaved task, run-to-run standard deviation of a few tenths of a percent is normal. A 0.4-point gap with no spread reported could easily be one seed's luck. Report mean and standard deviation over at least five seeds, and compare with a paired test on shared splits, per 2.4.6.
No baseline and no prevalence. If the majority class is 94%, then 96.2% is a two-point gain, not a 96-point achievement. Section 2.4.2's rule applies verbatim.
No statement of how the test set was used. If the architecture, learning rate and epoch count were selected by looking at test accuracy, then 96.2% is a selection statistic and the honest estimate is lower. This is the most common way state-of-the-art claims become unreproducible, and it is 2.1's rule being quietly broken.
No accuracy-adjacent reporting. Per-class recall, a confusion matrix, calibration, and disaggregation by any group the model will be applied to. And no architecture, parameter count, training budget or seeds, so nobody can reproduce it — a model that reaches 96.2% using 100× the compute of the 95.8% baseline has not made the same kind of progress.
P4.8.4 (synthesis) — Using 2.4.6 and 2.6, state what carries over from Unit 2B to neural networks unchanged, what gets worse, and what genuinely changes.
Unchanged: all of the arithmetic. The confusion matrix, precision and recall, ROC and AUC, cost-derived thresholds, regression metrics, ECE and Brier, and the whole of 2.6's fairness algebra including the impossibility proof. None of it ever depended on how the predictions were produced, so a network's scores are audited exactly as a logistic regression's were. The reporting discipline — validation for selection, test once, spread not point estimates — is also unchanged.
Worse: calibration and interpretability. Networks are more overconfident than the models of Unit 2, for identifiable reasons — large capacity trained to near-zero loss, batch normalisation, weak weight decay — so 2.4.6's warning applies more forcefully rather than less. And where a logistic regression offered one coefficient per feature, a network offers no faithful per-feature summary at all, so 2.6's interpretability requirement becomes something you approximate rather than something you read off.
Worse in a second way: variance across runs. Unit 2's models were mostly deterministic given the data. A network's result depends on the seed, and Worked 4.7's 16.5% is the extreme version. This makes single-run reporting actively misleading in a way it was not before.
Genuinely new: the epoch dimension, and double descent. Unit 2's models were fitted, not trained over time, so there was no "when to stop" question and no epoch curve to read. Early stopping, learning-rate schedules and the whole diagnostic vocabulary of section 4.8's first half have no Unit 2 analogue. And double descent means 1.6's U-shaped curve, which was reliable for every model in Unit 2, is no longer a safe guide at large capacity — the one place where a Unit 1 result genuinely needs qualifying rather than merely restating.
Cheat Sheet
Every formula in this unit, plus the spine's numbers so you can check your arithmetic against a case you have already seen worked.
4.1 Neuron and perceptron
z = wᵀx + b, a = φ(z)
Perceptron rule: w ← w + η(y − ŷ)x, same for b
Converges iff the data is linearly separable (Novikoff). Otherwise it cycles forever.
Says nothing about the margin — that is the SVM's job.
XOR is not separable: B and C sit on opposite corners.
4.1 Forward pass and losses
z⁽ˡ⁾ = W⁽ˡ⁾a⁽ˡ⁻¹⁾ + b⁽ˡ⁾, a⁽ˡ⁾ = φ(z⁽ˡ⁾), a⁽⁰⁾ = x
Stacking linear layers collapses to one layer.
Regression → linear out + MSE
Binary → sigmoid + BCE
Multi-class → softmax + CE
Each matched pair gives ∂L/∂z = ŷ − y.
MSE on a sigmoid is 50× weaker where the model is most wrong.
4.2 Convolution
O = floor((I − K + 2P)/S) + 1
params = (K²Cᵢₙ + 1) × Cᶜᵘᵗ — no I in it
"same" padding: P = (K−1)/2
Pooling: 0 parameters, channels independent
Convolution is equivariant; pooling gives invarianceL stacked 3×3 layers → receptive field 2L + 1
Kernel summing to 0 = edge detector; to 1 = smoother.
4.3 Activations
σ' = σ(1−σ), max 0.25 · tanh' = 1 − tanh², max 1.0
ReLU' = 1 or 0
Sigmoid at |z| = 6 retains 1% of its gradient.
10 sigmoid layers, best case: 0.25¹⁰ = 9.5 × 10⁻⁷
Dead ReLU: z < 0 for every example → gradient exactly 0 forever.
Softmax is shift-invariant; subtract the max before exponentiating.
Never use sigmoid in hidden layers.
4.4 Backpropagation
δ⁽ˡ⁾ = ∂L/∂z⁽ˡ⁾ (pre-activation convention)
δ⁽ᴱ⁾ = ŷ − y for matched output-loss pairs
δ⁽ˡ⁾ = (W⁽ˡ₊¹⁾ᵀδ⁽ˡ₊¹⁾) ⊙ φ'(z⁽ˡ⁾)
∂L/∂W⁽ˡ⁾ = δ⁽ˡ⁾(a⁽ˡ⁻¹⁾)ᵀ · ∂L/∂b⁽ˡ⁾ = δ⁽ˡ⁾
One forward + one backward pass, any number of parameters.
Memory cost: every a⁽ˡ⁾ must be kept.
Gradient check: central difference, ε = 10⁻⁵.
4.4 Parameter counts
Dense: (nᵢₙ + 1) × nᶜᵘᵗ
Conv: (K²Cᵢₙ + 1) × Cᶜᵘᵗ
Pool, ReLU, dropout, flatten: 0
BatchNorm: 2C learned + 2C running statistics
The flatten-to-dense layer is almost always the bulk of a CNN.
Global average pooling removes it.
4.5 Optimisers
SGD: w ← w − ηg
Momentum: v ← βv + g, w ← w − ηv
effective rate η/(1−β) — 10× at β = 0.9
Adam: m, v moments, bias-corrected by 1−βᵗ
first step is exactly −η·sign(g)
Stability on curvature L: η < 2/L
Adam is not always fastest — it discards gradient magnitude.
Use AdamW if you want weight decay.
4.6 Regularisation
L2 / weight decay: w ← w(1 − ηλ) − ηg — ridge, unchanged
Inverted dropout: keep with prob p, divide survivors by p, nothing at test
BatchNorm: ẑ = (z−μ)/√(σ²+ε), then γẑ + β
γ = σ, β = μ recovers the input exactly
test time uses running averages
Early stopping: restore the best epoch's weights, not the last.
4.7 Initialisation
Zero → no gradient at all.
All-equal → units stay identical forever.
Xavier: Var(w) = 2/(nᵢₙ + nᶜᵘᵗ) for tanh/sigmoid
He: Var(w) = 2/nᵢₙ for ReLU — the 2 offsets ReLU halving the variance
Biases: zero is correct.
10 layers, width 256: g=1 loses 10⁻⁴, g=2 holds at 1.000.
4.7 Tuning
Order: learning rate → architecture → batch size → regularisation → optimiser
Random search beats grid search when only some parameters matter.
Sample the learning rate log-uniformly.
Successive halving / Hyperband for a fixed budget.
XOR success: 2 units 16.5%, 8 units 96% — over-provision to make optimisation easy.
4.8 Diagnosis
Loss vs epoch: is it training, is it overfitting, is η right
Loss vs dataset size: would more data help — the epoch curve cannot say
Curves met at a HIGH loss → bias, get a bigger model
Gap still closing → variance, get more data
Loss stuck at ln K means no learning at all: ln 2 = 0.693, ln 10 = 2.303.
4.8 Reporting
ECE = Σ(nᵇ/n)|confᵇ − obsᵇ|
Networks are systematically overconfident, worst at the top bin.
Temperature scaling: divide logits by learned T, fitted on held-out data.
monotone, so accuracy and AUC cannot change.
Fairness: 2.6's algebra applies unchanged; unawareness fails harder.
Report several seeds. One run is a sample of size one.
The spine, end to end
Four points — A(0,0)→0, B(0,1)→1, C(1,0)→1, D(1,1)→0 — and a 2–2–1 network. Every number below is derived in this file.
| Quantity | Value | Section |
|---|---|---|
| Perceptron on AND: final weights | w = (2, 1), b = −2, after 10 updates | 4.1 |
| Perceptron on XOR | cycles through 6 states, never converges | 4.1 |
| Exact XOR solution | W₁=[[1,1],[1,1]], b₁=(0,−1), W₂=(1,−2), b₂=0 | 4.1 |
| Hidden representation of B and C | both (1, 0) — they COLLAPSE | 4.1 |
| Spine parameter count | 6 + 3 = 9 | 4.4 |
| MSE vs CE gradient at ŷ = 0.01, y = 1 | −0.0196 vs −0.99 — 50× | 4.1 |
| Ring image, vertical kernel, top-left window | +1 | 4.2 |
| Feature map size, 6×6 with 3×3, no padding | 4×4 | 4.2 |
| Small CNN total parameters | 52,138, of which 50,240 in one dense layer | 4.2 |
| Sigmoid derivative, maximum | 0.2500 at z = 0 | 4.3 |
| 10 sigmoid layers, best case | 9.5 × 10⁻⁷ | 4.3 |
| Worked 4.4 forward: z₁, a₁, z₂, ŷ | (0.5, 0.5), (0.5, 0.5), 0.0, 0.5 | 4.4 |
| Worked 4.4 loss | ln 2 = 0.693147 | 4.4 |
| Worked 4.4 δ₂, δ₁ | −0.5 and (−0.5, +0.5) | 4.4 |
| ∂L/∂W₁ | [[−0.5, 0], [+0.5, 0]] | 4.4 |
| After one step at η = 1 | loss 0.0889, and hidden unit 2 is DEAD | 4.4 |
| Gradient check on b₂ | central difference = −0.500000 ✓ | 4.4 |
| SGD / momentum / Adam after 4 steps on w² | 0.4096 / −0.3086 / 0.6039 | 4.5 |
| Valley after 40 steps, w₁ remaining | SGD 0.484, momentum 0.007, Adam 0.354 | 4.5 |
| BatchNorm on (2,4,6,8) | μ=5, σ²=5, ẑ=(−1.342,−0.447,+0.447,+1.342) | 4.6 |
| Inverted dropout, p=0.5, a=(2,4,6,8) | (4, 0, 12, 0) in training; unchanged at test | 4.6 |
| He sd at fan_in 2 / 784 / 256 | 1.0000 / 0.0505 / 0.0884 | 4.7 |
| XOR training success, 2 / 3 / 4 / 8 units | 16.5% / 52.0% / 65.5% / 96.0% | 4.7 |
| Zero init and all-equal init, after 4000 epochs | loss still ln 2 = 0.69315 | 4.7 |
| Worked 4.8 ECE | 0.0820, concentrated in the top bin | 4.8 |
Mixed Self-Test
Ten questions, unlabelled by section. Attempt all before opening any solution.
Q1. A network has input 64, hidden layers of 128 and 32 with ReLU, and a 5-way softmax output. Count the parameters. Then say how many if a batch-norm layer follows each hidden layer.
If the biases are removed from the two hidden layers, as is standard when batch norm follows, subtract 128 + 32 = 160, giving 12,773. Say which convention you used; both are marked correct if stated.
Q2. Run one perceptron update. Current w = (1, −2), b = 0.5, η = 0.5. The example is x = (2, 1) with target y = 1. Give the new weights.
The trap is computing an update reflexively. The perceptron rule is error-driven: a correct prediction produces e = 0 and the weights do not move, however close to the boundary the point sits. Contrast this with gradient descent on cross-entropy, which would produce a non-zero gradient here because σ(0.5) = 0.622 ≠ 1 — the perceptron is satisfied by being right, and logistic regression is not.
Q3. A 3×3 kernel with weights all equal to 1/9 and bias 0 is applied to a patch of all-identical pixels of value 7. What is the output? Now with the vertical-edge kernel of section 4.2?
The general result: on a constant patch of value v, a kernel outputs v × (sum of weights) + b. So a zero-sum kernel is blind to absolute brightness and responds only to variation, while a unit-sum kernel preserves brightness. This is why edge detectors are built to sum to zero and why a network trained on images with varying exposure tends to discover zero-sum filters in its first layer.
Q4. A hidden unit has pre-activation z = −3. Give its output and the gradient factor it contributes, under ReLU, leaky ReLU (α = 0.01), tanh and sigmoid. Which is worst and why?
ReLU is worst here, and by a category rather than a margin. A gradient factor of exactly 0 means this unit passes nothing back and, if z < 0 for every training example, will never recover. The other three all pass something: leaky ReLU 0.01, tanh 0.0098, sigmoid 0.0452. Small is recoverable; zero is not.
Notice the reversal against the usual ranking. On the positive side ReLU is the best of the four, with a factor of exactly 1 where the others saturate. The activations differ not in quality but in where they fail, which is why the answer to "which activation is best" is always "for which regime".
Q5. A network's output layer has logits (1.0, 3.0, 2.0) and the true class is the third. Compute the softmax, the cross-entropy loss, and ∂L/∂z for all three logits.
The gradient pushes the true class's logit up (its component is negative, so the update z ← z − η∂L/∂z increases it) and both others down, with the largest push against class 2, which is currently the most confident wrong answer. The components summing to zero is a consequence of softmax's shift-invariance and is a fast sanity check.
Q6. A two-layer network has a₁ = (2, 3), W₂ = (0.5, −1.0), b₂ = 0.5, sigmoid output, target y = 0, and both hidden pre-activations positive. Compute the loss and ∂L/∂W₁ given x = (1, 2).
Both columns are non-zero here because both inputs are non-zero, unlike Worked 4.4 where x₂ = 0 zeroed a column. And the second column is exactly twice the first, because x₂ = 2x₁ — the outer-product structure means the columns of ∂L/∂W are always proportional to the input.
Q7. On f(w) = 3w² starting at w = 1, give the largest stable learning rate for plain SGD, and for momentum with β = 0.9. Then take one Adam step with η = 0.01.
The Adam step is exactly η, independent of the curvature and of the gradient being 6 rather than 2 — the property proved in P4.5.2. Note the practical consequence: SGD's safe rate depends on L, which you do not know and which changes during training, while Adam's does not. That is the trade the whole optimiser section is about.
Q8. Training loss 0.05, validation loss 0.52, and the validation curve has been rising for 12 epochs while training loss keeps falling. The network has 2.1 million parameters and the training set has 3,000 examples. Diagnose and give three fixes in priority order.
Textbook high variance: 700 parameters per training example. The rising validation curve alongside a falling training curve is the signature, and the gap of 0.47 is large. Section 1.6's decomposition applies directly.
Fix 1: early stopping, restoring the best epoch's weights. The validation minimum was twelve epochs ago and everything since has been damage. This costs nothing, requires no retraining decision, and should have been on from the start.
Fix 2: reduce the effective capacity. With 700 parameters per example, this model can memorise the training set outright. Add dropout and L2, and consider a genuinely smaller architecture — if it is a CNN, check whether a flatten-to-dense layer holds most of those 2.1 million parameters, per Worked 4.2b, because global average pooling would remove them at a stroke.
Fix 3: more data, or the closest available substitute. Data augmentation if the domain permits it, and transfer learning from a pretrained model, which is usually the single most effective intervention at this data scale because it replaces most of the parameters with ones already fitted elsewhere.
Before any of that, plot loss against training-set size. If validation loss is still falling at n = 3{,}000, more data will help and is worth the cost; if it flattened at 1,500, it will not and fixes 1 and 2 are the whole answer.
Q9. A 12-layer ReLU network is initialised with Var(w) = 1/nᵢₙ. By what factor does the signal variance change from input to output, and what should it have been?
A factor of 4096 will not overflow anything, so the network will train — badly. The symptom is very slow early progress and a strong dependence on the learning rate, rather than an outright failure, which makes it harder to diagnose than a total collapse. At 30 layers the same error gives 2⁻³⁰ ≈ 10⁻⁹ and the network does not train at all, which is P4.7.3.
Note that this is exactly Xavier's scaling used with ReLU, so it is a mistake made by using a sensible-sounding default rather than an arbitrary number.
Q10. A hiring model built as a neural network reports 89% accuracy. Audited by group: group A has TPR 0.91 and PPV 0.84; group B has TPR 0.52 and PPV 0.83. Explain what is happening and what should be done.
Predictive parity nearly holds and equal opportunity fails badly. A flag means almost the same thing for both groups — 0.84 against 0.83 — so anyone checking "is a positive prediction equally reliable?" would find the model fair. But a qualified member of group B is found 52% of the time against 91% for group A, so nearly half of group B's qualified candidates are being missed. The 89% pooled accuracy reports neither number.
This is 2.6's impossibility theorem in its most uncomfortable form, and section 4.8 established that nothing about a network changes the algebra. If the base rates differ between the groups, equal PPV and equal TPR cannot both hold, so the near-equality of PPV is not evidence of fairness — it is the thing that forced the TPR gap.
What to do. First, decide which criterion the situation demands, and justify it: for a benefit-allocating decision like hiring, equal opportunity is the one that matters, so this model is indefensible as it stands. Then look for the mechanism — group-correlated proxies in the features, per 2.6, remembering that a network reconstructs a removed attribute more easily than a linear model. Consider group-specific thresholds, which close the TPR gap directly and are legally fraught in some jurisdictions, so this is a decision for people beyond the modelling team. Check whether the training labels themselves encode past discrimination, because a model that faithfully reproduces biased historical hiring is working exactly as trained.
And do not reach for interpretability tools as a defence. Section 4.8's warning applies: saliency and attribution methods on a network are approximations whose reliability is contested, and "we inspected the model and it looks fine" is not a finding. The disaggregated metrics are the evidence, and they are already damning.
Where This Goes Next
Unit 5 is this unit with the depth turned up, and every problem you have just met becomes the reason for a specific architectural fix.
What Unit 4 established, in one paragraph
One neuron is a linear classifier, and four points defeat it permanently. Adding a hidden layer fixes this not by classifying better but by changing coordinates until a line suffices — B and C land on the same point and the problem dissolves. Backpropagation computes every gradient in one backward pass by reusing a per-layer quantity, and it multiplies one weight matrix and one activation derivative per layer, which is why the activation function turned out to be the thing that decided whether deep networks worked at all. Then the practical half: three optimisers that behave differently on identical gradients, with the fanciest slowest on a well-scaled problem; four regularisers with genuinely different mechanisms, one of which is not really a regulariser; an initialisation scheme where a factor of two decides between training and silence; and the finding that the architecture proven sufficient for XOR finds it from one start in six.
| From here | Reappears as |
|---|---|
| 4.1 The hidden layer as a learned change of coordinates | The organising idea of Unit 5. Embeddings, encoders and every pretrained representation are this, scaled up. |
| 4.2 Convolution, receptive fields, parameter sharing | Unit 5's vision architectures directly — ResNet, and the reason a transformer's attention is described as an alternative way to mix information across positions. |
| 4.3 Vanishing gradients through depth | The reason residual connections exist. A skip connection gives the gradient an additive path with derivative 1, which is the fix for exactly the product computed in P4.3.2. |
| 4.3 Softmax over scaled scores | The attention mechanism's normalisation, unchanged — and the temperature there is the √d scaling factor. |
| 4.4 Backpropagation and its memory cost | Why large-model training is memory-bound, and why gradient checkpointing, mixed precision and model parallelism exist. |
| 4.5 Adam and learning-rate schedules | AdamW with cosine decay and warmup is the standard recipe for every model in Unit 5. |
| 4.6 Normalisation layers | Layer normalisation, for the batch-dependence reason given in Worked 4.6 — transformers cannot use batch statistics. |
| 4.7 Over-provisioning capacity to make optimisation easy | Part of the answer to why scale works, alongside double descent from 4.8. |
| 4.8 Calibration and fairness of an opaque model | Deepens throughout Unit 5 and remains unsolved. The metrics do not change; the ability to inspect the model gets worse. |
Before you move on
Eight things you should be able to do from a blank page. Run the perceptron learning rule for two epochs and say whether it will terminate. Prove XOR is not linearly separable. Compute a forward pass through a two-layer network. Give the output size and parameter count of a convolutional layer. Run one full backward pass and state every δ. Count the parameters of any architecture described in words. Take one step of SGD, momentum and Adam by hand. And diagnose a training curve as bias or variance and name the fix.
If any is shaky, that section's practice ladder is the fastest repair. Unit 5 assumes backpropagation and the activation arithmetic without re-deriving either.
Further reading
- Géron, Hands-On Machine Learning, 3rd ed., ch. 10–11 and 14 — the prescribed textbook. Chapter 10 introduces networks and Keras, 11 covers training deep networks including initialisation and optimisers, and 14 covers CNNs. The closest match to this unit.
- Goodfellow, Bengio and Courville, Deep Learning, ch. 6–8 — the standard reference. Chapter 6 derives backpropagation properly, chapter 7 covers regularisation including the dropout-as-ensemble argument, and chapter 8 covers optimisation. Freely available online.
- Nielsen, Neural Networks and Deep Learning — free online, and chapter 2's derivation of the four backpropagation equations is the clearest available. If section 4.4 did not land, read that.
- He et al. (2015) for He initialisation and Glorot and Bengio (2010) for Xavier — both short, and both consist mostly of the variance argument given in section 4.7, so they are unusually readable primary sources.
- Guo et al., "On Calibration of Modern Neural Networks" (2017) — the source of section 4.8's calibration material and of temperature scaling. Referenced in Unit 2B for the same reason.
- Santurkar et al., "How Does Batch Normalization Help Optimization?" (2018) — the paper that undermined the internal-covariate-shift story. Worth reading as an example of a field correcting an explanation without changing a practice.
Spine: the XOR function on four points, and a 2–2–1 network with nine parameters that solves it exactly with whole-number weights. Every numerical value in this file was computed rather than estimated, and every widget was driven through its range and checked against the printed worked examples.
Previous: Unit 3 — Unsupervised Learning · Next: Unit 5 — Deep Learning Foundation