Skip to content
mlmentorship

Convolution as matrix multiplication (im2col)

A 2D convolution is a matmul in disguise. Unfold the input into columns, multiply by a flattened filter matrix. The reason CNNs run fast on the same hardware as transformers.

Published · 5 min read ·Specialist ·Intermediate

Visual quick review

Visual first · depth when needed

Map each overlapping receptive field to one im2col matrix column, then trace how one flattened filter computes every output position with a single matrix multiplication.

Preparing the visual…

Summary

im2col rearranges a convolution input so each spatial location’s receptive field becomes a column of a matrix. The convolution then reduces to a single matmul: .

Modern hardware (GPUs, TPUs) is optimized for dense matmul. A naive 2D convolution loop is the wrong shape for that hardware: nested loops over spatial positions, channels, and kernel offsets, with poor memory locality. im2col turns the same arithmetic into a single GEMM call that lands on the highly tuned BLAS path.

Every major framework (cuDNN, MKL-DNN, XNNPACK) implements convolution as some variant of this idea. Understanding it explains why CNN inference cost scales like matmul, why grouped convolutions are cheap, and why depthwise-separable convolutions split into two matmuls.

The mechanism

For input , kernel , output :

  1. im2col: for each output position , extract the values in its receptive field and stack them as a column. The result is a matrix .
  2. Flatten kernel: reshape to .
  3. GEMM: , shape .
  4. col2im: reshape back to .

Memory cost: duplicates each input pixel up to times. For a kernel, that is a 9x blowup of the activation tensor.

Learning objective

Where do the columns in im2col come from?

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 - FLATTEN EACH PATCH INTO ONE COLUMN 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 COMPUTES EVERY POSITION W_flat = [1, 0, 0, -1] x X_col = [-4, -4, -4, -4] reshape four outputs to 2 x 2
Read it this way: scan the four overlapping 2 x 2 patches in output order. Flattening does not change their values: it places each patch in one column, including repeated input values such as 5. The flattened filter then dot-products with all columns in one GEMM. Explicit im2col stores those repeats; implicit GEMM generates the same virtual columns tile by tile. Original example checked against PyTorch Unfold and NVIDIA's convolution performance guide.

Variants

  • Implicit GEMM: avoid materializing in memory. Compute the matmul tile by tile, indexing back into on the fly. cuDNN’s default for most conv shapes.
  • Winograd: trade matmul FLOPs for additions via polynomial transforms. Faster for small kernels (e.g. ) on certain hardware. Lower numerical precision.
  • FFT convolution: . Wins for large kernels (rare in modern CNNs).
  • Depthwise convolution: each input channel has its own filter, so is block-diagonal. The matmul splits into tiny independent matmuls, much cheaper.

Interview focus

If asked “how does convolution actually run on a GPU,” the expected answer is: it is a matmul. Then walk through the im2col reshape, the GEMM call, and the memory blowup. Bonus points for noting that the flattened kernel has shape , so the FLOP count is . The same formula you see in every model card.

Common pitfalls

  • Forgetting the memory cost. im2col can be larger than the activation it came from. Implicit GEMM exists for this reason.
  • Conflating convolution with cross-correlation. Deep learning frameworks implement cross-correlation; the kernel is not flipped. Mathematicians’ convolution flips the kernel. Almost never matters in practice.
  • Treating depthwise and pointwise as a single op. They are two distinct matmuls with very different shapes. Profile separately.