В чём суть архитектуры U-Net и для каких задач она подходит?

U-Net — это архитектура сверточной нейронной сети, разработанная специально для сегментации изображений. Она была впервые представлена в 2015 году в статье “U-Net: Convolutional Networks for Biomedical Image Segmentation” Олафом Роннебергером и коллегами. U-Net получила широкое распространение благодаря своей способности точно локализовать и сегментировать объекты даже при небольшом объёме обучающих данных.

1. Общая структура U-Net

U-Net получила своё название из-за формы архитектуры, напоминающей букву "U". Она состоит из двух симметричных частей:

  1. **Сжатие (encoder / контрактирующая часть):
    **

    • Последовательность свёрточных слоёв и слоёв подвыборки (обычно MaxPooling)

    • Извлекает иерархические признаки

    • Постепенно уменьшает пространственное разрешение изображения и увеличивает глубину признаков

  2. **Восстановление (decoder / расширяющая часть):
    **

    • Последовательность транспонированных свёрток (или upsampling + conv), восстанавливающих размер

    • Соединяется с соответствующими слоями энкодера через скип-соединения (skip-connections)

    • Позволяет сохранить пространственную информацию

2. Подробная архитектура U-Net

Encoder (сжимающая часть):

  • Состоит из блоков:

    • 2 свёртки 3×3 (обычно с ReLU)

    • Batch Normalization (опционально)

    • MaxPooling 2×2 для уменьшения размера карты признаков вдвое

  • Каждый блок удваивает количество каналов (feature maps): 64 → 128 → 256 → ...

Bottleneck (дно "U"):

  • Самая глубокая часть сети

  • Максимальное количество фильтров, минимальное разрешение

  • Свёртки без MaxPooling

Decoder (восстанавливающая часть):

  • Каждый блок включает:

    • Upsampling (или ConvTranspose)

    • Конкатенация с соответствующим фичемапом из encoder (skip-connection)

    • 2 свёртки 3×3 + ReLU

  • Количество каналов уменьшается вдвое на каждом этапе: 512 → 256 → 128 → ...

Выходной слой:

  • 1×1 свёртка, проецирующая на нужное число классов (например, 1 для бинарной маски или N для многоклассовой сегментации)

  • В конце:

    • Sigmoid для бинарной маски

    • Softmax для многоклассовой маски

3. Skip connections: зачем они нужны

Простое восстановление изображения после downsampling теряет пространственную информацию. Чтобы компенсировать это, U-Net использует скип-соединения, которые копируют выходы соответствующих слоёв encoder и объединяют их с входами в decoder.

Роль skip-connections:

  • Восстанавливают локальные детали (границы, формы)

  • Улучшают точность сегментации

  • Упрощают обратное распространение градиента

4. Гиперпараметры и модификации

  • Глубина: число уровней encoder/decoder (обычно 4 или 5)

  • Число каналов: начинается с 64, может увеличиваться до 1024

  • Паддинг: same предпочтительнее, чтобы избежать обрезки и несоответствий при конкатенации

  • Loss-функция: зависит от задачи

    • Binary Cross Entropy, Dice Loss, IoU Loss — для бинарной сегментации

    • Categorical Cross Entropy, Tversky Loss — для многоклассовой

  • Активации: ReLU в скрытых слоях, Sigmoid/Softmax на выходе

5. Преимущества U-Net

  • Отлично работает при малом количестве данных благодаря агрессивному использованию аугментации и симметричной архитектуре

  • Сохраняет контекст и детализацию за счёт скип-соединений

  • Поддерживает разные размеры входа (при наличии соответствующей архитектуры)

  • Легко расширяется и модифицируется

6. Области применения U-Net

Хотя U-Net изначально разрабатывался для медицинских изображений, он применяется во множестве задач, где требуется пиксельная сегментация:

Медицинские изображения:

  • Сегментация опухолей, сосудов, органов (на КТ, МРТ, УЗИ)

  • Обнаружение аномалий

Сельское хозяйство и спутниковые снимки:

  • Сегментация полей, водоёмов, зданий на спутниковых данных

  • Обнаружение пожаров, вырубок лесов

