Unit 06.01: Channels, filters, feature maps, stride, and padding
Four settings control the shape of a convolution's output. Getting them wrong is the source of most "size mismatch" errors in CNN code.
The size rule, and how to check it
Kernel size is the filter's window. Padding adds a border so edge pixels get equal treatment and the output can keep its size. Stride is the step between positions — a stride of 2 roughly halves each dimension.
The formula is worth memorising: out = floor((in + 2·padding - kernel) / stride) + 1.
import torch
from torch import nn
x = torch.randn(1, 3, 32, 32) # batch, channels, height, width
for label, layer in [
("kernel 3, no padding", nn.Conv2d(3, 8, kernel_size=3)),
("kernel 3, padding 1 ", nn.Conv2d(3, 8, kernel_size=3, padding=1)),
("kernel 3, stride 2 ", nn.Conv2d(3, 8, kernel_size=3, stride=2, padding=1)),
("kernel 5, padding 2 ", nn.Conv2d(3, 8, kernel_size=5, padding=2)),
]:
out = layer(x)
print(f"{label}: {tuple(x.shape[1:])} -> {tuple(out.shape[1:])}")
# The size rule, worth memorising:
# out = floor((in + 2*padding - kernel) / stride) + 1
print("\ncheck by hand: (32 + 2*1 - 3)//1 + 1 =", (32 + 2 * 1 - 3) // 1 + 1)
print("stride 2 : (32 + 2*1 - 3)//2 + 1 =", (32 + 2 * 1 - 3) // 2 + 1)
# Parameters depend on channels and kernel, never on image size.
conv = nn.Conv2d(3, 8, kernel_size=3)
print("\nparameters:", 3 * 8 * 3 * 3, "weights +", 8, "biases =",
sum(p.numel() for p in conv.parameters()))
Each row shows the rule in action. kernel 3, padding 1 preserves 32×32 — the standard choice when you want the convolution to change depth without changing size. stride 2 halves it.
The last block is the one people miss: parameter count depends on channels and kernel size, never on image size. A convolution has the same weights for a 32×32 image as for a 512×512 one.
The mistake this prevents
Guessing the output size instead of computing it, then discovering the mismatch several layers later where the error message is least informative.
Takeaway
Apply the size rule as you build. Padding 1 with kernel 3 preserves size, and stride 2 halves it.
