What a convolution actually computes
Convolutional neural networks sound intimidating, but the "convolution" in the name is an operation you can do with a pencil. It is a small grid of numbers sliding over a bigger grid, multiplying overlapping cells and adding them up. Do it once by hand and the mystery of how a network "sees" edges disappears.
The one idea
A convolution slides a small kernel (a few numbers) across an input, and at every position computes one number: the sum of kernel cells times the input cells they cover. Change the kernel and you change what the operation detects. The kernel is a pattern, and the output is a map of where that pattern occurs. A network does not hand-design kernels; it learns them by gradient descent. But the machinery is the same either way.
Convolve a 1D signal
Start in one dimension so there is nothing to hide. Our kernel [-1, 1] computes the difference between neighbors, which is a crude edge detector: it is near zero on flat stretches and spikes where values jump.
def convolve1d(x, kernel):
k = len(kernel)
out = []
for i in range(len(x) - k + 1):
window = x[i:i + k]
out.append(sum(a * b for a, b in zip(window, kernel)))
return out
signal = [0, 0, 0, 5, 5, 5, 0, 0] # a flat step up then down
print(convolve1d(signal, [-1, 1]))
[0, 0, 5, 0, 0, -5, 0]
Flat regions gave 0. The jump from 0 to 5 gave +5, and the drop from 5 to 0 gave -5. The output literally marks where the signal changes and in which direction. That is edge detection, and we wrote it in six lines.
Move to 2D and find edges in an image
An image is a 2D grid, and a 2D convolution is the same idea with the window sliding in both directions. This kernel detects vertical edges: it fires where the left side is darker than the right.
def convolve2d(img, kernel):
kh, kw = len(kernel), len(kernel[0])
h, w = len(img), len(img[0])
out = [[0] * (w - kw + 1) for _ in range(h - kh + 1)]
for i in range(h - kh + 1):
for j in range(w - kw + 1):
s = 0
for a in range(kh):
for b in range(kw):
s += img[i + a][j + b] * kernel[a][b]
out[i][j] = s
return out
image = [[0, 0, 9, 9],
[0, 0, 9, 9],
[0, 0, 9, 9],
[0, 0, 9, 9]] # a dark left half, bright right half
vertical = [[-1, 1],
[-1, 1]]
for row in convolve2d(image, vertical):
print(row)
[0, 18, 0]
[0, 18, 0]
[0, 18, 0]
The output is bright (18) exactly down the column where dark meets light, and zero everywhere else. The convolution found the vertical edge. Rotate the kernel and it finds horizontal edges instead. Stack many such operations and a network builds edges into corners, corners into textures, textures into objects.
Why networks use this instead of plain layers
You could connect every pixel to every neuron, but a megapixel image would need trillions of weights, and it would have to relearn "an edge" separately in every corner of the frame. A convolution reuses the same small kernel everywhere, so it needs a handful of weights and it recognizes a pattern no matter where it appears. That reuse, few parameters, position-independent, is exactly why convolutions dominate vision. The learning part just means gradient descent adjusts the kernel numbers until they detect whatever the task rewards.
Where it shows up
Every image classifier, every photo filter that sharpens or blurs, every "detect the tumor / the pedestrian / the defect" model runs this sliding-window sum underneath. Once you have watched a [-1, 1] kernel light up an edge by hand, the deepest vision network is just this operation stacked and learned. That build-it-to-understand-it path is the whole point of the AI and deep learning track.