Автономные транспортные средства:

  • Сегментация дорог, пешеходов, разметки

  • Построение карты глубины

Индустриальное применение:

  • Контроль качества (обнаружение дефектов)

  • Сегментация текстур

Глубокая генерация (GAN + сегментация):

  • Используется в Pix2Pix, SPADE и других задачах image-to-image translation

7. Вариации и улучшения U-Net

За годы после выхода оригинальной статьи было предложено множество модификаций U-Net:

U-Net++ (Nested U-Net):

  • Использует плотные skip-соединения между уровнями decoder

  • Повышает точность, особенно в многоклассовой сегментации

Attention U-Net:

  • Добавляет механизм внимания (attention gates), чтобы выбирать наиболее релевантные признаки из encoder

  • Улучшает сегментацию на сложных структурах

3D U-Net:

  • Использует 3D-свёртки и работает с объемными данными (например, 3D-МРТ или КТ)

Residual U-Net:

  • Встраивает residual-блоки из ResNet внутрь U-Net

  • Увеличивает глубину и стабильность

Efficient U-Net:

  • Заменяет backbone на EfficientNet

  • Обеспечивает высокую точность при малом размере модели

8. Пример архитектуры U-Net (в числах)

Входное изображение: 572×572×1 (один канал, например, grayscale)

Этап Описание Размер выходов Кол-во фильтров
Input Входное изображение 572×572×1
--- --- --- ---
Conv1 2×Conv 3×3 + ReLU 568×568×64 64
--- --- --- ---
MaxPool 2×2 284×284×64
--- --- --- ---
Conv2 2×Conv 3×3 + ReLU 280×280×128 128
--- --- --- ---
MaxPool 2×2 140×140×128
--- --- --- ---
... ... ... ...
--- --- --- ---
Bottleneck Conv x2 28×28×1024 1024
--- --- --- ---
Decoder Upsample + Concat 56×56×512 512
--- --- --- ---
Output Conv 1×1 388×388×1 1
--- --- --- ---

(размеры могут отличаться в зависимости от паддинга и ядра)

9. Выход и loss-функции

  • **Для бинарной сегментации:
    **

    • Выход: 1 канал, активация Sigmoid

    • Loss: Binary Cross Entropy, Dice Loss

  • **Для многоклассовой:
    **

    • Выход: N каналов, Softmax

    • Loss: Categorical Cross Entropy, Tversky, Focal Loss

10. Код: реализация в PyTorch

class UNetBlock(nn.Module):
def \__init_\_(self, in_ch, out_ch):
super().\__init_\_()
self.block = nn.Sequential(
nn.Conv2d(in_ch, out_ch, 3, padding=1),
nn.ReLU(),
nn.Conv2d(out_ch, out_ch, 3, padding=1),
nn.ReLU()
)
def forward(self, x):
return self.block(x)
class UNet(nn.Module):
def \__init_\_(self):
super().\__init_\_()
self.enc1 = UNetBlock(1, 64)
self.pool1 = nn.MaxPool2d(2)
self.enc2 = UNetBlock(64, 128)
self.pool2 = nn.MaxPool2d(2)
self.bottleneck = UNetBlock(128, 256)
self.up2 = nn.ConvTranspose2d(256, 128, 2, stride=2)
self.dec2 = UNetBlock(256, 128)
self.up1 = nn.ConvTranspose2d(128, 64, 2, stride=2)
self.dec1 = UNetBlock(128, 64)
self.out = nn.Conv2d(64, 1, 1)
def forward(self, x):
e1 = self.enc1(x)
e2 = self.enc2(self.pool1(e1))
b = self.bottleneck(self.pool2(e2))
d2 = self.dec2(torch.cat(\[self.up2(b), e2\], dim=1))
d1 = self.dec1(torch.cat(\[self.up1(d2), e1\], dim=1))
return torch.sigmoid(self.out(d1))

U-Net — одна из самых универсальных архитектур для задач пиксельной классификации, в первую очередь сегментации. Благодаря симметричной структуре и использованию skip-соединений, она показывает высокую точность на малых и специализированных датасетах.