Semantic Segmentation — Pixel-Level Classification
U-Net architecture, skip connections, and how segmentation powers medical imaging and autonomous vehicles. Label every pixel in one forward pass.
Object detection draws rectangles. Semantic segmentation colours every pixel with its class — no rectangles, no approximations, pixel-perfect boundaries.
A radiologist reading a chest X-ray does not draw a box around the tumour and call it done. They need to know the exact boundary — how many cubic centimetres, which tissue is affected, where does it end. A bounding box cannot answer these questions. Semantic segmentation can. It produces a mask: every pixel labelled as "tumour", "healthy tissue", "background."
Practical examples: Lyft and Uber's dashcam systems segment road, vehicles, pedestrians, and lane markings pixel-by-pixel for driver safety scoring. Agri-tech startups segment satellite images into crop types for yield forecasting. Quality control systems at garment factories segment defect regions in fabric images to measure defect area precisely.
The output of segmentation is a mask — a 2D array of the same height and width as the input image, where each value is a class index. For a 3-class problem (background=0, road=1, vehicle=2), the mask contains integers 0, 1, or 2 at every pixel position.
Colouring a map. Detection is like placing stickers on a map — one sticker per city, approximately where each city is. Segmentation is like colouring the map by region — every pixel of the country is coloured by state, every coastline is traced exactly, every river is coloured blue. Much more precise, much more useful for geography.
The challenge: to colour pixels precisely, the model needs to understand both the broad context (what is in the image) and fine spatial detail (exactly where boundaries are). Pooling layers in CNNs lose spatial detail. U-Net's skip connections restore it — that is the key architectural insight.
Semantic vs instance vs panoptic — what each one produces
U-Net — encoder, bottleneck, decoder, and skip connections
U-Net (Ronneberger et al., 2015) was designed for medical image segmentation with very few training images. Its key insight: the encoder (contracting path) captures what is in the image by progressively downsampling. The decoder (expanding path) restores spatial resolution. Skip connections copy feature maps directly from encoder to decoder at each scale — providing fine spatial detail that pooling destroyed. The result: precise pixel boundaries even from a small dataset.
Loss functions, masks, and the complete training pipeline
Segmentation training is similar to classification but operates at the pixel level. The target is not a single integer per image — it is a 2D mask of shape (H, W) where each value is a class index. The loss is cross-entropy computed over all pixels simultaneously. Class imbalance is severe in segmentation — background pixels vastly outnumber foreground pixels in most tasks. Weighted loss or Dice loss addresses this.
Pixel accuracy, IoU, and mIoU — the segmentation metric family
Pixel accuracy — fraction of correctly classified pixels — is misleading when classes are imbalanced. A model that predicts "background" for every pixel gets 90% pixel accuracy on a dataset where 90% of pixels are background. The correct metrics are per-class IoU and mean IoU (mIoU).
DeepLab and SegFormer — pretrained segmentation models for fine-tuning
U-Net trained from scratch requires thousands of labelled images. For most production tasks, fine-tune a pretrained segmentation model instead. DeepLabV3+ (Google) and SegFormer (Nvidia) are the two most widely used pretrained models — both available via HuggingFace with ImageNet-pretrained backbones and COCO/Cityscapes-pretrained heads.
Every common segmentation mistake — explained and fixed
Where segmentation ships in production — and the mask cleanup step every deployment needs
The three practical examples from the start of this module — medical imaging, satellite and aerial imagery, and autonomous driving — sit at very different points on the speed-versus-accuracy spectrum, and that difference drives almost every architecture and deployment decision a team makes.
A raw model output is rarely the mask that actually ships, regardless of which of these three settings it runs in. Every production segmentation pipeline includes a post-processing step that cleans up the per-pixel argmax before anyone — a radiologist, a downstream planning module, an analytics dashboard — ever sees it.
Five things people get wrong about semantic segmentation
Detection localises individual object instances — even improved to pixel precision, its output is still "here is object #1, here is object #2." Semantic segmentation has no concept of instances at all: it labels every pixel by class, so two cars standing next to each other both get painted the same "vehicle" colour and merge into one region in the mask. If you need to count how many cars are present or track a specific one, you need instance segmentation (or panoptic segmentation, which combines both) — semantic segmentation genuinely cannot answer "which one," only "what."
Downsampling in the encoder is not just a cost-saving shortcut — pooling is what lets each successive layer's receptive field cover a larger area of the original image, which is how the network builds up context about what it's looking at rather than just raw local pixel patterns. A network that never downsampled would struggle to tell "this patch of pixels is part of a road" from "this patch of pixels is part of a wall," because it would never see enough surrounding context. The actual problem downsampling causes is losing precise spatial detail (where exactly the boundary is) — which is what U-Net's skip connections are specifically designed to recover, rather than avoiding downsampling altogether.
They look similar — both copy information from an earlier layer forward — but they solve different problems. ResNet's skip connections give gradients a shortcut path so very deep classification networks stay trainable. U-Net's skip connections concatenate the encoder's feature map directly into the decoder at the matching resolution, so the decoder has access to the fine spatial detail (exact edges, precise boundaries) that pooling destroyed on the way down. The bottleneck alone tells the decoder *what* is in the image; the skip connections tell it *exactly where* the boundaries are. Removing them doesn't primarily hurt training stability — it produces blurry, imprecise mask boundaries.
In classification, class imbalance across the dataset is a data problem you can address with resampling. In segmentation, the imbalance is baked into every single image — most pixels in a road scene are background or road, and only a small fraction belong to the object you actually care about (a pedestrian, a defect, a tumour). Plain cross-entropy treats every pixel equally, so a model can achieve very low loss by getting the abundant background pixels right and largely ignoring the rare foreground class. That's exactly why segmentation commonly uses class-weighted cross-entropy, Dice loss, or a combination of both — Dice directly measures overlap and isn't dominated by whichever class has the most pixels.
Pixel accuracy just counts what fraction of pixels got the right label, and with severe class imbalance that number can be high for a model that has essentially learned nothing useful. If 90% of an image's pixels are background, a model that predicts "background" everywhere scores 90% pixel accuracy while completely failing at the task — it never gets a single foreground pixel right. Mean IoU (mIoU), which averages IoU per class rather than counting pixels in aggregate, exposes this immediately: the all-background model scores near-zero IoU on every foreground class, which is why mIoU (not pixel accuracy) is the metric that actually gets reported in segmentation papers and production dashboards.
Semantic segmentation — 5 questions interviewers actually ask
Semantic segmentation labels every pixel by class only — all cars share one colour, with no distinction between individual cars. Instance segmentation goes further and gives each object instance its own mask, so car #1 and car #2 are separate, but it typically leaves background ("stuff" like sky and road) unlabelled. Panoptic segmentation combines both: every pixel gets a class, and every "thing" (countable object) also gets an instance ID. I'd use semantic segmentation for something like measuring total road surface area, instance segmentation for counting or tracking individual objects (how many vehicles, which one is which across frames), and panoptic segmentation when a system needs complete scene understanding, like autonomous driving.
The encoder progressively downsamples the image through pooling, which grows the receptive field so the network can understand broad context — what kind of scene this is, what objects are present. But pooling throws away precise spatial information along the way. The decoder then upsamples back to the original resolution to produce a full per-pixel mask. Skip connections concatenate each encoder stage's feature map directly into the corresponding decoder stage at the same spatial resolution, handing the decoder back the fine detail — exact edges, precise boundaries — that pooling removed. Without skip connections, U-Net would still classify regions roughly correctly but produce blurry, imprecise boundaries; the skip connections are what make its masks sharp.
Pixel accuracy is dominated by whichever class has the most pixels, which in most real-world segmentation tasks is the background. A model that predicts background for every pixel can score 90%+ pixel accuracy on a scene that's 90% background while completely failing to segment the object that actually matters. IoU (intersection over union between predicted and true regions) and Dice score both measure per-class overlap directly, so a class with very few pixels still gets fairly evaluated — the all-background model would score near zero on that class's IoU, immediately exposing the failure that pixel accuracy hides. Reporting mean IoU across classes is the standard because it weights every class's correctness equally rather than by pixel count.
A few complementary approaches, usually combined: weight the cross-entropy loss inversely by class pixel frequency so rare classes contribute proportionally more to the loss; use Dice loss (or a combined CrossEntropy + Dice loss), since Dice measures overlap directly and isn't dominated by whichever class has the most pixels; and, at the data level, oversample images that contain more of the rare class, or crop training patches centred on foreground regions rather than random crops that are mostly background. I'd also verify the fix worked by checking per-class IoU during training, not just the aggregate loss — the loss curve looking fine can hide a model that has quietly given up on the rare class.
Training U-Net from scratch needs a reasonably sized labelled dataset because every weight, including the low-level feature detectors, has to be learned from your data alone — that's thousands of labelled masks in most real settings. Fine-tuning a pretrained model like SegFormer or DeepLabV3+ reuses an ImageNet-pretrained backbone (and often a segmentation head pretrained on ADE20K or Cityscapes), so I only need to adapt the final classifier to my class set, which works with far fewer labelled images. In practice: swap in the new number of output classes, use a much smaller learning rate on the backbone than the head, and remember architecture-specific quirks — SegFormer, for instance, outputs logits at 1/4 resolution, so I need to upsample before computing the final per-pixel prediction or the loss.
You can segment any image. Next: get ImageNet-level features without ImageNet-level compute.
You have built segmentation from scratch and used pretrained models. Both required labelled masks — expensive to collect. Module 59 covers transfer learning for vision: how to use a ResNet or EfficientNet backbone pretrained on ImageNet as a feature extractor for your own task, freezing early layers and fine-tuning later layers. The same technique powers every production computer vision system at startups today — building on ImageNet representations instead of training from scratch.
Feature extraction vs fine-tuning, layer freezing, and choosing the right backbone for your task.
🎯 Key Takeaways
- ✓Semantic segmentation assigns a class label to every pixel — output is a 2D mask of shape (H, W) with integer class indices. Unlike detection (bounding boxes) it traces exact boundaries. Unlike classification (one label per image) it works at pixel granularity.
- ✓U-Net has two paths: the encoder (downsampling with MaxPool) captures what is in the image, the decoder (upsampling) restores spatial resolution. Skip connections copy encoder feature maps directly to the decoder at each scale — providing fine spatial detail that pooling destroyed. This is why U-Net produces sharp precise boundaries.
- ✓Input dimensions must be divisible by 2^(number of pooling layers). U-Net with 4 pooling layers requires input divisible by 16. Use F.pad in the decoder to handle any size mismatches between encoder skip connections and upsampled decoder features.
- ✓Never use ToTensor() on segmentation masks — it adds a channel dimension and normalises to [0, 1], destroying integer class indices. Convert masks with: torch.tensor(np.array(mask_pil), dtype=torch.long) for shape (H, W) with correct integer values.
- ✓Pixel accuracy is misleading for imbalanced datasets — always use mIoU (mean Intersection over Union). A model predicting all-background gets high pixel accuracy but near-zero mIoU. Use class-weighted CrossEntropyLoss or Dice loss to prevent the model from collapsing to predicting the majority class.
- ✓For production: fine-tune SegFormer or DeepLabV3 pretrained on ADE20K or Cityscapes. Requires far fewer labelled images than training U-Net from scratch. SegFormer outputs at 1/4 resolution — always upsample with F.interpolate(logits, size=(H,W), mode="bilinear") before argmax for the final prediction.
Discussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.