728x90
반응형

 

 

ResNet

이번에 볼 Backbone은 ResNet입니다. 원 논문은 "Deep Residual Learning for Image Recognition" 이고, 벌써 인용 수가 160,317회나 되네요. 저자는 Kaiming He 입니다. 굉장히 유명하신 분이죠. 무려 Microsoft Research 의 결과물이네요. 

 

 

 

ResNet의 구조 

단순 convolutional neural network에서 Layer를 무작정 늘렸을 때 성능이 오히려 떨어진다고 합니다. 그래서 나온 개념이 ResNet의 핵심 아이디어인 Residual block 입니다. 이는 H(x)를 기존의 네트워크라고 할 때, H(x)를 복잡한 함수에 근사시키는 것 보다 F(x) := H(x) - x일 때, H(x) = F(x) + x이고, F(x) + x를 근사시키는 것이 더 쉬울 것이라는 아이디어에서 출발합니다. 원래 Output에서 자기자신을 빼는 것이 F(x)의 정의이므로, Residual learning이라는 이름을 갖게 됩니다. 또한, x가 F(x)를 통과하고 나서 다시 x를 더해주기 때문에 이를 Skip Connection 이라고도 부릅니다. 


 

 

layer의 개수에 따른 구조는 아래와 같습니다.

 

 

 

 

 Plain neural network와 비교했을 때 Layer를 상당히 깊게 쌓았음에도 적게 쌓았을 때보다 에러가 낮으며 성능 저하가 발생하지 않았다고 합니다.  ResNet-18,34는 아래 그림에서 왼쪽 residual block을 사용하고, ResNet-50 부터는 오른쪽 BottleNeck을 사용한다고 합니다. ResNet-50 이상의 깊은 모델에서는 Inception에서와 마찬가지로, 연산상의 이점을 위해 "bottleneck" layer (1x1 convolution)을 이용했습니다. (파라미터 수가 감소 효과)

 

 

VGG-19 모델, 34-layer plain, 34-layer residual 을 나타낸 그림은 아래와 같습니다. 

 

 

 

 

 

PyTorch Implementation

from torchvision import models
from torchinfo import summary

class BasicBlock(nn.Module):
    expansion = 1
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()

        # BatchNorm에 bias가 포함되어 있으므로, conv2d는 bias=False로 설정합니다.
        self.residual_function = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(),
            nn.Conv2d(out_channels, out_channels * BasicBlock.expansion, kernel_size=3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(out_channels * BasicBlock.expansion),
        )

        # identity mapping, input과 output의 feature map size, filter 수가 동일한 경우 사용.
        self.shortcut = nn.Sequential()

        self.relu = nn.ReLU()

        # projection mapping using 1x1conv
        if stride != 1 or in_channels != BasicBlock.expansion * out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels * BasicBlock.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels * BasicBlock.expansion)
            )

    def forward(self, x):
        x = self.residual_function(x) + self.shortcut(x)
        x = self.relu(x)
        return x


class BottleNeck(nn.Module):
    expansion = 4
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()

        self.residual_function = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(),
            nn.Conv2d(out_channels, out_channels * BottleNeck.expansion, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(out_channels * BottleNeck.expansion),
        )

        self.shortcut = nn.Sequential()

        self.relu = nn.ReLU()

        if stride != 1 or in_channels != out_channels * BottleNeck.expansion:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels*BottleNeck.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels*BottleNeck.expansion)
            )
            
    def forward(self, x):
        x = self.residual_function(x) + self.shortcut(x)
        x = self.relu(x)
        return x

    
class ResNet(nn.Module):
    def __init__(self, block, num_block, num_classes=10, init_weights=True):
        super().__init__()

        self.in_channels=64

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

        self.conv2_x = self._make_layer(block, 64, num_block[0], 1)
        self.conv3_x = self._make_layer(block, 128, num_block[1], 2)
        self.conv4_x = self._make_layer(block, 256, num_block[2], 2)
        self.conv5_x = self._make_layer(block, 512, num_block[3], 2)

        self.avg_pool = nn.AdaptiveAvgPool2d((1,1))
        self.fc = nn.Linear(512 * block.expansion, num_classes)

        # weights inittialization
        if init_weights:
            self._initialize_weights()

    def _make_layer(self, block, out_channels, num_blocks, stride):
        strides = [stride] + [1] * (num_blocks - 1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels * block.expansion

        return nn.Sequential(*layers)

    def forward(self,x):
        output = self.conv1(x)
        output = self.conv2_x(output)
        x = self.conv3_x(output)
        x = self.conv4_x(x)
        x = self.conv5_x(x)
        x = self.avg_pool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

    
    # define weight initialization function
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)
                
def resnet18():
    return ResNet(BasicBlock, [2,2,2,2])

def resnet34():
    return ResNet(BasicBlock, [3, 4, 6, 3])

def resnet50():
    return ResNet(BottleNeck, [3,4,6,3])

def resnet101():
    return ResNet(BottleNeck, [3, 4, 23, 3])

def resnet152():
    return ResNet(BottleNeck, [3, 8, 36, 3])

model = resnet50()
summary(model, (1, 3, 224, 224))

 

 

 

 

Experiments

ResNet 논문의 실험 결과는 다음과 같습니다. ResNet에서 Layer가 깊을 수록 더 좋은 성능을 보여준다는 것을 나타냅니다. 

 

 

 

 

 

 

 

 

참고자료 1 : https://wikidocs.net/137252

 

3) ResNet, ResNet의 확장(레이어 152개 이하)

## ResNet ![](https://wikidocs.net/images/page/137252/IMG_62347E9C6248-1.jpeg) CNN을 연구하면서 기존 모델들…

wikidocs.net

참고자료 2 : https://velog.io/@lighthouse97/ResNet%EC%9D%98-%EC%9D%B4%ED%95%B4

 

ResNet의 이해

참고1 참고2 참고3 1. 동기(Motivation) 2015년 ILSVRC(ImageNet Large Scale Visual Recognition Challenge)에서 우승을 차지한 ResNet에 대해서 소개하려고 한다. ResNet은 마이크로소프트에서 개발한 알

velog.io

참고자료 3 : https://deep-learning-study.tistory.com/473

 

[논문 읽기] ResNet(2015) 리뷰

이번에 읽어볼 논문은 ResNet, 'Deep Residual Learning for Image Recognition' 입니다. ResNet은 residual repesentation 함수를 학습함으로써 신경망이 152 layer까지 가질 수 있습니다. ResNet은 이전 layer의 입력을 다음 l

deep-learning-study.tistory.com

 

728x90
반응형