3.4.14 · D2Convolutional Neural Networks

Visual walkthrough — Semantic segmentation (U-Net, FCN)

2,065 words9 min readBack to topic

The parent note told you that to go from a tiny feature map back up to a full label map we use a transposed convolution, and it handed you the size formula

as if it fell from the sky. This page derives that formula from absolute zero — no matrices assumed, no "deconvolution" hand-waving. By the end you will be able to draw the whole upsampling operation on graph paper and predict the output size in your head. Everything is grounded in one running numerical example so nothing is abstract.

Prerequisites we lean on but re-explain as needed: a normal convolution's sliding window (from Pooling Layers you already know a window sliding across a grid) and the idea of a learned kernel.


Step 1 — Remember what an ordinary convolution does to size

WHAT. We slide a small square of numbers (the kernel, size ) across the input grid. Each placement multiplies overlapping numbers and adds them into one output number. The stride is how many cells we jump between placements. The padding is how many rings of zeros we glue around the input first.

WHY. We start here because the transposed convolution is defined as the exact reverse of this motion. To reverse something, you must first see it clearly forwards.

PICTURE. In the figure below the input is a grid (blue). A kernel (yellow) slides with stride , no padding. Count the landing spots along one row: the kernel's left edge can sit at columns — that is 3 landing spots, so the output is (pink).

Figure — Semantic segmentation (U-Net, FCN)

Notice the direction: input output , it got smaller. Transposed convolution is going to run this arrow backwards.


Step 2 — Invert the arrow: from output back to input

WHAT. Solve the forward formula for instead of . If a forward conv turns a size- grid into a size- grid, the transposed conv is the operation that turns size back into size .

WHY. This is the whole trick in one line. "Transposed convolution" just means: take the shrinking motion and read the size relationship in reverse. We are not inventing a new formula — we are algebraically flipping the old one.

PICTURE. Same two grids as Step 1, but now the arrow points up: the small pink is the input, the big blue is what we want to produce.

Figure — Semantic segmentation (U-Net, FCN)

Take the forward formula and drop the floor for the moment (we return to it in Step 7). With :

Rename to match the transposed view — the small grid is now the input , the big grid is the output :

That is exactly the parent's formula. The rest of this page shows what physical motion produces it, term by term.


Step 3 — The term: spread the input apart

WHAT. Take the input grid and insert rows and columns of zeros between every pair of neighbouring cells. Do not pad the outside yet — only the internal gaps.

WHY. The forward stride skipped cells; to undo skipping we must re-create the skipped space as empty (zero) cells. This is why the term counts gaps, of them, each widened by .

PICTURE. Below: a pink input becomes a dilated grid when (one zero inserted in each internal gap). The chalk-blue cells are the original values; the dim cells are inserted zeros.

Figure — Semantic segmentation (U-Net, FCN)

Count the new side length: original cells with internal gaps, each gap holding zero:


Step 4 — The term: now slide a stride-1 kernel over the spread grid

WHAT. Over the zero-spread grid from Step 3, run an ordinary stride-1 convolution with a learnable kernel — but glue zeros of outer padding so the kernel can hang off both edges.

WHY. Spreading (Step 3) only makes holes; something must fill them. The learnable kernel smears each input value into a neighbourhood, painting real numbers into the zero holes. This is the "learned interpolation" — the network learns the best brush, unlike fixed bilinear upsampling.

PICTURE. The yellow kernel slides one cell at a time over the spread-out blue/dim grid; each landing sums a patch into one output pixel of the growing green result.

Figure — Semantic segmentation (U-Net, FCN)

Putting Step 3 and Step 4 together with no outer padding (): the dilated grid has side , and a stride-1 kernel of size adds to the side (the kernel sticking out on the far edge):

There is our formula with . Every symbol now has a picture behind it.


Step 5 — The term: padding runs backwards too

WHAT. In the forward conv, padding added a ring of zeros, which increased the output. Running backwards, the same must shrink the transposed output — so it enters with a minus sign and doubled (one ring per side): .

