Бумага для сегментации изображений | Четыре версии DeepLabV1V2V3V3+ | ICLR2015 CVPR2017

искусственный интеллект
  • v1 название статьи: "СЕМАНТИЧЕСКАЯ СЕГМЕНТАЦИЯ ИЗОБРАЖЕНИЙ С ГЛУБОКИМ СВЕРТОЧНЫМ СЕТКОМ И ПОЛНОСТЬЮ СВЯЗНОЙ CRFS"
  • ссылка на документ v1:АР Вест V.org/PDF/1412.70…
  • Название статьи v2: «DeepLab: Семантическая сегментация изображений с помощью глубоких сверточных сетей, Atrous Convolution и полносвязных CRF»
  • ссылка на документ v2:АР Вест V.org/ABS/1606.00…
  • Название статьи v3: «Переосмысление Atrous Convolution для семантической сегментации изображений»
  • ссылка на документ v3:АР Вест V.org/ABS/1706.05…
  • Название статьи v3+: «Кодер-декодер с Atrous Separable Convolution для сегментации семантического изображения»
  • ссылка на документ v3+:АР Вест V.org/ABS/1802.02…

0 Обзор

Всего DeepLab разделен на 4 версии, а именно: v1, v2, v3 и v3+. Небольшое чувство YOLO. Каждому поколению DeepLab есть что сказать об этом.

У Unet есть естественные преимущества для обработки медицинских изображений, поэтому, когда я читал статью DeepLab, я не стремился воспроизвести его код, а изучить его основные навыки, а затем интегрировать его в Unet.

1 v1

В версии первого поколения ключевым изменением является изменение структуры VGG на DRN.Чтобы узнать о DRN, см.:Наггетс.Талант/пост/691748…

Я нашел модифицированный код VGG в deeplabv1 pytorch, вы можете им насладиться:

import torch
import torch.nn as nn
from torchvision import models

class VGG16_LargeFOV(nn.Module):
    def __init__(self, num_classes=21, input_size=321, split='train', init_weights=True):
        super(VGG16_LargeFOV, self).__init__()
        self.input_size = input_size
        self.split = split
        self.features = nn.Sequential(
            ### conv1_1 conv1_2 maxpooling
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1),

            ### conv2_1 conv2_2 maxpooling
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1),

            ### conv3_1 conv3_2 conv3_3 maxpooling
            nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1),


            ### conv4_1 conv4_2 conv4_3 maxpooling(stride=1)
            nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),

            ### conv5_1 conv5_2 conv5_3 (dilated convolution dilation=2, padding=2)
            ### maxpooling(stride=1)
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=2, dilation=2),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=2, dilation=2),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=2, dilation=2),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            ### average pooling
            nn.AvgPool2d(kernel_size=3, stride=1, padding=1),

            ### fc6 relu6 drop6
            nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=12, dilation=12),
            nn.ReLU(True),
            nn.Dropout2d(0.5),

            ### fc7 relu7 drop7 (kernel_size=1, padding=0)
            nn.Conv2d(1024, 1024, kernel_size=1, stride=1, padding=0),
            nn.ReLU(True),
            nn.Dropout2d(0.5),

            ### fc8
            nn.Conv2d(1024, num_classes, kernel_size=1, stride=1, padding=0)
        )
        
        if init_weights:
            self._initialize_weights()
    
    def forward(self, x):
        output = self.features(x)
        if self.split == 'test':
            output = nn.functional.interpolate(output, size=(self.input_size, self.input_size), mode='bilinear', align_corners=True)
        return output
    
    def _initialize_weights(self):
        for m in self.named_modules():
            if isinstance(m[1], nn.Conv2d):
                if m[0] == 'features.38':
                    nn.init.normal_(m[1].weight.data, mean=0, std=0.01)
                    nn.init.constant_(m[1].bias.data, 0.0)
            

if __name__ == "__main__":
    model = VGG16_LargeFOV()
    x = torch.ones([2, 3, 321, 321])
    y = model(x)
    print(y.shape)

Нет ничего редкого, если вы установите pytorch, вы можете запустить его напрямую,Первый раз я увидел выпадающие 2D-слои, лол..

Выходные данные DCNN на приведенном выше рисунке относятся к результату работы VGG16, и затем они должны быть обработаны CRF. Эта CRF является условным случайным полем, я, честно говоря, не совсем понимаю эту штуку, но, к счастью, после DeepLabv3 она уже не используется. Как видно из рисунка:

  • CRF имеет эффект обработки краев, и после обработки краев эффект очень хороший;
  • CRF имеет количество итераций, поэтому в статье также предлагается CRFasRNN. Понятие поля достаточно сложное, благо после v3 не используется, иначе будет блокпост на инженерной дороге.

2 v2

Основанная на первой версии, эта версия находится под влиянием SPP (объединение пространственных пирамид) и предлагает объединение пространственных пирамид отверстий ASPP. Во-вторых, измените основу v1 с VGG16 на реснет.

В GoogleNet предлагается начальный модуль, а затем есть Xception.Эти начальные модули представляют собой сверточные слои разного размера ядер свертки параллельно, а затем, наконец, соединяются.Структура.

Структура ASPP здесь аналогична структуре Inception:

Он состоит из сверточных слоев с разными коэффициентами расширения, а затем применяется метод сращивания.сумма, здесь показан код pytorch структуры ASPP:

import torch
import torch.nn as nn
import torch.nn.functional as F


class _ASPP(nn.Module):
    """
    Atrous spatial pyramid pooling (ASPP)
    """

    def __init__(self, in_ch, out_ch, rates):
        super(_ASPP, self).__init__()
        for i, rate in enumerate(rates):
            self.add_module(
                "c{}".format(i),
                nn.Conv2d(in_ch, out_ch, 3, 1, padding=rate, dilation=rate, bias=True),
            )

        for m in self.children():
            nn.init.normal_(m.weight, mean=0, std=0.01)
            nn.init.constant_(m.bias, 0)

    def forward(self, x):
        return sum([stage(x) for stage in self.children()])

Кроме того, v2 использует стратегию распада учебной бригады Poly:

lriter=lr0*(1itermaxiter)powerlr_{iter} = lr_0*(1-\frac{iter}{max_{iter}})^{power}

В статье говорится, что при мощности = 0,9 эффект наилучший.

3 v3

  1. Использование Multi-grid, с точки зрения непрофессионала, заключается в использовании большего количества сверток расширения с большими коэффициентами расширения, коэффициент расширения v2 останавливается на 4, и теперь он равен 16:

  1. Добавить слой BN в структуру ASPP;

4 v3+

  1. Улучшение находится в разделе декодирования:

Как и в части декодера Unet, инновации в этой части ограничены.

  1. Опираясь на отделимую свертку MobileNet, она заменяется отделяемой сверткой коллизий:

  1. Рисунок на модуле Xception:

5 Резюме

В целом, я думаю, что самая большая инновация DeepLab — это объединить кодировщик DRN для семантической сегментации, а затем предложить модуль ASPP.

Кроме того, если вы чувствуете, что содержание моей статьи недостаточно подробно, вы можете прочитать исходную статью, приведенную в начале статьи, Что касается различных версий DeepLab, реализованных PyTorch, я нашел такой пост для справки:zhuanlan.zhihu.com/p/68531147