# U-Net

> Encoder-decoder with skip connections — Ronneberger et al. 2015. The standard for biomedical and small-data image segmentation.

Pick for image segmentation when training data is limited (<10k images). Skip connections preserve fine spatial detail that pure encoder-decoders lose. Default for medical imaging, satellite, and any pixel-level binary mask task.

- Category: Computer Vision
- Layers: 24
- Parameters: 720.7K
- Input shape (batchless): 3 × 256 × 256
- Output shape: 1 × 256 × 768
- Verifier verdict: warn
- Graph JSON: https://neurarch.com/templates/unet/model.json
- Open on the canvas: https://neurarch.com/?template=unet

## Structure

| # | Layer | Type | Parameters | Output shape |
| --- | --- | --- | --- | --- |
| 1 | image | Input | shape=[3, 256, 256] | 3 × 256 × 256 |
| 2 | enc1_conv | Conv2D | outChannels=64, kernelSize=3, stride=1 | 64 × 256 × 256 |
| 3 | enc1_bn | BatchNorm |  | 64 × 256 × 256 |
| 4 | enc1_relu | ReLU |  | 64 × 256 × 256 |
| 5 | enc1_pool | MaxPool2D | kernelSize=2, stride=2 | 64 × 128 × 128 |
| 6 | enc2_conv | Conv2D | outChannels=128, kernelSize=3, stride=1 | 128 × 128 × 128 |
| 7 | enc2_bn | BatchNorm |  | 128 × 128 × 128 |
| 8 | enc2_relu | ReLU |  | 128 × 128 × 128 |
| 9 | enc2_pool | MaxPool2D | kernelSize=2, stride=2 | 128 × 64 × 64 |
| 10 | bottleneck_conv | Conv2D | outChannels=256, kernelSize=3, stride=1 | 256 × 64 × 64 |
| 11 | bottleneck_bn | BatchNorm |  | 256 × 64 × 64 |
| 12 | bottleneck_relu | ReLU |  | 256 × 64 × 64 |
| 13 | up2 | TransposeConv2D | outChannels=128, kernelSize=2, stride=2 | 128 × 128 × 128 |
| 14 | dec2_skip | Concatenate |  | 128 × 128 × 256 |
| 15 | dec2_conv | Conv2D | outChannels=128, kernelSize=3, stride=1 | 128 × 128 × 256 |
| 16 | dec2_bn | BatchNorm |  | 128 × 128 × 256 |
| 17 | dec2_relu | ReLU |  | 128 × 128 × 256 |
| 18 | up1 | TransposeConv2D | outChannels=64, kernelSize=2, stride=2 | 64 × 256 × 512 |
| 19 | dec1_skip | Concatenate |  | 64 × 256 × 768 |
| 20 | dec1_conv | Conv2D | outChannels=64, kernelSize=3, stride=1 | 64 × 256 × 768 |
| 21 | dec1_bn | BatchNorm |  | 64 × 256 × 768 |
| 22 | dec1_relu | ReLU |  | 64 × 256 × 768 |
| 23 | output_conv | Conv2D | outChannels=1, kernelSize=1, stride=1 | 1 × 256 × 768 |
| 24 | segmentation_mask | Output |  | 1 × 256 × 768 |

## Verifier findings

- **warn** `deep-no-residual`: 8 conv/linear layers detected but no residual (Add/Skip) layers. Networks deeper than 8 layers are highly prone to vanishing gradients without skip connections. Fix: Add Residual or Add layers every 2-4 layers (ResNet-style). For transformers, use the built-in TransformerBlock which includes residuals.

## Exported PyTorch (first 46 lines)

```python
# Architecture designed with Neurarch: https://neurarch.com
# PyTorch: compatible with Python 3.8+ and torch>=1.12
# Colab: pip install torch torchvision  (usually pre-installed)

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple

class U_Netimagesegmentation(nn.Module):
    def __init__(self):
        super().__init__()

        self.conv2d_1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.batchNorm_1 = nn.BatchNorm2d(64)
        self.maxpool2d_1 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
        self.conv2d_2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.batchNorm_2 = nn.BatchNorm2d(128)
        self.maxpool2d_2 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
        self.conv2d_3 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.batchNorm_3 = nn.BatchNorm2d(256)
        self.transposeConv2d_1 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2, padding=0)
        self.conv2d_4 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.batchNorm_4 = nn.BatchNorm2d(128)
        self.transposeConv2d_2 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2, padding=0)
        self.conv2d_5 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.batchNorm_5 = nn.BatchNorm2d(64)
        self.conv2d_6 = nn.Conv2d(64, 1, kernel_size=1, stride=1, padding=0)

    def forward(self, x):
        # image shape: [3,256,256]
        conv2d_1_conv = self.conv2d_1(x)
        batch_norm_nc1_bn = self.batchNorm_1(conv2d_1_conv)
        relu_1_relu = F.relu(batch_norm_nc1_bn)
        maxpool2d_1_pool = self.maxpool2d_1(relu_1_relu)
        conv2d_2_conv = self.conv2d_2(maxpool2d_1_pool)
        batch_norm_nc2_bn = self.batchNorm_2(conv2d_2_conv)
        relu_2_relu = F.relu(batch_norm_nc2_bn)
        maxpool2d_2_pool = self.maxpool2d_2(relu_2_relu)
        conv2d_k_conv = self.conv2d_3(maxpool2d_2_pool)
        batch_norm_eck_bn = self.batchNorm_3(conv2d_k_conv)
        relu_k_relu = F.relu(batch_norm_eck_bn)
        transpose_conv2d_up2 = self.transposeConv2d_1(relu_k_relu)
        concatenate_concat = torch.cat([transpose_conv2d_up2, relu_2_relu], dim=-1)
        conv2d_2_conv = self.conv2d_4(concatenate_concat)
        batch_norm_ec2_bn = self.batchNorm_4(conv2d_2_conv)
```

## Machine access

- Every architecture: https://neurarch.com/a/index.json
- Verify a graph of your own: `POST https://www.neurarch.com/api/v1/check` (see https://neurarch.com/developer.html)
- MCP server, so an agent edits the graph with the checks in the loop: https://neurarch.com/docs/mcp.md
