[16′ CVPR] Deep Residual Learning for Image Recognition (ResNet)

This entry is part 6 of 22 in the series Vision Model

1. 들어가며

딥러닝의 세계에서 모델의 깊이는 중요한 요소 중 하나입니다. 깊은 모델은 복잡한 패턴과 특징을 학습할 수 있지만, 깊이를 늘릴수록 Gradient Vanishing 문제와 같은 어려움에 직면하게 됩니다. 이러한 문제를 극복하기 위해 등장한 것이 바로 ResNet입니다. 이번 글에서는 모델이 깊어질 때 발생하는 문제와 그 해결 방법, 그리고 ResNet의 핵심 구조와 원리에 대해 상세히 알아보겠습니다. ResNet은 발표된 지 오래되었지만 지금도 대표적인 CNN으로 여러 연구에서 Baseline 모델로 사용되고 있으며 ResNet을 시작으로 딥러닝 모델이 급격하게 발달했다는 점을 강조하고 싶습니다.

첫 번째 섹션에서는 모델을 깊게 구성하려는 이유와 깊은 모델에서 발생하는 Gradient Vanishing 문제에 대해 수식을 통해 자세히 설명합니다. 깊은 모델은 더 복잡한 특징을 학습할 수 있는 능력을 가지고 있지만, 그 깊이가 증가함에 따라 Gradient가 소실되는 문제가 발생합니다. 이 문제는 모델의 학습을 어렵게 만들며, 이를 해결하지 않으면 깊은 모델의 이점을 제대로 활용할 수 없습니다. 이를 통해 깊은 모델의 문제점을 명확하게 이해할 수 있습니다.

다음 섹션에서는 ResNet의 핵심인 Residual Path에 대해 알아보겠습니다. 지름길과 같은 역할을 하는 Residual Path가 Gradient Vanishing 문제를 어떻게 완화하는지, 수식을 미분하며 함께 알아보겠습니다. 또한, 깊은 ResNet 모델을 위한 Bottleneck 구조에 대해서도 상세히 설명합니다. Bottleneck 구조는 깊은 모델에서의 연산량을 줄이면서도 성능을 유지할 수 있는 중요한 구조입니다.

이어서, 파이썬을 사용하여 ResNet을 직접 구현하는 과정을 소개합니다. 이를 통해 ResNet의 구조와 원리를 더욱 명확하게 이해할 수 있습니다. 코드를 통해 실제로 ResNet이 어떻게 작동하는지를 볼 수 있으며, 이를 통해 이론과 실제의 연결고리를 더욱 강화할 수 있습니다.

실험 결과 섹션에서는 이미지넷 데이터셋에서의 ResNet의 성능을 살펴봅니다. 이를 통해 ResNet의 효과를 직접 확인할 수 있습니다. 이미지넷은 대규모 이미지 데이터셋으로, 이 데이터셋에서의 성능은 모델의 효과를 검증하는 데 중요한 지표입니다.

마지막으로, ResNet의 중요성과 의의를 다룹니다. ResNet은 깊은 네트워크의 학습 문제를 해결하며, 블록 형태의 아키텍처를 통해 복잡한 모델 구조를 쉽게 구현할 수 있게 만들었습니다. 이러한 점들은 ResNet이 현대 딥러닝 분야에서 중요한 위치를 차지하게 만든 요인입니다. 이번 글을 통해 ResNet에 대한 깊은 이해를 갖게 되길 바랍니다.

2. 문제 상황

2-1. 기존 방법의 문제점

먼저 2016년 당시 딥러닝 네트워크들이 처한 문제 상황을 살펴보겠습니다.

AlexNet의 성공 이후 VGGNet, AlexNet 등의 네트워크들이 제안되며 네트워크의 깊이가 깊어질수록 성능이 좋아진다는 생각이 널리 퍼졌습니다. 이는 언뜻 생각해 보면 그럴듯해 보이는데요. 모델의 깊이가 깊어지면 그만큼 학습 가능한 가중치가 많아질 것이고, 그만큼 모델의 표현력이 좋아질 테니 학습 데이터의 더 많은 특성을 학습할 수 있을 것이기 때문이죠.

그런데 ResNet 저자들은 이 부분에 의문을 제기합니다. 과연 모델의 깊이가 깊어질수록 정말 성능은 점점 좋아질까요? 만약 그렇다면 단순히 깊게만 만들면 최고 성능을 낼 수 있으니 딥러닝을 정복한 게 될 텐데요. 하지만 역시 문제는 그렇게 쉽지 않았습니다.

