CNN Parameter Lab
COMP90086 Computer Vision · CNN Workshop Exercise

Every parameter,
counted by hand

One small CNN, four questions. This lab builds the three ideas you need — what a convolution computes, why filters have depth, and how output sizes shrink — then walks the network end to end to the number 48,770.

RGB image 26 × 26 × 3 values 0–255 Convolutional layer 5×5 · valid · stride 1 20 filters · ReLU → ? A Convolutional layer 3×3 · valid · stride 1 40 filters · ReLU → ? B Max pooling 2×2 · valid · stride 2 → ? C flatten FC 10 units D
The exercise network. The → ? output shapes fill in as you solve part i below.
pre·01Prerequisite · what a convolution computes

Slide, multiply, sum

A convolutional layer slides a small grid of weights — the kernel — across the input. At every position it multiplies each weight with the input value under it and adds everything up. That single sum (plus a bias) becomes one cell of the output map.

Playground · 8×8 input, 3×3 kernel

kernel stride padding
input 8×8 · kernel window highlighted
this position, element by element
output 6×6
read this off the widget valid padding = no padding: the kernel must fit entirely inside, so the map shrinks. same pads a ring of zeros (dashed cells) so output size matches input (at stride 1). A bigger stride skips positions — the map shrinks again. Toggle ReLU with the edge kernel: every negative sum (pink) is clamped to 0. Positive sums pass through unchanged.
pitfallThe kernel values in a CNN are not chosen by you — they are learned weights. The presets here are just familiar filters so you can see the mechanics. The all 1s preset matters later: it is exactly what part iii asks about.
pre·02Prerequisite · filters have depth

A filter spans the full input depth

The exercise states it directly: “each kernel has the same number of channels as the input.” On an RGB input a 5×5 filter is really 5×5×3 — 75 weights — and its dot product with a 5×5×3 patch produces one number. One filter → one 2D map. Twenty filters → a stack of 20 maps.

One filter, one map — step through

step 1 / 5
input 26×26×3 5×5 patch · all 3 channels filter = 5×5×3 = 75 weights same depth as the input Σ+b one number slide everywhere → one 22×22 map × 20 20 filters → output 22×22×20
A filter spans all input channels; its dot product with one patch is one number.
why this decides everything Layer B’s input is the 20-channel output of layer A — so each of B’s 3×3 filters is really 3×3×20. Miss that and every count that follows is wrong.
pre·03Prerequisite · how big is the output?

The output-size formula

Count the positions where the kernel still fits. Start at the left edge; the last valid start is W−F; stepping by S:

W input width · F filter size · P padding per side (valid ⇒ P=0) · S stride. Same formula for height. Max pooling obeys it too — with F = pool size.

presets

Calculator

valid kernel start positions along one row
pitfallOff-by-one: it is (W−F)+1 positions, not W−F. Check with tiny numbers: a 3-wide kernel on a 3-wide input fits exactly once → (3−3)+1 = 1. ✓
pre·04Prerequisite · what counts as a parameter

Three layer types, three counting rules

Convolution

F·F weights per channel slice, through Cin channels, +1 bias — that’s one filter. Times N filters.

Parameters don’t depend on the image size — the same filter slides everywhere (weight sharing).

Max pooling

0

Pooling takes the max of each window. Nothing is learned — no weights, no biases. It only shrinks the map (same size formula, F = pool size).

It also never changes the channel count: 40 in → 40 out.

Fully connected

Every input connects to every unit — a weight per pair, plus one bias per unit. The flattened vector length is Nin.

Flatten itself is free: reshaping moves numbers, it doesn’t learn any.

and ReLU? , applied element-wise after each conv layer. It has zero parameters — remember it for parts ii and iii, where it decides what survives.
part iSolve · total number of parameters

Walk the network, keep two ledgers

At every stage track two things: the shape flowing out (you need it as the next layer’s Cin, and for the flatten), and the parameters added. Numbers animate in as you scroll.

input26×26×3→ A?→ B?→ C?→ flat?→ D?
Layer A · convolution5×5 · valid · stride 1 · 20 filters · in 26×26×3
shape  →  output 22×22×20
params 0
pitfallWriting 5·5·20 = 500 forgets the input depth (×3); writing 1500 forgets the 20 biases.
Layer B · convolution3×3 · valid · stride 1 · 40 filters · in 22×22×20
shape  →  output 20×20×40
params 0
pitfallCin here is 20 — the number of filters in A — not 3. The image’s RGB is long gone.
Layer C · max pooling2×2 · valid · stride 2 · in 20×20×40
shape  →  output 10×10×40 (channels untouched)
paramsnothing is learned =0
Flattenreshape only
shape values in a row · 0 params
pitfallFlatten the pooled shape. Using 20×20×40 = 16,000 here quietly quadruples layer D.
Layer D · fully connected4000 in · 10 units
params 0

Answer · part i

stageoutput shapeparameters
A conv 5×522×22×201,520
B conv 3×320×20×407,240
C maxpool + flatten10×10×40 → 40000
D fully connected1040,010
total0
where the parameters live · share of 48,770
A · 1,520 · 3.1% B · 7,240 · 14.8% C · 0 · 0% D · 40,010 · 82.0%

