Deep Learning Slides 📂 Introduction · 12 of 18 35 min read

Discrete Convolution: The Math Behind CNNs

Slide a small grid of weights across an image, and at each stop record how strongly it matches — that single operation, repeated everywhere, powers every CNN. This visual guide covers the sliding kernel, 1D and 2D worked examples, stride, padding, the output-size formula, and NumPy code from scratch.

Discrete Convolution

Slide a tiny window of weights across a signal or image, and at every stop record how strongly it matches. That one operation — the dot product, repeated everywhere — is the engine inside every convolutional neural network.
The Sliding Kernel Feature Maps Stride & Padding NumPy from Scratch

Press Next → or use ← → arrow keys

Section 01

The Story Behind Convolution

Imagine sweeping a small square flashlight across a painting, hunting for one specific pattern — say, a horizontal edge. At each spot you jot down a single number: how strongly that patch matches the pattern. Slide across the whole canvas and those numbers form a feature map showing where the pattern appears. That's convolution.
🔎
One small detector, applied everywhere

The flashlight is the kernel — a little grid of weights. Reusing the same detector at every position is exactly why convolution is so powerful and so parameter-efficient.

Section 01

What Discrete Convolution Does

A kernel slides over the input. At every position it computes a single dot product between its weights and the local patch — one output number per position.

y[n] = Σₖ x[k] · h[n − k]
1-D discrete convolution — sum of input × kernel, offset by n
·Each output = one dot product
Same kernel reused at every position
🗺️Outputs form a feature map
🧩
Weight sharing is the whole point

Instead of a separate weight for every pixel, one small kernel is shared across the entire input. A 3×3 kernel has just 9 weights — no matter how big the image.

Section 02

Convolution vs Cross-Correlation

Here's the honest truth every deep-learning course glosses over: what CNNs call "convolution" is really cross-correlation.

Signal processing flips the kernel 180° — horizontally and vertically — before sliding and taking dot products. The flip makes the operation mathematically commutative.
No flip. Use the kernel as-is, slide, and dot. Simpler — and since the kernel is learned anyway, the flip makes no practical difference.
🤫
The libraries all do cross-correlation

PyTorch's nn.Conv2d, TensorFlow's Conv2D, and essentially every framework implement cross-correlation but call it convolution. The network learns the right weights either way, so nobody flips.

Section 03

1-D Worked Example

Input [1, 2, 3, 4, 5], kernel [1, 0, −1] (a simple edge detector), stride 1, no padding. The kernel slides three times:

Input 1 2 3 4 5 Kernel +1 0 −1 Output −2 −2 −2 [1,2,3]·[1,0,−1] = 1 − 3 = −2
📐
Output = [−2, −2, −2]

Output size = 5 − 3 + 1 = 3. Every window gives −2 because the input rises at a constant slope — the detector reports the same steady gradient everywhere.

Section 04

The Kernel as a Feature Detector

Different weight patterns detect different things. A few classic 3×3 kernels:

↕️
Horizontal edge (Sobel)
Rows of +1 / 0 / −1 fire where brightness changes top-to-bottom — detecting horizontal edges.
↔️
Vertical edge (Sobel)
Columns of +1 / 0 / −1 respond to left-to-right transitions — vertical edges.
🌫️
Blur / average
All nine weights = 1/9. Averages the neighbourhood, smoothing out noise and detail.
🎓
In a CNN, kernels are learned

These hand-designed kernels are just for intuition. In a real network the weights are learned by backpropagation — the model discovers whichever detectors best solve the task.

Section 04

2-D Convolution Over an Image

For images the kernel is a small grid that slides in two directions. Each placement produces one cell of the feature map:

Input 4×4 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 Feature map 2×2 −3 3 −3 3
🔲
The highlighted 3×3 window → one output cell

Multiply the window by the kernel element-wise, sum it up, and write the result into the top-left of the feature map. Slide one step and repeat until the whole image is covered.

Section 04

2-D Worked Example — Finding a Vertical Edge

A 4×4 image with a bright stripe down the middle, convolved with a vertical-edge kernel [[1,0,−1],[1,0,−1],[1,0,−1]]:

