Skip to content

Phase 3 - Computer Vision with CNNs

Convolution exists because a Dense layer is the wrong shape for an image. On a 224×224×3 photograph, Dense(32) costs 4,816,928 parameters; a Conv2D(32, 3) costs 896 — and unlike the dense layer, that number does not change when the image does. Everything in this phase follows from weight sharing and locality.

Every number on these ten pages was measured on this machine, on a CPU, and the measurements are reproducible in minutes. Where a result contradicted the textbook claim, the page says so.

#PageThe measured headline
421Intro to CNNs896 parameters against 4,816,928; convolution matched TensorFlow at 2.38e-07; two 3×3 layers beat one 5×5 (18 weights against 25)
422Pooling & CNN Architectureaverage 0.8740, max 0.8720, strided convolution last at 0.8455; FlattenDense held 803,072 of 880,936 weight bytes
423Famous ArchitecturesVGG-ish 0.8800 wins; plain beat residual at 12 convolutions (0.8500 vs 0.8433)
423.5Normalisation Beyond Batchlayer norm won at batches 4/16/64, batch norm overtook it at 256 — exactly where the statistic counts cross
424Transfer Learningfine-tuning beat scratch by +0.0014, freezing lost at every size, and a wrong learning rate cost 0.4026
424.5Fine-Tuning and LoRALoRA reached 0.8911 training 2.80% of the weights against full fine-tuning’s 0.9385 — and training from scratch beat both at 0.9350
425Data Augmentationcost 0.1855 on canonical data and bought 0.1105 on rotated data — invariance, not accuracy
426Image Segmentationpixel accuracy 0.9737 against a do-nothing baseline of 0.8691; report IoU
427Interpreting Convnetsactivations grow 1.61 → 20.44 with depth; 4 of 64 channels silent; always run the random-weights check
428Object Detection32.8 : 1 cell imbalance; recall 0.8616 → 0.6799 while precision sat at 0.997
428.5Vision Transformersthe ViT beat the convnet at 1,000 and 4,000 rows — and overfitted 4× harder
diagram Diagram mermaid
figure 2,000 rows, 40 epochs — the same models on three test sets matplotlib
A grouped bar chart with three conditions. On canonical test data the un-augmented model leads at 0.763 and accuracy falls with augmentation strength to 0.552. On shifted test data the ordering reverses at the top: 0.626 unaugmented against 0.672 light and 0.683 medium. On rotated test data medium augmentation leads clearly at 0.707 against 0.596 unaugmented. A grouped bar chart with three conditions. On canonical test data the un-augmented model leads at 0.763 and accuracy falls with augmentation strength to 0.552. On shifted test data the ordering reverses at the top: 0.626 unaugmented against 0.672 light and 0.683 medium. On rotated test data medium augmentation leads clearly at 0.707 against 0.596 unaugmented.
Read the un-augmented bars left to right: 0.763, 0.626, 0.596. That model loses 0.1370 the moment the test images move and 0.1665 when they rotate — it never had to cope with either. The medium-augmented model reads 0.695, 0.683, 0.707: almost flat. It gave up 0.068 on canonical data and bought back 0.1105 on rotated data. Neither model is better; they are optimised for different deployment conditions.
figure Garments to footwear and bags, same sensor, disjoint labels matplotlib
Two panels. Left: best validation accuracy against target rows on a log axis for four strategies. From scratch, fine-tuned 1e-3 and frozen features cluster between 0.80 and 0.94, while fine-tuned 1e-4 starts far below at 0.4602 and catches up by 4,000 rows. Right: per-epoch curves at 250 rows showing fine-tuned 1e-3 and from scratch rising together to about 0.86, frozen features trailing at 0.81, and fine-tuned 1e-4 still climbing through 0.46 at epoch 12. Two panels. Left: best validation accuracy against target rows on a log axis for four strategies. From scratch, fine-tuned 1e-3 and frozen features cluster between 0.80 and 0.94, while fine-tuned 1e-4 starts far below at 0.4602 and catches up by 4,000 rows. Right: per-epoch curves at 250 rows showing fine-tuned 1e-3 and from scratch rising together to about 0.86, frozen features trailing at 0.81, and fine-tuned 1e-4 still climbing through 0.46 at epoch 12.
Three of the four curves are on top of each other, which is the result: at 250 target rows, training from scratch reaches 0.8628 and the best transfer strategy reaches 0.8642. The outlier is the low learning rate, and the right panel shows why — at 1e-4 the model is simply still training at epoch 12. That is not a property of fine-tuning, it is a budget mismatch, and it is the most common way transfer-learning comparisons get rigged.
figure Every claim this phase set out to test matplotlib
Horizontal bars, one per page in the phase, each labelled with the claim it tested and coloured by the verdict: green where the standard story held, amber where it held at a price, red where the measurement contradicted it. 4 of 6 claims contradicted, 1 held at a price, 1 held. Horizontal bars, one per page in the phase, each labelled with the claim it tested and coloured by the verdict: green where the standard story held, amber where it held at a price, red where the measurement contradicted it. 4 of 6 claims contradicted, 1 held at a price, 1 held.
Collected from the runs behind each page's own figures rather than measured afresh, so every bar is traceable to the page it names. Bar length is the log of the effect size, because the effects span from 0.0014 to 5,376 — the number that matters is printed on each bar. Across all nine phases, 34 of 54 claims were contradicted outright, 11 held at a cost that was worth stating, and 9 held as advertised.

