01

Convolutional Neural Networks

Starting from "what is a neural network", and finishing at how a real one runs on a GPU. No prior knowledge assumed.

Part 1 · Neural Network Basics — slides 2 to 10

  • Why we cannot simply write the rules by hand
  • The artificial neuron: multiply, add, squash
  • Activation functions, and why a bend is essential
  • Layers, and the forward pass
  • Loss: putting a number on how wrong we are
  • Gradient descent and backpropagation
  • The learning rate, and watching a network learn

Part 2 · Convolutional Networks — slides 11 to 33

  • Why the network from Part 1 breaks on images
  • Convolution, worked out on a real picture
  • Padding, stride, channels, receptive field
  • Pooling and the complete CNN pipeline
  • How a CNN is trained, in full
  • LeNet to ResNet, and residual connections
  • Efficiency, detection, segmentation, hardware

33 slides · ← / → to navigate, or put #N in the URL · every number and every figure here was computed, not asserted.

02 / Basics
Part 1 · Neural Network Basics

Why We Cannot Just Write the Rules START HERE

Two approaches side by side: writing rules by hand versus learning from labelled examples.

Traditional programming: you write the rules, the computer applies them. Machine learning: you supply examples, the computer works out the rules.

A Task You Can Do But Cannot Explain

You can look at a photograph and say "cat" in about 150 milliseconds. Now try to write down the instructions. Not "it has fur" — actual instructions, in terms of the numbers a computer sees, which is all a photograph is.

Nobody has ever managed it. People tried seriously for roughly forty years.

What a Computer Sees

There are no objects in an image file. There is a grid of numbers, one per pixel, each giving a brightness from 0 (black) to 255 (white). A colour photo has three such grids: red, green and blue.

A small 224 × 224 colour photo:
  224 × 224 × 3 = 150,528 numbers

Your task: write a function that maps
those 150,528 numbers to "cat" or "dog".

Every idea you have will break. Move the cat two pixels left and every number changes. Turn the light down and every number changes. The cat is still a cat.

The Three Ingredients of Machine Learning

IngredientWhat it isIn our example
DataExamples with correct answers10,000 photos, each labelled cat or dog
ModelA formula with adjustable numbersThe neural network
LossA score for how wrong it isOne number, which we want small

Training means adjusting the model's numbers until the loss is small. That is the whole idea. The next eight slides are only the details of how.

One Word You Will Hear Constantly

Supervised learning means every training example comes with the correct answer attached. Somebody had to label those 10,000 photos. That labelling is usually the expensive part of a real project, far more expensive than the computing.

03 / Basics

The Artificial Neuron — The Whole Building Block MULTIPLY, ADD, SQUASH

If you understand this one picture, you understand the atom that everything else is built from.

A single neuron: three inputs multiplied by weights, summed with a bias, then passed through a ReLU activation.

Step 1 — Multiply

Each incoming number is multiplied by a weight. A large positive weight means "this input matters a lot, and in favour". A negative weight means "this input argues against".

The weights are the neuron's opinion. They start random and are corrected by training.

Step 2 — Add

Sum all the multiplied values, then add one extra number called the bias. The bias shifts the whole result up or down, letting the neuron fire more or less readily regardless of its inputs.

Steps 1 and 2 together are just a weighted average with an offset.

Step 3 — Squash

Pass the sum through an activation function. Here it is ReLU: keep the number if it is positive, otherwise output zero.

This step looks trivial. The next slide shows that without it, nothing works at all.

The Only Thing You Need to Hold On To

A neuron has exactly two kinds of number: the inputs, which come from the data or from the previous layer and change with every example, and the weights and bias, which belong to the neuron and stay fixed while it is being used. Training is the process of changing the second kind. Nothing else about the network ever changes — not the structure, not the arithmetic, only those numbers.

04 / Basics

Activation Functions — Why the Bend Is Essential NO BEND, NO LEARNING

Sigmoid, tanh and ReLU plotted with their derivatives.

The Argument in Four Lines

Suppose we remove the activation function. Layer one computes y = W₁x. Layer two computes z = W₂y. Substitute:

z = W₂(W₁x) = (W₂W₁)x = Wx

Two layers collapse into one layer with a single combined weight matrix. Add a hundred more and they still collapse into one. A stack of straight lines is a straight line.

The activation function is the bend that stops the collapse. It is the only reason depth means anything.

Reading the Figure

The solid line is the function: what the neuron outputs. The dashed line is its slope at each point, which is the number training uses to decide how to adjust the weights.

Look at sigmoid on the left. Away from the centre the dashed line is almost flat on the floor. A slope near zero means "adjusting this weight changes nothing", so learning stops. That is the vanishing gradient problem, and it is why deep networks were considered untrainable for years.

Which One to Use

FunctionOutput rangeUse it
ReLU0 to ∞Everywhere inside the network. This is the default.
Sigmoid0 to 1Final layer only, for a yes/no answer as a probability
SoftmaxSums to 1Final layer, for choosing one of several classes
Tanh−1 to 1Rarely; largely replaced by ReLU
GELU≈ −0.17 to ∞Modern large models; a smoother ReLU

If you are unsure, use ReLU in the hidden layers and softmax at the output. That combination covers the overwhelming majority of real networks.

Why ReLU Is Genuinely Better, Not Just Simpler

  • Its slope is exactly 1 wherever the neuron is active, so the training signal is passed back through many layers without shrinking.
  • It is a comparison and a select, which a processor does in a single instruction. Sigmoid needs an exponential, which is far slower.
  • It outputs exact zeros, so a portion of the network is switched off for any given input. That sparsity is useful in itself.
05 / Basics

Layers — Wiring Neurons Together THE FORWARD PASS

A four-layer network: three inputs, two hidden layers, two outputs, fully connected.

The Vocabulary, Once

TermMeaning
LayerA row of neurons that all read the same inputs
Input layerThe raw numbers; it does no computing
Hidden layerAny layer in between. "Hidden" only means you do not observe it directly.
Output layerThe final answer: one neuron per class
WidthNeurons in a layer
DepthNumber of layers. "Deep learning" means nothing more than this.
Fully connectedEvery neuron reads every output of the previous layer
Forward passRunning data left to right to get a prediction

The Whole Computation, In One Expression

Doing this one neuron at a time is tedious, so a whole layer is written as a matrix multiply. For a layer with weights W and biases b:

a = f(Wx + b)

Chain three of those together and you have the entire network:

output = f₃(W₃ · f₂(W₂ · f₁(W₁x + b₁) + b₂) + b₃)

That expression is a complete neural network. Everything that follows in this deck is about choosing a better shape for W, and about how to find good values for the numbers inside it.

Counting the Parameters

Layer sizes: 3 → 4 → 4 → 2

weights: 3×4 + 4×4 + 4×2 = 12 + 16 + 8 = 36
biases :   4  +   4  +   2            = 10
                                  total = 46

Forty-six numbers to learn. A network that recognizes handwritten digits needs roughly 100,000. GPT-scale language models need hundreds of billions. The arithmetic on this slide does not change; only the size of the matrices does.

06 / Basics

Loss — Putting a Number on Being Wrong THE THING WE MINIMIZE

The network cannot improve until "wrong" becomes a number it can try to reduce.

The Idea

Run an example through the network. Compare the prediction with the correct answer. Produce a single number: large if wrong, small if right, zero if perfect. That number is the loss.

Average it over all the training examples and you have one number describing how good the entire network currently is. Training is the search for weights that make it small.

The Two You Will Actually Use

Mean squared error — for predicting a quantity, such as a house price:

L = (1/n) Σ (prediction − truth)²

Cross-entropy — for choosing a category, which is what classification means:

L = − log(probability assigned to the correct class)

Cross-entropy is worth a moment. If the network gives the right answer a probability of 1.0, the loss is −log(1) = 0: no penalty. As that probability approaches zero the loss grows without limit. It punishes confident mistakes far more harshly than uncertain ones, which is exactly the behaviour you want.

Worked Example — Three Predictions, One Truth

The correct answer is "cat". Here is what the loss says about three different networks:

P(cat)P(dog)VerdictLoss = −log P(cat)
0.990.01right, confident0.01
0.700.30right, unsure0.36
0.500.50no opinion0.69
0.200.80wrong1.61
0.010.99wrong, confident4.61

Note the last row. Being confidently wrong costs 460 times more than being confidently right. That steep penalty is what pushes the weights hard in the right direction.

A Useful Sanity Check

Before a network has learned anything it should guess uniformly at random. For C classes that gives each a probability of 1/C, so the starting loss should be approximately:

L₀ ≈ ln(C)

For 10 classes that is ln(10) = 2.303. If your training starts at 2.30, the setup is sound. If it starts at 47, something is wrong before you have trained at all, and no amount of training will repair it.

07 / Basics

Gradient Descent — How the Weights Actually Change WALK DOWNHILL

A loss surface drawn as contour ellipses with a gradient descent path stepping toward the minimum.

The Mental Picture

Imagine you are standing on a foggy hillside and want to reach the bottom of the valley. You cannot see the valley. But you can feel which way the ground slopes under your feet, so you take a step downhill, and repeat.

The hillside is the loss, plotted against the weights. Your position is the current set of weights. The slope you can feel is the gradient. That is the entire algorithm.

What a Gradient Is

