Key ideas
- Why convolutions: a fully connected layer gives each hidden unit its own weight for every pixel; for a one-megapixel image even 1000 hidden units need weights1. Two priors fix this1: locality (a unit only looks at a small window) and translation invariance (the same kernel is used at every position, i.e. weight sharing). In d2l's example, a hidden map needs parameters; translation invariance cuts this to and locality to , a few hundred for window radius 1. An MLP on flattened pixels has neither prior and must learn everything from data.
- Equivariance, not rotation invariance: a shifted input gives a shifted feature map (translation equivariance), but rotations and scale changes are not built in; they must be learned, e.g. by training on rotated and rescaled copies (data augmentation)7.
- The operation is cross-correlation: each output is the sum of the elementwise product of the kernel and the window under it, with no kernel flip. A true convolution flips the kernel in both directions; since kernels are learned, the difference does not matter for a CNN2 (but
np.convolveand SciPy'sconvolve2ddo flip). - Hand-made kernels: a single 1 in the centre is the identity; times all ones is a box blur; takes horizontal differences, so it responds to vertical edges and gives zero on purely horizontal ones2. A CNN learns such kernels from data instead of having them designed by hand2.
- Padding and stride3: without padding a kernel shrinks the height and the width by each, and border pixels take part in fewer windows than interior ones. Zero padding of rows and columns in total ( per side for odd ) keeps the size at stride 1 ("same"). Stride moves the window by , dividing height and width by about and the number of outputs, hence the compute, by about . Padding never changes the number of channels.
- Channels4: with input and output channels the kernel tensor is ; each output channel sums over all input channels; there is one bias per output channel. The parameter count does not depend on the image size, while the cost grows like . A convolution is a dense layer from to channels applied at every pixel.
- Pooling5: max or average over each window, separately per channel (channel count unchanged), no parameters; frameworks default the stride to the window size, so pooling halves height and width. Max pooling gives a little invariance to small shifts. Global average pooling averages each whole feature map, turning into numbers7, so a dense head after it needs far fewer weights than after flattening.
- Receptive field: the input region one unit depends on. Stacking layers enlarges it2,7: stacked stride-1 convs see ; after a stride or pooling every later layer adds more input pixels per step.
- Cheaper variants:
groups=gsplits the channels into groups that never exchange information, cutting weights and cost by ;groups=c_inis a depthwise convolution6. A depthwise convolution followed by a (pointwise) convolution that mixes the channels is a depthwise separable convolution7, the building block of MobileNets8. - Typical CNN: [conv → BatchNorm → ReLU → pool] × n → flatten or global average pooling → dense layers. Most parameters sit in the first dense layer after flatten;
BatchNorm2d(C)needs = the conv's output channels.
Formulas
Output size per dimension (input , kernel , padding per side, stride , dilation )3,6:
Receptive field, layer by layer for kernel and stride : and , starting from ( is the distance in input pixels between neighbouring units).
Worked example
Input :
Conv2d(3, 16, 5, padding=2): , so ; parameters .MaxPool2d(2): , no parameters.Conv2d(16, 32, 3, stride=2, padding=1): , so ; parameters ; multiply-adds .- Flatten: features;
Linear(8192, 10)has parameters, far more than both convolutions together.
In code
import torch
from torch import nn
net = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=5, padding=2), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1), nn.ReLU(),
nn.Flatten(), nn.Linear(32 * 16 * 16, 10))
x, shapes = torch.zeros(8, 3, 64, 64), []
for layer in net:
x = layer(x)
if not isinstance(layer, nn.ReLU): # (shape without batch, parameters)
shapes.append((tuple(x.shape[1:]), sum(p.numel() for p in layer.parameters())))
print(shapes) # [((16, 64, 64), 1216), ((16, 32, 32), 0), ((32, 16, 16), 4640), ((8192,), 0), ((10,), 81930)]
Common pitfalls
- Padding changes height and width, not channels; the number of output channels equals the number of kernels.
- Conv parameters do not depend on the input's height and width; the bias is one per output channel, not per position.
- Pooling, ReLU and dropout have no parameters; pooling never mixes channels.
- Sizes are floored: a map under pooling with stride 2 becomes , while "same"-style padding (, or TensorFlow's
"SAME") with stride 2 gives . - Two stacked convolutions with stride 1, no padding and no nonlinearity in between equal one convolution with kernel size ; the nonlinearity is what adds expressive power.
- Three layers see a region with instead of weights per channel pair.
- CNNs are not rotation invariant: a model trained on upright images can fail on rotated ones.
- A max of a window is not a weighted sum, so a single linear conv layer cannot imitate max pooling, while average pooling equals a depthwise conv with all weights and stride equal to the window.
References
- Dive into Deep Learning — Sec. 7.1 From Fully Connected Layers to Convolutions (introduction, 7.1.2.1 Translation Invariance, 7.1.2.2 Locality)
- Dive into Deep Learning — Sec. 7.2 Convolutions for Images (7.2.3 Object Edge Detection in Images, 7.2.4 Learning a Kernel, 7.2.5 Cross-Correlation and Convolution, 7.2.6 Feature Map and Receptive Field)
- Dive into Deep Learning — Sec. 7.3 Padding and Stride (7.3.1 Padding, 7.3.2 Stride)
- Dive into Deep Learning — Sec. 7.4 Multiple Input and Multiple Output Channels (7.4.2 Multiple Output Channels, 7.4.3 1×1 Convolutional Layer, 7.4.4 Discussion)
- Dive into Deep Learning — Sec. 7.5 Pooling (7.5.1 Maximum Pooling and Average Pooling, 7.5.2 Padding and Stride, 7.5.3 Multiple Channels)
- PyTorch documentation: torch.nn.Conv2d — Conv2d (groups parameter; Shape; Variables weight and bias)
- Wikipedia: Convolutional neural network — Architecture (Convolutional layers, Pooling layers, Receptive field); Translation equivariance and aliasing; Regularization methods, Artificial data
- Howard et al. (2017): MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications — Abstract; Sec. 3.1 Depthwise Separable Convolution