1
Top-left window (columns 0–2): each row is [0, 1, 1] → 0·1 + 1·0 + 1·(−1) = −1, ×3 rows = −3
2
Slide right (columns 1–3): each row is [1, 1, 0] → 1·1 + 1·0 + 0·(−1) = +1, ×3 rows = +3
3
Feature map: [[−3, +3], [−3, +3]] — negative on the left edge, positive on the right
🎯
The signs mark the edges

A negative response flags a dark-to-light transition, positive a light-to-dark one. The kernel has literally located both sides of the stripe.

Section 05

Stride & Padding

Two knobs control how the kernel moves and what happens at the borders:

How far the kernel jumps each step. Stride 1 visits every position; stride 2 skips every other one, halving the output size and downsampling the image.
Extra border of zeros around the input. "Same" padding keeps the output the same size as the input; no padding shrinks it and loses the edges.
⚖️
Why padding matters

Without padding, pixels at the very edge are visited by the kernel far less often than central ones, and the image shrinks with every layer. Padding preserves size and gives borders a fair say.

Section 05

The Output-Size Formula

One formula predicts the feature-map size from the input, kernel, padding, and stride:

O = (N − F + 2P) / S + 1
N = input size · F = kernel size · P = padding per side · S = stride
CaseSettingOutput
"Same" padding, stride 1P = (F−1)/2O = N (unchanged)
No padding, stride 1P = 0O = N − F + 1
28×28 input, 3×3 kernelP = 0, S = 126 × 26
📏
Always check the shape

A mismatched output size is the most common CNN bug. Plug the numbers into the formula before wiring layers together — it takes five seconds and saves an hour.

Section 06

Convolution From Scratch

The entire 2-D operation is a double loop of dot products:

import numpy as np

def conv2d_scratch(x, k, stride=1, padding=0):
    """x: (H,W) input, k: (Fh,Fw) kernel"""
    if padding > 0:
        x = np.pad(x, padding, mode='constant')
    H, W = x.shape
    Fh, Fw = k.shape
    Oh = (H - Fh) // stride + 1
    Ow = (W - Fw) // stride + 1
    out = np.zeros((Oh, Ow))
    for i in range(Oh):
        for j in range(Ow):
            patch = x[i*stride : i*stride+Fh,
                      j*stride : j*stride+Fw]
            out[i, j] = np.sum(patch * k)   # dot product
    return out
🔬
This is exactly what nn.Conv2d does

Real frameworks vectorise it heavily for speed, but the logic is identical: slide, multiply, sum. Everything else in a CNN is stacking this operation.

Section 07

Putting It Together — The CNN Layer

A convolutional layer isn't one kernel — it's K kernels applied at once, each producing its own feature map. Stack those maps and you get the layer's output volume.

1
Each kernel spans all channels: shape (F, F, C) — it looks at every input channel at once.
2
K kernels → K feature maps: output volume is (Hout, Wout, K).
3
Spatial shrinks, channels grow: e.g. 224×224×3 → 112×112×64 → 56×56×128.
🏗️
From edges to objects

Early layers detect edges and textures; deeper layers combine them into parts and whole objects. More channels carry more distinct detectors as the spatial resolution drops.

Section 08

Everything in One Table

ConceptWhat it is
Kernel / filterSmall grid of learned weights — the detector
Feature mapGrid of dot-product outputs — where the pattern appears
StrideStep size of the slide; larger = smaller output
PaddingZero border; "same" preserves size, "valid" shrinks
Output sizeO = ⌊(N − F + 2P)/S⌋ + 1
Weight sharingOne kernel reused everywhere — few parameters
What libraries doCross-correlation (no flip), called "convolution"
📌
Seven ideas, one operation

Master slide-multiply-sum and everything else is a parameter choice around it. That's the whole of discrete convolution.

Section 08

5 Golden Rules

  Convolution cheat-sheet
1Each output is one dot product between the kernel and a local patch of the input.
2Weight sharing makes CNNs efficient — one small kernel is reused across the whole input.
3Frameworks do cross-correlation, not true convolution — there's no kernel flip.
4Always verify output shape with O = ⌊(N − F + 2P)/S⌋ + 1 before stacking layers.
5Kernels are learned, not hand-crafted — backprop finds the detectors that solve the task.
🚀
The operation that gave machines sight

Slide a small detector everywhere, share its weights, and learn what to look for. From edge detection to ImageNet, it all starts with discrete convolution.