For a single weight, the gradient ∂L/∂w answers one question: if I increase this weight slightly, does the loss go up or down, and by how much?

  • Gradient is positive → increasing the weight makes things worse → decrease it.
  • Gradient is negative → increasing the weight makes things better → increase it.
  • Gradient is near zero → this weight is not the problem → leave it.

Subtracting the gradient handles all three cases at once, which is why the update rule is a single line.

The Update Rule, Term by Term

w ← w − η · ∂L/∂w
PieceRead it as
wOne weight, out of possibly millions
∂L/∂wWhich way is uphill for this particular weight
Go the other way, because we want to descend
η (eta)The learning rate: how large a step to take
Overwrite the old value with the new one

Every weight in the network is updated by this same rule, independently, at every step. There is no second algorithm hiding behind it.

Three Names for the Same Thing

VariantExamples used per stepIn practice
Batch GDAll of themAccurate but far too slow
Stochastic GDOneFast but very noisy
Mini-batch GD32 to 256What everybody actually uses

When somebody says "SGD" they almost always mean mini-batch. The batch size is chosen to keep the hardware busy, and 32 to 256 is the usual range.

08 / Basics

Backpropagation — Where the Gradients Come From ASSIGNING BLAME

Gradient descent needs a gradient for every weight. Backpropagation is the method that produces them all in one sweep.

Forward pass computing a prediction, then a backward pass sending error signals to each layer.

The Problem It Solves

The network predicted "dog" and the answer was "cat". Some weight buried in layer two contributed to that mistake. How much? You cannot measure it directly, because that weight only affects the loss through everything downstream of it.

The naive alternative is to nudge each weight, re-run the whole network and see what changes. For a million weights that is a million forward passes per step. Completely impractical.

The Trick: Work Backwards

Start at the loss, where the error is known exactly. Ask the last layer: given this error, how much did each of your weights contribute, and how much error should I pass to the layer before you? Then ask that layer the same question. Repeat to the start.

Each layer only needs the number handed down from the layer above it, plus values it already saved during the forward pass. Nothing is recomputed from scratch.

The Chain Rule, Which Is All This Is

∂L/∂w = ∂L/∂a · ∂a/∂z · ∂z/∂w

Read right to left: how the weight affects the sum, times how the sum affects the activation, times how the activation affects the loss. Multiply the three and you have how the weight affects the loss.

This is ordinary calculus from a first course, applied mechanically. The insight in backpropagation is not the mathematics; it is the order of evaluation that lets every weight share the work.

Why This Matters Enormously

MethodCost per training step
Nudge each weight and re-runOne forward pass per weight
BackpropagationAbout two forward passes, total

For a network with 25 million weights that is the difference between 25 million forward passes and two. Backpropagation is the reason training a large network is possible at all, and it is why the field stalled until it became widely understood in 1986.

You Will Never Write This

PyTorch, TensorFlow and JAX record every operation you perform and derive the backward pass automatically. In practice you write loss.backward() and the gradients appear. It is still worth understanding, because almost every training bug you will meet is a gradient that is zero, enormous, or not flowing at all.

09 / Basics

The Learning Rate and the Training Loop PUTTING IT TOGETHER

Three gradient descent runs on the same curve with a small, a good and an oversized learning rate.

Reading the Three Panels

  • Too small. Every step is correct but tiny. The network does eventually converge, and you wait ten times longer than necessary.
  • About right. Large steps far from the bottom, naturally smaller steps as the slope flattens, because the gradient itself shrinks.
  • Too large. Each step jumps past the minimum and lands higher up the other side. The loss grows and eventually becomes NaN.

If your loss becomes NaN in the first few steps, the learning rate is the first thing to divide by ten.

Words You Will See in Every Tutorial

TermMeaning
BatchA handful of examples processed together, usually 32 to 256
StepOne batch: forward, loss, backward, update
EpochOne complete pass over the training data
Learning rateStep size. Start at 0.1 for SGD, 0.001 for Adam.
OptimizerThe rule that applies the update. SGD and Adam are the two you need.
ScheduleA plan for shrinking the learning rate as training proceeds

The Training Loop — Five Steps, Repeated

for epoch in range(num_epochs):
    for batch_x, batch_y in training_data:

        # 1. Forward: what does the network say?
        prediction = model(batch_x)

        # 2. Loss: how wrong is that?
        loss = loss_function(prediction, batch_y)

        # 3. Clear the gradients from last time
        optimizer.zero_grad()

        # 4. Backward: blame every weight
        loss.backward()

        # 5. Update: w ← w − η · gradient
        optimizer.step()

That is a complete PyTorch training loop. It is the same five steps whether the model has 46 parameters or 46 billion, and it is the same loop used to train every network in the rest of this deck.

Why Step 3 Exists

PyTorch adds new gradients to whatever is already stored rather than replacing them. Omit zero_grad() and every batch silently accumulates on top of all previous batches. Training will not crash. It will simply behave strangely and you will not know why. This is the single most common beginner bug.

10 / Basics

Watching a Real Network Learn EVERYTHING SO FAR, IN ACTION

A two-layer network with 10 hidden neurons, trained by the exact loop on the previous slide. Both figures come from that one run.

The network's decision boundary at epoch 0, epoch 15 and epoch 400, moving from meaningless to correctly curved.
Training and validation loss falling together over 400 epochs.

What You Are Looking At

The two colours of dot are two classes that cannot be separated by a straight line. The background shading is the network's opinion at every point on the plane.

At epoch 0 the weights are random and the boundary is meaningless. By epoch 400 it has curved itself around the shape of the data. The only thing that changed is the values inside two small weight matrices.

The Loss Curve Is Your Instrument Panel

Training loss is measured on the data the network learns from. Validation loss is measured on data it has never seen, held back deliberately. The gap between them is the single most informative thing in machine learning:

  • Falling together, staying close — healthy, which is what this run shows.
  • Training falls, validation rises — overfitting: it is memorizing rather than learning.
  • Neither falls — underfitting: too small a model, or too low a learning rate.

End of Part 1

You now have the complete machinery: neurons, layers, a loss, gradients, backpropagation and a training loop. Everything in Part 2 reuses it without modification.

The next slide asks one question: what happens when we point this exact machinery at a photograph?

11 / Motivation
Part 2 · Convolutional Networks

Why the Network From Part 1 Fails on Images THE PARAMETER BLOWUP

Take the fully connected network you just learned, point it at a photograph, and it breaks in three specific ways.

Failure 1 — Parameter Count

A single ImageNet image is 224 × 224 × 3 = 150,528 numbers. Connect that to one modest hidden layer of 1,000 units:

150,528 × 1,000 = 150.5M weights

That is one layer. It already exceeds the 138M parameters of the entire VGG-16 network, and 25.6M of ResNet-50. At 4 bytes per float the layer alone is 602 MB.

Failure 2 — No Translation Structure

A dense layer has an independent weight for every pixel position. A cat in the top-left and the same cat in the bottom-right activate two completely disjoint sets of weights.

The network must learn "cat" separately at every one of the 50,176 spatial positions. Nothing in the architecture says the two are related, so nothing transfers.

Failure 3 — Locality Is Discarded

Flattening the image destroys adjacency. Pixel (10, 10) and pixel (10, 11) are neighbours; after flatten they are indices 2,250 and 2,253 with no relationship the layer can see. Permute all input pixels with a fixed permutation and a dense network trains to the same accuracy. That is the tell: it never used the spatial structure.

What Convolution Changes

PropertyDenseConv
ConnectivityGlobalLocal (K×K)
Weights per output150,52827 (3×3×3)
Weights reused?NoEvery position
Shift the inputUnrelated outputOutput shifts too
Params scale withImage sizeKernel & channels only

The last row is the one that matters most. A 3×3, 3→64 conv layer has 1,792 parameters whether the input is 32×32 or 4096×4096.

The Three Priors Baked Into a Conv Layer

  • Locality. Pixels that matter together are near each other. Edges, corners and textures are local phenomena.
  • Stationarity. A useful feature detector at one location is useful at every location, so share the weights.
  • Compositionality. Edges compose into motifs, motifs into parts, parts into objects. Stack the layers and the hierarchy emerges.

These are assumptions about images, not about learning. They are why a CNN needs far less data than an unstructured model to reach the same accuracy, and why they fail on data where the assumptions do not hold, such as tabular features in arbitrary column order.

Historical Note

Hubel and Wiesel (1962) found cells in the cat visual cortex that respond to edges in a small region of the visual field, and complex cells that pool over simple cells. Fukushima's Neocognitron (1980) turned that into an architecture. LeCun's LeNet-5 (1998) added backpropagation and shipped it on cheque digit recognition. The ideas are old; the compute and the labelled data arrived in 2012.

12 / The Idea

The Fix: Look at Small Patches, and Reuse the Same Detector SEE IT FIRST

Before any arithmetic, here is what a convolution filter actually does. This output was produced by running the kernel in the middle over the picture on the left.

An input image, a 3 by 3 kernel, and the resulting feature map in which vertical edges are highlighted.

What Just Happened

A tiny 3×3 window slid across every position of the image. At each stop it multiplied the nine pixels underneath by nine fixed numbers and added them up, producing one output number.

That is the entire operation. Slide, multiply, add.

Why the Result Looks Like That

Those nine numbers were chosen to compute "left side minus right side". Inside a flat region both sides are equal, so the answer is zero and the output is dark.

At a vertical edge the two sides differ sharply, so the answer is large. The filter found the edges without anyone telling it what an edge is.

