AI Architectures

U-Net

The shape that learned to label every pixel — born in medical imaging, now the quiet engine inside almost every diffusion model.

Prerequisites: A convolution slides a filter over an image + Pooling shrinks a feature map. That’s it.
10
Chapters
9+
Simulations
0
Assumed Knowledge

Chapter 0: A Label for Every Pixel

A normal image classifier answers one question about a whole image: “cat or dog?” One label for millions of pixels. But a huge class of problems needs something far more demanding: a label for every single pixel. Which pixels are tumor and which are healthy tissue? Which pixels are road, car, pedestrian, sky? This is semantic segmentation, and it's where U-Net was born — in 2015, for segmenting cells in biomedical microscopy images.

Here's why a classifier can't just be reused for this. To turn a big image into a single class, a classifier throws spatial information away on purpose — it pools and downsamples until the image collapses to one vector, then predicts one label. That's perfect for “cat or dog” and catastrophic for segmentation, where you need to know where everything is, down to the pixel. The very operation that makes classification work — discarding location — destroys what segmentation needs.

So segmentation has a tension at its heart. To know what an object is, you need to see a large region — context (is this blob a cell or an artifact? you must see its surroundings). But to label it precisely, you need fine spatial resolution — exactly which pixels belong to it. Context wants you to zoom out and pool; precision wants you to keep every pixel. U-Net is the architecture that resolves this tension, and its solution is so elegant it became a template for far more than segmentation.

The one-sentence version. U-Net first contracts an image down to capture what is in it (context), then expands it back to full resolution to say where everything is (precision) — and it carries fine detail directly across from the contracting side to the expanding side so the precise output isn't blurred away. That carrying-across is the whole trick.

What the output looks like

A segmentation model's output isn't one number — it's an image the same size as the input, where each pixel holds a class prediction. For a tumor segmenter, that's a “mask”: 1 where there's tumor, 0 where there isn't, for every pixel. The model must produce a high-resolution, spatially-precise map — the opposite of a classifier's single collapsed vector. That output shape requirement is what forces the whole U-shaped design.

See it: classifier vs. segmenter

The widget shows the same input two ways. A classifier pools the image down to a single label — watch the spatial grid collapse to one box. A segmenter must instead produce a full per-pixel mask — every cell gets its own prediction. Toggle between them to feel why one architecture can't do the other's job.

One Label vs. A Label Per Pixel

The classifier collapses the image to a single class (spatial info gone). The segmenter keeps every pixel and labels each one. Toggle to see the fundamentally different output shapes.

Common misconception. “Just remove the pooling so the classifier keeps resolution.” Then the network never sees a large enough region to understand context — each output pixel only “sees” a tiny patch and can't tell a cell from a smudge. You genuinely need both the wide view (which requires downsampling) and the fine resolution (which downsampling destroys). The art is getting both at once, which is exactly what the next chapters build.
Why can't a standard image classifier be directly used for pixel-level segmentation?

Chapter 1: The Encoder–Decoder — Down, Then Up

The first half of U-Net's idea is the encoder–decoder shape. It has two paths. The encoder (or contracting path) progressively shrinks the image — convolve, then downsample, again and again — squeezing a big detailed picture into a small stack of abstract features. The decoder (or expanding path) does the reverse — upsample, then convolve — growing those abstract features back into a full-resolution output. Down to understand, up to localize.

Why go down first

Each downsampling step does two things at once. It shrinks the spatial size (so the next layers are cheaper), and crucially, it widens the receptive field — how much of the original image each feature “sees.” After a few downsamples, a single feature summarizes a large region of the image, so it can encode high-level meaning: “this region is a cell nucleus.” Without going down, features stay local and the network never grasps context. The encoder trades resolution for understanding.

Resolution down, meaning up. As you descend the encoder, the feature maps get smaller in space but richer in channels — fewer pixels, but each pixel carries more abstract meaning, computed from a wider view. A 256×256 image with 3 color channels might become a 16×16 map with 512 channels at the bottom. You've traded “where” (lost spatial resolution) for “what” (rich semantic features). The decoder's job is to recover the “where” without losing the “what.”

Why go up after

The decoder reverses the journey. It takes the small, semantically-rich feature map at the bottom and progressively upsamples it — doubling the spatial size each step, applying convolutions to refine — until it's back to the original image resolution. Now the output is the same size as the input, so it can carry a prediction for every pixel. The encoder built up meaning by shrinking; the decoder rebuilds spatial precision by growing. Together they form the two strokes of the letter U — down the left side, up the right.