The convolutional layers do the seeing; the little FC head holds 82% of the parameters. Keep that in mind for part iv.

part iiSolve · initialise everything to zero

The chain of zeros

Set every weight and bias to 0 and push any image through. Follow one output cell of layer A:

a cell of A    — for every cell, every map
inputany image→ A22×22×20 of 0s→ B20×20×40 of 0s

Layer B then convolves an all-zero input with all-zero weights — zero twice over. The input image never matters: multiplying by 0 erases it at the first layer.

Answer · part ii The output of layer B is 20 × 20 × 40, and every value is exactly 0.
why nobody initialises this way Every filter is identical, so every filter receives the identical gradient — they can never become different (symmetry is never broken). And with all activations at 0, upstream weight gradients are 0 too: the conv layers cannot start learning. Real initialisers draw small random weights precisely to break this tie.
part iiiSolve · weights = 1 · biases = 0

Forty copies of a blur

With every weight equal to 1, a filter’s “weighted sum” is just a plain sum of its patch — an unnormalised box (averaging) filter that also adds the RGB channels together. Nothing is negative, so ReLU changes nothing. And since all 20 filters of A are identical, all 20 maps are the same map. Same again for B’s 40.

Run it yourself · draw a 26×26 image, watch it flow

input 26×26×3 · draw with your mouse or finger
brush
presets
layer A output · 22×22×20
layer B output · 20×20×40

Maps are shown brightness-normalised (the raw values are huge — see below). The stacked outline behind each map is the point: every channel is this identical picture, 20× for A and 40× for B.

What the numbers do

One A cell = Σ over 5×5×3 patch = up to 255·75 = 19,125.

One B cell = Σ over 3×3×20 window of A = 20 × (3×3 sum of the A map) — up to 3,442,500.

Sums of non-negatives are non-negative, so ReLU never fires — it passes everything through.

Cascaded box filters form a pyramid filter

Two box filters in a row are one bigger, gentler filter: 5×5 ∘ 3×3 has 7×7 support and a pyramid profile — the weights taper from the middle out (this is the separable-Gaussian story from the filtering workshop, in reverse):

Answer · part iii Layer B outputs 20 × 20 × 40: a stack of 40 identical, blurry, brightness-summed copies of the input — both conv layers act as (unnormalised) averaging filters. Values are large positive sums; ReLU is a no-op.
part ivSolve · fewer parameters, same filters, same effective kernel

Reduce parameters, keep filters and kernels

The constraint: keep the number of filters (20, 40) and their effective kernel size (5×5, 3×3 receptive fields). The two official moves both restructure the convolutions — and on this tiny network the arithmetic has surprises worth knowing.

Move 1 · separable convolution

Spatially separable — constrain each 5×5 kernel slice to be an outer product of a 5×1 column and a 1×5 row (exactly how Gaussian and Sobel filters factor). Per slice: 5+5 = 10 numbers instead of 25.

A: 20·3·10 + 20 = 620 (was 1,520)
B: 40·20·6 + 40 = 4,840 (was 7,240)

Depthwise separable (the MobileNet pattern) — first one F×F filter per channel (depthwise), then 1×1 filters to mix channels (pointwise). Receptive field still F×F; filter count unchanged.

A: (5·5·3+3) + (3·20+20) = 158 (9.6× fewer)
B: (3·3·20+20) + (20·40+40) = 1,040 (7.0× fewer)
standard 3×3 conv space + channels together 3·3·20·40 = 7,200 weights ⇒ depthwise 3×3 one slice per channel · space only 3·3·20 = 180 then pointwise 1×1 → 40 channels only · no space 20·40 = 800
Layer B rebuilt: 7,200 weights → 180 + 800 = 980. Same 3×3 receptive field, same 40 output maps.

Move 2 · stack small kernels in place of the 5×5

Two 3×3 convs see a 5×5 receptive field (each output cell of the second conv reaches 3+2 input cells) — and the output size even matches: 26→24→22. The VGG argument: for C channels in and out, . Bonus: an extra ReLU in between.

Caveat for layer A: its input depth is only 3, so the 5×5 is cheap (1,520) while the naïve stack pays twice for channel mixing. It only wins if the middle conv stays narrow:

middle width m 7
3×3×3→m  +  3×3×m→20
=
stacked params vs the original 1,520
marker = 1,520 · bar caps at 4,180 (m=20)

Answer · part iv Replace the conv layers with separable convolutions (spatial 5×5→5×1∘1×5, or depthwise + 1×1), and/or replace the 5×5 layer with stacked 3×3 convolutions. Filter counts and effective kernel sizes are untouched.
beyond the official answer 82% of the parameters sit in layer D. Swap flatten for global average pooling (mean over each 10×10 map → 40 values) and D shrinks from 40,010 to 40·10+10 = 410 — total 9,170. Filters and kernels untouched; worth raising in the workshop.
recapAnswers & the two formulas to keep

The whole exercise on a card

Answers

i1,520 + 7,240 + 0 + 40,010 = 48,770 parameters
ii20×20×40, all values exactly 0
iii20×20×40; 40 identical blurry copies of the input (conv layers = averaging filters)
ivseparable convolutions · stacked 3×3 in place of the 5×5

Keep these

Pooling, flatten, ReLU: 0 parameters. And always ask “what is Cin here?” before counting.