The Two Things This Buys

  • Nine numbers, not 150,528. The filter is tiny no matter how large the image is.
  • The same nine everywhere. Learn an edge detector once and it works in every corner of every image.

These two properties are exactly the two failures on the previous slide, solved.

The Single Most Important Sentence in This Deck

In a real network nobody chooses those nine numbers. They start random and are learned by exactly the gradient descent from slide 7. The network works out for itself that edge detectors are useful, because edge detectors reduce the loss.

13 / The Operation

Convolution — Worked Out by Hand SLIDE, MULTIPLY, SUM

The Definition

Slide a small weight matrix (the kernel or filter) over the input. At each position take the elementwise product with the patch underneath and sum it to a single number.

Y[i,j] = Σm Σn X[i+m, j+n] · W[m,n] + b

Strictly this is cross-correlation. True convolution flips the kernel first. Because the kernel is learned, the flip is irrelevant in practice, and every deep learning framework implements cross-correlation and calls it convolution.

The Input (5×5) and Kernel (3×3)

X =
  1  2  3  0  1
  0  1  2  3  1
  1  0  1  2  0
  2  1  0  1  3
  1  2  1  0  2
W =
  1  0  -1
  1  0  -1
  1  0  -1

# vertical edge
# detector

This kernel computes left column minus right column, summed over three rows. It fires positively on a light-to-dark vertical transition and negatively on dark-to-light.

One Output Element, Step by Step

Top-left 3×3 patch of X:

  1  2  3        1  0 -1
  0  1  2   ⊙    1  0 -1
  1  0  1        1  0 -1

= (1·1 + 2·0 + 3·-1)
+ (0·1 + 1·0 + 2·-1)
+ (1·1 + 0·0 + 1·-1)
= (1 + 0 - 3) + (0 + 0 - 2) + (1 + 0 - 1)
= -2 + -2 + 0
= -4

Sliding the Window

INPUT 5×5 — window at position (0,0) 12301 01231 10120 21013 12102 OUTPUT 3×3 -4 -24 0-4-1 20-3 Each cell = one dot product of 9 numbers with the same 9 weights

Reading the Output

Nine dot products, nine outputs, and critically the same nine weights every time. Whatever the kernel has learned to detect, it is detected everywhere at once. That is weight sharing, and it is the single most important structural idea in the architecture.

Notice the output shrank from 5×5 to 3×3. A K×K kernel with no padding loses K-1 pixels in each dimension because the window cannot hang off the edge. Stack ten 3×3 layers and a 32×32 image erodes to 12×12 purely from boundary effects. Padding, two slides on, fixes that.

Two Things People Get Backwards

  • The kernel is not applied to one pixel. It is applied to a K×K neighbourhood and produces one number, so it is a dimensionality reduction at each position.
  • The bias is one scalar per output channel, not one per output pixel. A 64-filter layer has 64 biases regardless of resolution.
14 / The Idea

Change the Nine Numbers, Change What Is Detected ONE LAYER HOLDS MANY FILTERS

The same input image convolved with four different kernels producing four different feature maps.

Same Input, Four Different Answers

Nothing changed except the nine numbers in the kernel. Each choice makes a completely different property of the image visible: vertical structure, horizontal structure, smoothness, or fine detail.

A real convolutional layer does not hold one filter. It holds 32, 64, or 256 of them, all applied to the same input, all producing their own output map. The layer then hands that whole stack of maps to the next layer.

Two Words to Learn Now

WordMeaning
Kernel / filterThe small grid of learned numbers, typically 3×3
Feature mapThe output image one filter produces
ChannelOne feature map inside the stack. A layer with 64 filters outputs 64 channels.

How Many Numbers Is That?

A layer with 64 filters of size 3×3
reading a 3-channel colour image:

  weights = 3 · 3 · 3 · 64 = 1,728
  biases  =             64 =    64
                            ───────
                              1,792

The fully connected layer on slide 11
needed 150,528,000 for the same job.

That is roughly 84,000 times fewer numbers to learn, and the convolutional version is the one that actually works. Fewer parameters is not a compromise here; it is the reason it generalizes.

The Filters in the Figure Are Hand-Picked. Real Ones Are Not.

These four are classic image-processing kernels chosen so the effect is obvious to the eye. A trained network discovers its own, and the early ones do come out looking remarkably like edge and colour detectors — which is a good sign, because it means the network independently rediscovered what human vision researchers found in the 1960s.

15 / Geometry

Padding, Stride, Dilation, and the Output-Size Formula GET THIS WRONG AND NOTHING RUNS

The One Formula to Memorize

O = ⌊ (I + 2P − Keff) / S ⌋ + 1

with the dilation-adjusted kernel size

Keff = D · (K − 1) + 1
SymbolMeaningTypical
IInput spatial size224, 32
KKernel size1, 3, 5, 7
SStride1 or 2
PZero-padding each side0 or (K−1)/2
DDilation1, 2, 4

Padding — Three Conventions

  • Valid (P = 0). Output shrinks by K−1. No invented data.
  • Same (P = (K−1)/2 with S = 1). Output size equals input size. This is why odd kernel sizes dominate: 3, 5, 7 give integer padding, 4 does not.
  • Full (P = K−1). Output grows by K−1. Rare in forward passes, but it is exactly what the backward pass computes.

Zero padding is the default but it is a lie about the world: it tells the network there are black pixels outside the frame. reflect and replicate padding avoid that artefact and matter for dense prediction tasks such as segmentation and super-resolution.

Worked Examples

LayerComputationOut
224, K7, S2, P3(224+6−7)/2+1 = 111+1112
112, maxpool K3, S2, P1(112+2−3)/2+1 = 55+156
56, K3, S1, P1(56+2−3)/1+1 = 55+156
56, K3, S2, P1(56+2−3)/2+1 = 27+128
28, K1, S1, P0(28+0−1)/1+1 = 27+128
7, K3, S1, P2, D2Keff=5; (7+4−5)/1+17

The first two rows are the ResNet stem: 224 → 112 → 56 before a single residual block runs.

Stride — Downsampling Inside the Convolution

STRIDE 1 — windows overlap, 5 positions out = 5 STRIDE 2 — windows skip, 3 positions out = 3 ≈ half the spatial size

Stride 2 halves height and width, which cuts the activation tensor to one quarter the elements and the downstream FLOPs with it. Modern architectures prefer strided convolution over pooling for downsampling because the downsampling itself becomes learnable.

Dilation — Reach Without Cost

D=1 · 3×3 covers 3×3 D=2 · same 9 weights, covers 5×5 9 weights either way. Same FLOPs. Wider view.

Stacking dilations 1, 2, 4 with 3×3 kernels reaches a 15×15 receptive field in three layers at full resolution: r = 1 + 2 = 3, then 3 + 4 = 7, then 7 + 8 = 15. Semantic segmentation networks (DeepLab) use this to get context without ever downsampling and having to upsample back.

The Floor Function Silently Discards Data

With I = 7, K = 2, S = 2, P = 0 the formula gives ⌊5/2⌋ + 1 = 3, and the last row and column of the input are never read. This is a real and common source of asymmetric behaviour at image borders. Use ceil_mode=True or pad to an even size when it matters.

16 / Channels

From 2-D to Multi-Channel — What a Filter Actually Is THE PART THAT CONFUSES EVERYONE

A Filter Is a 3-D Volume, Not a 2-D Square

A conv layer taking C_in channels and producing C_out channels holds C_out filters. Each filter has shape (C_in, K, K) — it spans all input channels at once.

One filter slides over the input and produces one 2-D output map. Stack the C_out maps and you have the output tensor.

W shape = (C_out, C_in, K, K)   b shape = (C_out,)

The depth dimension of each filter is fully connected; only the spatial dimensions are local and shared. A conv layer is, precisely, a dense layer applied identically to every K×K neighbourhood.

Parameter and Compute Formulas

params = (K · K · C_in + 1) · C_out
MACs = K · K · C_in · C_out · H_out · W_out

One multiply-accumulate is conventionally counted as 2 FLOPs. Note what is absent from the parameter formula: the spatial size. Note what is present in the MAC formula: the spatial size. Parameters and compute scale completely differently, which is why parameter count is a poor proxy for latency.

Worked: AlexNet conv1

Input   : 3 × 224 × 224
Filters : 96 of shape (3, 11, 11), stride 4
Output  : 96 × 55 × 55

params = (11·11·3 + 1) · 96
       = (363 + 1) · 96
       = 34,944

MACs   = 11·11·3 · 96 · 55 · 55
       = 34,848 · 3,025
       = 105.4M  (≈ 0.21 GFLOPs)

The Shape Flow

INPUT · 3 channels H×W×3 64 FILTERS · each (3,3,3) filter 1 filter 2 filter 64 OUTPUT · 64 channels H×W×64 Filter k produces output channel k. Every filter reads all 3 input channels; nothing is processed per-channel.

The 1×1 Convolution Is Not a No-Op

With K = 1 there is no spatial mixing at all, but there is still full mixing across channels. A 1×1, 256→64 conv is a learned linear projection from a 256-dimensional vector to a 64-dimensional vector, applied independently at every pixel.

Parameters: 1·1·256·64 + 64 = 16,448. It is the cheapest way to change channel depth, and it is the workhorse of Inception bottlenecks, ResNet bottleneck blocks, and depthwise separable convolutions. Slide 27 quantifies the saving.