The data flow, traced

input
256×256, 3 ch
↓ encode
128×128, 64ch
conv + downsample
bottleneck
16×16, 512ch — max meaning, min resolution
↑ decode
128×128, 64ch
upsample + conv
output
256×256 mask

See it: the resolution pyramid

The widget shows feature-map sizes through a plain encoder–decoder. Step through: watch the spatial size halve on the way down (and channels grow), reach the tiny bottleneck, then double back up to full resolution. This down-then-up profile is the skeleton of U-Net — but as the next chapter shows, the skeleton alone produces a disappointingly blurry result.

The Encoder–Decoder Resolution Pyramid

Each block is a feature map; width = spatial size, color intensity = channel richness. Step down (shrink, enrich) to the bottleneck, then up (grow) back to full size.

Depth (how far down) 4

Common misconception. “The encoder and decoder are separate networks.” They're one network trained end-to-end, and they're mirror images — the decoder has roughly the same structure as the encoder, reversed. That symmetry is deliberate: it's what lets the next chapter's skip connections line up perfectly, connecting each encoder level to the decoder level at the same resolution.
What does downsampling in the encoder accomplish beyond making feature maps smaller?

Chapter 2: The Blur — Why Encoder–Decoder Isn’t Enough

Here's the problem that makes plain encoder–decoders fail, and it sets up U-Net's one big idea. The decoder has to reconstruct a full-resolution, pixel-precise output — but the only thing it receives is the tiny bottleneck feature map. And that bottleneck threw away almost all the fine spatial detail on the way down. The result: a blurry, smeared output that gets the rough shape right but the exact boundaries wrong.

Why the bottleneck can't carry detail

Think about the numbers. A 256×256 image has 65,000 pixels of spatial detail. The bottleneck might be 16×16 — just 256 spatial locations. Even though each location is rich in channels (meaning), there are simply not enough spatial positions to encode where every fine edge was. The encoder deliberately discarded that — pooling a 2×2 region into one value forgets which of the four pixels was the bright one. So when the decoder upsamples, it has to guess the fine detail, and its best guess is a smooth, blurry average. The sharp boundary of a cell becomes a fuzzy gradient.

The information was destroyed, not hidden. This isn't a training problem you can fix with more data — the fine spatial detail is physically absent from the bottleneck. Pooling is lossy and irreversible: once you've averaged a 2×2 patch into one number, the exact pixel layout is gone forever. No decoder, however clever, can recover information that was thrown away. The detail must be preserved somewhere and handed to the decoder — which is precisely what skip connections do.

The tension, made sharp

So we're stuck between two needs that pull in opposite directions. The encoder must downsample to build context (what the object is). But downsampling destroys the fine detail the decoder needs for precise boundaries. A plain encoder–decoder gives you context at the cost of precision — blurry masks. We need a way to give the decoder both: the high-level meaning from the deep bottleneck and the fine detail from the shallow, high-resolution early layers. Hold those two streams of information until they can be combined.

See it: reconstruction from the bottleneck alone

The widget shows a crisp input mask, then what a plain encoder–decoder reconstructs from the bottleneck alone. Crank the bottleneck size down (more downsampling) and watch the output get blurrier — the boundaries smear because the spatial detail was pooled away. This blur is exactly the disease skip connections cure.

Reconstruction from the Bottleneck (no skips)

Left: crisp input. Right: what the decoder rebuilds from only the bottleneck. Shrink the bottleneck and watch the boundaries blur — lost detail can't be recovered.

Bottleneck size (smaller = more pooling) 6

Common misconception. “Just make the bottleneck bigger to keep detail.” A bigger bottleneck keeps more detail — but it also means less downsampling, so the network sees less context and costs far more compute. You're back to the original tension. The breakthrough isn't a bigger bottleneck; it's keeping the bottleneck small (for context and efficiency) while routing the lost detail around it through a separate path. That path is the skip connection.
Why does a plain encoder–decoder produce blurry segmentation masks?

Chapter 3: Skip Connections — The One Big Idea

This is the heart of U-Net, the idea that turns a blurry encoder–decoder into a boundary-sharp segmenter. The insight: the fine detail the decoder needs still exists — in the encoder's early layers, before downsampling threw it away. So instead of forcing all information through the lossy bottleneck, copy each encoder feature map directly across to the decoder at the same resolution. These shortcuts are the skip connections, and they're what put the cross-bars in the letter U.

How a skip connection works