1.모델의 깊이에 따른 학습 데이터셋 성능
그림1. 모델의 깊이에 따른 학습 데이터셋 성능

위 그림은 동일한 구성의 20 layer 모델과 56 layer 모델의 성능을 비교한 그래프입니다. 왼쪽은 학습량에 따른 학습 데이터 성능을, 오른쪽은 테스트 데이터 성능을 나타낸 것이죠. 만약 모델의 깊이가 깊어질수록 성능이 좋아진다면, 학습 데이터와 테스트 데이터 모두에서 더 깊은 모델의 성능이 얕은 모델의 성능보다 좋아야겠죠. 하지만 결과를 보면 정반대 경향을 보이고 있습니다. 학습 데이터와 테스트 데이터 모두에서 깊은 모델의 성능이 얕은 모델의 성능보다 안 좋죠. 이는 Overfitting 문제라고 할 수도 없습니다. Overfitting 문제였다면 학습 데이터의 성능은 더 좋지만 테스트 데이터에서의 성능이 안 좋았어야 하죠. 하지만 지금은 학습 데이터와 테스트 데이터 모두에서 성능이 안 좋은 모습을 보이고 있습니다. 이건 그냥 ‘학습이 충분히 안 됐다.’라고 해석할 수밖에 없는 상황인 거죠.

2-2. 깊은 모델의 문제점

왜 학습이 충분히 안 됐을까요? 학습이 충분히 안되었다는 건 모델의 표현 능력은 충분한데, 가중치들이 적절한 위치로 이동하지 못했다는 의미인데요. 그럼 자연스럽게 ‘Loss로부터의 Gradient가 모델의 가중치들에게 전달이 안되었나?’라는 의문이 생깁니다.

이 의문을 풀기 위해 간단한 CNN 모델에서의 Gradient 전파 과정을 생각해 보죠.

2.세개 층의 가상 CNN
그림2. 세개 층의 가상 CNN

위 그림은 세 개 층으로 이루어진 CNN을 나타낸 그림입니다. 세 개의 convolution 연산은 각각 w1, w2, w2로 이루어진 f1, f2, f3 함수라고 표현하겠습니다. 이때 가장 아래에 있는 f1의 가중치 w1이 받는 Loss로부터의 Gradient는 다음과 같습니다.

3.가상 CNN구조에서의 Gradient
그림3. 가상 CNN구조에서의 Gradient

위 수식을 보면 Chain Rule에 따라 Loss로부터의 w1에 대한 Gradient는 그 전의 모든 단계들로부터의 Gradient를 순서대로 곱해준 값임을 알 수 있습니다. 그럼 모델의 깊이가 깊어질수록 아래층에 있는 가중치들은 Gradient를 구하기 위해 더 많은 앞 단계의 Gradient를 곱해줘야 한다는 사실을 알 수 있습니다. 하지만 이 자체가 문제라고는 말할 수 없습니다. 값을 여러 개 곱하더라도 모든 값들이 크기만 크면 최종 결괏값도 크게 유지될 테니까요.

문제는 저 연산들 사이에 Activation Function이 포함될 때 발생합니다. 대표적인 Activation Function인 Sigmoid와 ReLU를 생각해 볼까요? Sigmoid는 입력값이 0에서 멀어질수록 기울기가 급격하게 줄어들어 0에 수렴하는 특성을 갖습니다. 심지어 ReLU는 입력값이 0보다 작은 구간에서는 아얘 기울기가 0이죠. 이 말은 아래층에 있는 가중치의 Gradient는 중간중간 Activiation Function으로부터 나오는 Gradient인 0을 곱해서 구해주는 경우가 많아질 거라는 뜻입니다. 따라서 아래층에 있는 가중치의 Gradient일수록 0이 될 확률이 크겠죠. 그 말은 위층의 가중치는 쉽게 학습되지만, 아래층에 있는 가중치일수록 Gradient가 전달되지 않아 학습이 어려워진다는 말입니다.

3. 제안 방법

이번 챕터에서는 위에서 설명한 당시의 문제를 풀기 위한 ResNet의 아이디어들을 살펴보겠습니다.

3-1. Residual Learning