Memory Layout Matters

PyTorch defaults to NCHW (batch, channel, height, width); TensorFlow defaults to NHWC. Tensor Cores and most vendor kernels prefer NHWC (channels_last in PyTorch), and switching layout on a ResNet-50 mixed-precision training run is commonly worth 20 to 40 percent throughput for a one-line change. It is the highest-leverage flag most people never set.

17 / Inductive Bias

Weight Sharing, Equivariance, and Invariance WHY IT GENERALIZES

Equivariance Is Not Invariance

Equivariant: shift the input, and the output shifts by the same amount. Convolution is exactly translation-equivariant.

conv(shift(x)) = shift(conv(x))

Invariant: shift the input, and the output does not change at all. Convolution is not invariant, and it should not be — for segmentation and detection you need to know where the feature is.

Invariance is manufactured later and deliberately: pooling gives local invariance to small shifts, global average pooling gives full invariance over the image, and data augmentation supplies invariance to the transformations convolution does not handle natively.

What Convolution Does Not Give You for Free

TransformationBuilt in?How it is obtained
TranslationYesWeight sharing
Small local shiftYesPooling
RotationNoAugmentation
ScaleNoImage pyramids, FPN, augmentation
IlluminationNoNormalization, colour jitter
ViewpointNoAugmentation, more data

The honest summary: a CNN is a translation-equivariant model, and every other invariance is bolted on by the training pipeline rather than the architecture.

The Statistical Payoff, Quantified

Compare a dense layer and a conv layer, both mapping a 32×32×3 image to 32×32×64 features:

DenseConv 3×3
Parameters201.3M1,792
Ratio112,000× fewer
Training examples neededEnormousThousands
dense: 3072 · 65536 = 201,326,592
conv : 3·3·3·64 + 64 = 1,792

Fewer parameters is not the point in itself. The point is that each parameter sees far more training signal: a conv weight receives a gradient contribution from all 1,024 spatial positions of every image, so one image gives it 1,024 effective updates rather than one.

The Feature Hierarchy That Emerges

Four stages of a CNN: edges, then corners and texture, then object parts, then whole objects.

The Aliasing Caveat

Perfect translation equivariance holds only for stride 1. Any strided layer subsamples, and subsampling without a low-pass filter aliases. Zhang (2019) showed that shifting an ImageNet input by one pixel can flip a ResNet-50 prediction, and that adding a blur before each downsample both fixes much of the instability and improves accuracy. Modern CNNs are approximately, not exactly, shift-equivariant.

18 / Receptive Field

Receptive Field — How Depth Buys Context THE DESIGN CONSTRAINT

Definition and Recurrence

The receptive field of a unit is the region of the input image that can influence its value. A layer-1 unit with a 3×3 kernel sees 3×3 pixels. A layer-2 unit sees 3×3 layer-1 units, each of which sees 3×3 pixels, so it sees 5×5.

rl = rl−1 + (kl − 1) · jl−1
jl = jl−1 · sl    with r0 = 1, j0 = 1

j is the jump: how many input pixels apart two adjacent units at this layer are. Stride does not grow the receptive field directly — it grows the jump, which then multiplies the growth of every later layer. That compounding is why strided layers are so effective at expanding context.

Worked Trace

LayerksjRF
input11
conv13113
conv23115
maxpool2226
conv331210
conv431214
maxpool22416
conv531424

Eight layers reach 24×24. After the first pool, each 3×3 conv adds (3−1)·2 = 4 pixels rather than 2. After the second pool it adds 8.

Two 3×3 Beat One 5×5 — The VGG Argument

one 5×5 layer 25C² params two 3×3 layers · same 5×5 reach 18C² params · 28% fewer and two nonlinearities 3 × 3×3 = 27C² vs 7×7 = 49C² · 45% fewer

Stacked small kernels are strictly better for a fixed receptive field: fewer parameters, fewer FLOPs, and more nonlinearity, which means more expressive power. This is the observation that made 3×3 the near-universal default after VGG in 2014, and it is why 11×11 and 7×7 kernels vanished from everything except the network stem.

The Effective Receptive Field Is Much Smaller

The theoretical receptive field is the set of pixels that can influence a unit. Luo et al. (2016) measured the gradient magnitude and found the influence is approximately Gaussian and concentrated in the centre: the effective receptive field grows as O(√L) in depth while the theoretical one grows as O(L).

Practical consequence: a network whose theoretical receptive field just barely covers the object will underperform. Design for a theoretical receptive field two to three times the size of the objects you care about, or add dilation, or add explicit global context such as a pooling pyramid.

Where This Bites in Practice

  • Small objects. Detect them on early, high-resolution feature maps. Late maps have the semantics but not the resolution. Feature Pyramid Networks exist for exactly this trade-off.
  • Large objects. If the receptive field is smaller than the object, the network classifies texture rather than shape, which is a documented failure mode of ImageNet CNNs.
  • Segmentation. Needs both fine resolution and wide context, which is why U-Net keeps skip connections from the encoder to the decoder.
19 / Nonlinearity & Pooling

ReLU and Pooling — The Other Two Ingredients CONV ALONE IS STILL LINEAR

Stack two convolutions with nothing between them and the composition is one convolution. Without a nonlinearity, depth buys nothing.

Why ReLU Won

ActivationDefinitionProblem
Sigmoid1/(1+e−x)Gradient ≤ 0.25; vanishes over depth
Tanhtanh(x)Saturates at both ends
ReLUmax(0, x)Dead units below zero
Leaky ReLUmax(0.01x, x)Extra hyperparameter
GELU / SiLUx·Φ(x) / x·σ(x)Costlier to evaluate

ReLU has gradient exactly 1 for all positive inputs, so the gradient signal does not decay multiplicatively with depth. It is also a comparison and a select, which is far cheaper than an exponential. AlexNet reported reaching a 25 percent training error threshold on CIFAR-10 roughly six times faster with ReLU than with tanh, and that speedup is what made deep CNNs trainable in practice.

Dead ReLUs — A Real Failure Mode

If a unit's pre-activation is negative for every input in the dataset, its gradient is zero forever and the unit never recovers. This typically follows a large learning rate driving the bias strongly negative. Symptoms: a large fraction of channels output identically zero, and accuracy plateaus below what the capacity should allow.

Fixes, in order of preference: lower the learning rate, add BatchNorm before the activation, or switch to Leaky ReLU or GELU.

Pooling — Downsample and Buy Local Invariance

A 4 by 4 feature map reduced to 2 by 2 by taking the maximum of each 2 by 2 block.

Global Average Pooling Replaced the Classifier Head

VGG-16 flattens a 512×7×7 map into 25,088 values and feeds three dense layers: 123.6M of its 138M parameters live in that head, roughly 90 percent of the model, doing almost none of the feature work.

fc1: 25088 × 4096 = 102,760,448
fc2:  4096 × 4096 =  16,777,216
fc3:  4096 × 1000 =   4,096,000
                    ─────────────
                    123,633,664

Global average pooling collapses C×H×W to C by averaging each channel, then a single linear layer maps C→classes. For ResNet-50 that is 2048×1000 = 2.05M parameters instead of 123.6M, and it accepts any input resolution. Introduced in Network-in-Network (2014), adopted by GoogLeNet and everything after.

Pooling Is Falling Out of Favour

Max pooling discards 75 percent of the activations in a 2×2 window and has no parameters to learn what is worth keeping. Strided convolution downsamples and learns the reduction at the same time, at the cost of parameters and FLOPs. Most post-2015 architectures keep exactly one max pool in the stem and use strided convolution everywhere else. Global average pooling, by contrast, is now universal.

20 / Putting It Together

The Complete CNN, End to End EVERY PIECE SO FAR, IN ORDER

You now have all the parts. This is how they are stacked, and it is the shape of essentially every convolutional network ever built.

An image passing through conv, ReLU, pool, conv, pool and a final dense layer.

The Repeating Unit

Three operations, repeated a few times:

  • Convolution — slide learned filters over the input to find patterns.
  • ReLU — throw away the negatives, so the network can express something other than a straight line.
  • Pooling — shrink the map, keeping the strongest responses.

Then one small fully connected layer turns the final features into a decision. That last layer is exactly the network from Part 1, and it is now doing an easy job because the hard work has already been done.

The Two Trends as You Move Right

QuantityDirectionWhy
Spatial sizeShrinks: 32 → 16 → 8Pooling and striding; exact position matters less as you go deeper
ChannelsGrows: 3 → 32 → 64More kinds of pattern to keep track of
MeaningLow to highFrom "there is an edge here" to "this is a cat"

The Full Thing in Twelve Lines of PyTorch

model = nn.Sequential(
    nn.Conv2d(3,  32, 3, padding=1),  # find edges
    nn.ReLU(),
    nn.MaxPool2d(2),                  # 32×32 → 16×16

    nn.Conv2d(32, 64, 3, padding=1),  # find shapes
    nn.ReLU(),
    nn.MaxPool2d(2),                  # 16×16 → 8×8

    nn.Flatten(),
    nn.Linear(64*8*8, 10),        # decide
)

That is a working image classifier. Train it with the identical five-step loop from slide 9 — forward, loss, zero_grad, backward, step. Nothing about training changes.

Where Part 2 Goes From Here

Everything remaining in this deck is refinement of this picture: how to size the filters and strides precisely, how to keep very deep stacks trainable, what the famous architectures changed, and how the whole thing is made to run quickly. The skeleton above does not change.