At each level of the decoder, just before refining, you have two feature maps at the same spatial size: the upsampled one coming up from below (rich in context, but coarse), and the copied one skipping across from the matching encoder level (rich in fine detail, high-resolution). U-Net concatenates them — stacks them along the channel dimension — then runs convolutions over the combined stack. Now the decoder's refinement step can use both the high-level meaning and the precise spatial detail at once. Context from below, detail from the side, fused.

Two roads for two kinds of information. The deep path (down through the bottleneck and back up) carries semantic information — what things are. The skip connections carry spatial information — exactly where edges and boundaries are. By giving each kind of information its own route and merging them at every resolution, U-Net never has to choose between context and precision. The bottleneck answers “what,” the skips answer “where,” and the decoder combines them into “what is exactly here.”

Worked example: concatenation

At decoder level 2, suppose the upsampled feature map from below is 128×128 with 256 channels (carrying context), and the encoder's level-2 feature map — copied across the skip — is also 128×128 but with 128 channels (carrying fine detail). Concatenating along channels gives a 128×128 map with 256 + 128 = 384 channels. The next convolution sees all 384 channels together, so a single output pixel can be computed from both the “this region is a cell” signal and the “the edge is exactly here” signal. The spatial dimensions must match for concatenation to work — which is exactly why the encoder and decoder are mirror images with matching resolutions at each level.

From scratch: the skip in code

python
def unet_forward(x):
    # encoder — SAVE each feature map before downsampling
    e1 = enc_block1(x);        s1 = e1;  e1 = down(e1)   # save s1 (high-res)
    e2 = enc_block2(e1);       s2 = e2;  e2 = down(e2)   # save s2
    b  = bottleneck(e2)                                  # deepest, lowest-res
    # decoder — CONCATENATE the saved skip at each level
    d2 = up(b)
    d2 = dec_block2(torch.cat([d2, s2], dim=1))   # ← skip: context + detail
    d1 = up(d2)
    d1 = dec_block1(torch.cat([d1, s1], dim=1))   # ← skip again
    return final_conv(d1)                            # full-res mask

The whole U-Net difference is those two torch.cat([..., s], dim=1) calls. The encoder saves its feature maps (s1, s2) before downsampling; the decoder concatenates them back in at the matching resolution. Without those lines you have a plain (blurry) encoder–decoder. With them, you have U-Net.

See it: skip on vs. off

The same image, segmented with skip connections on and off. With skips off, the output is the blur from Chapter 2 — context but no precise boundaries. Toggle skips on and the high-resolution detail floods back in: crisp, accurate edges. This single toggle is the difference between a useless segmenter and a state-of-the-art one.

Skip Connections: Off vs. On

Left: ground-truth mask. Right: the model's output. Toggle skip connections and watch the boundaries snap from blurry to sharp as the encoder's detail is routed back in.

Common misconception. “Skip connections are the same as ResNet's residual connections.” They're cousins but different. ResNet adds a layer's input to its output (same shape) to ease gradient flow within a deep stack. U-Net concatenates an encoder feature map onto a decoder one across the whole network, to reunite spatial detail with semantic context. ResNet skips are short and additive; U-Net skips are long and concatenative. Both are “skips,” but they solve different problems.
What do U-Net's skip connections actually carry from encoder to decoder, and why does it fix the blur?

Chapter 4: The Full U — Watch It Flow

Now see the whole architecture in motion. This is a complete U-Net: an image enters at the top-left, flows down the contracting encoder (blocks shrinking, getting deeper), across the bottleneck at the bottom, then up the expanding decoder (blocks growing back), and out as a segmentation mask at the top-right. The horizontal arrows across the middle are the skip connections, carrying each encoder level's detail straight to the matching decoder level. That shape — down, across, up, with rungs between — is why it's called U-Net.

Press Run to send data through and watch the flow. Then toggle the skip connections and re-run: with skips on, the output mask is crisp; with skips off, the same network produces the familiar blur. You can see the detail traveling across the rungs of the U.

Live U-Net Data Flow

Down the encoder, across the bottleneck, up the decoder. Skip connections (rungs) carry detail across. Run the flow and toggle skips to watch the output go sharp or blurry.

What to take away. The U-shape isn't decorative — it's a precise statement of the information flow. The vertical dimension is resolution (high at top, low at bottom). The left–right dimension is encode vs decode. The horizontal rungs are the skip connections that reunite, at every resolution, the “what” from the deep path with the “where” from the shallow path. Every later variant — and every diffusion model — keeps this exact skeleton.
Common misconception. “Deeper U-Nets (more down/up levels) are always better.” More levels means more context and a wider receptive field, but also more lost detail to carry back and more compute. The right depth depends on object scale: tiny structures need shallower nets (less downsampling) or more skip connections; large-context tasks benefit from deeper ones. The U is a template you tune, not a fixed recipe.

