Pooling and Downsampling
Pooling summarizes each small patch of a feature map into one value, trading exact positions for cheaper maps and sturdier features.
Why Does This Exist?
A 1080p frame at with float32 channels costs bytes, about MB for one feature map, before any batch or gradient. Running every later layer at full resolution is unaffordable, and exact pixel positions are often noise: a classifier should fire whether the tire sits ten pixels left or right. Pooling compresses each local patch into one value, halving spatial sides per step while keeping the strongest signals. Two stride-2 steps shrink that MB map -fold to about MB. This page covers window pooling and its learned cousin, strided convolution. What flows through the windows is taught in feature maps, and the stride arithmetic in padding and stride.
Think of It Like This
Judging a choir by section
A judge scores a choir split into sections. For each section the judge writes down one number: with max-pooling it is the loudest singer's volume, with average-pooling it is the section's overall hum. Swapping two singers inside one section changes nothing on the score sheet.
The window is the section, the stride is how sections tile the stage, and swap-proof scores are translation invariance. The analogy stops at seating: the judge still remembers where each section sat, but pooling forgets exact positions. Tasks that need locations, like drawing boxes or masks, must recover them elsewhere.
How It Actually Works
A pooling layer slides a window with stride over each channel independently, where is the window side and is the step between windows. The output side is for input side , so and halves each side and quarters the values. Max pooling outputs over window , keeping the strongest activation and killing the rest, which suits sparse signals like edges. Average pooling outputs , smoothing the patch, which suits texture and density but dilutes a lone strong spike. Global average pooling pushes this to the limit: one value per channel for the whole map, feeding the classifier with no dense layer. Strided convolution instead steps its learned kernel with , downsampling with weights the network tunes rather than a fixed rule.
Worked 4 by 4 example
Input rows , , , with , give output side . The top-left window max-pools to and averages to . Scoring all four windows gives max output rows and , and average output rows and .
Code
img = [ [1.0, 3.0, 2.0, 4.0], [5.0, 6.0, 1.0, 2.0], [0.0, 1.0, 8.0, 7.0], [3.0, 2.0, 4.0, 5.0],]
def pool(img, fn): out = [] for r in (0, 2): row = [] for c in (0, 2): window = [img[r + dr][c + dc] for dr in (0, 1) for dc in (0, 1)] row.append(fn(window)) out.append(row) return out
print(pool(img, max))# -> [[6.0, 4.0], [3.0, 8.0]]
def mean(xs): return sum(xs) / len(xs)
print(pool(img, mean))# -> [[3.75, 2.25], [1.5, 6.0]]Watch Out For
Pooling away the resolution a locator needs
Classification survives heavy downsampling, but detectors and segmenters read positions off the map. The symptom is vanished small objects and blobby masks after a total stride of on tiny inputs. Budget the strides across the backbone, and recover detail with skip connections or dilated convolutions instead of stacking more pools.
Average-pooling a sparse signal map
When only a few cells carry the defect, edge or spike, averaging divides it by the whole window. The symptom is a strong spike inside a window of zeros scoring and missing its threshold. Use max pooling for sparse presence signals and reserve averaging for texture, density and tail summaries.
The Quick Version
- Pooling summarizes each patch into one value with stride between windows.
- A stride-2 pool halves each side and quarters the value count.
- Max pooling keeps the strongest activation; average pooling smooths the patch.
- Example: the map above max-pools to rows and .
- Global average pooling collapses each channel to one value for the classifier.
- Strided convolution downsamples with learned weights instead of a fixed rule.