21 / Normalization

Batch Normalization and Its Relatives THE LAYER THAT MADE DEPTH TRAINABLE

The Operation

For each channel c, compute mean and variance over the batch and both spatial dimensions — that is N·H·W values per channel:

x̂ = (x − μc) / √(σ²c + ε)     y = γc · x̂ + βc

γ and β are learned, giving 2C parameters per layer. They exist so the layer can undo the normalization if that is what minimizes the loss; normalization is offered, not imposed.

Two further buffers, the running mean and running variance, are not learned by gradient descent. They are exponential moving averages updated during training and used in place of batch statistics at inference, so that a single image produces the same output regardless of what it is batched with.

Train Mode Versus Eval Mode

model.train()model.eval()
Statistics usedCurrent batchRunning averages
Running statsUpdatedFrozen
Depends on batch peersYesNo

Forgetting model.eval() before validation is the most common bug in this entire field. Symptom: validation accuracy is noisy, far below training accuracy, and changes when the batch size changes. It costs one line and hours of debugging.

Batch Size Sensitivity

BatchNorm estimates statistics from the batch, so the estimate degrades as the batch shrinks. Below roughly 8 to 16 images per device the noise becomes harmful; at batch size 1 the variance is zero and the layer is meaningless. Detection and segmentation, which often run 2 images per GPU because of resolution, hit this constantly. The workarounds are SyncBatchNorm across devices, frozen BatchNorm from a pretrained model, or switching to GroupNorm.

What Gets Averaged Over

VariantNormalizes overBatch-dependent?Used in
BatchNormN, H, W (per channel)YesClassification CNNs
LayerNormC, H, W (per sample)NoTransformers, ConvNeXt
InstanceNormH, W (per sample, channel)NoStyle transfer
GroupNormH, W and a group of channelsNoDetection, small batches

GroupNorm with 32 groups matches BatchNorm accuracy on ImageNet at large batch and clearly beats it below batch size 8, which is why detection frameworks default to it.

Why It Helps — The Story Changed

The original 2015 paper attributed the benefit to reducing "internal covariate shift". Santurkar et al. (2018) tested that directly by injecting noise after BatchNorm to deliberately increase covariate shift, and training still improved. The explanation that survived scrutiny is that BatchNorm smooths the loss landscape: it bounds the gradient magnitude and makes it more predictable, which permits much larger learning rates.

The practical effects are not in dispute and are large:

  • Learning rates 10 to 30 times higher become stable.
  • Networks past roughly 20 layers become trainable at all.
  • Weight initialization stops being delicate.
  • Batch statistics add noise, which regularizes; models with BatchNorm often need less dropout.

Ordering and the Bias Term

Conv → BatchNorm → ReLU     # the standard

nn.Conv2d(..., bias=False)   # bias is redundant:
                            # BN subtracts the mean,
                            # so β absorbs any bias

At inference the BatchNorm affine transform is a per-channel scale and shift, so it can be folded into the preceding convolution weights: the layer disappears entirely and costs nothing. Every deployment toolchain does this automatically, which is why BatchNorm is free in production and only expensive during training.

22 / Regularization

Keeping a High-Capacity Model Honest DATA BEATS ARCHITECTURE

Data Augmentation — The Highest Return per Unit of Effort

Augmentation manufactures the invariances convolution does not provide. It is free supervision: no new labels, no new architecture.

TransformInvariance taughtCare required
Random crop + resizeScale, positionDo not crop the object out
Horizontal flipLeft-right symmetryWrong for text and digits
Colour jitterIlluminationWrong if colour is the label
Rotation ±15°Small tilt6 and 9 collide at 180°
Cutout / random eraseOcclusionKeep the erased fraction modest
Mixup / CutMixLinear behaviour between classesRequires soft-label loss

AlexNet credited random 224×224 crops plus flips from 256×256 images with a large reduction in overfitting; the crop-and-flip pipeline alone multiplies the effective dataset size by more than 2,000.

Augment Training Only

Random augmentation applied to the validation set makes the metric noisy and pessimistic, and it silently invalidates every comparison you make. Validation should be deterministic: resize, centre crop, normalize. Test-time augmentation is a separate, deliberate technique in which several fixed views are averaged, and it must be applied identically to every model being compared.

Explicit Regularizers

TechniqueMechanismTypical setting
Weight decayPenalize ‖w‖²1e−4 (5e−4 for small data)
DropoutZero units at random0.5 in dense heads only
DropBlockZero contiguous regionsConv layers, block 7
Label smoothingTarget 0.9 instead of 1.0ε = 0.1
Stochastic depthSkip whole residual blocksVery deep networks
Early stoppingHalt on validation plateauPatience 10 epochs

Do not apply weight decay to BatchNorm's γ and β or to bias terms. Shrinking γ toward zero attenuates the signal for no regularization benefit, and excluding those parameters is typically worth a few tenths of a percent of accuracy.

Dropout Largely Left Convolutional Layers

Standard dropout zeroes individual activations independently. In a convolutional feature map neighbouring activations are strongly correlated, so the surviving neighbours carry the dropped information and little is actually removed. Two things replaced it:

  • Spatial dropout drops entire channels, which does remove a feature.
  • BatchNorm already injects batch-dependent noise, and combining the two often hurts because the variance BatchNorm calibrates during training differs from the variance at inference.

ResNet uses no dropout at all in its convolutional trunk; VGG uses 0.5 but only in its dense head.

Reading the Curves

SymptomDiagnosisAction
Train ≫ val accuracyOverfittingMore augmentation, more data, more weight decay
Both low, both flatUnderfittingBigger model, higher learning rate, train longer
Train loss will not fallOptimization bugOverfit 10 examples first; if that fails the bug is in the code
Val loss rises, val accuracy flatOverconfidenceLabel smoothing; accuracy is still the metric
23 / Anatomy

A Complete CNN — Every Shape and Parameter Accounted For CIFAR-10, 141K PARAMS

Shape and Parameter Trace

LayerOutput shapeParamsMACs
input3 × 32 × 320
conv 3→32, 3×3, p132 × 32 × 328960.88M
BN + ReLU32 × 32 × 3264
conv 32→32, 3×3, p132 × 32 × 329,2489.44M
BN + ReLU32 × 32 × 3264
maxpool 2×232 × 16 × 160
conv 32→64, 3×3, p164 × 16 × 1618,4964.72M
conv 64→64, 3×3, p164 × 16 × 1636,9289.44M
maxpool 2×264 × 8 × 80
conv 64→128, 3×3, p1128 × 8 × 873,8564.72M
global avg pool1280
linear 128→10101,2900.001M
total140,71429.2M

MACs are per image. Multiply by 2 for FLOPs: roughly 58 MFLOPs per forward pass.

Three Patterns Worth Naming

  • Resolution halves, channels double. 32×32×32 → 16×16×64 → 8×8×128. Each transition keeps the activation tensor shrinking by 2× while capacity per position grows, which is the standard way to trade spatial detail for semantic depth.
  • Parameters concentrate late, compute concentrates early. conv5 holds 52 percent of the parameters but only 16 percent of the MACs; conv2 holds 7 percent of the parameters and 32 percent of the MACs. Optimizing for parameter count and optimizing for latency are different problems.
  • Global average pooling caps the head. The classifier is 1,290 parameters, under 1 percent of the model. The VGG-style alternative would have been 128·4·4 × 512 and more.

Activation Memory — What Actually Fills the GPU

Stored activations per image (fp32):
  conv1 out   32·32·32 =  32,768
  conv2 out   32·32·32 =  32,768
  pool1        32·16·16 =   8,192
  conv3 out    64·16·16 =  16,384
  conv4 out    64·16·16 =  16,384
  pool2         64·8·8  =   4,096
  conv5 out    128·8·8  =   8,192
  pool3        128·4·4  =   2,048
                        ─────────
                          120,832 floats
                        ≈ 0.47 MB per image

batch 128  →  ≈ 60 MB of activations
weights    →  141K × 4 B = 0.56 MB

Activations exceed weights by more than 100×. Backpropagation must retain every one of them for the backward pass, which is why memory scales with batch size and why gradient checkpointing (recompute instead of store) is the standard remedy when a model will not fit.

The Same Model in PyTorch

def block(cin, cout):
    return nn.Sequential(
        nn.Conv2d(cin, cout, 3, padding=1, bias=False),
        nn.BatchNorm2d(cout),
        nn.ReLU(inplace=True),
    )

model = nn.Sequential(
    block(3, 32),  block(32, 32),  nn.MaxPool2d(2),
    block(32, 64), block(64, 64),  nn.MaxPool2d(2),
    block(64, 128),
    nn.AdaptiveAvgPool2d(1), nn.Flatten(),
    nn.Linear(128, 10),
)

AdaptiveAvgPool2d(1) is global average pooling and works for any input resolution. This model reaches roughly 92 to 93 percent on CIFAR-10 with standard augmentation and a cosine schedule.

24 / Conv Backprop

Gradients Through a Convolution THE GRADIENT OF A CONV IS A CONV

Setup

Forward pass, dropping batch and channel indices for clarity:

Y[i,j] = Σm,n X[i+m, j+n] · W[m,n] + b

Backpropagation hands us dL/dY, the gradient of the loss with respect to every output element. We need three things: dL/dW, dL/db, and dL/dX to pass upstream.

