Convolutional Filters and Feature Maps

Convolutional filters are small, learnable matrices that slide across an input image to detect patterns like edges, textures, and shapes — the outputs of this process are called feature maps. They are the foundational building block of Convolutional Neural Networks (CNNs), which power nearly every modern computer vision system from facial recognition to medical imaging. The counterintuitive fact: a filter trained to detect vertical edges in photos of cats will also detect vertical edges in photos of cars — filters learn geometry, not objects.

The core insight

A convolutional filter is just a small grid of numbers (say 3×3) that acts like a spotlight, scanning across an image and asking "does this pattern exist here?" at every position. The filter's numbers are learned during training, not hand-crafted. When the filter slides over the image and produces a score at every location, the resulting grid of scores is the feature map — a spatial record of "where did I find this pattern?" This is why CNNs are so powerful: stacking many filters lets the network build up from simple edges to complex object parts automatically.

How It Works

A convolutional filter performs a mathematical operation called a cross-correlation: it slides across the input, and at each position multiplies its values element-wise with the overlapping patch of the input, then sums everything up into a single number.

1
S(i,j)=∑m∑nI(i+m,  j+n)⋅K(m,n)\quad S(i,j) = \sum_{m}\sum_{n} I(i+m,\; j+n) \cdot K(m,n)

Explanation: At position (i,j) in the output feature map, we multiply each element of the kernel K with the corresponding element of the image patch at that location, then sum all those products into one number.

  • S(i,j)S(i,j) — the single output value (one pixel in the feature map) at row ii, column jj
  • II — the input image (or input feature map from a previous layer)
  • KK — the kernel / filter (a small matrix of learned weights, e.g. 3×3)
  • m,nm, n — index variables stepping over the kernel's rows and columns

After the convolution, a bias term is added and an activation function (usually ReLU) is applied:

2
A(i,j)=ReLU ⁣(S(i,j)+b)\quad A(i,j) = \text{ReLU}\!\left(S(i,j) + b\right)

Explanation: We shift the raw score by a learned bias b, then clip any negative values to zero — this introduces nonlinearity and lets the network decide "yes, this pattern is present" (positive) vs. "no" (zero).

  • A(i,j)A(i,j) — the activated output pixel in the feature map
  • bb — a learned scalar bias, one per filter
  • ReLU(x)=max⁡(0,x)\text{ReLU}(x) = \max(0, x) — the Rectified Linear Unit activation

The output feature map size depends on the input size, kernel size, padding, and stride:

3
Hout=⌊Hin−K+2PS⌋+1\quad H_{out} = \left\lfloor \frac{H_{in} - K + 2P}{S} \right\rfloor + 1

Explanation: This tells you exactly how tall the output feature map will be given an input of height H_in, a kernel of size K, padding P rows of zeros around the border, and a stride S (how many pixels the filter jumps each step).

  • HoutH_{out} — output height (width formula is identical, using WinW_{in})
  • HinH_{in} — input height in pixels
  • KK — kernel size (e.g. 3 for a 3×3 kernel)
  • PP — padding (0 = "valid", ⌊K/2⌋\lfloor K/2 \rfloor = "same" to preserve size)
  • SS — stride (1 = move one pixel at a time, 2 = skip every other pixel)

Here is the complete operation in Python using NumPy, then PyTorch: ```python import numpy as np def convolve2d(image, kernel): """Manual 2D convolution (no padding, stride=1).""" H, W = image.shape K = kernel.shape[0] # assume square kernel H_out = H - K + 1 W_out = W - K + 1 feature_map = np.zeros((H_out, W_out)) for i in range(H_out): for j in range(W_out): patch = image[i:i+K, j:j+K] # extract local patch feature_map[i, j] = np.sum(patch * kernel) # element-wise multiply & sum return feature_map

# A hand-crafted vertical edge detector (Sobel filter) vertical_edge_kernel = np.array([ [-1, 0, 1], [-2, 0, 2], [-1, 0, 1] ], dtype=float)

# Tiny 5x5 grayscale image (values 0–255) image = np.array([ [10, 10, 80, 80, 80], [10, 10, 80, 80, 80], [10, 10, 80, 80, 80], [10, 10, 80, 80, 80], [10, 10, 80, 80, 80], ], dtype=float) fmap = convolve2d(image, vertical_edge_kernel) print("Feature map:\n", fmap)

# High values appear where the vertical edge (column 1→2) exists ``` ```python import torch import torch.nn as nn

# Define a Conv layer: 1 input channel, 8 output filters, 3x3 kernel conv = nn.Conv2d(in_channels=1, out_channels=8, kernel_size=3, padding=1)

# Fake grayscale image: batch of 1, 1 channel, 28x28 pixels x = torch.randn(1, 1, 28, 28)

# Forward pass feature_maps = conv(x) print(feature_maps.shape)

# → torch.Size([1, 8, 28, 28])

# 1 image, 8 feature maps (one per filter), same 28x28 size (padding=1) ``` ```python import torch import torch.nn as nn import matplotlib.pyplot as plt

# A simple CNN to visualize feature maps class TinyCNN(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1) self.relu = nn.ReLU() self.pool = nn.MaxPool2d(2) # halves spatial size self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1) def forward(self, x): x = self.pool(self.relu(self.conv1(x))) # → [B, 16, 14, 14] x = self.pool(self.relu(self.conv2(x))) # → [B, 32, 7, 7] return x model = TinyCNN() dummy_input = torch.randn(1, 1, 28, 28) output = model(dummy_input) print("Final feature map shape:", output.shape)

# → torch.Size([1, 32, 7, 7]) ```