ResNet 아이디어의 핵심은 Residual Learning이라고 할 수 있습니다. Residual이란 ‘나머지’라는 의미인데요, 그럼 Residual Learning 이란 나머지 (잔차)를 학습한다는 의미입니다. 사실 Residual Learning의 핵심은 ‘잔차’ 보다는 ‘지름길’입니다. 위에서 문제 상황을 설명하면서 문제의 핵심은 Gradient가 깊은 위치의 가중치까지 닿지 않는 것이라고 말씀드렸습니다. ResNet의 아이디어는 ‘그럼 Gradient가 잘 도달할 수 있게 지름길을 뚫어주자’라고 요약할 수 있습니다.

3-1-1. Residual ㅣㄷㅁ의 의미

그럼 ‘나머지’는 어디서 등장하는 걸까요? 이때 뚫어주는 Gradient 지름길의 내용이 ‘나머지’인 겁니다.

4.Residual Learning을 적용한 가상 CNN
그림4. Residual Learning을 적용한 가상 CNN

위 그림은 문제 상황에서 사용했던 연산 예시에 Residual Learning 개념을 적용하여 표현한 그림입니다. 기존과 달리 y1과 y3 사이에 지름길 (shortcut)이 생긴 모습을 볼 수 있습니다. 그리고 이 지름길은 y3에 y1 정보를 그대로 전달하고 있죠.

y3 입장에서 한번 생각해 볼게요. 이렇게 되면 y3 입장에서는 무엇이 달라진 걸까요? 원래 y3가 학습해야 하는 내용이 있었을 텐데요, 이 정보는 y1 정보에 무언가가 더 연산되어 추가된 내용이었을 겁니다. 그런데 구조가 바뀌면서 y1 정보는 그대로 전달이 된다고 하는 거죠. 그럼 y3 입장에서는 y1 정보를 제외한 나머지 정보만 학습하면 되는 거죠. 따라서 이를 나머지를 학습하는 Residual Learning이라고 부릅니다.

3-1-2. Residual Learning의 효과

그런데 이렇게 지름길을 만들어준 이유는 깊은 위치의 가중치까지 Gradient가 잘 전달될 수 있게 하기 위함이었잖아요? 새로 생긴 지름길은 Gradient의 측면에서 어떤 영향을 줄까요?

5.가상 CNN에서의 Gradient 전파 경로
그림5. 가상 CNN에서의 Gradient 전파 경로

위 그림은 순방향 전파가 아닌, 역방향 전파 시 (Backpropagation) Gradient의 전파 경로를 나타낸 그림입니다. 파란색 선은 기존의 Gradient 전파 경로를 표현하고 있습니다. 기존 연결은 그대로 유지했기 때문에 기존의 Gradient 전파 경로도 그대로 유지될 겁니다.
반면 초록색은 새로 생긴 지름길로 인해 새로 생긴 Gradient의 전파 경로입니다. Loss로부터의 Gradient가 y2를 건너뛰어 바로 y1으로 전달되는 모습을 볼 수 있습니다.

이제 w1 입장에서 Loss로부터의 Gradient를 한번 계산해 보겠습니다.

6.지름길을 적용한 Gradient 수식
그림6. 지름길을 적용한 Gradient 수식

w1에 대한 Loss의 Gradient는 위의 식과 같이 계산할 수 있습니다. 파란색은 기존 경로로부터의 Gradient를, 초록색은 새로 생긴 지름길로부터의 Gradient를 표현한 그림입니다. 파란색은 기존의 Gradient랑 변함이 없어요. 따라서 이 부분은 여전히 0이 될 위험이 상당히 크다고 볼 수 있습니다.

반면 지름길로부터의 Gradient를 볼까요? 지름길로부터의 Gradient는 기존 Gradient와 달리 한 단계를 건너뛰면서 곱해야 할 Gradient 요소들의 개수가 줄어든 모습을 볼 수 있습니다. 우선 이 자체도 Gradient 값을 유지하는데 도움을 주겠네요. 또한 y1에 대한 y3의 Gradient는 위 그림의 수식과 같이 전개되어 1에 가까운 값을 가질 겁니다. 따라서 최종 Gradient값에 큰 영향을 주지 않겠네요. 결론적으로 기존 Gradient는 4개의 Gradient 요소값을 곱해서 구했지만, 지름길로부터의 Gradient는 2개의 Gradient 요소값만 곱해서 구하게 되었습니다. 결국 지름길로부터의 Gradient는 0이 되지 않고 살아남을 확률이 커졌다고 말할 수 있겠죠. 따라서 Residual Learning 방식으로 지름길을 연결해 주면 네트워크의 깊이를 깊게 만들어주어도 Gradient가 더 잘 전파되어 잘 학습할 수 있게 됩니다.

