11

Convolutions and vision

How does a network learn to see edges, shapes, and objects?

6 min

Convolutions and vision

With voice. You can pause and jump to any step.

Lesson complete

Subtitles

A one-hundred-by-one-hundred pixel image has ten thousand values. Connecting each one to a thousand neurons takes ten million weights, in the first layer alone.

Voice

No voice: the lesson advances on its own with subtitles.

The voice for this lesson is being recorded: for now it advances on its own with subtitles.

0:00 / 0:00

What you will achieve

  • Explain what a convolution kernel does and compute by hand one output value of a three-by-three kernel.
  • Understand why sharing weights makes it possible to process images with very few parameters.
  • Describe how stacked layers go from edges to parts to objects, and what limits these networks.

Key ideas

  1. Connecting every pixel to every neuron does not scale: a small image already needs millions of weights.
  2. A kernel is a small grid of weights that sweeps across the image; at each position it multiplies, adds, and produces an output pixel.
  3. The same kernel is reused across the image (shared weights): few parameters and the same detector at any position.
  4. Kernel values are learned with backpropagation; a layer has many kernels, and each produces a feature map.
  5. Stacking layers builds a hierarchy from edges to parts to objects. ImageNet-trained networks tend to lean on textures, and vision networks can be fragile to subtle changes.

What many people believe

Common belief A vision network “sees” the image like a person: it recognizes objects and understands the scene.

What is true It detects statistical patterns in pixel numbers. It is very good at what it saw in training, but it tends to lean on textures and can be wrong with changes a person would not even notice.

Check yourself

Four questions. No grade, no account: they just show whether the idea stuck.

ConceptWhat is the main advantage of sharing the same kernel across the whole image?
CalculationA 28 × 28 image is convolved with a 3 × 3 kernel, no padding, stride 1. What size is the output map?
ConceptWhat does 2 × 2 max pooling do?
ApplicationA quality-inspection system works in the lab, but in the factory, with different lighting, it fails a lot. What is the most likely explanation?

Under the hoodFor those who want the math and the code. You do not need it to follow the course.

The operation

Given an image II and a k×kk\times k kernel KK, the output (without padding) at position (i,j)(i,j) is

O[i,j]=b+∑u=0k−1∑v=0k−1K[u,v]  I[i+u, j+v].O[i,j] = b + \sum_{u=0}^{k-1}\sum_{v=0}^{k-1} K[u,v]\; I[i+u,\,j+v].

Technically this is cross-correlation; mathematical convolution flips the kernel, but in deep learning the kernel is learned and the distinction does not matter (Goodfellow et al., 2016, ch. 9). With an n×nn\times n image, no padding (p=0p=0), and stride 1, the output is n−k+1n-k+1 per side; with stride ss and padding pp (rows and columns of zeros around the border): ⌊(n+2p−k)/s⌋+1\lfloor (n+2p-k)/s \rfloor + 1 (Dumoulin and Visin, 2016). Quiz example: 28×2828\times28 with k=3k=3 gives 26×2626\times26; with p=1p=1 the size stays 28×2828\times28.

Parameters: fully connected versus convolutional

A fully connected layer from 10410^4 inputs to 10310^3 neurons has 10710^7 weights (plus biases). A convolutional layer with CoutC_{out} kernels of k×kk\times k over CinC_{in} channels has

Cout (k2 Cin+1)C_{out}\,(k^2\,C_{in} + 1)

parameters, independent of the image size. With Cin=1C_{in}=1, k=3k=3 and 16 kernels: 16⋅(9+1)=16016\cdot(9+1)=160.

The hook’s comparison (10 million versus 10) sets a single kernel against a thousand neurons, and one kernel produces a single map. With 1,000 3×33\times3 kernels on the same 100×100100\times100 image (no padding) there are 1,000⋅(9+1)=10,0001{,}000\cdot(9+1)=10{,}000 parameters, a thousand times fewer than 10710^7; but the output is 1,000 maps of 98×9898\times98 (9,604,000 values) and 982⋅9⋅1,000≈8698^2\cdot 9\cdot 1{,}000\approx 86 million multiplications, more than the dense layer’s 10 million. Sharing weights saves parameters (and the data needed to fit them), not operations: it does not change the order of the forward-pass running time, only the memory for the parameters (Goodfellow et al., 2016, §9.2).

