티스토리 뷰

목차



     

    논문에서 제시한 내용만 보고 구현한 코드

     

    약간의 Naive한 점이 있지만 확실히 ResNet 등의 CNN 모델보다 학습속도가 빠르다!

     

    그럼 바로 시작

     

    1. Setup

     

    기존과 동일하다.

     

    import torch.nn as nn
    import torch
    import torchvision
    import torchvision.transforms as transforms
    from torch.utils.data import DataLoader
    import torch.optim as optim
    import time
    import numpy as np
    
    import random
    import torch.backends.cudnn as cudnn
    
    seed = 2022
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    np.random.seed(seed)
    cudnn.benchmark = False
    cudnn.deterministic = True
    random.seed(seed)

    2. BottleNeck Residual Block

     

    stride 1에 대해서 skip connection을 추가해줬다.

     

    논문에서 multiplier는 6으로 고정시켰으나 상황에따라 조정할 수 있게 parameter로 넣어두었다.

     

    layer마다 BN은 추가하여 실험했기에 내 코드에도 추가해두었다.

     

    class Bottleneck(nn.Module):
        def __init__(self, in_channels, out_channels, multiplier = 6, stride = 2):
            super(Bottleneck, self).__init__()
            self.stride = stride
            mid_ch = in_channels * multiplier
            self.net = nn.Sequential(
                nn.Conv2d(
                    in_channels=in_channels,
                    out_channels=mid_ch,
                    kernel_size=1,
                    stride=1
                ),
                nn.BatchNorm2d(mid_ch),
                nn.ReLU6(),
                nn.Conv2d(
                    in_channels=mid_ch,
                    out_channels=mid_ch,
                    stride=stride,
                    kernel_size=3,
                    padding=1,
                    groups=mid_ch,
                ),
                nn.BatchNorm2d(mid_ch),
                nn.ReLU6(),
                nn.Conv2d(
                    in_channels=mid_ch,
                    out_channels=out_channels,
                    stride=1,
                    kernel_size=1
                ),
                nn.BatchNorm2d(out_channels)
            )
            self.identity = nn.Conv2d(
                in_channels=in_channels,
                out_channels=out_channels,
                kernel_size=1,
                stride=1
            )
            self.relu = nn.ReLU6()
    
        def forward(self, x):
            x2 = self.net(x)
            if self.stride == 1:
                x = self.identity(x)
                x2 = torch.add(x2, x)
            x2 = self.relu(x2)
            return x2

     

     

     

    3. MobileNet v2

     

    Block을 반복해서 넣어야하는 부분이 귀찮아 함수로 만들었다.

     

    def make_layer(n, in_channels, out_channels, stride, multiplier):
        b_l = []
        for i in range(n):
            if i == 0:
                b_l.append(Bottleneck(
                    in_channels=in_channels,
                    out_channels=out_channels,
                    stride=stride,
                    multiplier=1
                ))
            else:
                b_l.append(
                    Bottleneck(
                        in_channels=out_channels,
                        out_channels=out_channels,
                        stride=1,
                        multiplier=multiplier
                    )
                )
        return b_l

     

     

    전체 Architecture에 맞게 구성하면 다음과 같이 코드를 짤 수 있다.

     

    class MobileNetV2(nn.Module):
        def __init__(self, in_channels, num_classes, multiplier=6):
            super(MobileNetV2, self).__init__()
            self.conv1 = nn.Conv2d(
                in_channels=in_channels,
                out_channels=32,
                kernel_size=3,
                padding=1,
                stride=2
            )
            self.relu1 = nn.ReLU6()
            self.batch1 = nn.BatchNorm2d(32)
            self.bottleneck1 = Bottleneck(
                in_channels=32,
                out_channels=16,
                multiplier=1,
                stride=1
            )
            self.bottleneck2 = nn.Sequential(*make_layer(2, 16, 24, 2, multiplier))
            self.bottleneck3 = nn.Sequential(*make_layer(3, 24, 32, 2, multiplier))
            self.bottleneck4 = nn.Sequential(*make_layer(4, 32, 64, 2, multiplier))
            self.bottleneck5 = nn.Sequential(*make_layer(3, 64, 96, 1, multiplier))
            self.bottleneck6 = nn.Sequential(*make_layer(3, 96, 160, 2, multiplier))
            self.bottleneck7 = nn.Sequential(*make_layer(1, 160, 320, 1, multiplier))
    
            self.conv2 = nn.Conv2d(
                in_channels=320,
                out_channels=1280,
                kernel_size=1,
                stride=1
            )
            self.batch2 = nn.BatchNorm2d(1280)
            self.relu2 = nn.ReLU6()
            self.avgpool = nn.AdaptiveAvgPool2d(1)
    
            self.conv3 = nn.Conv2d(
                in_channels=1280,
                out_channels=num_classes,
                kernel_size=1,
                stride=1
            )
            self.soft = nn.Softmax(dim = 1)
    
        def forward(self, x):
            x = self.conv1(x)
            x = self.batch1(x)
            x = self.relu1(x)
            x = self.bottleneck1(x)
            x = self.bottleneck2(x)
            x = self.bottleneck3(x)
            x = self.bottleneck4(x)
            x = self.bottleneck5(x)
            x = self.bottleneck6(x)
            x = self.bottleneck7(x)
    
            x = self.conv2(x)
            x = self.batch2(x)
            x = self.relu2(x)
            x = self.avgpool(x)
            x = self.conv3(x)
            x = x.squeeze()
            x = self.soft(x)
            return x

     

    첫 bottleneck은 multiplier가 1이라 직접 입력해놓았고, nn.Linear가 아닌 1x1 Conv와 squeeze를 활용해 마지막 classification을 위한 tensor를 구성한 후 softmax에 통과시켜 return 시켰다.

     

    모든 Activation Function은 ReLU6로 사용했다.

     

    논문리뷰는 다음 링크를 참고하면 좋을 것이다.

    https://ai.dreamkkt.com/60

     

    [논문 리뷰] MobileNet V2 간단 리뷰!!

    V1에서 달라진 점과 Architecture 중심으로 이야기를 하려고 한다. 0. Abstract MobileNet V2는 Bottleneck 간 skip-connection과 Inverted Residual Structure를 바탕으로 설계되었다. Block 안 expansion layer..

    ai.dreamkkt.com

    참고 논문 - https://arxiv.org/abs/1801.04381v4

     

    MobileNetV2: Inverted Residuals and Linear Bottlenecks

    In this paper we describe a new mobile architecture, MobileNetV2, that improves the state of the art performance of mobile models on multiple tasks and benchmarks as well as across a spectrum of different model sizes. We also describe efficient ways of app

    arxiv.org