Convolution Mathematics
Convolution blends two functions by flipping a kernel and sliding it across a signal, so each output is a weighted average of its neighbours, and CNN layers compute its unflipped twin.
Why Does This Exist?
Images, audio and sensor traces are not bags of independent numbers: neighbours belong together. A dense layer treats pixel 5 and pixel 6 as strangers; convolution treats each output as a weighted average of a small neighbourhood, so edges, slopes and textures emerge from the arithmetic rather than from memorised positions.
The same operation answers two needs. In signal processing it filters: smooth, sharpen, or extract an edge by choosing the weights. In deep learning it detects: each kernel becomes a learned pattern matcher sliding across the input, and stacking such layers builds a hierarchy from edges to objects. The mathematics is one formula; the CNN reading of it is in convolution operation.
The one idea you need first
Each output is a dot product: multiply overlapping pairs and add. If overlaps weights , the output is . Everything below is that product, repeated at every position with one twist: the flip.
Think of It Like This
A frosted window sliding along a picket fence
Imagine a picket fence with numbered slats, and a frosted window exactly three slats wide that you slide along it. At each stop you read the three visible numbers through the glass, but the glass weights them: the left pane counts triple, the middle single, the right double. You write down one blended number per stop, then slide one slat over.
Two details carry the mathematics. First, the glass is installed backwards: the triple pane sits over the rightmost visible slat, because convolution flips the kernel before blending. Second, the reported number at each stop depends only on the slats in view, never on distant ones, which is why convolution sees local structure and ignores position.
Where it stops: a real window blurs light, while convolution multiplies and adds exact numbers, and a real window never learns. In a CNN the pane weights are trained, and because they are learned the flip stops mattering, which is why libraries quietly implement cross-correlation instead.
How It Actually Works
The 1D formula
Discrete convolution of signal with kernel is:
In plain words: at output position , overlap the kernel with the signal, multiply each overlapping pair, and add. The index runs backwards through as runs forwards through , which is the flip: the kernel's first weight meets the signal's last overlapping sample.
Cross-correlation drops the flip:
It slides the kernel as written. Deep learning layers compute this, not convolution, calling it convolution anyway. Since the weights are learned, some flipped twin of every kernel is equally learnable, so nothing is lost, but a hand-designed kernel (a Sobel edge finder, say) must be flipped before pasting into a framework, or its orientation reverses.
Worked example in 1D
Take signal and kernel . Flip to , then slide without padding (valid mode), giving outputs:
- Position 0, overlap : .
- Position 1, overlap : .
- Position 2, overlap : .
Output , exactly the three cells the diagram shows with the middle one highlighted. Cross-correlation without the flip would give : same first value by coincidence, different rest.
Worked example in 2D
Take a input with rows , , and kernel rows , . Top-left placement overlaps input rows , .
Cross-correlation, kernel as written: .
True convolution flips the kernel both ways to rows , : .
Same window, versus : the flip is not bookkeeping, it changes the number. Sliding the kernel over all four placements fills a output, and padding or stride then decides the final size, which is the shape arithmetic the Fourier convolution theorem page builds on.
Code
import numpy as np
def conv_valid_1d(f: np.ndarray, g: np.ndarray) -> np.ndarray: """True convolution: flip g, then slide (valid mode, no padding).""" return np.convolve(f, g, mode="valid")
def xcorr_valid_1d(f: np.ndarray, g: np.ndarray) -> np.ndarray: """Cross-correlation: slide g as written (what CNN layers compute).""" return np.correlate(f, g, mode="valid")
f = np.array([1.0, 4.0, 1.0, 2.0, 0.0])g = np.array([3.0, 1.0, 2.0])print(conv_valid_1d(f, g)) # -> [ 9. 15. 4.]print(xcorr_valid_1d(f, g)) # -> [ 9. 17. 5.]
kernel = np.array([[1.0, 2.0], [3.0, 4.0]])patch = np.array([[1.0, 2.0], [4.0, 5.0]])print(float(np.sum(patch * kernel))) # -> 37.0 cross-correlationprint(float(np.sum(patch * kernel[::-1, ::-1]))) # -> 23.0 true convolutionThe 1D outputs and match the hand computation, and the 2D pair versus reproduces the flip difference.
Watch Out For
Pasting a hand-designed kernel into a CNN without flipping
Textbook kernels assume true convolution. Frameworks implement cross-correlation. Paste a Sobel vertical-edge kernel into a CNN layer and the detector fires on the mirror-image edge: gradients that should be positive come out negative, and the feature map is flipped relative to every diagram in the textbook.
The fix is one line: flip the kernel both ways before assigning it (kernel[::-1, ::-1]), or equivalently negate the expected orientation. Learned kernels need no fix, only transplanted designed ones. Whenever a hand-built filter behaves like its mirror image, the missing flip is the first suspect.
Off-by-one output sizes from padding and stride arithmetic
Valid convolution on length with kernel gives outputs: 5 and 3 gave 3 above. Forgetting the when stacking layers shrinks feature maps faster than planned, and a network designed for maps silently receives , crashing the first dense layer or, worse, running with misaligned skip connections.
Write the size formula before the code: output length for padding and stride . Same padding ( chosen so stride 1 preserves ) is the usual way to keep sizes predictable while the network deepens.
The Quick Version
- Convolution flips the kernel, then slides it: , one dot product per position.
- Cross-correlation slides without flipping and is what CNN layers actually compute under the name convolution.
- On with kernel , valid outputs are ; cross-correlation gives .
- The 2D flip changes real answers: versus on the same window, so transplant designed kernels flipped.
- Valid mode shrinks length with kernel to ; account for it with the padding and stride formula.
- Learned kernels absorb the flip, which is why frameworks skip it, but orientation-sensitive analysis must not.