Where the idea comes from

Fukushima’s neocognitron (1980) already combined layers of local feature detectors with layers tolerant to shifts, but it was trained with a self-organizing rule, without supervision: it lacked an end-to-end supervised algorithm such as backpropagation (LeCun, Bengio, and Hinton, 2015; Goodfellow et al., 2016, §9.10). LeCun et al. (1989) trained with backpropagation a network with shared weights to recognize handwritten zip code digits provided by the U.S. Postal Service: 9,298 already-segmented digits (7,291 for training, 2,007 for testing) in 16×1616\times16 images. Its 64,660 connections were controlled by 9,760 independent parameters, and the error was 0.14% on the training set (10 mistakes) and 5.0% on the test set (102 mistakes); a fully connected network with 40 hidden units, tried as a comparison, reached 8.1% on the test set. That network did not use pooling, but convolutions that jumped two pixels at a time. LeNet-5 (LeCun et al., 1998) has 7 layers, a 32×3232\times32 input, 5×55\times5 kernels, 340,908 connections, and about 60,000 free parameters; its “sub-sampling” averages each 2×22\times2 block (with a trainable coefficient and bias) instead of taking the maximum.

The lesson’s kernel

The lesson’s edge kernels are those of the Sobel operator (OpenCV documentation, “Sobel Derivatives”). The vertical-edge kernel is

Kx=(−101−202−101),Ky=Kx⊤.K_x=\begin{pmatrix}-1&0&1\\-2&0&2\\-1&0&1\end{pmatrix},\qquad K_y=K_x^\top.

Its magnitude (Kx∗I)2+(Ky∗I)2\sqrt{(K_x * I)^2 + (K_y * I)^2} estimates edge strength (same source). Here the kernels are chosen by hand to show the operation; in a trained network they are learned, and in the first layers they tend to resemble oriented-edge detectors and color blobs (Krizhevsky et al., 2012, fig. 3; Yosinski et al., 2014).

Receptive field and hierarchy

Each layer sees a larger region of the original image. With rL=1r_L=1 at the last layer, the receptive field satisfies rl−1=sl rl+(kl−sl)r_{l-1}=s_l\,r_l+(k_l-s_l) (Araujo, Norris, and Sim, 2019): two stacked 3×33\times3 convolutions “see” 5×55\times5; with a 2×22\times2 pooling in between (3×33\times3, pooling, 3×33\times3) they see 8×88\times8, and a unit of the lesson’s example network, after its two poolings, sees 10×1010\times10 pixels. That is why early layers detect local patterns (edges, colors) and deep layers detect broad combinations.

In an AlexNet-type network, Zeiler and Fergus (2014) visualized what excites each layer: layer 2 responds to corners and edge/color conjunctions; layer 3, to textures and more complex patterns; layer 4, to class-specific parts (dog faces); layer 5, to whole objects with significant pose variation. LeCun, Bengio, and Hinton (2015) summarize it as edges, motifs, parts, and objects, and stress that these layers of features are not designed by human engineers: they are learned from data. What a person does design is the architecture (how many layers, what kernel size, where the pooling goes).

Pooling and shifts

2×22\times2 max pooling with stride 2 halves the map per side and makes the output approximately invariant to small shifts (Goodfellow et al., 2016, §9.3; LeCun, Bengio, and Hinton, 2015). It is only approximate: in the scene, shifting the image by one pixel changes the edge map by 56% (sum of absolute differences over the sum of absolute values) and after pooling by 19%; with 400 random-stripe images, the mean change drops from 0.97 to 0.50 and in 50 of the 400 pooling does not reduce it (script verify-data.ts). Azulay and Weiss (2019) show that in full networks shifting or rescaling the image a little can change the prediction a lot. In the scene, pooling takes the maximum of the absolute value; in a real network the activation (for example ReLU) comes first.