1. Gradient With Respect to the Weights

W[m,n] was used at every output position, so by the multivariable chain rule its gradient sums over all of them:

dL/dW[m,n] = Σi,j dL/dY[i,j] · X[i+m, j+n]

That expression is itself a cross-correlation: the input correlated with the output gradient. This summation is the mathematical face of weight sharing. A single 3×3 kernel applied to a 56×56 map accumulates 3,136 gradient contributions per image per channel pair, which is exactly why conv weights are so well determined by comparatively little data.

2. Gradient With Respect to the Bias

dL/dbc = Σn,i,j dL/dY[n, c, i, j]

One scalar per output channel: sum the output gradient over batch and both spatial dimensions.

3. Gradient With Respect to the Input

Element X[p,q] contributed to every output whose window covered it. Collecting those terms:

dL/dX[p,q] = Σm,n dL/dY[p−m, q−n] · W[m,n]

The index signs are flipped relative to the forward pass. Concretely, this is a full convolution of dL/dY with W rotated by 180°, which is precisely the transposed convolution operation. Interior elements receive K² contributions; border elements receive fewer, because fewer windows covered them.

The Consequence

Both backward computations are convolutions. A framework therefore needs one highly optimized primitive, not three, and the backward pass costs roughly twice the forward pass: one convolution for the weight gradient, one for the input gradient.

forward : 1 conv  ·  backward : 2 convs  ·  total ≈ 3×

This 3× rule is the standard estimate for training cost versus inference cost per image, before any optimizer overhead.

Backward Through the Other Layers

LayerBackward rule
ReLUPass gradient where x > 0, zero elsewhere
Max poolRoute the full gradient to the argmax; zero to the rest
Avg poolDistribute gradient equally, 1/k² to each input
Global avg poolBroadcast 1/(H·W) across the whole map
BatchNormThrough γ, β, and also through μ and σ², since both depend on x

Max pooling requires storing the argmax indices from the forward pass, which is why it carries a memory cost despite having no parameters. The BatchNorm backward pass is the subtle one: treating μ and σ² as constants gives a wrong gradient, and it is a classic error in hand-rolled implementations.

Verify by Finite Differences

torch.autograd.gradcheck(
    layer, (x.double().requires_grad_(),), eps=1e-6)

Use float64: float32 rounding swamps the finite-difference estimate and produces failures that look like real bugs. If you ever write a custom convolution kernel, this check is not optional.

25 / Architectures

LeNet to ResNet — What Each Generation Actually Contributed 1998 TO 2017

YearModelDepthParamsFLOPsTop-5 errThe contribution
1998LeNet-5760K0.0005GMNIST 0.8%Conv, pool, dense head, trained by backpropagation. Deployed on cheque digits.
2012AlexNet860M0.7G15.3%ReLU, dropout, heavy augmentation, two GPUs. Beat the runner-up by 10.8 points and ended the hand-crafted-feature era.
2013ZFNet860M11.7%Smaller stem (7×7 s2 instead of 11×11 s4), found by visualizing what the filters had learned.
2014VGG-1616138M15.5G7.3%Uniform 3×3 stacks. Proved depth matters and that small kernels beat large ones. 90% of its parameters are in the dense head.
2014GoogLeNet226.8M1.5G6.67%Inception modules with 1×1 bottlenecks; global average pooling instead of dense layers. 20× fewer parameters than VGG at better accuracy.
2015ResNet-505025.6M4.1G≈7.1%*Residual connections plus BatchNorm. Depth stopped being a barrier.
2015ResNet-15215260M11.6G4.49%*Same idea, 152 layers. The ILSVRC-winning ensemble reached 3.57%, below the ≈5.1% human estimate.
2017MobileNetV1284.2M0.57G≈10.5%Depthwise separable convolution throughout. Built for phones, not leaderboards.
2017SENet154115M21G2.25%Squeeze-and-excitation: learn a per-channel gate from global context. Final ILSVRC winner.

*Reported numbers mix single-model against ensemble and single-crop against multi-crop, so cross-row comparisons are indicative rather than exact. ResNet-50 single-crop top-5 accuracy is approximately 92.9%.

The Trend That Reversed

Parameters rose from 60K to 138M and then fell to 6.8M and 25.6M while accuracy kept improving. VGG was the peak of brute force. Everything after it bought accuracy with better structure — bottlenecks, residuals, global pooling — rather than with more weights.

What Persisted

  • 3×3 kernels almost everywhere.
  • 1×1 kernels to change channel depth cheaply.
  • Resolution halves as channels double.
  • BatchNorm after every convolution.
  • Global average pooling as the head.
  • Residual connections by default.

What Was Abandoned

  • Large kernels outside the stem.
  • Local response normalization.
  • Large dense classifier heads.
  • Dropout in convolutional trunks.
  • Splitting a model across GPUs by hand.
  • Max pooling after nearly every block.
26 / Residual Learning

Why Depth Needed Skip Connections THE DEGRADATION PROBLEM

The Observation That Started It

He et al. (2015) trained a plain 20-layer and a plain 56-layer CNN on CIFAR-10. The 56-layer network was worse — on training error, not just test error.

That rules out overfitting entirely. It is also provably not a capacity problem: take the trained 20-layer network, append 36 identity layers, and you have a 56-layer network with at least equal training error. The solution exists in the parameter space. Gradient descent simply could not find it.

The diagnosis: a stack of conv layers finds it hard to represent the identity function, because doing so requires every layer to learn a precisely balanced near-identity mapping.

The Fix — Learn the Residual Instead

y = F(x, W) + x

Rather than asking the block to produce the desired mapping H(x), ask it to produce F(x) = H(x) − x and add x back. Now the identity is the default: drive the weights to zero and the block passes its input through unchanged. A block that is not useful can cheaply become a no-op, so adding depth can no longer hurt.

The gradient view is equally direct. Differentiating the skip path:

∂y/∂x = ∂F/∂x + 1

The +1 is an unattenuated gradient highway straight to earlier layers. Vanishing gradients require the product of many small factors; the additive identity term prevents that product from ever collapsing.

Basic Block and Bottleneck Block

BASIC · ResNet-18/34 3×3, 64 → 64 3×3, 64 → 64 + identity params = 2 · 3·3·64·64 = 73,728 BOTTLENECK · ResNet-50+ 1×1, 256 → 64 3×3, 64 → 64 1×1, 64 → 256 + params = 16,384 + 36,864 + 16,384 = 69,632 · but 256 channels wide 3×3 at 256→256 would be 589,824

The Bottleneck Arithmetic

Plain 3×3 at 256 → 256:
  3·3·256·256           = 589,824

Bottleneck 256→64→64→256:
  1·1·256·64            =  16,384
  3·3·64·64             =  36,864
  1·1·64·256            =  16,384
                          ─────────
                          69,632   (8.5× fewer)

The 1×1 layers compress to a cheap subspace, do the expensive spatial work there, then expand back. This is what lets ResNet-50 run 50 layers on 4.1 GFLOPs while VGG-16 needs 15.5 GFLOPs for 16.

Two Implementation Details

  • Shape mismatch. When a block changes channels or stride, the identity path cannot be used directly. Use a 1×1 convolution with matching stride on the shortcut. Do this only where required; identity shortcuts are both free and better.
  • Add before the activation. The order is conv → BN → ReLU → conv → BN → add → ReLU. Placing ReLU before the addition truncates the shortcut to non-negative values and degrades the gradient highway.
27 / Efficiency

Cheaper Convolutions — 1×1, Grouped, Depthwise Separable FACTORING THE OPERATION

A standard convolution mixes across space and across channels in one step. Every efficient variant separates those two jobs.

Depthwise Separable — Worked in Full

Input 64 × 112 × 112, output 128 × 112 × 112, kernel 3×3.

# Standard convolution
MACs = 3·3·64·128 · 112·112
     = 73,728 · 12,544
     = 924,844,032

# Depthwise: one 3×3 kernel per input channel,
# no cross-channel mixing at all
MACs = 3·3·64 · 112·112
     = 576 · 12,544
     = 7,225,344

# Pointwise: 1×1 mixes channels 64 → 128
MACs = 64·128 · 112·112
     = 8,192 · 12,544
     = 102,760,448

total = 109,985,792   →  8.41× fewer
reduction = 1 / (1/C_out + 1/K²)

With K = 3 the 1/K² = 1/9 term dominates once C_out is large, so the saving asymptotes at about 9×. Parameter counts fall by the identical factor: 73,728 becomes 8,768.

8.4× Fewer FLOPs Is Not 8.4× Faster

Depthwise convolution performs very little arithmetic per byte of memory it touches: each weight is used across one channel only, so there is almost no data reuse to amortize the loads. It is memory-bandwidth-bound, while a standard convolution is compute-bound and sits where GPUs are fastest.

In practice MobileNet's 8.4× FLOP reduction commonly yields 2× to 3× wall-clock speedup on a GPU, and considerably more on a mobile CPU where the compute ceiling is the binding constraint. Measure latency on the target device; never ship a decision made on a FLOP count alone.

The Full Family

VariantParamsMixes spaceMixes channels
Standard K×KK²·Cin·CoutYesYes
1×1 (pointwise)Cin·CoutNoYes
Depthwise K×KK²·CinYesNo
Grouped, g groupsK²·Cin·Cout/gYesWithin group
Separable (DW + PW)K²·Cin + Cin·CoutYesYes, separately

