1 분 소요

본 글은 Pytorch 기반 ResNeXt50 구조 처음부터 구현해보는 내용입니다.
하나하나 자세히 분해해봅시다.

논문 : Aggregated Residual Transformations for Deep Neural Networks
코드 : pytorch-cifar100/models/resnext.py - weiaicunzai
블로그 글 코드 : poeun - resnext.ipynb
파이토치 torchvision/models/resnet.py : resnet.py

[논문 리뷰] Aggregated Residual Transformations for Deep Neural Networks 글을 먼저 올렸다. 이를 바탕으로 구현해본다.
설명은 코드 부분에 자세히 적었다.
수정 필요



ResNextBottleNeck 클래스 정의하기

image

cardinality = 32 # path를 결정하는 변환 그룹의 크기
depth = 4 # 각 그룹 당 가지고 있는 채널 수
base_width = 64 # 기초 채널 수

# 그림 3의 (c)의 grouped convolution 레이어는 입출력 채널이 4차원인 32개의 컨볼루션 그룹(=cardinality)을 형성한다.
# grouped convolution은 그 그룹을 레이어의 출력으로 concatenate한다.
class ResNextBottleNeck(nn.Module):
    # __init__는 클래스 내의 생성자라 불리고 초기화를 위한 함수이다.
    # self는 인스턴스 자신이다.
    # 인자는 in_channels, out_channels, stride를 받는다.
    def __init__(self, in_channels, out_channels, stride):
         # super(모델명, self).__init__() 형태로 호출
        super().__init__()

        groups = cardinality # 특징 맵(feature map)이 분할된 그룹 수

        num_depth = int(depth * out_channels / base_width) # 그룹당 채널 수(depth per group)
        self.split_transform = nn.Sequential(
            nn.Conv2d(in_channels, groups * num_depth, kernel_size=1, groups=groups, bias=False),
            nn.BatchNorm2d(groups * num_depth),
            nn.ReLU(),
            nn.Conv2d(groups * num_depth, groups * num_depth, kernel_size=3, stride=stride, groups=groups, padding=1, bias=False),
            nn.BatchNorm2d(groups * num_depth),
            nn.ReLU(),
            nn.Conv2d(groups * num_depth, out_channels * 4, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channels * 4),
        )

        self.shortcut = nn.Sequential()

        if stride != 1 or in_channels != out_channels * 4:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels * 4, stride=stride, kernel_size=1, bias=False),
                nn.BatchNorm2d(out_channels * 4)
            )

    def forward(self, x):
        return F.relu(self.split_transform(x) + self.shortcut(x))



ResNeXt 클래스 정의하기

class ResNext(nn.Module):

    def __init__(self, block, num_blocks, class_names=1000):
        super().__init__()
        self.in_channels = 64

        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, 3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU()
        )

        self.conv2 = self._make_layer(block, num_blocks[0], 64, 1)
        self.conv3 = self._make_layer(block, num_blocks[1], 128, 2)
        self.conv4 = self._make_layer(block, num_blocks[2], 256, 2)
        self.conv5 = self._make_layer(block, num_blocks[3], 512, 2)
        self.avg = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * 4, 100)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = self.avg(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

    # _make_layer에서 resnext block  생성
    # block: 블록 유형(default resnext bottleneck c)
    # num_block: 레이어당 블록수
    # out_channels: 블록당 출력 채널 수
    # stride: 블록 stride
    # return : resnext 레이어
    def _make_layer(self, block, num_block, out_channels, stride):
        strides = [stride] + [1] * (num_block - 1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels * 4

        return nn.Sequential(*layers)



ResNeXt50, 101, 152 정의하기

# Resnet과 형태는 똑같다.
def resnext50():
    return ResNext(ResNextBottleNeck, [3, 4, 6, 3])

def resnext101():
    return ResNext(ResNextBottleNeck, [3, 4, 23, 3])

def resnext152():
    return ResNext(ResNextBottleNeck, [3, 4, 36, 3])





댓글남기기