3-1-3. Residual Learning 수식

위에서 설명한 지름길의 내용을 논문에서는 다음과 같이 표현합니다.

7.Residual Learning 논문 표현
그림7. Residual Learning 논문 표현

우측의 x라는 정보가 그대로 (identity) 다음 Layer를 건너뛰어 전달되는 모습을 볼 수 있습니다. 그 덕분에 두 번의  weight layer와 한 번의 ReLU 연산을 건너뛰어 전파될 수 있었습니다. 최종적으로 마지막 노드에서 전달되는 정보는 다음과 같은 수식으로 표현할 수 있습니다.

8.Residual Learning 수식
그림8. Residual Learning 수식

기존의 정보 전달 방식인 F 외에 원래 정보를 그대로 더해서 전달해 주는 모습을 볼 수 있습니다. 이때 weight layer에서의 연산 결과 이전 정보와 이후 정보의 차원이 달라지는 경우도 있을 텐데요. 이러면 차원이 달라서 단순 더하기 연산이 불가능해집니다. 이런 경우에는 다음과 같이 이전 정보 x에 linear projection을 통해 다음 정보와 차원을 맞춰 전달해 줍니다.

9.Residual Learning + linear projection 수식
그림9. Residual Learning + linear projection 수식

W를 x에 곱해 차원을 맞춰주는 모습을 볼 수 있습니다.

3-2. ResNet Architecture

이제 Residual Learning의 핵심 개념은 이해했으니, Residual Learning을 적용하여 만든 ResNet Architecture를 살펴보겠습니다.

10.ResNet Architecture
그림10. ResNet Architecture

위 그림은 VGGNet과 비슷한 콘셉트로 만들었지만 Residual Learning 개념이 적용되지 않은 Plain 모델, 그리고 Residual Learning 개념을 적용한 모델의 Architecture를 표현한 그림입니다.

우선 Plain 모델을 살펴보겠습니다. VGGNet의 철학을 이어받아 모든 Convolution 연산이 3×3 필터로 구성되어 있는 모습을 볼 수 있습니다. 또한 색상은 각각의 Layer를 나타내고 있는데요. Layer의 깊이가 깊어질수록 Pooling을 통해 Feature Map의 사이즈는 절반으로 줄어들 겁니다. 그 대신 Channel 사이즈를 두배로 늘려주어 Layer별 연산량을 유지해 주는 모습을 볼 수 있습니다.

이제 Residual 모델을 살펴보겠습니다. Plain 모델과 동일한데 중간중간 Residual Shortcut이 들어가 있는 모습을 볼 수 있습니다. 이렇게 VGGNet의 핵심 철학을 계승하며 Residual Learning이라는 아이디어를 추가한 모델이 바로 ResNet입니다.

3-3. BottleNeck Block

3-3-1. 연산량의 문제

지금까지 Residual Learning의 개념과 이러한 아이디어를 구현한 모델인 ResNet Architecture를 살펴봤습니다. 그럼 자연스럽게 이런 의문이 생기는데요. 이제 이 구조로 엄청 깊은 네트워크를 만들어주면 잘 동작할까요? 일단, 지금까지의 논리 전개를 봤을 때 성능상으로 문제는 없을 것 같습니다. 그럼 연산량의 측면에서는 어떨까요? Residual Learning 개념을 적용하여 엄청 깊은 네트워크를 만들었을 때 연산량에는 문제가 없을까요?

생각해 보면, 현재의 논리대로라면 깊이가 깊어질수록 네트워크의 연산 요구량은 기하급수적으로 증가할 겁니다. 문제는 Layer가 깊어질수록 두 배씩 커지는 Channel 수입니다. ResNet Architecture를 설명하면서 Layer가 한 층 씩 깊어질수록 Feature Map의 사이즈는 절반씩 줄어드니, Channel 수는 두 배씩 늘려주었다고 말씀드렸습니다. 그렇게 되면 깊은 Layer에서의 3×3 Convolution 연산량은 기하급수적으로 커지게 됩니다. 왜냐하면 Convolution 필터 사이즈는 3×3 x Channel인데, Channel 수가 2배씩 계속 커지고 있으니까요. 이렇게 되면 성능은 좋을지언정 연산량이 무지막지하게 커져 현실적으로 쓸모없는 모델이 되어버립니다. 돈을 많이 안 쓰고 좋은 성능을 내는 게 어렵지, 돈을 무제한으로 쓸 수 있으면 좋은 성능 내는 건 어렵지 않은 것과 마찬가지죠.

