1x1 Convolutions

A convolution with no spatial extent sounds pointless. It is one of the most useful layers in modern architectures.

Overview

The operation that looks like nothing

A 3×3 convolution mixes a pixel with its eight neighbours. A 1×1 convolution has no neighbours to combine. At first reading it appears to times each pixel by a number. This Is a scaling and hardly worth a layer.

That reading forgets the channel dim, and the channel dim is where all happens.

A feature map isn't a grid of numbers. It's a stack of grids, one per channel. A layer with 256 channels holds 256 values at each spatial spot. A 1×1 convolution stands at one spot, takes all 256 values there, and finds a weighted sum of them. Then does it again for each output channel, and then repeats the whole thing at each spot with the same weights.

So it doesn't mix neighbours. It mixes channels. Drag the two controls above and watch the link pattern: each input channel reaches each output channel, and nothing spatial happens at all.

1x1 Convolutions

This module needs JavaScript: the images are computed in the page rather than downloaded.

Worth knowing

A 1×1 convolution looks at one pixel position and all of its channels. It mixes channels, never neighbours.
It is a fully-connected layer applied identically at every spatial position, which is why it is written as a convolution at all.
Its main job is changing the channel count — usually reducing it before an expensive layer, which is what a bottleneck is.
Weights: Cin × Cout, against 9 × Cin × Cout for a 3×3.

1x1 Convolutions

A kernel with no spatial extent. Why almost each modern architecture is full of them.

It is a fully-connected layer in disguise

Fix one spatial spot and the op is just a dense layer: Cin inputs, Cout outputs, Cin × Cout weights. What makes it a convolution is that the same dense layer is applied, unchanged, at each spot in the map.

That is the same weight-sharing arg that motivates convolution in the first place. If mixing channels in a given way is handy at one location, it's probably handy at all of them, and sharing the weights makes the layer free of the input's spatial size.

What it is actually for

Changing the channel count. This is the common case. A 1×1 convolution is the cheapest possible way to turn 256 channels into 64, or 64 into 256, and it's why the layer is at times called a map.

Bottlenecks. Put a channel reduction before an costly spatial convolution and an expansion after it. ResNet's bottleneck block is just this: 1×1 down to 64 channels, 3×3 at 64, 1×1 back up to 256. The 3×3 — by far the costliest part — runs on a quarter of the channels, and the two 1×1s cost almost nothing by check.

Count it. A 3×3 straight from 256 to 256 channels needs 9 × 256 × 256 ≈ 590,000 weights. The bottleneck version needs 256×64 + 9×64×64 + 64×256 ≈ 70,000. Same input and output shape, an eighth of the params, and an extra two non-linearities thrown in.

Adding non-linearity without touching detail. Each 1×1 is followed by an activation, so a stack of them grows representational depth at constant spatial size and negligible cost. This was the "network in network" idea that named the technique.

Replacing the classifier head. Global mean pooling followed by a 1×1 convolution does the job of a large dense layer with a fraction of the params, and works for any input size.

The arithmetic

The readout above checks the two counts right. For Cin input and Cout output channels:

KernelWeightsSees
1×1Cin × Coutone position, all channels
3×39 × Cin × Cout3×3 positions, all channels
Depthwise 3×39 × Cin3×3 positions, one channel each

That last row is worth noticing. A depthwise separable convolution splits the work in two: a depthwise 3×3 that mixes neighbours but not channels, followed by a 1×1 that mixes channels but not neighbours. Together they rough a full 3×3 at roughly a ninth of the cost. MobileNet is built almost fully from that pair, and half of it's 1×1 convolutions.

The bottleneck, counted

The 1×1's main job is changing the channel count cheaply, and the ResNet bottleneck shows why that is worth a layer. Compare a straight 3×3 from 256 to 256 channels against the bottleneck that squeezes down, does the spatial work narrow, and expands back:

designweights
one 3×3, 256 → 2569 × 256 × 256 = 589,824
1×1 to 64, 3×3 at 64, 1×1 to 256256·64 + 9·64·64 + 64·256 = 69,632

Same input and output shape, 8.5× fewer params, and two extra non-linearities thrown in — because the costly 3×3 now runs on a quarter of the channels while the two 1×1s that resize the stack cost almost nothing. That is the whole reason deep ResNets are affordable. The 1×1 is doing no spatial work at all. Fix one spot and it's just a dense layer, Cin × Cout weights, applied unchanged at each spot — so it adds nothing to the receptive field. Its entire value is mixing channels. This Is just what the spatial kernels can't afford to do at full width.

Beyond resizing channels, the 1×1 buys non-linearity at constant detail — each is followed by an activation. So A stack of them deepens the network spatially for almost nothing. This Was the "network in network" idea that named the technique — and it's half of a depthwise-separable convolution, the pointwise step that mixes channels after the depthwise step mixes neighbours. Two cautions: the bottleneck is a genuine info bottleneck. So Squeezing 256 channels to 8 before the spatial work can cost more fit than the work it saves. And a 1×1 with no activation after it, stacked on another linear layer, merges into one linear map, because two mats times'd together are a single mat.

Where it goes wrong with 1x1 Convolutions

Expecting spatial work from it. It can't smooth, sharpen or find an edge. If the receptive field needs to grow, a 1×1 adds nothing — its part to the receptive field is just zero.

Reducing channels too aggressively. The bottleneck is a genuine info bottleneck. Squeezing 256 channels to 8 before the spatial convolution saves work and can cost more fit than it's worth.

Losing the activation. A 1×1 with no non-linearity after it, stacked on another linear layer, merges into a single linear map. Two mats times'd together are one mat.

Questions people ask about 1x1 Convolutions

What does a 1x1 convolution do? It takes a weighted sum across channels at each pixel independently. Spatial neighbours are not mixed at all.

Why use one if it sees no neighbours? To change the number of channels cheaply and add a non-linearity. It reduces or expands channel depth without touching spatial size.

How is it different from a fully connected layer? It is the same weights applied at every position, so it works on any image size. A fully connected layer is tied to a fixed input shape.

What is a bottleneck block? A 1x1 reduces channels, a 3x3 does the spatial work on the thinner tensor, and another 1x1 expands again. This saves many multiplications, as in ResNet.

How many parameters does a 1x1 convolution have? Input channels times output channels, plus biases. Going from 256 to 64 channels takes 16,384 weights.

Where did the idea come from? From the Network in Network paper, and it was popularised by Inception modules for dimension reduction.

A convolution that looks at one pixel

A 1x1 kernel has no spatial extent at all. This Sounds useless. It's a per-pixel linear layer across channels. It's what makes bottleneck blocks and depthwise-separable convolutions affordable.

example_01.pyNumPy
Output

The two main practical uses are cutting parameter counts and building bottleneck blocks.

example_02.pyNumPy
Output

A 1x1 convolution also serves as a cross-channel mixer and turns a classifier into a fully convolutional segmenter.

example_03.pyNumPy
Output

Check yourself

0 of 3

Answer without scrolling back up.

  1. What does a 1x1 convolution actually combine?

  2. Why does a ResNet bottleneck put 1x1 convolutions around the 3x3?

  3. What happens to two stacked 1x1 convolutions with no activation between them?

Cheat sheet

1x1 Convolutions

See how a 1x1 convolution mixes channels without touching spatial dimensions, and why it appears in Inception, ResNet and every bottleneck.

COMPUTER VISION · vizlearn.in/computer_vision/one_by_one_convolutions.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.