Architectures Before Transformers
Convolutional neural networks
Sharing weights across space for vision
A photo is a grid of numbers, and the thing that makes it a cat is local: an ear is a small arrangement of nearby pixels, not a relationship between the top-left and bottom-right corners. A fully-connected layer ignores that - it wires every pixel to every neuron and burns millions of weights re-learning "an edge is an edge" at each spot. A convolutional layer bakes the structure in: take one tiny filter, slide it everywhere, and at each stop compute a single dot product between the filter and the patch beneath it. Same nine weights, reused across the whole image. That one move - a shared local dot product - is the entire idea, and it is what powered vision for a decade.
The one-line idea
Slide a filter, take a dot product
Line the filter up over a patch of the image the same size as itself. Multiply each filter weight by the pixel under it, sum all nine products, and write that single number into the output at the filter's current position. Slide one column right and repeat; when you reach the edge, drop to the next row. This sweep is the convolution (in deep learning we actually compute the un-flipped version, cross-correlation, but everyone calls it convolution):
Here is the image, is the kernel, and is one output pixel - nothing but the dot product of the kernel with the patch it currently covers. Two properties fall out immediately, and they are the whole reason CNNs work. Locality: each output only looks at a small neighborhood - a pixel sees its neighbors, nothing far away. Weight sharing: the same nine numbers are reused at every position, so an edge detector learned in one corner works in all of them.
A filter is a feature detector
Change the nine weights and you change what lights up. A vertical-edge kernel subtracts the left column from the right, so it reads near zero on flat regions and spikes where brightness jumps sideways. A blur kernel averages the patch; a sharpen kernel amplifies the center against its neighbors. In a real CNN these weights are not hand-set - backpropagation learns them, discovering the edge, corner, and texture detectors that happen to reduce the loss. Below, you set them yourself. Move your pointer across the image to slide the filter and watch its output pixel compute in real time.
Left: an 8×8 grayscale image (a bright bar on a dark field). Right: the 6×6 feature map being built. Move your pointer over the image (or drag, on touch) to reposition the amber window; the panel below shows the exact dot product for that one output pixel. Map colours: positive negative activation.
And then it shrinks. Stack a convolution, then a 2×2 max-pool (keep the biggest value in each block), then convolve again. Each stage trades spatial size for depth of meaning: from many raw pixels to a few rich features:
image
→
feature map
2×2 →
pooled
→
deep feature
Counting the weights CNNs save
Take a small colour image - that is input numbers. Wire it to a modest hidden layer of units with a dense layer and you pay million weights for one layer. Now a convolutional layer with filters of size :
Roughly 1,700× fewer - and, crucially, that count does not grow when the image gets bigger, because the same filters slide over a larger canvas. Locality makes each connection cheap; weight sharing makes them few. In general a conv layer costs weights, set by the filter, not the image.
The aha: a hierarchy built from small parts
import numpy as np
def conv2d(image, kernel): # 'valid' convolution, single channel
kh, kw = kernel.shape
H, W = image.shape
out = np.zeros((H - kh + 1, W - kw + 1)) # 8x8, 3x3 -> 6x6
for i in range(out.shape[0]): # slide down the rows
for j in range(out.shape[1]): # slide across the cols
patch = image[i:i+kh, j:j+kw] # the local receptive field
out[i, j] = np.sum(patch * kernel) # dot product = one output pixel
return out
edge = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]]) # vertical-edge (Sobel) detector
feature_map = conv2d(img, edge) # same 9 weights, every locationCheck yourself
Reusing one 3×3 filter across every position of the image gives a CNN:
A 2×2 max-pool with stride 2 turns a 6×6 feature map into:
Recall what a single convolution output value is, and the two properties that make a conv layer so much cheaper than a dense one.
Try to state it, then check.
Lock it in
- A conv layer holds one small filter (kernel), slides it everywhere, and at each stop computes a dot product with the patch beneath - the filter is the pattern it detects.
- Two properties make it work: locality (each output sees only a neighborhood) and weight sharing (the same weights reused at every position), so the parameter count is set by filter size, not image size.
- That is orders of magnitude fewer weights than a dense layer (about in the worked example), and the count does not grow when the image gets bigger.
- Stacking conv layers with pooling grows the receptive field, so filters compose bottom-up: edges into corners and textures, into object parts, into whole objects - and backprop learns the weights rather than hand-setting them.
- CNNs bet that nearby-is-relevant (great for images); attention drops that constraint for a global receptive field from layer one, which is why transformers replaced CNNs for language.
Primary source
Ask your teacher