3-3-2. 해결 방법

이러한 연산량의 문제를 해결하기 위해 ResNet에서는 깊은 모델에서 사용하는 Bottleneck Block이라는 아이디어를 제안합니다. Bottleneck Block을 기존의 Residual Block과 비교해 보면 쉽게 이해할 수 있습니다.

11.Residual Block과 Bottleneck Block 비교
그림11. Residual Block과 Bottleneck Block 비교

위 그림은 Residual Block과 Bottleneck Block을 비교한 그림입니다. Residual Block은 우리가 아는 대로 3×3 Convolution 연산과 이들을 건너뛰는 지름길로 구성된 모습을 볼 수 있습니다. 반면 Bottleneck Block은 한 번의 3×3 Convolution 연산과 두 개의 1×1 Convolution 연산으로 구성된 모습이죠. 달라진 거라고는 1×1 Convolution이 들어갔다는 건데요. 이 1×1 Convolution의 역할은 무엇일까요?

위에서 설명하면서 문제의 원인은 Channel수가 너무 커진 것이라고 말씀드렸는데요. 따라서 1×1 Convolution을 사용해서 3×3 Convolution 전에 Channel 수를 줄여주는 겁니다. 그리고 3×3 Convolution 연산되어 나온 Feature map은 다시 1×1 Convolution을 사용해서 Channel 수를 복원해 주는 거죠. 이를 Channel수의 관점에서 보면 이렇습니다.

12.channel수 관점에서 본 Bottleneck Block
그림12. channel수 관점에서 본 Bottleneck Block

1×1 Convolution을 거치면서 연산량을 줄이기 위해 Channel 수를 줄여주고요, 3×3 Convolution을 거친 뒤에는 다시 1×1 Convolution을 사용해서 Channel 수를 원복 해주는 거죠. 지금 그림을 보면 3×3 Convolution때 Channel수가 줄어들면서 병목현상 (Bottleneck)이 생긴 모습을 볼 수 있는데요. 따라서 이러한 구조를 Bottleneck Block이라고 부릅니다. 지금까지 설명한 Bottleneck Block을 ResNet 논문에서는 다음과 같이 표현하고 있습니다.

13.Bottleneck Blcok의 논문 표현
그림13. Bottleneck Blcok의 논문 표현

기존 Residual Block과 달리 3×3 Convolution의 앞 뒤로 1×1 Convolution이 둘러싸고 있는 모습을 볼 수 있습니다.

4. 파이썬 구현

이번 챕터에서는 파이썬을 사용하여 ResNet을 직접 구현해 봅니다. 이 과정을 통해 위에서 살펴본 내용들을 정확히 이해했는지 되짚어 보겠습니다.

4-1. Import Module

먼저 필요한 Module들을 Import 해줍니다.

import torch
import torch.nn as nn

4-2. Residual Block Class 정의하기

다음으로 ResNet의 핵심 구성 모듈인 Residual Block Class를 정의해줍니다. Residual Block의 가장 핵심은 지름길인데요. 이 부분을 self.shortcut에서 정의해주고 있습니다. forward 함수를 보면 out에 shortcut 출력값을 그대로 더해주는 모습을 볼 수 있습니다.

# Residual Block
class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride),
                nn.BatchNorm2d(out_channels)
            )
            
    def forward(self, x):
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = self.relu(out)
        return out

4-3. ResNet Class 정의하기

다음은 Residual Block을 모아 ResNet Class를 정의해주겠습니다.

# ResNet
class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        
        self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
        self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
        
        self.fc = nn.Linear(512, num_classes)
        
    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
        return nn.Sequential(*layers)

4-4. ResNet 사용하기

이렇게 정의한 ResNet은 다음과 같은 방법으로 사용할 수 있습니다.

# Create ResNet model
model = ResNet(ResidualBlock, [2, 2, 2, 2])

5. 효과

지금까지 ResNet이 해결하고자 했던 문제와, 이를 해결하기 위한 아이디어를 살펴봤습니다. 이번에는 실험 결과를 통해 ResNet이 실제 어떠한 효과가 있는지 살펴보겠습니다.

