Part X — Computer Vision: CNNs, ResNets, Object Detection & Segmentation · Chapter 9

Semantic segmentation & the U-Net

Hook

A detector draws a box. A segmentation network has to color in every pixel inside it. But every encoder in this course shrinks its input down to extract features — by the time an image reaches a useful bottleneck, most of its spatial detail is already gone. How do you get it back?

Intuition
1.03.06.02.04.02.08.05.07.01.03.09.00.05.02.06.0
input (4x4)

Encoding: each pool keeps only the block maximum, throwing the rest away.

A tiny image, pooled down twice to a single number, then upsampled back out with nothing else to go on. Step through the trace and watch exactly how much detail each pooling step throws away — and how little a plain upsample can do to bring it back.

Formalize

A U-Net is an encoder-decoder: the encoder repeatedly pools an image down toward a small bottleneck, and the decoder upsamples it back out to full resolution. A skip connection carries each encoder stage's own feature map across to the matching decoder stage, so the decoder has real spatial detail to combine with, not just a coarse upsampled guess:

decodei=combine(upsample(decodei+1), skipi)\text{decode}_i = \text{combine}\big(\text{upsample}(\text{decode}_{i+1}),\ \text{skip}_i\big)
  • decodei\text{decode}_i — the decoder's output at resolution level ii (matching the encoder's resolution at that same level).
  • skipi\text{skip}_i — the encoder's own feature map at level ii, saved before it was pooled down further.
  • combine\text{combine} — how the upsampled signal and the skip are merged (concatenation and a conv in a real U-Net; a plain average here, to keep the arithmetic exact by hand).
  1. Pooling is lossy, on purpose

    Each 2×22\times2 max pool keeps only a block's maximum — the other three values are gone the moment the encoder moves to the next stage. That's what makes the bottleneck cheap to compute over.

  2. Upsampling alone can't undo that loss

    Nearest-neighbor upsampling only repeats each value into a bigger block. Chained twice from a 1×11\times1 bottleneck, it can only ever produce one repeated number across the whole image.

  3. The skip connection is the only path detail can take back

    Because skipi\text{skip}_i was saved before the corresponding pooling step, it still has the detail that step discarded — the decoder can only recover what the skip connection hands it directly.

Play
1.03.06.02.04.02.08.05.07.01.03.09.00.05.02.06.0
original input (4x4)
9.09.09.09.09.09.09.09.09.09.09.09.09.09.09.09.0
decoded without skip connections
3.84.87.35.35.34.38.36.87.54.56.09.04.06.55.57.5
decoded with skip connections

The same 4×44\times4 input, decoded two ways: one path only ever saw the bottleneck's single number, the other had the encoder's own feature maps to combine with at every stage. The reconstruction error tells the same story the heatmaps show.

Worked example

Input:

[1362428571390526]\begin{bmatrix} 1 & 3 & 6 & 2 \\ 4 & 2 & 8 & 5 \\ 7 & 1 & 3 & 9 \\ 0 & 5 & 2 & 6 \end{bmatrix}
  1. Encoder: two poolings to a bottleneck

    Taking the max of each 2×22\times2 block:

    • Top-left block [1342]\begin{bmatrix}1&3\\4&2\end{bmatrix}: max =4=4
    • Top-right block [6285]\begin{bmatrix}6&2\\8&5\end{bmatrix}: max =8=8
    • Bottom-left block [7105]\begin{bmatrix}7&1\\0&5\end{bmatrix}: max =7=7
    • Bottom-right block [3926]\begin{bmatrix}3&9\\2&6\end{bmatrix}: max =9=9

    giving skip1=[4879]\text{skip}_1=\begin{bmatrix}4&8\\7&9\end{bmatrix}. Pooling that once more gives a bottleneck of just 99 — the single largest pixel in the whole image.

  2. Without skip connections: a uniform blur

    Upsampling 99 twice produces a 4×44\times4 grid of nothing but 99s. Squared error against the original input, averaged over all 1616 pixels: MSE=31.75\text{MSE}=31.75.

  3. With skip connections: real detail comes back

    First, average the upsampled bottleneck (99 everywhere) with skip1\text{skip}_1's own four values:

    decode1=[(9+4)/2(9+8)/2(9+7)/2(9+9)/2]=[6.58.589]\text{decode}_1 = \begin{bmatrix}(9+4)/2 & (9+8)/2\\(9+7)/2 & (9+9)/2\end{bmatrix} = \begin{bmatrix}6.5&8.5\\8&9\end{bmatrix}

    Upsampling that once more and averaging with the original input recovers real detail, in place of a flat 99 either way:

    • the top-left pixel becomes (1+6.5)/2=3.75(1+6.5)/2=3.75
    • the pixel at row 4, column 3 becomes (2+9)/2=5.5(2+9)/2=5.5

    Averaged over all 16 pixels: MSE=5.421875\text{MSE}=5.421875.

  4. The skip connection alone accounts for the entire gap

    Both decoders start from the exact same bottleneck. The only difference is whether skip0\text{skip}_0 and skip1\text{skip}_1 ever entered the computation — and that alone cuts reconstruction error by 31.75/5.4218755.86×31.75/5.421875\approx5.86\times.

Checkpoint

Toggle the decoder until reconstruction error drops below 10.

Pick a decoder mode to try it
Summary
decodei=combine(upsample(decodei+1), skipi)\text{decode}_i = \text{combine}\big(\text{upsample}(\text{decode}_{i+1}),\ \text{skip}_i\big)

Encoders discard spatial detail to make a bottleneck cheap; skip connections are the only path that detail can take back to the decoder, since upsampling alone can only repeat coarse values, never recover what was pooled away. This is the same identity-shortcut idea from residual connections, applied to resolution instead of depth — carry forward exactly what the next stage would otherwise have to reconstruct from nothing. This closes out the architectures in this part; the capstone ahead puts one of them to work end-to-end on a real pretrained model.