No quiz — the flow is the test. If you can trace why turning off the rungs blurs the output, you understand U-Net's core.

Chapter 5: Upsampling — How the Decoder Grows

We've talked about the decoder “upsampling” — growing a small feature map back to a larger one. But how do you actually add spatial resolution? You're conjuring more pixels than you started with, so something must fill in the gaps. There are two main approaches, and the choice has real consequences for the output's quality.

Method 1: interpolation + convolution

The simple approach: blow the feature map up with plain image resizing — nearest-neighbor (copy each value into a 2×2 block) or bilinear (smoothly interpolate between values) — then run a normal convolution to refine. The resize adds the resolution (dumbly), and the convolution learns to clean it up. It's simple, robust, and the modern default in many architectures.

Method 2: transposed convolution

The learned approach: a transposed convolution (sometimes loosely called “deconvolution”) does the upsampling and the learning in one step. Instead of sliding a filter to shrink, it does the reverse — each input value is multiplied by a learned filter and painted into a larger output region, with overlapping regions summed. The network learns how to expand. More flexible, but with a notorious failure mode.

The checkerboard artifact. Transposed convolutions are prone to an ugly checkerboard pattern in their output. The reason: when the filter size isn't evenly divisible by the stride, some output pixels get contributions from more overlapping filter placements than their neighbors, so they come out systematically brighter — producing a regular grid of light and dark squares. It's a pure artifact of the arithmetic, not the data. This is exactly why many modern U-Nets prefer interpolation + convolution, which has no such overlap problem.

Worked example: why the checkerboard appears

Imagine a transposed convolution with a filter of size 3 and a stride of 2 (a common setting that causes trouble). As the filter steps across the output by 2 but spans 3, adjacent output positions receive overlapping contributions — but unevenly. Output pixel A might be covered by 2 filter placements while neighbor B is covered by only 1. So A accumulates roughly twice the signal of B, every other pixel, in both directions — a checkerboard. The fix is to make the filter size a multiple of the stride (so coverage is even), or to avoid transposed convs entirely and use resize-then-convolve, where every output pixel is treated identically.

See it: three ways to upsample

A small feature map grown four ways: nearest-neighbor (blocky), bilinear (smooth), transposed-convolution done well (even coverage), and transposed-convolution done badly (the checkerboard). Toggle between them to see the tradeoffs — and to recognize the checkerboard artifact when you spot it in a real generated image.

Upsampling Methods Compared

The same small map upsampled four ways. Notice the checkerboard pattern in the “bad transposed conv” mode — the artifact that pushed many U-Nets toward resize+conv.

Resolution is added, then learned. Whichever method you use, the principle is the same: crudely add spatial positions (by copying, interpolating, or painting), then let convolutions — informed by the skip connections from Chapter 3 — fill in the correct detail. The upsampling provides the canvas; the skip-fed convolutions paint the precise picture. That's why upsampling method and skip connections work together: neither alone makes a sharp output.
What causes the “checkerboard” artifact in transposed-convolution upsampling?

Chapter 6: Training a U-Net — The Loss Matters

A U-Net outputs a per-pixel prediction, so the obvious loss is per-pixel cross-entropy: classify each pixel (tumor or not) and average the loss over all pixels. It works — but in segmentation there's a vicious trap that makes this loss alone dangerous: class imbalance.

The imbalance trap

In medical images, the thing you care about is often tiny. A tumor might be 2% of the pixels; the other 98% are healthy background. Now watch what per-pixel accuracy does: a model that predicts “background” for every single pixel — finding nothing at all — scores 98% accuracy. The loss is happily low. The model has learned to do nothing, and the metric congratulates it. Cross-entropy, averaged over pixels, barely notices the tumor because it's drowned out by the sea of easy background pixels.

Accuracy lies when classes are imbalanced. “98% of pixels correct” sounds excellent and means nothing when 98% of pixels are background. The model that finds zero tumors and the model that finds them perfectly can have nearly identical pixel accuracy. You need a loss and a metric that focus on the overlap with the rare class, not the raw pixel count — one that gives the tumor pixels a vote proportional to their importance, not their scarcity.

The fix: Dice loss