5-1. 학습 데이터셋에서의 성능

ResNet 이야기를 시작하면서 깊은 모델에서는 학습 데이터셋에 대한 성능이 오히려 중간 깊이의 모델보다 더 낮음을 지적했는데요. ResNet 구조를 적용하면서 학습에 따른 학습 데이터셋에서의 성능은 어떻게 변했을까요?

14.학습 데이터셋에 대한 Plain 모델과 ResNet 모델 성능 비교
그림14. 학습 데이터셋에 대한 Plain 모델과 ResNet 모델 성능 비교

위 그림은 Plain 모델과 ResNet 모델의 서로 다른 깊이 모델에서의 학습 데이터셋에 대한 성능 그래프를 나타낸 그림입니다. 왼쪽 그래프는 18층, 34층의 Plain 모델을, 오른쪽 그래프는 ResNet 모델을 나타내고 있습니다.

먼저 왼쪽을 보겠습니다. Plain 모델에서는 앞서 살펴본 바와 같이 34층 모델이 18층 모델보다 오히려 학습 데이터셋에 대한 에러가 높은 모습을 보입니다. 즉 모델의 깊이는 깊어졌지만 깊은 위치의 가중치들의 학습이 제대로 이루어지지 않는 모습을 볼 수 있습니다.

반면 오른쪽의 ResNet 모델의 결과를 보겠습니다. ResNet 모델에서는 34층 모델에서의 에러가 18층 모델에서의 에러보다 더 작은 모습을 볼 수 있습니다. 이는 ‘ResNet 구조를 적용하면 모델의 깊이를 더 깊게 만들어주어도 깊은 층의 가중치까지 잘 학습할 수 있어 학습 능력이 향상된다.’라고 해석할 수 있습니다.

5-2. 검증 데이터셋에서의 성능

위에서 살펴본 건 학습 데이터셋에서의 성능입니다. 즉 진짜 성능이라기보다는 모델의 학습 능력이라고 할 수 있죠. 우리한테 중요한 건 검증 데이터셋에서의 성능입니다. 이번에는 검증 데이터셋에서의 성능을 살펴보겠습니다.

15.Validation 데이터셋 성능 비교
그림15. ResNet – Validation 데이터셋 성능 비교

위 그림은 마찬가지로 Plain 모델과 ResNet 모델의 깊이에 따른 ImageNet Validation 데이터셋 에러를 정리한 표입니다.

먼저 Plain 모델을 보면 학습 데이터셋에서와 마찬가지로 더 깊은 모델의 에러가 더 큰 모습을 볼 수 있습니다. 반면 ResNet 모델에서는 더 깊은 모델의 에러가 더 작아진 모습을 볼 수 있죠. 또한 18층 모델과 34층 모델 모두가 ResNet 구조를 적용하면서 Plain 모델보다 성능이 개선된 모습을 볼 수 있습니다. 이는 ‘ResNet 구조를 적용하면 성능이 향상되며, 더 깊은 구조의 네트워크도 효과적으로 학습할 수 있다.’라고 해석할 수 있습니다.

5-3. Bottleneck Block의 효과

34층까지는 일반적인 Residual Block을 적용한 구조인데요. Bottleneck Block을 적용하면 성능은 어떻게 될까요?

16.Bottleneck Block 효과
그림16. Bottleneck Block 효과

위 그림은 다양한 모델과 다양한 깊이의 ResNet 모델의 ImageNet Validation 성능을 나타낸 표입니다. ResNet-50, ResNet-101, ResNet-152는 Bottleneck Block을 적용한 모델들입니다. 깊이가 깊어질수록 ImageNet 성능이 향상되는 모습을 볼 수 있습니다. 이는 ‘Bottleneck Block 구조를 적용하여 매우 깊은 모델도 효과적으로 학습할 수 있게 되었다.’라고 해석할 수 있습니다.

5-4. 종합

위의 세 가지 실험 결과를 통해 “ResNet구조를 적용하면 모델의 성능이 개선되며 또한 매우 깊은 모델을 만들어도 효과적으로 학습할 수 있다고 결론 내릴 수 있습니다.

6. 의의

다음은 ResNet 논문의 의의에 대해 살펴보겠습니다.