Depthwise convolution is grouped convolution with g = C_in. Standard convolution is grouped convolution with g = 1. It is one operator with a single dial.

Grouped Convolution

Split the input channels into g groups and convolve each group only with its own filters. Parameters and FLOPs divide by g exactly. AlexNet used 2 groups purely because the model did not fit on one GTX 580 with 3 GB; the accidental discovery was that it cost almost no accuracy.

ResNeXt made it deliberate, replacing the bottleneck 3×3 with a grouped 3×3 at cardinality 32 and reporting better ImageNet accuracy than ResNet at matched FLOPs and parameters. The cost: groups never exchange information, so ShuffleNet interleaves a channel shuffle between grouped layers to restore cross-group flow.

nn.Conv2d(256, 256, 3, padding=1, groups=32)
# 589,824 → 18,432 weights

nn.Conv2d(64, 64, 3, padding=1, groups=64)
# depthwise: 36,864 → 576 weights

Other Levers, Ranked by Return

TechniqueTypical gainAccuracy cost
INT8 quantization4× memory, 2–4× speed< 1% top-1
FP16 / BF162× memory, 2–8× on Tensor CoresNone
Depthwise separable2–3× latency1–2% top-1
Structured pruning1.5–3×0–2%, needs fine-tuning
Knowledge distillationSmall model, teacher accuracyRecovers most of the gap
Lower input resolutionQuadratic in FLOPsTask-dependent, often large

Reach for mixed precision first. It is one line, it is lossless, and on modern accelerators it is the largest single win available.

28 / Beyond Classification

Detection, Segmentation, and Learned Upsampling SAME BACKBONE, DIFFERENT HEAD

The Shared Structure

Backbone ResNet, pretrained Neck FPN, multi-scale class head box head mask head One trunk, many heads.

Almost every vision task reuses a classification backbone and changes only the head and the loss. This is why ImageNet pretraining transferred so broadly and why backbone improvements propagate to every downstream task at once.

Object Detection

FamilyApproachTrade-off
R-CNN (2014)Region proposals, CNN on each cropAccurate, ~47s per image
Fast R-CNNOne CNN pass, RoI pooling~200× faster
Faster R-CNNLearned region proposal networkEnd to end, ~5 fps
YOLO / SSDSingle shot, dense grid predictionReal time, weaker on small objects
RetinaNetSingle shot + focal lossFixes class imbalance
DETRTransformer, set predictionNo anchors or NMS, slow to converge

The recurring problem is class imbalance: a dense detector evaluates roughly 100,000 candidate locations of which a handful contain objects. Focal loss down-weights easy negatives so the rare positives are not drowned out.

Semantic Segmentation and Transposed Convolution

Classification collapses space deliberately. Segmentation needs a label per pixel, so the spatial resolution has to be recovered. Transposed convolution is the learnable inverse of a strided convolution:

O = (I − 1)·S − 2P + K + output_padding
I=7, S=2, P=1, K=4
O = 6·2 − 2 + 4 = 14   # doubled

Mechanically it is the backward pass of a convolution run forward: each input element is multiplied by the whole kernel and the results are accumulated into an enlarged output.

Checkerboard Artifacts

When K is not divisible by S, output positions receive an uneven number of contributions and a periodic grid pattern appears in the result. The 3×3 stride-2 combination is the classic offender. Two reliable fixes:

  • Choose K divisible by S, such as 4×4 stride 2.
  • Better: replace transposed convolution with nearest-neighbour or bilinear upsample followed by a 3×3 convolution. Same expressive power, no artifacts, and it is now the default in most generative and segmentation architectures.

U-Net — Why the Skips Are Load-Bearing

An encoder-decoder alone loses the fine detail during downsampling and cannot invent it back. U-Net concatenates each encoder feature map into the matching decoder stage, so the decoder has both the semantics from the deep path and the boundary precision from the shallow path.

encoder: 572 → 284 → 140 → 68 → 32
                │      │      │     │
                └──────┴──────┴─────┘  concat
decoder:  32 →  68 → 140 → 284 → 388

It was designed for biomedical images with only 30 annotated training examples, and the skip connections plus aggressive elastic augmentation are what made that possible. The pattern generalized far beyond its original domain, and diffusion models use the same backbone today.

29 / Implementation

A Complete, Correct Training Script EVERY LINE EARNS ITS PLACE

Model and Data

import torch, torch.nn as nn
from torchvision import datasets, transforms

MEAN, STD = (0.4914, 0.4822, 0.4465), (0.247, 0.243, 0.261)

train_tf = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(MEAN, STD),
])
# Validation is deterministic. No random transforms.
val_tf = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(MEAN, STD),
])

train_ds = datasets.CIFAR10("./data", train=True,
                            download=True, transform=train_tf)
val_ds   = datasets.CIFAR10("./data", train=False,
                            transform=val_tf)

train_dl = torch.utils.data.DataLoader(
    train_ds, batch_size=128, shuffle=True,
    num_workers=8, pin_memory=True, drop_last=True)
val_dl = torch.utils.data.DataLoader(
    val_ds, batch_size=256, shuffle=False, num_workers=4)

Normalization constants are the per-channel mean and standard deviation of the training set. Using ImageNet constants on CIFAR is a small but free loss of accuracy, and it is a very common copy-paste error.

The Loop

device = "cuda"
model = build_model().to(device, memory_format=torch.channels_last)

opt = torch.optim.SGD(
    model.parameters(), lr=0.1, momentum=0.9,
    weight_decay=5e-4, nesterov=True)
sched = torch.optim.lr_scheduler.OneCycleLR(
    opt, max_lr=0.1, epochs=EPOCHS, steps_per_epoch=len(train_dl))
scaler = torch.amp.GradScaler()
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)

for epoch in range(EPOCHS):
    model.train()                       # BN uses batch stats
    for x, y in train_dl:
        x = x.to(device, non_blocking=True,
                 memory_format=torch.channels_last)
        y = y.to(device, non_blocking=True)

        opt.zero_grad(set_to_none=True)
        with torch.autocast("cuda", dtype=torch.bfloat16):
            loss = criterion(model(x), y)
        scaler.scale(loss).backward()
        scaler.step(opt)
        scaler.update()
        sched.step()                    # per step, not per epoch

    model.eval()                        # BN uses running stats
    correct = total = 0
    with torch.inference_mode():
        for x, y in val_dl:
            x, y = x.to(device), y.to(device)
            correct += (model(x).argmax(1) == y).sum().item()
            total   += y.numel()
    print(f"epoch {epoch}  val acc {correct/total:.4f}")

The Five Lines People Omit

  • model.train() / model.eval() — switches BatchNorm and dropout. Omitting this is the single most common bug in CNN code.
  • zero_grad(set_to_none=True) — gradients accumulate by default; forgetting it silently sums across batches.
  • torch.inference_mode() — no graph is built, so evaluation runs faster and uses far less memory.
  • sched.step() inside the batch loop — OneCycleLR is a per-step schedule; calling it per epoch runs the wrong curve entirely.
  • channels_last plus autocast — commonly 20 to 40 percent throughput for two arguments, with no accuracy change.
30 / Transfer Learning

Do Not Train From Scratch THE DEFAULT ANSWER

Why It Works

Early convolutional layers learn edge, colour and texture detectors. Those are properties of natural images in general, not of the 1,000 ImageNet classes. A network trained on ImageNet has already solved the low-level and mid-level vision problem, and your dataset almost certainly does not contain enough examples to solve it again.

Typical outcome on a 2,000-image, 10-class problem: from scratch reaches roughly 60 to 70 percent; fine-tuning a pretrained ResNet-50 reaches roughly 90 to 95 percent, in a small fraction of the training time.

Choosing a Strategy

Your dataSimilar to sourceDifferent from source
Small (< 5K)Freeze the trunk, train the head onlyFreeze early layers, retrain later blocks
Large (> 50K)Fine-tune everything, low learning rateFine-tune everything, or train from scratch

The governing risk with a small dataset is that full fine-tuning at a normal learning rate destroys the pretrained features in the first few hundred steps, before the randomly initialized head has produced a useful gradient signal. Warm up the head first.

Match the Preprocessing Exactly

A pretrained model expects the normalization statistics it was trained with — for torchvision ImageNet weights, mean (0.485, 0.456, 0.406) and std (0.229, 0.224, 0.225) on RGB in [0,1]. Feeding BGR, or [0,255], or the wrong statistics degrades accuracy in a way that looks like a modelling problem and is not.

Feature Extraction — Freeze the Trunk

from torchvision.models import resnet50, ResNet50_Weights

model = resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)

for p in model.parameters():
    p.requires_grad = False

# New head. requires_grad=True by default.
model.fc = nn.Linear(model.fc.in_features, NUM_CLASSES)

opt = torch.optim.AdamW(model.fc.parameters(), lr=1e-3)

Fast, needs little memory, and hard to get wrong. Also call model.eval() on the frozen trunk during training if you do not want its BatchNorm running statistics to drift toward your small dataset.

Fine-Tuning With Discriminative Learning Rates

# Stage 1: head only, a few epochs, so the head
# stops producing garbage gradients.

# Stage 2: unfreeze, and give earlier layers a
# much smaller learning rate than later ones.
for p in model.parameters():
    p.requires_grad = True

