Компьютерное зрение, Исследования

ResNeXt: с нуля

ResNeXt следует простой концепции «разделяй и властвуй». ResNeXt часто называют расширенной версией ResNet. Некоторые из его важных приложений находятся в области отдела биомедицинской инженерии и особенно в отделе биоимиджинга. Здесь я собираюсь исследовать «создание ResNeXt: с нуля».

Модули: PyTorch, CUDA (необязательно)

Если вы не уверены в том, как установить PyTorch в вашей системе, вы можете проверить эту ссылку здесь. Это вам поможет! Движение вперед…

ResNeXt

Архитектура ResNeXt очень похожа на архитектуру ResNet. Если вы хотите узнать об архитектуре ResNet, то направляйтесь в это направление. Это алгоритм, основанный на глубоком обучении, основная задача которого - глубже понять особенности изображения. С чего же тогда начать?…

import torch
import torch.nn as nn

Это начальный блок кода, который инициализирует библиотеку PyTorch в среде Python. Это довольно мощные архитектуры и, следовательно, требуют большого количества вычислений. По умолчанию архитектура ожидает, что система будет иметь хорошие характеристики (с точки зрения мощности ЦП и графического процессора), чтобы иметь возможность выполнить свою задачу в срок и с максимальной точностью.

Теперь, если вы новичок в Python и хотите понять основы этих больших архитектур CNN, тогда это может быть непродуктивно для вас из-за того, что в этом определении ResNeXt было определено множество экземпляров наследования и классов. что сложно даже опытным программистам. Это может показаться поразительным для новичков, поэтому я прошу вас сначала изучить основы ООП.

Достаточно ли комфортно с ООП? Вперед…

class resnext_block(nn.Module):
    def __init__(self, in_channels, cardinality, bwidth, idt_downsample=None, stride=1):
        super(resnext_block, self).__init__()
        self.expansion = 2
        out_channels = cardinality * bwidth
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, groups=cardinality, stride=stride, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.conv3 = nn.Conv2d(out_channels, out_channels*self.expansion, kernel_size=1, stride=1, padding=0)
        self.bn3 = nn.BatchNorm2d(out_channels*self.expansion)
        self.relu = nn.ReLU()
        self.identity_downsample = idt_downsample

Начиная с определения блока слоев, в первом блоке мы определили последующие компоненты, которые потребуются для продвижения вперед со структурой. Это всего лишь фаза инициализации. Каждый раз, когда мы вызываем класс, первое, что он делает, - это инициализирует эти модули вместе с их определенными спецификациями.

Одна из вещей, которые вы могли бы связать, если вы изучали ResNet и исследовательскую работу по ResNeXt, - это то, что у нас есть мощность и базовая ширина групп, определенных в предыдущей функции. Мы не определили это в ResNet, потому что теперь мы разделяем всю структуру и складываем их бок о бок, а затем все анализируем. Таким образом, мощность будет определять группы в общей архитектуре, а ширина базы будет полностью определять выходные каналы в архитектуре.

Что делать дальше?…

def forward(self, x):
        identity = x
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)
        x = self.conv3(x)
        x = self.bn3(x)
        
        if self.identity_downsample is not None:
            identity = self.identity_downsample(identity)
            
        x += identity
        x = self.relu(x)
        return x

Функция вперед будет вызываться сразу после инициализации. Он будет содержать все методы в упорядоченном виде, как описано в исследовательской статье. Подобие функции вперед вы можете найти в блоге ResNet.

Представляем вам архитектуру ResNeXt…

class ResNeXt(nn.Module):
    def __init__(self, resnet_block, layers, cardinality, bwidth, img_channels, num_classes):
        super(ResNeXt, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(img_channels, 64, kernel_size=7, stride=2, padding=3)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU()
        self.cardinality = cardinality
        self.bwidth = bwidth
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        
        # ResNeXt Layers
        self.layer1 = self._layers(resnext_block, layers[0], stride=1)
        self.layer2 = self._layers(resnext_block, layers[1], stride=2)
        self.layer3 = self._layers(resnext_block, layers[2], stride=2)
        self.layer4 = self._layers(resnext_block, layers[3], stride=2)
        
        self.avgpool = nn.AdaptiveAvgPool2d((1,1))
        self.fc = nn.Linear(self.cardinality * self.bwidth, num_classes)

Мы подошли к формальному определению архитектуры «ResNeXt». Опять же, это этап инициализации. Можно заметить, что существует инициализация некоторого метода «_layers», но при этом не существует каких-либо экземпляров того же самого определенного значения. Это будет определено, когда мы пройдем следующие шаги ...

def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        
        x = self.avgpool(x)
        x = x.reshape(x.shape[0], -1)
        x = self.fc(x)
        return x

А вот и функция «вперед». Официально определена наша функция ResNeXt. Мы структурируем функцию «вперед», как это было определено в статье. Использование начальных этапов слоев Conv, а затем настройка всего слоя в зависимости от требований.

def _layers(self, resnext_block, no_residual_blocks, stride):
        identity_downsample = None
        out_channels = self.cardinality * self.bwidth
        layers = []
        
        if stride != 1 or self.in_channels != out_channels * 2:
            identity_downsample = nn.Sequential(nn.Conv2d(self.in_channels, out_channels*2, kernel_size=1,
                                                          stride=stride),
                                                nn.BatchNorm2d(out_channels*2))
        
        layers.append(resnext_block(self.in_channels,  self.cardinality, self.bwidth, identity_downsample, stride))
        self.in_channels = out_channels * 2
        
        for i in range(no_residual_blocks - 1):
            layers.append(resnext_block(self.in_channels, self.cardinality, self.bwidth))
        
        self.bwidth *= 2
        
        return nn.Sequential(*layers)

Итак, мы все определили, и нам пора определить, верны наши предположения или нет. Мы можем реализовать приведенный ниже блок кода для тестирования архитектуры.

def ResNeXt50(img_channels=3, num_classes=1000, cardinality=32, bwidth=4):
    return ResNeXt(resnext_block, [3,4,6,3],  cardinality, bwidth, img_channels, num_classes)

И готово! Это была тяжелая работа. Спасибо, что придерживались последнего. Это важная архитектура в сфере CNN, и понять ее действительно сложно! Если вам нужна дополнительная помощь, см. Разделы ниже…

Помощь уже в пути!

Если вы все еще чувствуете, что вам нужен весь код, перейдите по этой ссылке здесь.

использованная литература

Пожалуйста, ознакомьтесь с оригинальными работами исследователей.