Part VIII — Deep Learning

Chapter 27 · Convolutional Neural Networks

First, the intuition — four optical illusions that show how brains label what they see by matching features, a trained CNN's ranked guesses on real photos, the search-interest curve of CNNs overtaking the field, and the two people behind it all: Hinton and his student LeCun. Then the mathematics. Chapter 26's network read 12 tidy numbers; this one reads a photograph: thousands of pixels, a cat anywhere in the frame, and the question — cat or dog? The convolutional neural network replaces the connect-everything-to-everything layer with a small sliding patch of weights that looks for the same feature everywhere in the image. The convolution and max-pooling are computed by hand, with exact arithmetic, on a small grid — then the real cat-dog network (813,217 parameters, verified) trains on real photographs, and its genuinely overfit learning curve becomes the chapter's most instructive figure.

📖 Chapter 27 of 33 ⏱ ~100 min read Mostly Core Stable Volatile 📝 Quiz · 35 MCQs

🎯 Learning Objectives

1. How Seeing Works — Features

Before any mathematics, one experiment. Look at the face below. Do you see a person looking at you? Or a person looking to the right? Look at the left border of the image. Now look at the right border. Your answer flips.

Hybrid-face optical illusion: viewed from the left it is a face looking at you, viewed from the right it is a right-facing profile
Figure 27.1 — One picture, two faces. From the left border, the person faces you. From the right border, a right-facing profile. Your brain cannot hold both at once.

Your brain struggles because it is doing something very specific: it hunts for features. The features closest to your centre of focus win. They decide what you see. The next two illusions make the same point.

Ambiguous drawing that reads as a young woman looking away or as an old woman looking down
Figure 27.2 — "My Wife and My Mother-in-Law" (1915): a young woman looking away, or an old woman looking down. The young woman's chin is the old woman's nose. Her ear becomes the old woman's eye. Whichever set of features your brain locks onto picks the person.
Hand-drawn ambiguous figure readable as a duck facing left or a rabbit facing right
Figure 27.3 — The duck or the rabbit. First printed in 1892, it is the oldest illusion of this kind on record. Bill pointing left: a duck. Same lines as ears pointing right: a rabbit.

And one more. Look at it for just a second. Most people feel almost dazzled. Their eyes jump up and down the face. Both readings seem plausible — so the brain cannot settle on either.

Surreal portrait with two stacked pairs of eyes and two stacked mouths
Figure 27.4 — Two stacked pairs of eyes, two mouths. The brain flips between them and cannot decide. That dizzy feeling is what "not enough features to classify" feels like from the inside.

Here is the lesson of the four illusions. Your brain labels what it sees by matching features. The features it picks decide the label. Not enough features, or too little time — and it guesses wrong. You have done this. A quick glance over your shoulder, and a shadow becomes a car. A ball turns out to be a cat.

A convolutional neural network sees in almost the same way. It hunts for small features in an image. It weighs them. Then it outputs a label — with a probability. Keep the illusions in mind through the whole chapter. They are the intuition. The mathematics that follows is the machinery.

2. What a Trained CNN Says

From human brains to machine brains. The slide below comes from a talk by Geoffrey Hinton. It shows three images from a test set — and, under each, a trained network's ranked guesses.

