Convolutional networks: output shapes and parameter counts

Compute how convolution and pooling change the size of an image, and why sharing weights saves parameters.

Python
def conv_out(n, k, stride=1, pad=0):
    return (n + 2 * pad - k) // stride + 1


size, channels = 28, 1
size = conv_out(size, 5)
conv1_params = (5 * 5 * channels + 1) * 6
channels = 6
print(size, channels, conv1_params)
size = conv_out(size, 2, stride=2)
print(size, channels)
size = conv_out(size, 5)
conv2_params = (5 * 5 * channels + 1) * 16
channels = 16
print(size, channels, conv2_params)
size = conv_out(size, 2, stride=2)
print(size, channels, size * size * channels)
print((28 * 28) * (24 * 24 * 6) + 24 * 24 * 6)
print(256 * 120 + 120)
Output
24 6 156
12 6
8 16 2416
4 16 256
2712960
30840

Explanation

A convolutional neural network (CNN) is built for data with a grid structure, such as images. A convolutional layer slides small filters over the input. Each filter has a fixed number of weights and is applied at every position, so the same weights are reused: this is weight sharing. A layer with c_out filters of size k × k on an input with c_in channels has (k·k·c_in + 1)·c_out parameters, including one bias per filter, however large the image is.

The size of the output depends on the input size n, the filter size k, the stride s (how far the filter moves), and the padding p added around the border: out = floor((n + 2p − k) / s) + 1. Pooling layers shrink the maps further, for example 2 × 2 max pooling with stride 2 halves the width and height. Each output value depends only on a small neighbourhood of its input, so the network is built from local features that later layers combine into larger patterns. Deep learning libraries actually compute cross-correlation, which does not flip the filter, and call it convolution.

The program follows a small LeNet-style network on a 28 × 28 grayscale image. The first layer has 6 filters of size 5 × 5 and needs only 156 parameters. A fully connected layer producing the same 24 × 24 × 6 output from all 784 inputs would need 2,712,960. The second convolution, with 16 filters, adds 2,416 parameters. After pooling, 4 × 4 × 16 = 256 values feed into a dense layer with 120 units.

Explore the dry run
Guided walkthroughReady
1def conv_out(n, k, stride=1, pad=0):
2 return (n + 2 * pad - k) // stride + 1
3
4
5size, channels = 28, 1
6size = conv_out(size, 5)
7conv1_params = (5 * 5 * channels + 1) * 6
8channels = 6
9print(size, channels, conv1_params)
10size = conv_out(size, 2, stride=2)
11print(size, channels)
12size = conv_out(size, 5)
13conv2_params = (5 * 5 * channels + 1) * 16
14channels = 16
15print(size, channels, conv2_params)
16size = conv_out(size, 2, stride=2)
17print(size, channels, size * size * channels)
18print((28 * 28) * (24 * 24 * 6) + 24 * 24 * 6)
19print(256 * 120 + 120)

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Output size = floor((n + 2·pad − k) / stride) + 1: how many positions the filter fits.—
—
Each of 6 filters has 5 × 5 × 1 weights plus a bias: 26 × 6 = 156 parameters, regardless of image size.—
—
A 28 × 28 image becomes 24 × 24 with 6 channels.—
24 6 156
After the second convolution and pooling, the maps are 4 × 4 with 16 channels: 256 values to flatten.—
24 6 156
12 6
8 16 2416
4 16 256
A fully connected layer with the same output size would need almost 2.7 million parameters.—
24 6 156
12 6
8 16 2416
4 16 256
2712960
The dense layer after flattening has 30,840 parameters, far more than the convolutions before it.—
24 6 156
12 6
8 16 2416
4 16 256
2712960
30840

Cost and common mistakes

A convolution layer costs O(out_h · out_w · c_out · k² · c_in) multiply-adds, and stores only O(k² · c_in · c_out) parameters.

  • Shape mistakes are the most common bug in CNNs. Compute the output size of every layer before training.
  • Filters are learned, not designed. The 5 × 5 size is a hyperparameter you choose.
  • Pooling and strides discard spatial detail. Too much downsampling can lose information needed for fine tasks.

Try it yourself

What output size does a 3 × 3 filter with padding 1 and stride 1 give for a 32 × 32 input?

Reveal answer and reasoning

(32 + 2·1 − 3) / 1 + 1 = 32. Padding of 1 with a 3 × 3 filter preserves the size.

Further reading