The standard answer is the Dice coefficient (and its loss form). Dice measures overlap: roughly, twice the area where prediction and ground-truth tumor agree, divided by the total tumor area in both. It's 1 when the predicted mask perfectly matches the true mask, and 0 when they don't overlap at all — and crucially, it ignores the background entirely. Predict all-background and Dice is 0 (no overlap with the tumor), not 0.98. The model can no longer cheat by ignoring the rare class. In practice people often combine cross-entropy (stable gradients everywhere) with Dice (focus on the overlap) to get the best of both.

Worked example: the two losses disagree

A 100-pixel image with a 4-pixel tumor. The model predicts all-background (finds nothing). Pixel accuracy: 96 of 100 pixels correct = 96% — looks great. Dice: the overlap between the predicted tumor (empty) and the true tumor (4 pixels) is zero, so Dice = 2×0 / (0 + 4) = 0 — correctly screams “you found nothing.” Same prediction, and the two scores couldn't disagree more. Train on Dice (or Dice + CE) and the model is forced to actually locate the tumor, because all-background scores zero.

See it: accuracy vs. Dice as the tumor shrinks

A model that always predicts “background.” Shrink the true tumor and watch pixel accuracy soar toward 100% (it's right about all the background) while Dice stays pinned at 0 (it never overlaps the tumor). The gap between the two is the imbalance trap made visible — and the reason segmentation is trained on overlap losses, not accuracy.

Pixel Accuracy vs. Dice (a do-nothing model)

The model predicts all-background. As the tumor shrinks, pixel accuracy climbs toward 100% while Dice stays at 0 — revealing why accuracy is a dangerous metric for imbalanced segmentation.

Tumor size (% of image) 8%

Common misconception. “High pixel accuracy means a good segmentation.” For anything imbalanced — which is most medical and many real-world segmentation tasks — accuracy is almost meaningless. Always report Dice (or IoU / Jaccard). A model can be 99% accurate and clinically useless. Also: heavy data augmentation (Chapter on augmentation) matters enormously here, because medical datasets are small — U-Net's original paper leaned hard on elastic deformations to multiply its tiny training set.
Why is per-pixel accuracy a dangerous loss/metric for segmenting a small tumor, and what fixes it?

Chapter 7: U-Net as the Diffusion Backbone

Here's why U-Net matters far beyond medical imaging. When diffusion models — Stable Diffusion, the original DALL·E 2, Imagen — took over image generation, the network at their core, the one doing the actual work, was almost always a U-Net. The same shape you just learned for segmenting cells became the engine of AI art. Understanding why reveals something deep about what U-Net is really good at.

What diffusion asks the network to do

A diffusion model generates an image by starting from pure noise and removing a little noise at a time, over many steps, until a clean image emerges. The network's job at each step is: given a noisy image, predict the noise that was added (so it can be subtracted). Look at the shape of that task — the input is a full-resolution image, and the output is also a full-resolution image (the predicted noise). Image in, image out, same size. That is exactly the shape U-Net was built for. Segmentation maps pixels to pixel-labels; denoising maps pixels to pixel-noise. Same architecture, different output meaning.

Why U-Net specifically, and not a plain CNN. Denoising needs both scales at once, just like segmentation. To remove noise coherently, the network must understand global structure (“there's a face here, so these pixels should form an eye”) — that's the deep bottleneck, the context. But it must also place every pixel precisely — that's the skip connections preserving spatial detail. A diffusion U-Net's encoder grasps what the image should contain; its skips ensure the denoised result is sharp, not blurry. The exact context-plus-precision balance U-Net was invented for is what high-quality image generation demands.

Two additions: time and text

Diffusion U-Nets add two things to the segmentation U-Net. First, time conditioning: the network must know which denoising step it's on (early steps remove coarse noise, late steps refine fine detail), so the timestep is encoded and injected into every block. Second, cross-attention to text: for text-to-image, attention layers are inserted so the denoising can be guided by a prompt (“a cat in a hat”). The U-shape with skips stays exactly the same — these are conditioning signals threaded through the familiar backbone.

The bridge to DiT

Recently, some models replaced the U-Net backbone with a pure transformer — the Diffusion Transformer, or DiT, which powers Sora and newer image models. DiT chops the image into patches and runs attention, scaling more smoothly than convolutional U-Nets. But the conceptual job is identical (noise in, noise out), and many production systems still use U-Nets or U-Net/transformer hybrids. U-Net was the backbone that made the diffusion era possible; DiT is its successor, but the lineage is direct. (See the DiT and Diffusion lessons.)

See it: denoising with a U-Net

Step through a diffusion process. Start from pure noise; at each step the U-Net predicts the noise, it's subtracted, and a clearer image emerges — until a clean result appears. Watch the same “image in, image out” network you learned for segmentation, now generating.

Diffusion Denoising (the U-Net predicts noise each step)

From pure noise to a clean image, one denoising step at a time. Each step, the U-Net predicts the noise to remove. Step through and watch the image emerge.

Common misconception. “Diffusion models and U-Nets are different things.” For years they were inseparable — the diffusion process (add/remove noise) is the algorithm, and the U-Net is the network that implements the crucial noise-prediction step. When people said “Stable Diffusion,” the thing actually running on the GPU billions of times was a U-Net. Knowing U-Net means knowing the workhorse of the entire generative-image era.
Why is U-Net a natural fit for the noise-prediction step in diffusion models?

Chapter 8: The U-Net Family

U-Net's skeleton — contract, bottleneck, expand, with skip connections — turned out to be so good that it spawned a whole family of variants, each adapting the template to a new domain or fixing a limitation. Knowing them shows just how general the core idea is.

The major variants

The skeleton outlived its parts. Notice what every variant keeps: the contracting path, the bottleneck, the expanding path, and — above all — the skip connections. What they swap is the building blocks (2D→3D, conv→transformer) or the skip behavior (plain copy → attention-gated). The U-shape is a structural principle for any task that maps a high-resolution input to a high-resolution output while needing multi-scale context. The blocks are fashion; the U is permanent.

See it: what each variant changes

Select a variant and see which part of the base U-Net it modifies (highlighted), while everything else stays the same. It's a vivid reminder that these are all the same architecture with one targeted change — the skeleton is shared, the modification is local.

One Skeleton, Many Variants

Pick a variant; the modified part of the U lights up. The contract–bottleneck–expand shape with skips is shared by all of them.

Common misconception. “Newer/fancier U-Net variants always win.” nnU-Net is the standing counterexample: a carefully auto-configured plain U-Net routinely beats elaborate custom architectures on medical-imaging leaderboards. The lesson echoes the whole field — getting the data pipeline, augmentation, and training recipe right usually matters more than a clever architectural twist. The base U-Net, well-tuned, is a ferociously strong baseline.
What do essentially all U-Net variants (3D, Attention, Transformer, nnU-Net) keep unchanged?

Chapter 9: Connections & Cheat Sheet

You now understand U-Net from the inside: why pixel-level prediction needs both context and precision, how the encoder–decoder captures context, why the bottleneck alone produces blur, how skip connections rescue the fine detail, how upsampling works, how to train it with overlap losses, why it became the diffusion backbone, and how its variants all share one skeleton. The thread: map a high-resolution input to a high-resolution output by going down for meaning and up for precision — and carry the detail across so precision survives the round trip.

The cheat sheet

Shape:  contracting encoder → bottleneck → expanding decoder (the letter U)
Encoder:  conv + downsample; resolution ↓, channels ↑, receptive field ↑ (context)
Decoder:  upsample + conv; resolution ↑ back to full size (precision)
The blur problem:  bottleneck alone can't carry fine detail (pooling is lossy)
Skip connections:  concatenate encoder feature maps into the decoder at matching resolution
Upsampling:  interpolation+conv (safe) or transposed conv (watch the checkerboard)
Training:  Dice loss (overlap) + cross-entropy; never trust pixel accuracy on imbalanced masks
Beyond segmentation:  the noise-prediction backbone of diffusion models (image in, image out)

A decision guide

Need a high-res output the same size as input?
U-Net (segmentation, denoising, depth, super-res, translation).
Boundaries coming out blurry?
Check your skip connections — that's almost always the cause.
Small/imbalanced target (medical)?
Train on Dice (+CE), augment heavily, consider nnU-Net.
Seeing checkerboard artifacts?
Replace transposed convs with resize + convolution.

Where this connects

The one thing to remember. U-Net answers a question every dense-prediction task asks: how do I produce a sharp, full-resolution output while still understanding the big picture? Its answer — go down for context, up for resolution, and carry the fine detail across on skip connections — was so clean it escaped its origin in cell microscopy and became the backbone of the entire image- generation revolution. The U is one of the most reused shapes in all of deep learning.
You're building a model that takes a photo and outputs a same-size depth map (a value per pixel). What architecture and what to watch for?

“To see clearly, you must first step back to take in the whole — then return to attend to every detail.” That round trip, with memory carried across, is the U.