첫 번째 의의는 깊은 네트워크에서의 학습 문제를 해결했다는 점입니다. 딥러닝에서 네트워크의 깊이는 모델의 성능에 큰 영향을 미치는데, 일반적으로 네트워크가 깊을수록 더 복잡한 특징을 학습할 수 있습니다. 그러나 너무 깊은 네트워크는 “그래디언트 소실(Vanishing Gradient)” 또는 “그래디언트 폭발(Exploding Gradient)” 같은 문제로 학습이 어려워집니다. ResNet은 이러한 문제를 “Residual Block”이라는 구조를 통해 해결합니다. Residual Block 내에서 입력 데이터는 직접 출력에 더해지는데, 이를 “Skip Connection” 또는 “Shortcut Connection”이라고 합니다. 이 구조 덕분에 그래디언트가 더 쉽게 역전파되어 깊은 네트워크도 효과적으로 학습할 수 있습니다.

두 번째 의의는 블록 형태의 아키텍처를 제안했다는 점입니다. 이는 딥러닝 모델 설계에 있어서 모듈화와 확장성을 크게 향상시킵니다. 전통적인 딥러닝 모델은 일련의 계층을 순차적으로 쌓는 형태였습니다. 이러한 방식은 모델의 구조를 변경하거나 확장하기 어렵게 만듭니다. 반면에 ResNet은 “Residual Block”이라는 기본 블록을 사용하여 모델을 구성합니다. 이 블록은 입력과 출력이 더해지는 형태로, 이러한 블록들을 여러 개 쌓아서 원하는 깊이와 복잡성의 모델을 쉽게 만들 수 있습니다. 이 “블록 형태의 아키텍처”는 딥러닝 모델을 더욱 유연하게 만들어 줍니다. 예를 들어, 같은 Residual Block을 사용하여 더 깊거나 얕은 모델을 쉽게 만들 수 있습니다. 또한, 이러한 블록 구조는 다른 연구자들이 새로운 형태의 블록을 설계하고 기존의 ResNet 아키텍처에 쉽게 적용할 수 있게 만듭니다. 따라서 ResNet의 이러한 “블록 형태의 아키텍처”는 딥러닝 모델의 설계와 확장성에 큰 영향을 미치며, 이는 ResNet이 널리 사용되는 또 다른 중요한 이유입니다.

7. 마치며

이번 글을 통해 ResNet의 근본적인 구조와 원리, 그리고 그 중요성에 대해 깊게 이해할 수 있었기를 바랍니다. ResNet은 딥러닝의 여러 문제점, 특히 깊은 네트워크에서의 Gradient 소실 문제를 해결하는 데 큰 도움을 주는 구조입니다. 이러한 문제를 해결함으로써, 더 깊고 복잡한 모델을 효과적으로 학습시킬 수 있게 되었습니다.

ResNet의 Residual Path와 Bottleneck 구조는 모델의 깊이를 늘리면서도 효율적인 학습을 가능하게 만들어줍니다. 이러한 구조적 특징은 ResNet이 여러 이미지 분류 작업에서 뛰어난 성능을 보이게 만들었습니다. 또한, 이 글에서 소개한 파이썬 코드를 통해 ResNet의 구조를 직접 구현하고 실험해볼 수 있습니다. 이를 통해 이론과 실제 사이의 간극을 줄이고, ResNet의 작동 원리를 더욱 명확하게 이해할 수 있습니다.

이미지넷과 같은 대규모 데이터셋에서의 실험 결과는 ResNet의 효과를 더욱 확실하게 보여줍니다. 이러한 성능은 ResNet이 여전히 많은 연구와 실제 응용 분야에서 활발하게 사용되는 이유입니다. ResNet의 이러한 성공은 또한 딥러닝 모델의 발전에 큰 기여를 하였습니다.

마지막으로, ResNet의 의의 섹션에서는 이 모델이 딥러닝 분야에 어떤 영향을 미쳤는지, 그리고 앞으로 어떻게 활용될 수 있는지에 대해 다루었습니다. ResNet은 딥러닝의 발전을 크게 이끌어간 중요한 모델 중 하나이며, 이를 통해 더욱 효과적이고 강력한 딥러닝 모델을 구축할 수 있게 되었습니다.

이 글을 마무리하며, 여러분도 ResNet과 같은 모델을 통해 더욱 깊고 풍부한 딥러닝의 세계를 탐험해보시길 바랍니다. 여러분의 연구와 프로젝트에 ResNet이 큰 도움이 되길 바랍니다!

Series Navigation<< [15′ ICML] Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift2. Vision Model 응용 버전 >>
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Scroll to Top