Skip to content
AI360Xpert
Beta

Feature Maps in CNNs

Each filter draws one map of where its pattern fired. Stack the maps and you hold the image as the network sees it, channels first.

Two 3 by 3 filters slide over a 5 by 5 input and stack two 3 by 3 maps into a 2 by 3 by 3 tensor.
Two 3 by 3 filters slide over a 5 by 5 input and stack two 3 by 3 maps into a 2 by 3 by 3 tensor.

Why Does This Exist?

Raw pixels mix everything: light, object and background share the same numbers. A classifier reading pixels directly must untangle all of it at once, which is why dense layers on images explode in weights and still miss shifted patterns.

Convolutional layers split the job. Each filter hunts one pattern and records where it fired as a 2D sheet. Stack those sheets and later layers read pattern locations instead of pixels. This page names that stack and its shape. How the stack shrinks lives in padding and stride, and what the whole network does with it lives in feature extraction.

You need one idea first. A channel is a detector. Bright cells mean the pattern fired there.

Think of It Like This

Transparent overlays on a city map

You lay one clear sheet per question over a city map: one marks parks, one marks schools, one marks bus stops. Each sheet is sparse but honest. Stacked, they answer where a family should live.

Filters are those questions, maps are the marked sheets, and the stack is the pile you hand to the next analyst. Where the analogy stops: map sheets are drawn by people, while feature maps are learned numbers that shift during training.

How It Actually Works

One filter, one map

Filter kk with weights WkW_k slides over input XX and writes map YkY_k with one number per placement. With CoutC_{\text{out}} filters the output stacks to Cout×Hout×WoutC_{\text{out}} \times H_{\text{out}} \times W_{\text{out}}. Early maps fire on edges and blobs, middle maps on textures and parts, deep maps on object level patterns.

A worked tensor shape

Input 5×5×15 \times 5 \times 1, two filters 3×33 \times 3, stride 11, no padding. Each map is:

(5−3)/1+1=3(5 - 3) / 1 + 1 = 3

So each map is 3×3=93 \times 3 = 9 numbers, and the stack is 2×3×3=182 \times 3 \times 3 = 18 numbers. A real early block with 6464 filters on 224×224224 \times 224 gives 64×224×224=3,211,26464 \times 224 \times 224 = 3{,}211{,}264 numbers, which is why memory, not weights, often caps batch size.

Reading maps honestly

Bright means strong match, not probability. Maps are unnormalized activations that later layers rescale. Dead maps that stay near zero signal dead filters, often from too high a learning rate, and pruning them rarely hurts.

Code

import numpy as np

def feature_stack(h_in: int, w_in: int, c_out: int, k: int) -> tuple:    """Map shape and total numbers for square input, square kernel, stride 1."""    h_out = h_in - k + 1  # valid padding arithmetic    return (c_out, h_out, h_out), c_out * h_out * h_out

print(feature_stack(5, 5, 2, 3))  # -> ((2, 3, 3), 18)

Watch Out For

Reading raw maps as heatmap proof

A bright patch shows filter match, not object proof. It shifts with normalization and means little alone. The symptom is screenshots presented as explanations that change under a re seed. Pair maps with ablations before claiming one region caused the decision.

Adding channels past memory

Doubling channels quadruples the next 3×33 \times 3 cost and doubles activation memory. The symptom is out of memory at larger batches while accuracy barely moves. Widen in steps and watch validation per million activations, not per channel.

The Quick Version

  • Each filter writes one 2D map of where its pattern fired.
  • Stacking maps gives a channels by height by width tensor the next layer reads.
  • Two 3 by 3 filters on a 5 by 5 input give a 2 by 3 by 3 stack of 18 numbers.
  • Bright means match strength, not probability, and dead maps signal dead filters.