Slide titled Examples from the test set (with the network's guesses) showing a cheetah, a train and a strainer-like object
Figure 27.5 — "Examples from the test set (with the network's guesses)": three test images and how a trained CNN voted on each.

You will meet tables like this everywhere in computer vision, so let us learn to read one properly. The top word is the true label — what the image actually is. Under it come the network's top guesses, ranked, each with a probability. And the network was not choosing among four options. It was choosing among thousands of classes at once.

True labelThe network's ranked guessesVerdict
cheetahcheetah (~95–99%) · leopard · snow leopard · Egyptian catright, and confident
bullet trainbullet train · passenger car · subway train · electric locomotiveright — telling fine grades of "train" apart
magnifying glassscissors · hand glass (magnifying glass) · frying pan · stethoscopewrong — but the true answer was a close second

Read the third row like the illusions of Section 1. The image is genuinely unclear. A human hesitates between a frying pan, scissors and a magnifying glass too. The network hesitated the same way: its probabilities were spread, not peaked. It put scissors first. The true answer came second. That is why the slide marks it in red. A network that hesitates when the image deserves hesitation is a healthy network.

One more thing to hold on to. This slide is years old, and today's networks are far better. By Section 10 you will train your own — and read its ranked guesses on real cat and dog photos.

⚠ Volatile content — Reviewed: October 2026 · Next review: January 2027

3. Why CNNs Are Taking Over

Is this field actually going anywhere? Search interest says yes. The screenshot below compares two search terms on Google Trends: convolutional neural network in blue, against artificial neural network in red.

Google Trends interest-over-time chart: convolutional neural network (blue) climbing to overtake artificial neural network (red)
Figure 27.6 — Google Trends, worldwide: "convolutional neural network" (blue) climbs from almost nothing to overtake "artificial neural network" (red) — the specialised term passing the general one.

The red line — the general term — stays flat. The blue line climbs from nearly zero and passes it. A specialised term overtaking its own parent field is rare. It says: this is where the energy went.

Where exactly? Self-driving cars: recognising people at the curb and stop signs in a split second. Photo tagging: a few years ago you tagged friends by hand; now faces are found and named for you. Medical scans, factory inspection, satellite images — the same architecture again and again.

4. The People: Hinton and LeCun

Two names stand behind this chapter. If Geoffrey Hinton is the godfather of deep learning, then Yann LeCun is the grandfather of convolutional neural networks. And LeCun was Hinton's student. In the 1980s and 1990s LeCun did the foundational work that made CNNs real. Hinton went on to pioneer deep learning at Google. LeCun became director of AI research at Facebook and a professor at NYU.

Geoffrey Hinton the “godfather” of deep learning pioneering deep learning at Google student Yann LeCun the “grandfather” of CNNs Facebook AI Research · professor at NYU LeCun built CNNs in the 1980s–90s; this group calls itself the “conspiracy of deep learning”
Figure 27.7 — A small dynasty: Hinton (deep learning, Google) and his student LeCun (CNNs, Facebook AI Research and NYU).
📝
Further reading: Yann LeCun, Léon Bottou, Yoshua Bengio and Patrick Haffner, Gradient-Based Learning Applied to Document Recognition, Proceedings of the IEEE, 1998 — the paper that made CNNs practical. Its architecture diagram (LeNet-5) is one of the most reproduced images in deep learning, and it floats around the internet without credit. Now you know where it comes from.

5. Pixels as Data

Start at the smallest piece. A black-and-white photo — even one of just 2 × 2 pixels — is a grid of numbers. Each pixel holds one brightness value from 0 to 255. Eight bits, and 28 = 256 options. 0 is a fully black pixel. 255 is a fully white pixel. Everything between is a shade of grey.

A colour pixel holds three such numbers: one for red, one for green, one for blue — RGB. So a colour photo is not one grid but three stacked grids. The computer never sees "colours". It sees ones and zeros bundled into these numbers.

black-and-white photo = one grid 0 128 255 12 one value per pixel: 0–255 (8 bits → 2⁸ = 256 levels) 0 = black · 255 = white in between = shades of grey add colour colour photo = three stacked grids (RGB) R G B 203 45 17 230 three values per pixel — one per channel, each 0–255; the mix is the exact colour the network’s input (this chapter): 64 × 64 × 3 = 12,288 such numbers
Figure 27.8 — Images are numbers. Greyscale: one 0–255 value per pixel. Colour: three stacked 0–255 grids, one each for red, green and blue.

To keep the hand arithmetic readable, this chapter simplifies the range: white becomes 0, black becomes 1. Every rule we compute in this tiny 0/1 world transfers unchanged back to the full 0–255 range. Here is the whole idea in one picture — a smiley face, its pixel grid, and the matrix the network actually receives:

Three panels: a smiley face drawing, the same face as a seven by seven pixel grid of black and white cells, and the corresponding seven by seven matrix of zeros and ones
Figure 27.9 — The bridge: picture → pixel grid → matrix of 0s and 1s. The eyes, nose and smile are just cells with value 1. Everything a CNN does, it does to numbers like these.

With images as numbers, the whole chapter ahead is four steps: convolution (slide a small pattern across the image to find features), max pooling (keep the strongest finds and shrink the grids), flattening (line the numbers up into one vector), and full connection (a classic Chapter 26 head that makes the final call). Sections 6–7 compute the first two by hand:

input image 64×64×3 numbers 1 · convolution slide a kernel, find features 2 · max pooling keep the max, shrink 3 · flattening grids → one vector 4 · full connection Dense + sigmoid (Ch 26) → output: “cat” or “dog”, with a probability
Figure 27.10 — The four steps of every CNN: convolution, max pooling, flattening, full connection. The rest of the chapter walks this strip left to right.

To a computer, a photograph is a stack of three grids — red, green, blue — each holding a brightness number per pixel. The network's input here is resized to 64 × 64 × 3 = 12,288 numbers. The task is Chapter 26's shape exactly — binary classification, sigmoid output — but the input is now raw perception, and that changes everything about the architecture.

Why not feed the 12,288 numbers into Chapter 26's Dense network? Two fatal reasons, and they are the chapter's motivation:

📝
Note: the 64 × 64 resize is a deliberate compromise. Real photos are far larger; downsampling throws away detail (a cat's whiskers are a few pixels at 64 × 64) but keeps training fast. Section 10 returns to this trade-off with real consequences.

6. The Convolution, by Hand

A convolution slides a small grid of weights — the kernel (or filter) — across the image. At each position it computes one number: the sum of element-wise products between kernel and the patch underneath (exactly the dot product every chapter since Chapter 4 has leaned on). The output grid of all those numbers is the feature map — a map of where the kernel's pattern occurs.

Three names, one thing. The sliding grid is called a kernel, a filter, or a feature detector. All three mean the same small grid of weights. 3 × 3 is the convention. Famous networks break it — AlexNet used 7 × 7, others 5 × 5 — but small kernels dominate. We will use the names interchangeably, and "feature detector" may be the most honest of the three: the grid's whole job is to detect a feature.

6.1 The walkthrough: the smiley, one window at a time

Take the smiley from Section 5 — the 7 × 7 grid of 0s and 1s — and one 3 × 3 feature detector with four 1s in it. The convolution places the detector on the top-left corner of the image. It multiplies each detector value by the pixel underneath it. Then it adds up the nine products. That one sum is the first cell of the feature map:

Convolution walkthrough: the 7 by 7 smiley input, a 3 by 3 feature detector outlined in blue on its top-left corner, and the first cell of the feature map filled in
Figure 27.11 — Step one. The detector (highlighted window) covers the image's top-left 9 pixels; nine multiplications and one sum fill the feature map's first cell.

Then it slides. The step size is called the stride. Here the stride is 1: the window moves one pixel to the right, multiplies and sums again, and fills the second cell. At the right edge it drops one row and starts over. A stride of 2 would skip every other position — the map comes out smaller still, and 2 is the common choice in large networks. Slide all the way across and the feature map is complete:

Completed convolution walkthrough: the detector at the final bottom-right position and all 25 feature map cells filled
Figure 27.12 — The completed feature map. Every cell is nine multiplications and one sum — pick any cell and check it by hand.

Now read the finished map. Most cells are small — 0, 1, 2. Where the detector's pattern fits nothing, the sum is 0. But one cell holds a 4, the largest value in the map. It sits exactly where the detector matched the image perfectly: all four of its 1s landed on 1s, on the patch straddling the smiley's nose and the left end of its smile (verified by recomputation). That is the whole purpose in one number. The feature map is brightest where the feature lives.

Notice also what the map is not. It is smaller than the image: 7 × 7 became 5 × 5, and a stride of 2 would shrink it further. Do we lose information? Some. But look at what is kept. The map answers "where is this pattern?" — and where is exactly what vision needs. You do not recognise a cheetah by checking every pixel. You recognise the black tear marks under its eyes. Not every pixel of a train — its shape. Your eyes take in gigabytes per second and keep only the features. The convolution is that trick in arithmetic: keep the feature, drop the rest.

📝
Going deeper: for the full mathematics of convolution in neural networks, read Jianxin Wu, Introduction to Convolutional Neural Networks (Nanjing University) — a short, beginner-friendly survey written precisely for newcomers to the field.

6.2 The same operation with real numbers

Watch it work by hand. A 5 × 5 grayscale patch with a vertical edge (bright left, dark right), and a 3 × 3 vertical-edge kernel:

10101000 10101000 10101000 10101000 10101000 input (5 × 5) solid = window 1 → 0 · dashed = window 2 → 30 ⊛ slide 10−1 10−1 10−1 kernel (3 × 3) sum 03030 03030 03030 feature map (3 × 3)
Figure 27.13 — The hand convolution. The kernel is the pattern "bright on the left, dark on the right"; the feature map lights up (30) exactly where the edge sits.

Verify one cell by hand — the top-middle output, from the dashed window (input columns 2–4, rows 1–3):

10(1) + 10(0) + 10(−1) + 10(1) + 10(0) + 10(−1) + 10(1) + 10(0) + 0(−1) = 30 − 0 + 0 = 30
Three bright cells (10) hit the kernel's +1 column, three dark cells (10) hit its 0 column, and the dark column contributes 0 × (−1). The left-most window straddles no edge → 0. The map has found the edge and reported where it is.

Three properties make this operation the right tool for vision, and all three are visible in the tiny example:

💡
Pro Tip — the size arithmetic: a 3 × 3 kernel on an n × n input (no padding, stride 1) produces (n − 2) × (n − 2). Here 5 − 2 = 3. Section 7 chains this rule down the real network.

6.3 Many maps make a layer

One detector finds one pattern. A network needs many. So the same image is convolved with many different detectors — each one producing its own feature map. Stack those maps together and you have the convolutional layer:

Diagram: the input image with detector windows connected to a stack of overlapping feature maps, bracketed as the convolutional layer
Figure 27.14 — Many feature maps make one convolutional layer. Each map comes from a different detector; each answers a different "where?".

Each map hunts a different pattern — this edge, that curve. Which detectors matter for cats, and which for trains? Nobody assigns them. During training the network decides for itself which features are worth looking for. Section 11 shows the ones our cat-dog network actually learned.

6.4 Kernels you already know

Convolutions are not exotic. Photo editors have used them for decades. These four examples come from the documentation of GIMP, the free image editor: each is a small kernel applied to a photo of the Taj Mahal — exactly our walkthrough, just with real pixel values:

Sharpen kernel: 5 at the centre minus 1 around, applied to the Taj Mahal photo Blur kernel: a block of ones, applied to the Taj Mahal photo Edge Enhance kernel applied to the Taj Mahal photo: flat areas go black, outlines stay Emboss kernel: asymmetric minus 2 to plus 2 diagonal, giving a chiselled relief look
Figure 27.15 — The same operation, in a photo editor: Sharpen (5 at the centre, −1 around), Blur (a block of equal weights), Edge Enhance (flat areas collapse to black, boundaries survive), Emboss (an asymmetric −2…+2 kernel gives the chiselled look).

Read each kernel with the walkthrough in mind. Sharpen: the centre pixel is amplified, its neighbours subtracted — edges pop. Blur: equal weights in a block — every pixel is averaged with its neighbours, and differences melt. Edge Enhance: keep the centre, subtract a neighbour — flat areas go dark, boundaries stay bright. Emboss: asymmetric weights, one corner negative and the opposite positive — the photo gains a raised, chiselled look. The cousin that matters most to us is Edge Detect: hunting boundaries is most of what a CNN's early layers do.

And here is the beautiful part. In a trained CNN, nobody designs these kernels. Backpropagation invents them. The learned detectors often look like noise to a human eye and have no name at all — and they work. Section 11 shows our network's own.

GIMP's Convolution Matrix filter dialog with a five by five kernel grid, preview of the Taj Mahal, divisor and offset controls
Figure 27.16 — The tool itself: GIMP's Convolution Matrix dialog — the same little grids of weights, one number different each time.

6.5 ReLU — breaking the linearity

One small step sits on top of the convolution. It is not a new operation. It is the rectifier — the same activation function Chapter 26 placed inside every neuron. Some courses list it as its own step ("step 1B"). We treat convolution and rectifier as one step, applied to every feature map:

Pipeline diagram: input image to convolutional layer of feature maps, then a rectifier box with the ReLU curve and formula phi of x equals max of x and 0
Figure 27.17 — Step 1, complete: input image → convolutional layer → rectifier. The formula is the friendliest in this book: φ(x) = max(x, 0).

That formula does one thing. Anything negative becomes 0. Everything else stays. Why is this needed? Because the convolution itself is linear: multiply and add, then multiply and add again. Stack linear operations and you get one big linear operation. But images are not linear. Borders, shadows, colour changes — neighbouring pixels jump, they do not slide. A network that can only express straight-line relationships cannot see that. The rectifier breaks the linearity.

Watch it on a real photograph. A city skyline goes through a feature detector. The detector's output is signed: white cells are positive, black cells are negative. Then ReLU removes the black — every negative cell becomes 0:

Original black-and-white photograph of a riverside city skyline with a rower on the water Signed feature map of the skyline: white positive edges, black negative regions, grey near zero Feature map after ReLU: only non-negative values remain, bright edges on a flat grey background
Figure 27.18 — The example start to finish: the original photo (left); its signed feature map (middle: black = negative, white = positive); after ReLU (right: only non-negative values — the black is gone, every negative cell is now 0).

Where exactly is the "breaking" in that? Look at a building's shadow in the middle panel. Bright, then grey, then darker, then black — a smooth, straight-line descent from light to dark. After ReLU the black is cut away. The gradual staircase is gone; what remains jumps abruptly to zero. That abrupt cut is the non-linearity. This is a rough, by-fingers explanation of a mathematical fact — the two papers below make it precise.

And in the code of Section 9 you have already written it, perhaps without noticing: activation='relu' rides inside every Conv2D line. Keras has no separate rectifier layer here — it comes along with the convolution.

Two papers, if you want to go deeper. The first answers exactly our question — why a non-linear activation is essential at the filter output. The second, from the team at Microsoft Research, proposes a parametric ReLU (PReLU) with a small learnable slope for the negative side — and with it, human-level performance on ImageNet:

Slide: Additional reading, Understanding Convolutional Neural Networks with a Mathematical Model by C.-C. Jay Kuo 2016, arxiv 1609.04112, with sigmoid, ReLU and leaky ReLU curves Slide: Additional reading, Delving Deep into Rectifiers by Kaiming He et al. 2015, arxiv 1502.01852, with ReLU and PReLU curves
Figure 27.19 — Further reading: Kuo (2016), Understanding Convolutional Neural Networks with a Mathematical Model (arXiv 1609.04112) — see its first question, why non-linear activation is essential; He et al. (2015), Delving Deep into Rectifiers (arXiv 1502.01852) — the parametric ReLU, f(y) = ay for y < 0.

7. Pooling & the CNN Architecture

7.1 Max-pooling, by hand

The convolution's companion shrinks the feature map: a 2 × 2 window slides with stride 2, and only the maximum in each window survives. Hand example on a 4 × 4 map:

Input (4 × 4)Max-pooled (2 × 2)
1 · 3 · 2 · 4
5 · 6 · 7 · 8
0 · 9 · 1 · 2
3 · 4 · 5 · 6
→ keep each
window's max →
6 · 8
9 · 6
max(1,3,5,6)=6 · max(2,4,7,8)=8
max(0,9,3,4)=9 · max(1,2,5,6)=6

Two gifts for the price of zero parameters: the map halves in each dimension (fewer numbers downstream), and small shifts of the input barely change the output — the "6" survives whether the 6 sits in its window's corner or centre. Pooling buys tolerance to exact position, which is precisely what a cat that moved two pixels needs.

7.2 The real architecture, layer by layer

Input 64×64×3 Conv 3×3×32 62×62×32 MaxPool 2×2 31×31×32 Conv 3×3×32 29×29×32 MaxPool 2×2 14×14×32 Flatten 6,272 Dense 128 ReLU Sigmoid P(dog)
Figure 27.20 — The source architecture: two conv-pool pairs extract features; the familiar Chapter 26 head (Flatten → Dense → Sigmoid) classifies them.

Trace the dimensions with the two rules (conv 3 × 3 subtracts 2; max-pool 2 × 2 with stride 2 halves and floors): 64 → 62 → 31 → 29 → 14. Then the 14 × 14 × 32 tensor flattens into a 6,272-long vector, and Chapter 26 takes over: Dense 128 (ReLU), sigmoid output. Every dimension below was verified on the real built model:

LayerOutput shapeRuleParameters
Input64 × 64 × 3resized photo—
Conv2D, 32 filters, 3×3, ReLU62 × 62 × 3264 − 2 = 623·3·3·32 + 32 = 896
MaxPool 2×2, stride 231 × 31 × 3262 / 20
Conv2D, 32 filters, 3×3, ReLU29 × 29 × 3231 − 2 = 293·3·32·32 + 32 = 9,248
MaxPool 2×2, stride 214 × 14 × 32⌊29 / 2⌋ = 140
Flatten6,27214 × 14 × 320
Dense 128, ReLU128full connection6,272·128 + 128 = 802,944
Dense 1, sigmoid1P(dog)128 + 1 = 129
Totalcount_params()813,217
⚠
Warning — where the parameters actually live: the two convolutions that do the seeing own just 896 + 9,248 = 10,144 parameters (1.2%). The Flatten → Dense junction owns 802,944 (98.8%) — because flattening reconnects everything to everything again. Deep networks stack more convolutions precisely to delay, and shrink, that junction.

Why two conv-pool pairs? Compose the local views: after the first pair, a unit summarises a ~5-pixel patch; after the second, its receptive field has grown again. Early filters detect edges and blobs; later filters combine them into textures and parts — ears, snouts, fur direction. The hierarchy is learned, not designed.

8. Data Augmentation

A photo of a cat is one sample; the same photo slightly sheared, zoomed, or mirrored is the same cat to any human — and should be free training data. The ImageDataGenerator creates these variants on the fly, every epoch:

SettingValueWhat it does
rescale = 1./255÷ 255pixels from 0–255 to 0–1 — Chapter 3's feature scaling, applied to images (both sets)
shear_range = 0.2±20%tilts the image, as if photographed at an angle
zoom_range = 0.2±20%random zoom in/out — the cat may fill the frame or sit small
horizontal_flipTruemirror image — a cat facing left is still a cat

Augmentation is an overfitting defence: the network almost never sees the same image twice, so it cannot memorise 2,000 photographs — it must learn cat-ness and dog-ness. Section 10's real curves show it working, and also show its limit.

⚠
Warning — never augment the test set: the test generator applies rescale only. The test set is the umpire; umpires do not wear costumes. Augmenting test data would corrupt the measurement and defeat its purpose.

The generators also read the folder structure itself: flow_from_directory('dataset/training_set') expects one sub-folder per class (cats/, dogs/) and infers the labels. The mapping lands in class_indices — the real run printed {'cats': 0, 'dogs': 1} (alphabetical) — which makes the sigmoid output P(dog) exactly.

📋 Stable content — Reviewed: October 2026

9. Implementation in Python (Keras)

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# ---- Part 1: data (augment train, rescale-only test)
train_datagen = ImageDataGenerator(rescale=1./255,
                                   shear_range=0.2,
                                   zoom_range=0.2,
                                   horizontal_flip=True)
training_set = train_datagen.flow_from_directory('dataset/training_set',
                                                 target_size=(64, 64),
                                                 batch_size=32,
                                                 class_mode='binary')
test_datagen = ImageDataGenerator(rescale=1./255)
test_set = test_datagen.flow_from_directory('dataset/test_set',
                                            target_size=(64, 64),
                                            batch_size=32,
                                            class_mode='binary')
print(training_set.class_indices)     # {'cats': 0, 'dogs': 1}

# ---- Part 2: the CNN (813,217 parameters)
cnn = tf.keras.models.Sequential([
    tf.keras.layers.Input(shape=(64, 64, 3)),
    tf.keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=2, strides=2),
    tf.keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=2, strides=2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(units=128, activation='relu'),
    tf.keras.layers.Dense(units=1, activation='sigmoid'),
])

# ---- Part 3: compile and train (validate each epoch)
cnn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
cnn.fit(x=training_set, validation_data=test_set, epochs=25)

# ---- Part 4: a single prediction
import numpy as np
from tensorflow.keras.preprocessing import image
test_image = image.load_img('dataset/single_prediction/cat_or_dog_1.jpg',
                            target_size=(64, 64))
test_image = np.expand_dims(image.img_to_array(test_image), axis=0)
result = cnn.predict(test_image)            # P(dog), since dogs = 1
prediction = 'dog' if result[0][0] >= 0.5 else 'cat'
print(prediction)

Listing 27.1 — The complete cat-dog CNN (source: convolutional_neural_network.py, modernised: Input layer, explicit threshold).

📝
Note — the dataset is not in this repo. The source expects dataset/training_set (8,000 images: 4,000 cats + 4,000 dogs), dataset/test_set (2,000) and dataset/single_prediction/, shipped with the original course bundle rather than the code repository. This chapter's real run uses the freely downloadable filtered subset of the same Kaggle source — 2,000 train / 1,000 test (cats_and_dogs_filtered.zip, a public mirror such as the Pytorch-Image-Classification GitHub release) arranged into the same folder layout. Structure your folders exactly: dataset/training_set/cats/*.jpg, dataset/training_set/dogs/*.jpg, and the two test_set/ equivalents.
# restructure a downloaded filtered subset into the source layout
mkdir -p dataset/training_set dataset/test_set dataset/single_prediction
cp -r cats_and_dogs_filtered/train/cats  dataset/training_set/cats
cp -r cats_and_dogs_filtered/train/dogs  dataset/training_set/dogs
cp -r cats_and_dogs_filtered/validation/cats dataset/test_set/cats
cp -r cats_and_dogs_filtered/validation/dogs dataset/test_set/dogs

Listing 27.2 — Preparing the accessible subset (2,000 train / 1,000 validation images).

10. Training & Results — the Real Run

The honest setup first: the original course bundle's 8,000 training images were not available for this run; the freely downloadable filtered subset (2,000 train, 1,000 test) was trained with the identical architecture, augmentation, and 25 epochs, seed 0. Every number below is that single real run — including the parts that hurt, which turn out to be the best teaching material in the chapter.

0.60 0.70 0.80 0.90 1 5 10 15 20 25 best val_acc 0.742 training acc validation acc
Figure 27.21 — The real learning curves, all 25 epochs. Training climbs to 0.88; validation flattens near 0.73 from epoch 10. The gap is the overfitting.
Epoch123510152025
Train loss0.6890.6620.6430.5940.5200.4340.3480.295
Train acc0.5540.6290.6330.6810.7350.8020.8430.884
Val loss0.6600.6700.6200.6960.5540.5940.5580.603
Val acc0.6110.5860.6260.5900.7290.7220.7420.738

Read the two curves the way Chapter 6 taught bias and variance, now in epochs. By epoch 10 the network has learned most of what 2,000 photos can teach: validation loss bottoms at 0.529 (epoch 12). After that, training loss keeps falling — but validation loss climbs (0.554 → 0.603) and validation accuracy merely wobbles around 0.73. That divergence is the real-time signature of the network memorising its training photos instead of generalising: train 0.884 vs validation 0.738 at epoch 25, a 14.6-point gap. The best validation accuracy, 0.742 at epoch 20, marks where training should have stopped — the classic early stopping argument, made by the data.

💡
Pro Tip — the validation wobble is the augmentation: every epoch's training batch is randomly sheared, zoomed and flipped anew, so training accuracy itself is noisy and validation jumps epoch to epoch (0.729 → 0.641 → 0.739 across epochs 10–12). Judge the trend, not single epochs; smooth with more epochs or a running average.

10.1 The final scoreboard (epoch 25, threshold 0.5)

Predicted
ActualCat (0)Dog (1)
Cat (500)341 TN159 FP — cats read as dogs
Dog (500)103 FN — dogs read as cats397 TP

Accuracy 738/1,000 = 0.738; precision 397/556 = 0.714; recall (dogs) 397/500 = 0.794; F1 0.752. The asymmetry is the interesting part: the network finds dogs markedly easier than cats (recall 0.794 vs 0.682 for cats). Cats vary more — posture, flattening, ambiguity — while dog photos in this dataset tend to be more stereotyped (face-forward, tongue out). Real datasets are rarely symmetrically hard; the confusion matrix is where it shows. The probabilities are genuinely confident where correct: min 0.0001, max 1.0000, mean 0.562.

10.2 Single predictions — the source's finale, for real

The source ends by predicting two unseen images one at a time. The real run's answers:

dog image → P(dog) = 1.0000 → "dog"   ·   cat image → P(dog) = 0.0588 → "cat"
Both correct — and note the plumbing: load_img resizes to 64 × 64, img_to_array makes the 64×64×3 tensor, expand_dims(..., axis=0) wraps it into a batch of one (predict always wants a batch), and the raw 0–255 values are divided by 255 to match training. Skip the rescale and the network sees an image ten times brighter than anything it trained on.
🌎
Real World: 0.738 on 2,000 photos with a 1-minute network is exactly where production projects start — and the fix is rarely a cleverer architecture. In rough order of payoff: more data (the course's full 8,000-image set outperforms this filtered subset), transfer learning (Section 12), and only then architecture. Data beats architecture at small scale, almost always.

11. What It Learned, and When CNNs

The two conv-pool pairs are a learned feature hierarchy. Visualising trained filters (or activating images per filter) reliably shows the same progression across CNNs of any size: first-layer filters — direction-selective edges and colour blobs (close cousins of the hand kernel of Section 6, discovered independently by gradient descent); second-layer filters — corners, textures, fur patterns; deeper layers in larger networks — ears, eyes, snouts. The 128-unit Dense layer then plays Chapter 26's role on 6,272 features that actually mean something.

And the honest ledger for this chapter's tool:

SituationRight toolWhy
Images, audio, videoCNN — no serious rival in this courselocal structure + translation invariance is what convolutions are
Tabular business dataForests/boosting (Ch 17, 32)CNNs find no spatial structure in columns; Chapter 26's caveat doubles
TextEmbeddings/transformers (Ch 25 §8)order matters more than locality
Huge images, little dataTransfer learning (next section)never train feature detectors from scratch twice
⚠
Warning — the compute bill: the run above trained in minutes on a CPU at 64 × 64. Double the image size and the convolutions quadruple; add filters and it multiplies again. The entire deep-learning revolution sat behind hardware (GPUs) as much as ideas. Budget compute before promising accuracy.
⚠ Volatile content — Reviewed: October 2026 · Next review: January 2027

12. The Road Ahead

Where image recognition went after this architecture — and why this chapter's from-scratch network is now a teaching artefact rather than a production recipe:

The course now turns from more data to fewer dimensions. Part IX — Principal Component Analysis first — asks the mirror-image question: when features are many and redundant, which few combinations actually carry the information? Spoiler: eigenvectors, which have been waiting since Chapter 2, finally get their starring role.

🛠 Hands-On: Train, Diagnose, and Improve the DetectorIntermediate

You inherit the cat-dog classifier at 73.8% and one instruction: "make it better, and prove you know why it improved." Walk the full diagnostic path: verify the architecture arithmetic, train, read the curves, stop at the right epoch, then test one improvement.

Steps

  1. Hand-verify Section 6's convolution (three cells of your choice) and Section 7's max-pool table.
  2. Hand-verify the dimension chain 64 → 62 → 31 → 29 → 14 and the parameter table; confirm against count_params() = 813,217 and the per-layer shapes.
  3. Prepare the dataset (Listing 27.2), train for 25 epochs, and reproduce curves shaped like Figure 27.21 (your exact numbers will differ slightly — seed and library builds vary).
  4. From your curves: locate the minimum validation loss and the best validation accuracy epoch; recommend a stopping epoch with evidence.
  5. Compute the confusion matrix, accuracy/precision/recall/F1, and the cat-vs-dog recall asymmetry.
  6. Predict two single images (one cat, one dog) through the full single-prediction plumbing — including the 1/255 rescale — and report the probabilities.
  7. Improvement test: retrain with target_size=(128, 128) (adjust input_shape accordingly and re-derive the dimension chain by hand first). Report what moved.
View Solution / Walkthrough

Steps 1–2 (hand): convolution output equals [[0, 30, 30], [0, 30, 30], [0, 30, 30]]; max-pool of [[1,3,2,4], [5,6,7,8], [0,9,1,2], [3,4,5,6]] is [[6, 8], [9, 6]]. Dimensions: 64−2=62; ⌊62/2⌋=31; 31−2=29; ⌊29/2⌋=14. Parameters: 896 + 9,248 + 802,944 + 129 = 813,217.

Step 7 dimension chain at 128 input: 128 → 126 → 63 → 61 → 30 (⌊61/2⌋); flatten 30 × 30 × 32 = 28,800; Dense layer becomes 28,800 × 128 + 128 = 3,686,528 parameters — total ≈ 3.7M, 4.5× the original. Expect a modest accuracy gain at several times the training cost: the compute bill of Section 11, experienced personally.

import numpy as np, tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
np.random.seed(0); tf.random.set_seed(0)

train_gen = ImageDataGenerator(rescale=1./255, shear_range=0.2,
                               zoom_range=0.2, horizontal_flip=True)
train = train_gen.flow_from_directory('dataset/training_set',
        target_size=(64,64), batch_size=32, class_mode='binary')
test = ImageDataGenerator(rescale=1./255).flow_from_directory(
        'dataset/test_set', target_size=(64,64), batch_size=32,
        class_mode='binary', shuffle=False)

cnn = tf.keras.models.Sequential([
    tf.keras.layers.Input(shape=(64,64,3)),
    tf.keras.layers.Conv2D(32, 3, activation='relu'),
    tf.keras.layers.MaxPool2D(2, 2),
    tf.keras.layers.Conv2D(32, 3, activation='relu'),
    tf.keras.layers.MaxPool2D(2, 2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')])
print('params', cnn.count_params())          # 813217

cnn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
h = cnn.fit(train, validation_data=test, epochs=25, verbose=0)
va = h.history['val_accuracy']
print('best val_acc %.3f at epoch %d' % (max(va), int(np.argmax(va))+1))

probs = cnn.predict(test, verbose=0).ravel()
y = test.classes; yp = (probs >= .5).astype(int)
from sklearn.metrics import confusion_matrix, accuracy_score
print(confusion_matrix(y, yp), accuracy_score(y, yp))

from tensorflow.keras.preprocessing import image
img = image.load_img('dataset/single_prediction/cat_or_dog_1.jpg',
                     target_size=(64,64))
arr = np.expand_dims(image.img_to_array(img)/255.0, axis=0)
print('P(dog) %.4f' % cnn.predict(arr, verbose=0)[0][0])

Expected results (real canonical run, seed 0): class_indices {'cats': 0, 'dogs': 1}; 813,217 params; best val_acc 0.742 at epoch 20; min val_loss 0.529 at epoch 12; epoch-25 train 0.884 vs val 0.738 (the overfitting gap); CM [[341, 159], [103, 397]], acc 0.738, P 0.714, R 0.794, F1 0.752; cat recall 0.682 vs dog recall 0.794; singles: dog → P(dog) 1.0000, cat → 0.0588. Your run will land near these values but not on them — CPU TensorFlow is nondeterministic across builds; the shape of the story (fast early gains, val plateau, growing gap) is the reproducible part.

🎯 Key Takeaways

1

Dense layers break on images: 1.57M parameters to connect 12,288 pixels to 128 units, and a weight per location — no translation invariance.

2

A convolution slides one small learned kernel everywhere: hand example 5×5 ⊛ 3×3 → 3×3 map that lights up (30) exactly on the edge. Same 9 weights, every position.

3

Max-pooling halves the map with zero parameters and buys position tolerance: [[1,3],[5,6]] → 6. Dimension chain: 64 → 62 → 31 → 29 → 14.

4

813,217 parameters — but 98.8% sit at Flatten→Dense; the two convolutions that do the seeing own only 10,144. Stack convs to shrink that junction.

5

Augment training images (shear, zoom, flip — never the test set): the network can't memorise 2,000 photos it never sees twice.

6

The real run overfit honestly: train 0.884 vs val 0.738; best val 0.742 at epoch 20 → early stopping. Dogs easier than cats (0.794 vs 0.682 recall). Data beats architecture at small scale.

Test Your Understanding

35 questions across the chapter — features and illusions, ranked guesses, pixels, the smiley walkthrough, the hand convolution, kernels, ReLU, pooling, architecture arithmetic, augmentation, and the real training run. Choose one answer per question, then check your answers.

Q1.The dataset and task:

Q2.A 64 × 64 RGB image arrives as:

Q3.Why not feed pixels into Chapter 26's Dense network?

Q4.A convolution is best described as:

Q5.(Hand) In Section 6's example, the top-middle feature-map cell equals 30 because:

Q6.The 9 kernel weights in a real CNN come from:

Q7.(Hand) A 3 × 3 kernel (no padding, stride 1) on a 5 × 5 input produces:

Q8.Weight sharing means:

Q9.(Hand) Max-pool 2×2, stride 2 on [[1,3,2,4],[5,6,7,8],[0,9,1,2],[3,4,5,6]] gives:

Q10.What max-pooling buys:

Q11.(Hand) After conv(3×3) → pool(2×2) → conv(3×3) → pool(2×2) on a 64 × 64 input, the map is:

Q12.Of the CNN's 813,217 parameters, where do they live?

Q13.conv1's 896 parameters decompose as:

Q14.The Flatten layer's job:

Q15.Data augmentation (shear 0.2, zoom 0.2, horizontal flip) exists to:

Q16.rescale = 1./255 is:

Q17.The test generator rescales but never shears/zooms/flips because:

Q18.With class_indices {'cats': 0, 'dogs': 1}, the sigmoid output means:

Q19.The single-prediction pipeline load_img → img_to_array → expand_dims → /255:

Q20.Why expand_dims(arr, axis=0) before predicting?

Q21.(Real) The training curves of Figure 27.21 show:

Q22.(Real) Validation loss was lowest at epoch 12 (0.529) and best validation accuracy was 0.742 at epoch 20. The right conclusion:

Q23.(Real) The confusion matrix [[341, 159], [103, 397]] says:

Q24.(Real) The two single-image predictions came out:

Q25.The noisy validation curve (0.729 → 0.641 → 0.739 across epochs 10–12) is explained by:

Q26.Stacking two conv-pool pairs (rather than one) gives:

Q27.Raising target_size to 128 × 128 would:

Q28.For tabular business data (say, churn), the right tool is:

Q29.Transfer learning, the highest-value trick on small image datasets:

Q30.Chapter 28 (PCA) will ask the mirror-image question of Part VIII's:

Q31.The optical illusions that open the chapter (the hybrid face, the duck-rabbit) teach one specific lesson:

Q32.In a colour image, one pixel is stored as:

Q33.The stride of a convolution is:

Q34.(Hand) In the smiley walkthrough, the feature map's largest value (the 4) appears where:

Q35.The rectifier (ReLU) applied on top of the convolution: