В чём суть архитектуры U-Net и для каких задач она подходит?
U-Net — это архитектура сверточной нейронной сети, разработанная специально для сегментации изображений. Она была впервые представлена в 2015 году в статье “U-Net: Convolutional Networks for Biomedical Image Segmentation” Олафом Роннебергером и коллегами. U-Net получила широкое распространение благодаря своей способности точно локализовать и сегментировать объекты даже при небольшом объёме обучающих данных.
1. Общая структура U-Net
U-Net получила своё название из-за формы архитектуры, напоминающей букву "U". Она состоит из двух симметричных частей:
-
**Сжатие (encoder / контрактирующая часть):
**-
Последовательность свёрточных слоёв и слоёв подвыборки (обычно MaxPooling)
-
Извлекает иерархические признаки
-
Постепенно уменьшает пространственное разрешение изображения и увеличивает глубину признаков
-
-
**Восстановление (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-соединений, она показывает высокую точность на малых и специализированных датасетах.