researcher · Yann LeCun | b. 1960 | Invented Convolutional Neural Networks

Yann LeCun, a French-born computer scientist now at NYU and Meta AI, developed the first practical convolutional neural network — LeNet-5 — in 1989 while at Bell Labs, using it to read handwritten digits on checks for the US Postal Service. His key insight was that weight sharing across spatial positions (the same filter applied everywhere) could dramatically reduce parameters while encoding translation invariance. Surprisingly, LeCun's ideas were largely ignored by the mainstream ML community for nearly 20 years until AlexNet's 2012 ImageNet victory — a victory that used the exact same core idea he had demonstrated decades earlier.

Figure
Convolution Operation: Filter Sliding Over Input to Produce Feature Map
Convolution: One Filter → One Feature Map Input Image (5×5) 10101 01010 11100 00111 10010 Filter / Kernel (3×3, learned) 1 0-1 2 0-2 1 0-1 (edge detector) ⊗ element-wise multiply & sum 4.0 one value slide & repeat Feature Map (3×3 output) 4.01.0-2 3.02.00.0 1.0-12.0 Blue highlight: active patch (input) → produces highlighted cell (output) 32 filters in a Conv layer → 32 separate feature maps like this one
Figure
Deep CNN Feature Map Hierarchy
Feature Hierarchy: What Each Layer Detects Raw Input pixels 224×224 Conv Layer 1 Edges & Gradients 64 feature maps 112×112 Conv Layer 2 Textures & Corners 128 feature maps 56×56 Conv Layer 3 Object Parts 256 feature maps 28×28 Classifier Head "Cat: 94%" Dog: 4% Bird: 2% Each layer: more abstract features, smaller spatial size, more feature maps

Intuition

The Mental Model

Imagine you are a detective examining a crime scene photo with a magnifying glass that has a special filter — but instead of one magnifying glass, you have 64 of them, each one sensitive to a different clue. The first magnifying glass lights up when it sees a vertical line. The second reacts to horizontal lines. A third one reacts to diagonal lines at 45 degrees. Each one slides across the entire photo, position by position, and makes a mark on its own notepad wherever it found what it was looking for. At the end, you have 64 "notepads" — those are your feature maps. Now hand those 64 notepads to a second set of detectives (the next Conv layer), and they can start combining clues: "a vertical line next to a horizontal line could be a corner... two corners could be a window... four windows could be a building." That is the entire story of how CNNs go from raw pixels to recognizing a cat. The detective gets better the more crime scenes (training images) they see — they refine exactly what patterns their magnifying glasses are sensitive to.

Historical Development

Origins · 1 minhow this was discovered
History
1959
Hubel & Wiesel discover that neurons in the cat visual cortex respond to specific edge orientations — biological inspiration for learned feature detectors.
1980
Kunihiko Fukushima introduces the Neocognitron, the first network with weight-sharing convolution-like layers, inspired directly by Hubel & Wiesel.
1989
Yann LeCun, Bottou, Bengio & Haffner train the first CNN with backpropagation (LeNet-1) at Bell Labs to read handwritten ZIP codes on mail.
1998
LeCun publishes the full LeNet-5 paper, with 7 layers, 5×5 filters, and average pooling — used commercially to process millions of bank checks daily.
2012
Alex Krizhevsky, Ilya Sutskever & Geoffrey Hinton win ImageNet with AlexNet — using 5 conv layers with ReLU activations, GPU training, and dropout, dropping error rate by 10+ percentage points, reigniting the entire field.
2014
VGGNet (Simonyan & Zisserman) demonstrates that stacking many small 3×3 filters outperforms a few large filters, establishing the modern design philosophy.
2015
ResNet (He et al.) introduces skip connections, enabling networks with 100+ conv layers to be trained stably — feature maps now carry information both through and around each layer.
2017
Zeiler & Fergus' DeconvNet visualizations show for the first time which image patterns activate each filter — confirming the edge→texture→part hierarchy.
2021
Vision Transformers (ViT, Dosovitskiy et al.) challenge CNN feature maps by replacing convolutions with attention patches — but conv layers still dominate efficient real-time models.

Why It Matters

Real-World Impact
iPhone Face IDApple's Neural Engine runs a CNN that uses convolutional filters to extract 3D facial feature maps from an infrared dot-projection image, matching your face in under 1 second with 1-in-1,000,000 false-accept rate