안녕하세요. 오늘 리뷰할 논문은 'Very Deep Convolutional Networks for Large-Scale Image Recognition'로 세계최고의 인공지능 학회 중 하나인 ICLR에 2015년에 논문이 출판되었습니다. 아마 논문 제목만 보면 어떤 네트워크인지 감이 안오실겁니다. 혹시, VGGNet이라는 네트워크는 들어보셨을 겁니다. 오늘 리뷰할 논문이 VGG라는 합성곱 신경망 (Convolutional Neural Network; CNN)을 제안한 것이죠.
당시에도 CNN은 굉장히 다양한 분야에서 높은 성능을 보이고 있는 상태였습니다. 이러한 성능 향상의 비법은 바로 ImageNet이라는 대규모 데이터셋이죠. ImageNet 데이터셋에 대한 자세한 내용은 제 포스팅을 참조바랍니다.
ImageNet 데이터셋을 주최측에서는 이 데이터셋을 이용해서 매년 엄청나게 큰 규모의 챌린지를 개최합니다. 바로 ImageNet Larget-Scale Visual Recognition Challenge (ILSVRC)입니다. VGGNet은 ILSVRC 2014년에 2등을 차지하게 되었죠. 참고로 1등은 현재 InceptionNet으로 유명한 네트워크의 근간이 되는 GoogLeNet (InceptionNet v1)입니다. 그렇다면 VGGNet의 비결이 무엇인지 알아보겠습니다.
1. Architecture
본 논문에서는 VGGNet의 configuration에 따라서 6가지 변형이 존재합니다. 첫번째 행은 각 모델의 configuration에 따라 가중치를 가지는 계층의 개수를 알려주고 있습니다. 일반적으로 저희가 VGG11, VGG16, VGG19 이렇게 표기하는 것은 가중치를 가지는 계층의 개수가 각각 11개, 16개, 19개임을 의미하죠. 이러한 표기법은 나중에 소개시켜드릴 ResNet에서도 이어지고 최근 논문의 대부분에서 이와 같은 표기법을 선택하고 있습니다.
기본적으로 6개의 VGGNet 모두 $224 \times 224$의 RGB 영상을 입력으로 받습니다. 그리고 각 채널에 대해서 평균을 빼주어 데이터셋에 존재하는 픽셀 강도의 편향을 제거해줍니다. 전처리된 영상은 $3 \times 3$의 커널을 가지고 stride 1, padding 1로 주어 합성곱 계층을 통과하여 특징맵을 추출하게 됩니다. Table1에서 conv3-64와 같이 적혀있는 것을 볼 수 있습니다. 이는 $3 \times 3$의 커널 64개를 사용해서 특징맵을 추출한다는 의미입니다. 여기서, C에서는 비선형성을 추가적으로 적용하기 위해 $1 \times 1$의 커널을 사용하기도 합니다. 다음으로 해상도를 줄이기 위해 $2 \times 2$의 커널과 stride 2로 주어 해상도를 절반으로 줄여줍니다. 마지막으로 3개의 Full Connected Layer가 연결되어 각각 4096개, 4096개, 1000개의 노드를 가지고 학습이 진행됩니다. 참고로 마지막 노드의 개수는 사용하는 데이터셋에 따라서 달라지니 유의해서 설정하시면 됩니다.
또한, 각 계층에서는 활성화 함수로 ReLU를 이용하였습니다. 여기서 B에서는 LRN 계층을 도입하여 실험하여 실험을 해보기는 하였지만 성능 향상은 없었다고 합니다.
Table2에서는 각 모델의 configuration에 따른 모델 파라미터의 개수를 보여주고 있습니다. 당연하지만, 네트워크가 점점 깊어질수록 그만큼 사용하는 가중치가 많아지기 때문에 파라미터가 많아지게 됩니다. 여기서 단위는 백만으로 가장 많은 파라미터를 가지는 VGG19는 약 144,000.000개 (1억 4천4백만개)의 파라미터를 가집니다.
2. Discssion
저는 개인적으로 본 논문에서 가장 의미있다고 보는 것은 $3 \times 3$의 커널을 사용한 것 입니다. 최근 네트워크의 대부분은 $3 \times 3$의 합성곱 계층을 여러 개 쌓아서 만드는 데 이러한 이유를 잘 설명해주고 있다고 느끼기 때문입니다. 일반적으로 커널의 크기가 클수록 입력 특징맵의 더욱 넓은 부분을 보기 때문에 더욱 좋은 특징을 추출할 수 있다고 말합니다. 이와 같은 1개의 합성곱 결과를 얻기 위한 이전 특징맵의 영역을 수용영역 (Receptive Field; RF)라고 합니다. 당장 예를 들어서, $3 \times 3$크기의 커널은 9개의 픽셀을 보고 하나의 값을 결정하지만 $5 \times 5$나 $7 \times 7$ 크기의 커널은 각각 25개, 49개의 픽셀을 보기 때문에 수용영역이 훨씬 넓습니다. 하지만, 파라미터의 개수는 각각 9개, 25개, 49개로 기하급수적으로 커지기 때문에 수용영역과 파라미터의 개수는 서로 비례관계라고 볼 수 있습니다. 이를 완화하기 위해 $3 \times 3$ 크기의 커널을 3번 쌓게 되면 파라미터는 출력 특징맵의 채널이 $C$라고 할 때 $27C^{2}$이지만, $7 \times 7$ 크기의 커널 1번은 $49C^{2}$입니다. 하지만, 수용영역은 $3 \times 3$ 크기의 커널을 3번 쌓았을 때 훨씬 넓게 되는 것이죠. 따라서, 수용영역도 넓히고 파라미터의 개수도 줄이기 위해 $3 \times 3$ 크기의 커널을 여러 번 쌓는 것이라고 해석할 수 있습니다.
3. 실험 결과
본 논문에서는 ImageNet 데이터셋을 이용해서 실험을 진행하였습니다. 셜과를 보시면 네트워크의 깊이가 깊어질 수록 top-1 error와 top-5 error과 감소하는 것을 볼 수 있습니다. 하지만, 그만큼 파라미터의 개수는 증가하기 때문에 모델의 configuration은 각자 상황에 맞추어 하는 것이 중요할 것 같네요.
4. 모델 구현
import torch
import torch.nn as nn
configurations = {
'A' : [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], # VGG11
'B' : [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], # VGG13
'D' : [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], # VGG16
'E' : [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'], # VGG19
}
class BasicConv(nn.Module) :
def __init__(self, in_ch, out_ch, kernel_size, stride, padding):
super(BasicConv, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(in_ch, out_ch, kernel_size, stride, padding),
nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True))
def forward(self, x):
return self.conv(x)
class VGG(nn.Module) :
def __init__(self, model, num_channels=3, num_classes=1000):
super(VGG, self).__init__()
self.in_channels = num_channels
self.num_classes = num_classes
if model == 'VGG11' :
configuration = configurations['A']
elif model == 'VGG13' :
configuration = configurations['B']
elif model == 'VGG16' :
configuration = configurations['D']
elif model == 'VGG19' :
configuration = configurations['E']
else :
print('wrong model choice : [VGG11, VGG13, VGG16, VGG19]')
sys.exit()
self.feature = self._make_layer(configuration)
self.fc = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096), nn.Dropout(p=0.5),
nn.Linear(4096, 4096), nn.Dropout(p=0.5),
nn.Linear(4096, self.num_classes)
)
def forward(self, x):
feature = self.feature(x)
output = self.fc(feature.reshape(x.size(0), -1))
return output
def _make_layer(self, configuration):
layers = []
for layer in configuration :
if type(layer) == int :
layers.append(BasicConv(in_ch=self.in_channels, out_ch=layer, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)))
self.in_channels = layer
elif layer == 'M' :
layers.append(nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)))
return nn.Sequential(*layers)
마지막으로 VGGNet을 Pytorch로 구현하고 실험결과를 확인한 뒤 마무리하도록 하겠습니다. 저는 모델의 configuration 중 A, B, D, E 만을 구현하였습니다. 모델을 구성하는 원리는 미리 configuration 변수를 지정한 뒤 사용자가 원하는 모델을 선택하면 이에 맞게 변형해주는 형태입니다. configuration을 얻게 되면 _make_layer 함수에서 실질적으로 모델이 구성됩니다. configuration을 보시면 크게 2가지로 나눌 수 있습니다. 바로 숫자와 문자열 'M' 이죠. 숫자는 합성곱 계층, 배치정규화, 활성화함수를 포함하는 하나의 합성곱 블럭을 의미하고 문자열 'M'은 max pooling 계층을 의미합니다. 각 configuration에 맞게 합성곱 블럭과 max pooling 계층을 리스트에 하나씩 넣어준 뒤 nn.Sequential 함수를 이용해서 pytorch가 인식할 수 있는 형태로 바꾸어주면 됩니다.
마지막으로 영상 분류를 위해 fully-connected layer를 추가하여 합성곱 블럭들로 얻는 결과를 벡터화하여 입력해주게 됩니다. 마지막에는 데이터에 따라 클래스의 개수를 지정해주면 됩니다.
CIFAR10 | CIFAR100 | |||
Model | TOP-1 Error (%) | TOP-5 Error (%) | TOP-1 Error (%) | TOP-5 Error (%) |
VGG11 | 12.67 | 0.56 | 39.58 | 16.13 |
VGG13 | 10.41 | 0.39 | 36.18 | 14.05 |
VGG16 | 10.30 | 0.44 | 33.98 | 13.24 |
VGG19 | 9.98 | 0.46 | 34.02 | 13.08 |
위 표는 CIFAR10, CIFAR100으로 실험한 결과를 보여주고 있습니다. 모든 실험은 배치사이즈 256, 최적화함수는 Stochastic Gradient Descent, 초기 학습률은 0.01로 시작하여 cosine annealing learning rate scheduler를 이용해서 0.000001까지 감소시켰습니다. 결과적으로 깊이가 높을수록 높은 성능을 가지는 것을 볼 수 있습니다. 특히, VGG11과 VGG19를 비교해보면 CIFAR10에서는 2.69%, CIFAR100에서는 5.56% 향상된 TOP-1 Error를 얻을 수 있습니다.
전체 학습코드는 다음 깃허브 링크에서 다운받으실 수 있습니다.
'논문 함께 읽기 > 2D Image Classification (IC2D)' 카테고리의 다른 글
[IC2D] Deep Networks with Stochastic Depth (ECCV2016) (0) | 2023.05.03 |
---|---|
[IC2D] Identity Mappings in Deep Residual Networks (ECCV2016) (0) | 2023.04.21 |
[IC2D] Deep Residual Learning for Image Recognition (CVPR2016) (0) | 2023.04.12 |
[IC2D] Going Deeper with Convolutions (CVPR2015) (0) | 2023.02.17 |
2D Image Classification Summary (0) | 2023.02.17 |