Skip to content
mlmentorship

Convolution as matrix multiplication (im2col)

Lower convolution patches into matrix columns, verify the filter product, and distinguish explicit storage, implicit kernels and backward overlap accumulation.

Published · 8 min read ·Specialist ·Intermediate
Reading depth

Convolution lowering puts each receptive field in a matrix column so flattened filters can score all output positions together.

Summary

How do four overlapping patches become the four output values, and what must be stored? Use the input rows , , . The filter is , with stride one, no padding and no bias. Use deep-learning cross-correlation: do not flip the filter.

The mechanism

Flatten channel, kernel-row and kernel-column in that order; visit output positions row by row.

Output locationPatch columnDot product with
Top-left
Top-right
Bottom-left
Bottom-right

The four column scores reshape to a two-by-two output; no input reconstruction happens during that reshape. The patch matrix stores sixteen values from nine source values, ratio .

Learning objective

Each local patch becomes one column in the matrix product

Each patch becomes one matrix columnTwo adjacent 2x2 patches become these columns. Input rows are (1,2,3) and (4,5,6); shared pixels appear in both patches.Overlapping patches reuse the samepixels123456patch 01245patch 12356Pixels 2 and 5 belong to bothcolumns.
Derivation
A convolution unfolded into matrix multiplication A three by three single-channel input is covered by four overlapping two by two patches at stride one. Flattening each patch creates one column of a four by four im2col matrix. The flattened filter one, zero, zero, negative one multiplies all four columns at once, producing four negative fours that reshape to a two by two output. Repeated values in overlapping columns show the memory cost of explicit im2col. 1 - SLIDE A 2 x 2 PATCH, STRIDE 1 input X 1 2 3 4 5 6 7 8 9 four overlapping patches: TL = [1, 2, 4, 5] TR = [2, 3, 5, 6] BL = [4, 5, 7, 8] BR = [5, 6, 8, 9] 5 occurs in every patch 2 - PATCHES TO COLUMNS X_col shape: 4 patch values x 4 output positions TLTRBLBR 1 2 4 5 2 3 5 6 4 5 7 8 5 6 8 9 same values, new layout 3 - ONE GEMM W_flat = [1, 0, 0, -1] x X_col = [-4, -4, -4, -4] reshape four outputs to 2 x 2
each patch becomes one column in output order. The center value 5 occurs in all four columns. Filter scores become four negative fours; forward only reshapes them. Explicit lowering stores the repeats. Original example checked against PyTorch Unfold and NVIDIA's convolution guide.
Check: Which input values produce the top-right output? Where do they appear in X_col?
Read it this way: Two adjacent 2x2 patches become these columns. Input rows are (1,2,3) and (4,5,6); shared pixels appear in both patches.
Why do the middle image pixels appear in both output columns?

For one image with channels, let and . Unfold gives of shape ; flattened filters have shape . Compute plus per-output-channel bias, then reshape to . For batch size , explicit columns store entries.

Dense work is multiply-accumulates, or approximately twice that many FLOPs under the two-FLOP convention. The tiny example uses sixteen MACs even though its particular filter contains zeros. Materialized storage and whole-operator memory traffic are different quantities.

Change storage or channel structure

Avoid full materialization

New task: preserve these outputs without storing every patch simultaneously.

Picture and change: process one four-value column at a time and emit its score.

Check: patch scratch falls from sixteen to four scalar values here, while all four outputs remain minus four. Input, output and filter storage remain; repeated reads and indexing still cost work.

Use when: unfold storage or traffic is binding; benchmark an appropriate implicit/tiled kernel under the same operator.

Separate depthwise and pointwise work

New task: apply per-channel spatial filters, then mix channels.

Picture and change: take two input channels, four output positions and two-by-two depthwise filters. Depthwise work is MACs. Mixing those channels into three outputs with a pointwise filter adds MACs.

Check: total work is 56 MACs versus 96 for a dense two-by-two convolution with three output channels. These are different parameterizations, not generally interchangeable learned functions.

Use when: the architecture explicitly separates spatial filtering and channel mixing.

Reconstruct backward accumulation

Why is folding patches not an inverse reshape?

Reasoned answer

Overlapping columns contribute to the same input coordinate, so backward sums their contributions. Folding ones gives center coverage four in this example. Actual input gradients also include filter coefficients; with this filter and unit output gradients, the center derivative is zero.

Variants

For each axis, output length is for valid parameters. Here is symmetric padding, dilation and stride; apply the formula independently to height and width. Groups restrict each filter to input channels, changing the MAC count accordingly. Depthwise is a grouped case; pointwise performs dense channel mixing at each location.

Implicit GEMM indexes virtual patch tiles instead of storing the complete unfold. Direct kernels, Winograd and FFT implementations use different layouts or transforms. FFT requires the correct padding/correlation convention; Winograd changes numerical behavior. Shape, hardware, precision and workspace determine useful choices, not a universal framework implementation rule.

Interview focus

Derive complete input, filter, column and output shapes, then state flatten order, bias and resource convention. Algebraic lowering is not evidence that the actual kernel allocated the patch matrix.

Common pitfalls

Flipping the example’s filter changes all outputs to positive four; convolution versus cross-correlation can matter. Explicit storage expansion depends on shape, stride, padding and dilation, not a constant nine-fold multiplier. Forward output reshape and backward overlap-add solve different tasks. Profile depthwise/pointwise work and their fusion boundary separately.