Related to: Deep Learning

배경

당시 연구결과에 따르면 네트워크의 깊이는 매우 중요한데, 실제로 많은 모델들이 깊은 네트워크를 통해 좋은 성능을 보였다고 합니다.

Network의 Depth가 중요해지면서, layer를 많이 쌓았을 때 발생하는 Vanishing / Exploding gradient 현상은 큰 방해 요소였습니다.

ResNet이란?

Untitled 49.png

ResNet은 컴퓨터 비전 관련 딥러닝 모델로, 2015년 ImageNet 대회에서 우승한 모델입니다.

ResNet은 각 레이어에서 입력 데이터를 단순화하고 보다 정확하게 분류하기 위해 특별한 구조인 Skip-Connection를 사용합니다. Skip-Connection은 모델이 보다 깊은 레이어를 학습하고 잘 작동할 수 있게 해줍니다.

ResNet의 특징

Untitled 1 38.png

Shortcut connection을 도입하여 기존보다 더 깊게 층을 쌓을 수 있게 되었습니다.

  • 기존 네트워크는 입력 x를 받고 layer를 거쳐 H(x)를 출력하는데, 이는 입력값 x를 타겟값 y로 mapping 하는 함수 H(x)를 얻는 것이 목적입니다.
  • 하지만 ResNet의 Residual Learning은 H(x)가 아닌 출력과 입력의 차인 H(x) - x를 얻도록 목표를 수정했습니다.

Untitled 2 24.png

  • 곱셈 연산에서 덧셈 연산으로 변형되어 몇 개의 layer를 건너뛰는 효과가 있었는데, 이 덕에 forward와 backward path가 단순해지는 효과가 있었으며, gradient의 소멸 문제를 해결 할 수 있었다고 합니다.

ResNet 구현

  • ConvBlock

    class ConvBlock(nn.Module):
        def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1):
            super().__init__()
     
            #\#fill it##
            self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding) # kernel size = ...
            self.batchnorm = nn.BatchNorm2d(out_channels)
     
        def forward(self, x):
            
            #\#fill it##
            x = self.conv(x)
            x = self.batchnorm(x)
            return x
  • ResBlock

    class ResBlock(nn.Module):
        def __init__(self, in_channels, out_channels, pool_stride = 1):
            super().__init__()
     
            self.kernel_size = 3
            self.padding = 1
            self.stride = 1
            self.relu = nn.ReLU()
            self.pool_stride = pool_stride
            self.in_channels = in_channels
            self.out_channels = out_channels
            if(in_channels == out_channels):
              self.skip = torch.nn.Identity()
            else:
              self.skip = torch.nn.Conv2d(in_channels=in_channels, 
                                   out_channels=out_channels, 
                                   kernel_size=1, 
                                   stride=self.pool_stride)
              
            self.conv1 = ConvBlock(in_channels=in_channels, 
                                   out_channels = out_channels, 
                                   kernel_size=self.kernel_size, 
                                   stride=self.stride, 
                                   padding=self.padding)
            
            self.conv2 = ConvBlock(in_channels=out_channels, 
                                   out_channels = out_channels, 
                                   kernel_size=self.kernel_size, 
                                   stride=self.pool_stride, 
                                   padding=self.padding)
     
        def forward(self, x):
            
            #\#fill##
            y = self.conv1(x)
            y = self.relu(y)
            y = self.conv2(y)
            return  self.relu(y + self.skip(x))
  • Resnet Model

    class ResNet(nn.Module):
        def __init__(self, in_channels, out_channels, nker=64, nblk=[3,4,6,3]):
            super(ResNet, self).__init__()
     
            self.enc = ConvBlock(in_channels, nker, kernel_size=7, stride=2, padding=1)
            self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
            self.average_pool = nn.AvgPool2d(kernel_size=5, stride=1)
     
     
            #\#fill##
            self.relu = nn.ReLU()
            layers = []
            for j, b in enumerate(nblk):
              __out_chennel = 64 * (j+1)
              for i in range(b):
                if(j != 0 and i == 0):
                  __pool_stride = 2
                  __in_chennel = 64 * j
     
                else:
                  __pool_stride = 1
                  __in_chennel = __out_chennel
                print(__in_chennel, __out_chennel, __pool_stride)
                layers.append(ResBlock(__in_chennel, __out_chennel, __pool_stride))
            print('complete auto layer making')
            self.conv = nn.Sequential(*layers)
     
            self.fc = nn.Linear(nker*2*2, 10)
     
        def forward(self, x):
            x = self.enc(x)
            x = self.max_pool(x)
     
            #\#fill##
            x = self.conv(x)
            x = self.average_pool(x)
            x = x.view(x.shape[0], -1)
            out = self.fc(x)
     
            return out

참조

https://arxiv.org/abs/1512.03385

https://phil-baek.tistory.com/entry/ResNet-Deep-Residual-Learning-for-Image-Recognition-논문-리뷰

Week 4