WHY. Consistency. If you agree the transposed conv is the size-inverse of a forward conv that used padding , then whatever padding gave you in the forward direction it must take away in reverse. Symmetry forces the sign flip.

PICTURE. Left board: forward conv with a padding ring makes the output one cell bigger. Right board: the transposed conv with the same crops that same ring, making the output one cell smaller.

Figure — Semantic segmentation (U-Net, FCN)

  • :::: spreading the input (Step 3).
  • :::: the kernel painting into the holes (Step 4).
  • :::: outer padding removed, one ring each side (this step).

Step 6 — Full worked example: FCN-32s,

WHAT. The parent claimed FCN-32s upsamples straight to in "one shot ×32". Let us pick the numbers that make the formula land exactly on .

WHY. A formula you cannot plug numbers into is not yet yours. We prove the ×32 claim is real, not marketing.

PICTURE. A number line from to , annotated with the stride , kernel , padding — every quantity tagged onto the arrow.

Figure — Semantic segmentation (U-Net, FCN)

Plug :

  • :::: six gaps between seven cells, each stretched by 32.
  • :::: a fat kernel wide enough to smear smoothly across the huge holes.
  • :::: trims the outer padding back so we land exactly on 224, not 256.

That is why FCN-32s is "coarse": one input pixel now controls a block of output — blocky boundaries. This is precisely the motivation the parent gave for skip connections (FCN-16s, FCN-8s) and for ResNet-style fusions of shallow, high-resolution features.


Step 7 — The degenerate & edge cases you must never trip on

WHAT. We check the corners of the formula so no input surprises you.

WHY. The contract: every quadrant, every sign, every zero. Upsampling has three sharp edges.

PICTURE. Three mini-boards side by side: (a) , (b) , (c) the floor-ambiguity of the forward map.

Figure — Semantic segmentation (U-Net, FCN)

Case (a): single-pixel input, . Then — no gaps to spread because there is only one cell. The output is : the kernel simply stamps itself once. Segmentation networks never start from , but it confirms the term vanishes correctly.

Case (b): stride one, . Then and no zeros are inserted (Step 3 gap = ). : this is just a padded ordinary conv that grows the map by a little. Transposed conv with does not upsample — a common beginner trap.

Case (c): the checkerboard artifact. When is not divisible by , some output pixels are hit by more kernel overlaps than others, producing a faint checkerboard. The formula still gives the right size, but the quality suffers — which is why practitioners pick a multiple of (e.g. in Step 6: , clean).


The one-picture summary

Everything on this page is one sentence: spread the input with zeros between cells (giving the term), slide a learnable kernel over it (giving ), then crop the outer padding (giving ).

Figure — Semantic segmentation (U-Net, FCN)
Recall Feynman retelling — say it back in plain words

A normal convolution walks a little window across a picture and shrinks it. To grow a picture back, I run that motion in reverse. First I take my small grid and pull the pixels apart, dropping empty cells into every gap — that stretches the side to . Those gaps are empty, so I slide a learnable brush of width across the stretched grid; it smears real numbers into the holes and adds more to the side. Finally, if the forward version had used padding, I crop that same padding off — twice, once per side — so I subtract . Add it up and I get . In FCN-32s I choose and turn a into a crisp . The only traps: doesn't actually upsample, a single-pixel input just stamps the kernel once, and if isn't a multiple of I get an ugly checkerboard.

Recall Quick self-test

With , what is ? ::: — matching U-Net's decoder step. Why concatenate rather than add these upsampled features to encoder features in U-Net? ::: Concatenation keeps all channels so later convs can choose what to fuse; addition forces a fixed linear blend and can lose detail.

Related: Loss Functions (pixel-wise cross-entropy consumes this full-resolution output), Data Augmentation and Batch Normalization (stabilise training of these deep encoder–decoders), Transfer Learning (the FCN encoder is a pretrained backbone), Object Detection and Attention Mechanisms (alternative dense-prediction ideas).