Joshu Leonardy
畳 · 込 · 演 · 算

Convolution Lab

A convolution is one small grid of numbers sliding over a larger one. Padding, stride and dilation are variations on where that window goes. Channels are what turn it into a layer. And the weights inside it are not designed — they are learned. Five sections, each one a thing you can run.

— 01 —
窓 · 走 · 査

The sliding window

Here is the whole operation in one line. As the window moves below, the symbols in this equation light up with the values they currently hold. Set the stepping to single multiply and you can walk through the nine products one at a time.

Two coordinate systems are at work here, and confusing them is the usual stumbling block. x and y say where the window sits on the image, and change once per output value. i and j scan inside the kernel, and reset every time the window moves. The expression I(x+i, y+j) is an address, not a product — add first, then look up that cell. And notice that F takes only i and j: the weights are identical wherever the window is standing.

Input

Kernel

Output

Step by
Operation
Padding
Stride
Window
Dilation
Input size
Input pattern
— 02 —
像 · 特 · 徴

The same operation, on an image

Nothing changes except the size of the grid. A photograph is a grid of numbers too, so those nine multiplications happen once per pixel. Each kernel below holds a different set of nine weights and produces a different feature map. Try the camouflage image — the animal is there, and most of these filters cannot find it.

Input
Use your own image
Feature map Weights
One layer, eight filters
Your photograph never leaves the browser. Everything here runs on your own machine and nothing is uploaded anywhere.
— 03 —
層 · 深 · 度

From one filter to a layer

Everything above used a single plane of numbers. A real image has three, and deeper in a network a feature map has hundreds. A convolution does not run separately on each — it runs on all of them at once and adds the results into one output. Step through the accumulation and watch a single output map being built from three inputs.

The sum over cin sits inside the expression. That is the whole point: a convolution collapses every input channel in one step, which is why the kernel is not a 3×3 grid but a four-dimensional tensor of shape (Cout, Cin, k, k), plus one bias per output channel.

Red channel Slice 0
Green channel Slice 1
Blue channel Slice 2
Running sum
Kernel

What a layer actually costs

This is where most people's intuition is wrong. A 3×3 convolution does not have nine parameters.

Input channels Output channels Kernel size
Groups
Notice what the formula does not contain: image height and width. The same kernel is reused at every position, so the parameter count is independent of input size. That is weight sharing, and it is why a convolutional layer is so much smaller than a fully connected one on the same input.
— 04 —
縮 · 約 · 化

Pooling throws information away

Pooling has no weights and learns nothing. It divides the map into blocks and replaces each block with one number — the largest, or the average. On a grid of digits that difference looks trivial. On an image it is not.

Block size
Original
Max pooling
Average pooling
Max pooling keeps the brightest value in each block, so bright structure survives and edges harden. Average pooling pulls everything toward the local mean, so texture disappears. Watch the mean brightness as you raise the block size — max pooling drifts upward while average pooling holds steady. Max pooling is not a neutral downsampler: it biases the feature distribution, and the bias grows with how aggressive you are.
— 05 —
学 · 習 · 重

Where the weights come from

Every filter you have used so far was designed by a person decades ago. A convolutional network designs its own. Below, nine weights start as random numbers and a target output is given. Press play and watch gradient descent turn noise into an edge detector — nobody tells it the answer, only how wrong it currently is.

The error δ is simply the difference between what the filter produced and what was wanted. The gradient with respect to each weight is that error correlated against the input — another cross-correlation, the same operation running backwards. Each weight then takes a small step downhill. This is the entire learning algorithm.

Input
Target output
Current output
Error
Learned weights
True weights
Loss
Target filter
Optimiser
Learning rate
Switch to plain gradient descent and watch it crawl. The reason is conditioning. Neighbouring pixels in an image are strongly correlated, so the loss surface is a long narrow valley and one step size cannot suit every direction at once. Adam gives each weight its own effective step size and arrives in a couple of hundred iterations. This is not an artefact of the toy — it is why nobody trains a real network with plain gradient descent.