[Классификация изображений] Yiwen Society VGGNet (pytorch)

искусственный интеллект

содержание

1. Введение модели

2. Структура модели

3. Особенности модели

4. Репродукция Питорча


1. Введение модели

VGGNet — это глубокая сверточная сетевая структура, предложенная Visual Geometry Group (VGG) в Оксфордском университете, и они заняли второе место в задаче классификации ILSVRC 2014 года с частотой ошибок 7,32% (чемпион был GoogLeNet с ошибкой 6,65%). заняла первое место в задаче локализации (Localization) с процентом ошибок 25,32% (уровень ошибок GoogLeNet составил 26,44%), а название сети VGGNet было взято из аббревиатуры названия группы. VGGNet является первой моделью, в которой частота ошибок классификации изображений снижена до уровня менее 10%.В то же время принятая в сети идея ядра свертки лежит в основе многих последующих моделей.Модель была опубликована в 2015 году. Международная конференция по характеристике обучения (International Conference On Learning. Representations, ICLR) была процитирована более 14 000 раз, yyds! . Магистральные сети многих моделей обнаружения объектов (таких как SSD, M2Det) используют VGGNet, а также передачу стиля изображения, сегментацию изображения и т. д., поэтому VGGNet — одна из моделей, которую мы должны изучить для глубокого обучения.

2. Структура модели


На приведенном выше рисунке показана модель VGG16-3.VGGNet в оригинальной статье содержит 6 версий эволюции, соответствующих VGG11, VGG11-LRN, VGG13, VGG16-1, VGG16-3 и VGG19, как показано ниже:

Различные значения суффикса указывают на разные сетевые уровни (VGG11-LRN указывает, что LRN используется на первом уровне VGG11, а VGG16-1 указывает, что последний уровень свертки в последних трех группах блоков свертки использует размер ядра свертки 1 × 1. Соответствующий VGG16-3 указывает, что размер ядра свертки составляет 3 × 3). Представленный в этом разделе VGG16 — это VGG16-3. Подробная конфигурация параметров выглядит следующим образом:

Сетевой уровень размер ввода ядерный размер выходной размер Количество параметров
Сверточный слой C_11  224×224×3  3×3×64/1  224×224×64  (3×3×3+1)×64
Сверточный слой C_12  224×224×64  3×3×64/1  224×224×64  (3×3×64+1)×64
Максимальный пул 1  224×224×64  2×2/2  112×112×64  0
Сверточный слой C_21  112×112×64  3×3×128/1  112×112×128  (3×3×64+1)×128
Сверточный слой C_22  112×112×128  3×3×128/1  112×112×128  (3×3×128+1)×128
Максимальный пул 2  112×112×128  2×2/2  56×56×128  0
Сверточный слой C_31  56×56×128  3×3×256/1  56×56×256  (3×3×128+1)×256
Сверточный слой C_32  56×56×256  3×3×256/1  56×56×256  (3×3×256+1)×256
Сверточный слой C_33  56×56×256  3×3×256/1  56×56×256  (3×3×256+1)×256
Пул Максимальный пул 3  56×56×256  2×2/2  28×28×256  0
Сверточный слой C_41  28×28×256  3×3×512/1  28×28×512  (3×3×256+1)×512
Сверточный слой C_42  28×28×512  3×3×512/1  28×28×512  (3×3×512+1)×512
Сверточный слой C_43  28×28×512  3×3×512/1  28×28×512  (3×3×512+1)×512
Максимальный пул 4  28×28×512  2×2/2  14×14×512  0
Сверточный слой C_51  14×14×512  3×3×512/1  14×14×512  (3×3×512+1)×512
Сверточный слой C_52  14×14×512  3×3×512/1  14×14×512  (3×3×512+1)×512
Сверточный слой C_53  14×14×512  3×3×512/1  14×14×512  (3×3×512+1)×512
Максимальный пул 5  14×14×512  2×2/2  7×7×512  0
Полностью подключенный слой FC_1  7×7×512  (7×7×512)×4096  1×4096  (7×7×512+1)×4096
Полностью связанный слой FC_2  1×4096  4096×4096  1×4096  (4096+1)×4096
Полностью связанный слой FC_3  1×4096  4096×1000  1×1000  (4096+1)×1000

3. Особенности модели

  • Вся сеть использует один и тот же размер ядра свертки 3×3 и максимальный размер пула 2×2.
  • Смысл свертки 1×1 в основном заключается в линейном преобразовании, при этом количество входных каналов и выходных каналов остаются неизменными, а уменьшения размерности не происходит.
  • Объединение двух сверточных слоев 3×3 эквивалентно сверточному слою 5×5, а размер рецептивного поля равен 5×5. Точно так же эффект трех последовательных сверточных слоев 3×3 эквивалентен одному сверточному слою 7×7. Этот метод подключения уменьшает параметры сети, а функция многоуровневой активации делает сеть более способной к обучению функциям.

Примечание: VGGNet имеет небольшой трюк во время обучения.Сначала обучите неглубокую простую сеть VGG11, а затем повторно используйте вес VGG11 для инициализации VGG13.Повторное обучение и инициализация VGG19 могут ускорить сходимость обучения. В процессе обучения используется многомасштабное преобразование для улучшения исходных данных, так что модель нелегко переобучить.

4. Репродукция Питорча

"""
vgg16
"""
class VGG16(nn.Module):

    def __init__(self, num_classes):
        super(VGG16, self).__init__()

        # calculate same padding:
        # (w - k + 2*p)/s + 1 = o
        # => p = (s(o-1) - w + k)/2

        self.block_1 = nn.Sequential(
            nn.Conv2d(in_channels=3,
                      out_channels=64,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      # (1(32-1)- 32 + 3)/2 = 1
                      padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(in_channels=64,
                      out_channels=64,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=(2, 2),
                         stride=(2, 2))
        )

        self.block_2 = nn.Sequential(
            nn.Conv2d(in_channels=64,
                      out_channels=128,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(in_channels=128,
                      out_channels=128,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=(2, 2),
                         stride=(2, 2))
        )
        
        self.block_3 = nn.Sequential(
            nn.Conv2d(in_channels=128,
                      out_channels=256,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(in_channels=256,
                      out_channels=256,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(in_channels=256,
                      out_channels=256,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=(2, 2),
                         stride=(2, 2))
        )

        self.block_4 = nn.Sequential(
            nn.Conv2d(in_channels=256,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(in_channels=512,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(in_channels=512,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=(2, 2),
                         stride=(2, 2))
        )

        self.block_5 = nn.Sequential(
            nn.Conv2d(in_channels=512,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(in_channels=512,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.Conv2d(in_channels=512,
                      out_channels=512,
                      kernel_size=(3, 3),
                      stride=(1, 1),
                      padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=(2, 2),
                         stride=(2, 2))
        )

        self.classifier = nn.Sequential(
            nn.Linear(512, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.65),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.65),
            nn.Linear(4096, num_classes),
        )

        for m in self.modules():
            if isinstance(m, torch.nn.Conv2d) or isinstance(m, torch.nn.Linear):
                nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='leaky_relu')
#                 nn.init.xavier_normal_(m.weight)
                if m.bias is not None:
                    m.bias.detach().zero_()

        # self.avgpool = nn.AdaptiveAvgPool2d((7, 7))

    def forward(self, x):

        x = self.block_1(x)
        x = self.block_2(x)
        x = self.block_3(x)
        x = self.block_4(x)
        x = self.block_5(x)
        # x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        logits = self.classifier(x)
        probas = F.softmax(logits, dim=1)
        # probas = nn.Softmax(logits)
        return probas
        # return logits