opt = torch.optim.AdamW([
    {"params": model.layer1.parameters(), "lr": 1e-5},
    {"params": model.layer2.parameters(), "lr": 3e-5},
    {"params": model.layer3.parameters(), "lr": 1e-4},
    {"params": model.layer4.parameters(), "lr": 3e-4},
    {"params": model.fc.parameters(),     "lr": 1e-3},
], weight_decay=1e-4)

Rationale: early layers hold the most general features and need the least change; the head is random and needs the most. A geometric ladder spanning roughly two orders of magnitude is the standard recipe.

When Transfer Learning Does Not Help

  • Very different input statistics. Medical scans, satellite multispectral bands, audio spectrograms. The gain shrinks, though it is rarely negative.
  • Non-3-channel input. Adapt the stem: average the RGB filters across the channel dimension for grayscale, or replicate them for more bands.
  • Abundant in-domain data. Past roughly a million labelled examples, pretraining mainly buys faster convergence rather than final accuracy.
  • Licensing. Pretrained weights carry licences. Check them before shipping.
31 / Systems View

How Convolution Actually Runs on Hardware NOBODY WRITES THE SEVEN NESTED LOOPS

im2col — Turn Convolution Into One Matrix Multiply

The naive implementation is seven nested loops over batch, output channel, input channel, output row, output column, kernel row and kernel column. It has poor locality and cannot use the vendor GEMM kernels that decades of engineering went into. So reshape the problem instead.

Flatten each K×K×C_in patch into a column:

  im2col(X) : (C_in·K·K) × (H_out·W_out)
  W reshaped: (C_out)     × (C_in·K·K)

  Y = W_mat @ X_col
            : (C_out)     × (H_out·W_out)

then reshape Y back to (C_out, H_out, W_out).

The cost is memory. Overlapping windows duplicate every input element roughly times, so a 3×3 convolution inflates the input by about in the column buffer. cuDNN chooses per layer between im2col, implicit GEMM which avoids materializing the buffer, FFT, and Winograd.

Winograd and FFT

AlgorithmBest forMultiplications
Direct / GEMMAnythingBaseline
Winograd F(2×2, 3×3)Small kernels, stride 136 → 16, a 2.25× reduction
FFTLarge kernels (K ≥ 7)O(N² log N) instead of O(N²K²)

Winograd trades multiplications for additions and for transform overhead, which is a good trade because multipliers are the scarce resource. It is numerically less stable and is normally restricted to stride 1 with small kernels — that is, exactly the 3×3 layers that dominate modern networks. FFT convolution loses to Winograd at K = 3 because the transform overhead is not amortized.

Where the Time and Memory Actually Go

Layer typeBound byImplication
3×3 conv, wide channelsComputeRuns near peak FLOPs
1×1 convMemory bandwidthLow reuse per byte loaded
Depthwise convMemory bandwidthFLOP savings do not fully materialize
BatchNorm, ReLUMemory bandwidthFuse into the preceding conv
Data loadingDisk and CPUFrequently the real bottleneck

Before optimizing the model, profile. A surprising share of "slow training" turns out to be a starved input pipeline: too few dataloader workers, JPEG decoding on the main process, or images being read one at a time from network storage. Watch GPU utilization; if it is not near 100 percent, the model is not the problem.

Memory Budget for Training

Total ≈ weights
      + gradients        (= weights)
      + optimizer state  (2× weights for Adam)
      + activations      (batch × per-image)
      + workspace        (cuDNN scratch)

ResNet-50, batch 256, fp32:
  weights          25.6M × 4 B  =  0.10 GB
  gradients                     =  0.10 GB
  Adam state                    =  0.20 GB
  activations      ≈ 25 MB/img  =  6.4 GB   ← dominates

Activations dominate by an order of magnitude, and they scale linearly with batch size. The levers, in order: mixed precision (halves activations), gradient checkpointing (recompute rather than store, roughly 30 percent slower for a large memory reduction), then gradient accumulation to simulate a large batch with a small one.

Inference Is a Different Problem

At inference there are no gradients, no optimizer state, and no stored activations beyond the current layer. Fold BatchNorm into the preceding convolution, quantize to INT8, export to a graph compiler (TensorRT, ONNX Runtime, Core ML), and let it fuse conv-BN-ReLU into a single kernel. A ResNet-50 that trains at 4.1 GFLOPs per image routinely serves several times faster than the naive PyTorch eager-mode path suggests.

32 / Perspective

CNNs Versus Vision Transformers INDUCTIVE BIAS VERSUS DATA

The Core Trade-off

A CNN has locality, translation equivariance and hierarchy built into the architecture. A Vision Transformer has essentially none of them: it splits the image into patches and applies global self-attention, so it must learn that neighbouring pixels are related.

Strong priors are a loan against data. When training data is limited the prior is correct and free, and the CNN wins. When data is abundant the prior becomes a ceiling, and the model that learns its own structure wins.

Training dataWinner
ImageNet-1k (1.3M)CNN, clearly
ImageNet-21k (14M)Roughly even
JFT-300MViT, clearly

This is the central result of Dosovitskiy et al. (2020): ViT underperforms a comparable ResNet when trained on ImageNet-1k alone, and overtakes it after pretraining on 300M images.

The Convergence

The two families borrowed from each other and largely met in the middle. Swin Transformer reintroduced locality through windowed attention and a hierarchical pyramid. ConvNeXt (2022) took a plain ResNet and applied the transformer-era training recipe and design choices — AdamW, heavy augmentation, LayerNorm, GELU, depthwise 7×7 kernels, fewer activations — and matched Swin at equal FLOPs.

The honest conclusion from ConvNeXt is that a meaningful share of the reported transformer advantage was the training recipe rather than the attention mechanism.

Practical Selection

SituationChooseWhy
< 100K labelled imagesCNNPriors substitute for data
Edge or mobile deploymentCNNMature quantization and kernels
Dense prediction, high resolutionCNN or hierarchical ViTAttention is quadratic in tokens
Very large pretraining corpusViTScales better with data
Multimodal with textViTShares the transformer stack
You are unsurePretrained CNNStrongest baseline per unit of effort

Known CNN Failure Modes

  • Texture bias. Geirhos et al. (2019) showed ImageNet CNNs classify predominantly by texture rather than shape, contrary to human perception. Training on stylized images shifts the bias and improves robustness.
  • Shift instability. A one-pixel translation can change the prediction, because strided downsampling aliases.
  • Adversarial fragility. Perturbations far below the threshold of human perception flip predictions with high confidence.
  • Spurious correlation. The model will happily learn that grass predicts "cow" if the dataset says so, and then fail on a cow at the beach.

None of these are transformer-free problems. They are consequences of training discriminatively on finite, biased data, and they persist across architectures.

The Durable Idea

Convolution is not a neural network technique. It is the statement that a useful local operator should be applied uniformly across a signal, and it applies wherever the data has a grid structure with translation symmetry: 1-D over audio and time series, 2-D over images, 3-D over video and volumetric scans. Even in a transformer stack, convolution keeps reappearing in the patch embedding and in hybrid stems, because the underlying claim about signals remains true.

33 / Summary

What to Remember and What to Check THE SHORT VERSION

Ten Things Worth Carrying Away

  • A dense layer on a 224×224×3 image costs 150M parameters for one layer. Convolution costs 1,792 for a 3×3, 3→64 layer at any resolution.
  • The three priors are locality, weight sharing and compositionality. Everything else follows.
  • Output size is ⌊(I + 2P − K_eff)/S⌋ + 1. Learn it once.
  • Parameters are (K²·C_in + 1)·C_out; MACs multiply that by the output area. They scale differently, so neither predicts the other.
  • Convolution is translation equivariant. Invariance comes from pooling and augmentation.
  • Effective receptive field grows as O(√depth), not O(depth). Design generously.
  • The gradient of a convolution is a convolution, so training costs about 3× inference.
  • Residual connections turned depth from a liability into a resource.
  • 1×1 convolutions are the cheapest lever in the toolbox: bottlenecks, channel mixing, separable convolutions.
  • Start from pretrained weights. Almost always.

Debugging Checklist, in Order

#Check
1Overfit 10 examples to near-zero loss. If that fails, the bug is in the code, not the model.
2model.eval() before every validation pass.
3Normalization statistics match the pretrained weights, or the training set.
4No random augmentation on the validation set.
5Loss starts near ln(num_classes). For 10 classes that is 2.303.
6Labels align with images after shuffling and after augmentation.
7Learning rate swept over a log range; it is the highest-impact hyperparameter by a wide margin.
8Weight decay excluded from BatchNorm parameters and biases.
9GPU utilization near 100 percent, otherwise fix the data pipeline first.
10No leakage between train and validation splits, including near-duplicate images.

Primary Sources

  • LeCun et al., Gradient-Based Learning Applied to Document Recognition, 1998 — LeNet-5.
  • Krizhevsky et al., ImageNet Classification with Deep CNNs, 2012 — AlexNet.
  • Simonyan and Zisserman, Very Deep Convolutional Networks, 2014 — VGG.
  • Szegedy et al., Going Deeper with Convolutions, 2014 — Inception.
  • Ioffe and Szegedy, Batch Normalization, 2015.
  • He et al., Deep Residual Learning for Image Recognition, 2015 — ResNet.
  • Luo et al., Understanding the Effective Receptive Field, 2016.
  • Liu et al., A ConvNet for the 2020s, 2022 — ConvNeXt.

Related Decks on This Site