The limits, with numbers

  • Textures. Geirhos et al. (2019) showed ImageNet-trained networks images with the shape of one object and the texture of another. When forced to choose, ResNet-50 answered by texture 77.9% of the time (VGG-16, 82.8%; GoogLeNet, 68.8%; AlexNet, 57.1%); people chose shape in 95.9% of their correct decisions. With the same architecture, training on a “stylized” version of ImageNet changed that bias: it depends on the data and the task, not only on convolution.
  • Nearly invisible changes. Szegedy et al. (2014) found hardly perceptible perturbations that change the class a network predicts. Goodfellow, Shlens, and Szegedy (2015) took GoogLeNet’s answer on a photo of a panda from “panda” (57.7% confidence) to “gibbon” (99.3%) by adding a perturbation of magnitude 0.007, the smallest bit of an 8-bit image.
  • Data unlike the training data. Recht et al. (2019) rebuilt ImageNet test sets with the same procedure and model accuracy dropped by 11% to 14%. Beery et al. (2018): with wildlife camera traps, good performance where it was trained and poor generalization to new places. Hendrycks and Dietterich (2019): ImageNet-C applies 75 alterations (15 types at 5 intensities, including brightness, contrast, fog, and noise), and relative robustness barely improved from AlexNet to ResNet.
  • Scenes. Goodfellow et al. (2016, §9.10) note that the human visual system does much more than recognize objects (it understands entire scenes, relationships between objects, and 3D geometry) and that applications of convolutional networks to those problems were then in their infancy.

Reusing an already trained model

Yosinski et al. (2014) measured what can be reused: first-layer features (such as Gabor filters and color blobs) do not depend on the task or the data, while last-layer features do. He, Girshick, and Dollár (2018, technical report) compared, in object detection on COCO, starting from a model pre-trained on ImageNet versus training from scratch: pre-training speeds up convergence, but with enough iterations training from scratch matched the accuracy, even with 10% of the data; only with very little data (fewer than about 10,000 COCO images) did pre-training clearly improve the result.

In code

import numpy as np

def conv2d(img, K, b=0.0):
    k = K.shape[0]
    h, w = img.shape[0] - k + 1, img.shape[1] - k + 1
    out = np.zeros((h, w))
    for i in range(h):
        for j in range(w):
            out[i, j] = b + np.sum(K * img[i:i + k, j:j + k])
    return out

Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
img = np.zeros((6, 6)); img[:, 3:] = 1          # half dark, half bright: a vertical edge
print(conv2d(img, Kx))                           # values 4 in the edge columns, 0 elsewhere

What comes next

Backpropagation from lesson 8 trains the kernels like any other weight. Convolutional networks came to dominate vision from 2012 on (lesson 13); LeCun, Bengio, and Hinton (2015) describe them as the dominant approach for almost all recognition and detection tasks. For sequential data such as language, another idea is needed (lesson 12).

Decision lens

What this idea means if your job is to decide, not to code.

  • A vision system performs well on images similar to its training data: ask for it to be tested with your cameras, your lighting, and your real cases.
  • If the vendor talks about “99% accuracy,” ask on which set of images and with what variety: performance drops when conditions change.
  • These networks can fail in ways a person does not understand (tiny changes, backgrounds, textures). For critical decisions, design human review and adversarial test cases.
  • Starting from an already trained vision model usually speeds up training and helps most when you have little data of your own; with lots of data, training from scratch can match it. Ask whether they use a pretrained one and how they adapted it.

Terms in this lesson

Full transcript
CommentsThis lesson’s public conversation: questions, facts to check, ideas.

Comments live in GitHub Discussions, in the course’s public repository; writing one requires a GitHub account. They load from giscus.app when you open this section. The course reviewers read them, and if a change is accepted, it is applied in both languages. Open the thread on GitHub

Sources