Four results that contradict the usual story

Section titled “Four results that contradict the usual story”

Each of these is measured on this data, at this scale — which is exactly the caveat that makes them useful rather than misleading.

  1. Strided convolution came last. The “learnable downsampling replaces pooling” advice lost to plain average pooling by 0.0285. A fixed rule with no parameters beat a learned one at equal capacity.
  2. Plain convolutions beat residual ones at 12 layers. Residual connections solve a problem that starts at depths this phase cannot afford; at 12 convolutions they cost 0.0067. Their direction was still visible — depth helped the residual family eight times more.
  3. Augmentation hurt at every dataset size. Fashion-MNIST is centred and pose-normalised, and the un-augmented model was not overfitting. Augmentation encodes an invariance assumption; if the test set has no such variance, it is pure cost.
  4. The ViT beat the convnet at 1,000 rows. “Transformers need enormous data” is a claim about 196+ tokens on 224×224 photographs. At 16 tokens on centred 28×28 greyscale, the convolutional prior is worth very little.
sketch How often the standard story survived p5.js
Step through the phases. Each bar splits the claims that phase tested into contradicted, held at a price, and held as advertised - the totals are summed live.

Three mistakes that were mine, and are now on the pages

Section titled “Three mistakes that were mine, and are now on the pages”

Every one of these produced a plausible-looking number before it was caught.

  • Batch normalisation’s default momentum of 0.99 needs roughly 460 batches for its moving averages to settle. A 376-batch run reported validation accuracy of 0.1540 with a training loss of 0.27. Lowering it to 0.9 fixed it, and the incident is written up on page 423.
  • A ReLU placed before the Add in a residual block made the residual model worse than the plain one, because activations then grow additively with depth. The classic ordering is conv-BN-relu-conv-BN → Add → relu.
  • A fixed epoch budget measured the budget rather than the treatment: augmented runs were still climbing at epoch 15 while plain runs had stopped. Page 425 reports the best epoch per run for exactly this reason.
sketch Augmentation is a bet on the test set p5.js
Drag to choose how much augmentation to apply, then click a test set. The measured accuracy changes sign depending on which distribution you deploy on.
Idea from this phaseWhere it comes back
weight sharing over a structured axis1D convolution over time, Phase 4
layer normalisation, no batch statisticsevery transformer block, Phases 4–6
residual connections around a sublayertransformer blocks, diffusion U-Nets
the encoder–decoder hourglass with skipsU-Net diffusion models, Phase 6
attention over a set of tokensthe whole of Phase 5
“compare against the do-nothing baseline”forecasting, detection, generation
pch.quizTag pch.quizDefaultTitle
  1. On 224x224x3 input, `Dense(32)` costs 4,816,928 parameters and `Conv2D(32, 3)` costs 896. Which part of that comparison matters most?

    pch.quizShowAnswer

    B — That the convolution's count does not depend on the image size at all, because the same 3x3 kernel is reused at every position

  2. Augmentation cost 0.1855 on canonical test data and bought 0.1105 on rotated test data. What is the correct conclusion?

    pch.quizShowAnswer

    B — Augmentation encodes an invariance assumption, so it pays exactly when the deployment distribution contains that variance and costs accuracy when it does not

  3. Fine-tuning at learning rate 1e-4 reached only 0.4602 at 250 rows while 1e-3 reached 0.8642. Why is the low-rate result NOT evidence against fine-tuning?

    pch.quizShowAnswer

    B — Because the per-epoch curve shows it was still climbing at epoch 12 - it is a budget mismatch, not a property of the method

  4. Batch normalisation's default momentum of 0.99 needs roughly 460 batches to settle, and a 376-batch run reported validation accuracy of 0.1540 with a training loss of 0.27. What was actually broken?

    pch.quizShowAnswer

    B — Nothing in the model - the moving averages used at inference time had not converged, so training and validation were effectively using different normalisation statistics

  5. A vision transformer BEAT the convnet at 1,000 and 4,000 rows, contradicting 'transformers need enormous data'. What makes both statements true at once?

    pch.quizShowAnswer

    B — The usual claim is about 196+ tokens on 224x224 photographs; at 16 tokens on centred 28x28 greyscale, the convolutional prior is worth very little, so there is far less for the transformer to relearn

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading