VGGNet 논문 리뷰

김태훈·2023년 6월 23일
0

본 페이지에서는 VGGNet의 등장배경과 특징에 대해서 말하고자 합니다.


1. VGGNet 특징

기존의 모델들의 특징은 다음과 같습니다.

  1. 모델의 깊이가 깊지 않기 때문에 Feature들의 특징을 모두 담기 힘들다.

  2. 더 정교한 Feature map들을 추출하기 위해서 Kernel size를 키우거나 Stride를 작게 하는 방식으로 모델의 성능을 향상 시키기 때문에 학습시킬 파라미터의 양과 연산량이 많다.

이러한 단점을 해결하기 위해 VGGNet은 다음과 같은 특징을 갖습니다.

  1. 모델의 Kernel size를 3x3 사이즈로 하여 학습시킬 파라미터의 양을 줄였다.

  2. 학습시킬 파라미터의 양이 줄게되면서 동시에 상대적으로 깊게 Layer들을 쌓을 수 있었다.

이러한 모델의 특징은 VGGNet을 구현을 간단하도록 하였고 괜찮은 성능을 내기도 하였다.

2 3x3 Convolution

VGGNet은 3x3 사이즈의 kernel을 많이 사용하였는데 그 이유는 다음과 같다.

5x5 conv layer를 한번 쓰는 것과 3x3 conv layer를 두번 쌓는 것과 같다고 볼 수 있고 7x7 conv layer는 3x3 conv layer를 세번 쌓은 것과 같다고 볼 수 있다.

이때 3x3 conv layer를 세번 사용하는 것이 7x7 conv layer를 사용하는 것보다 나은 이유를 설명하면 다음과 같다.

7x7 conv layer를 한번 사용하면 비선형 변환을 한번 하게 되자만 3번의 3x3 conv layer를 사용하게 되면 세번의 비선형 변환이 가능하다.

또한 conv layer를 지난 후의 채널의 수가 C라고 할때

3개의 3x3 conv layer의 파라미터 수는 다음과 같고

3(32C2)=27C23(3^2C^2) = 27C^2

7x7 conv layer의 파라미터 수는 다음과 같다.

72C2=49C27^2C^2 = 49C^2

추가적으로 본 모델에서는 1x1 conv layer가 존재하는데 이는 비선형 변화를 늘리기 위해 주로 사용하였다.

3. 구조

Convolution Layer

이미지는 매우 작은 3x3 size kernel 을 이용해 Convolution 연산을 진행한다. 이때 stride는 1이며 입력에 대해 zero padding을 추가한다.

Spatial Pooling

Pooling Layer는 일부 Convolution Layer 뒤에 적용된다.

kernel size는 2x2 이며 stride는 2로 하여 MaxPooling 연산을 진행한다.

Fully Connected Layer

마지막 Convolution Layer 뒤에 세개의 Fully Connected Layer를 적용한다.

처음 두개의 FC Layer는 4096개의 채널을 가지고

세번째는 1000개의 클래스에 대해서 1000개의 채널을 가지고 softmax를 적용한다.

4. 상세구조

위의 table1은 열마다 다른 깊이의 모델을 보여주며 ABCDE로 나누고 각각의 다른 점은 깊이만 다른 것이다.

각 모델의 첫 채널은 64로시작하며 최종적으로 512까지 늘린다.

Table 2는 각 모델의 파라미터 수를 나타내고 깊이는 깊어졌지만 다른 얕지만 큰 kernel size를 사용한 모델보다는 weight의 수가 크지 않다.

5. GoogleLeNet과 다른 점

모델의 깊이가 깊어질 수록 성능이 좋아진다는 것은 이미 증명이 되었지만.

GoogleLeNet은 22개의 레이어를 3x3,1x1,5x5 conv layer를 모두 골고루 사용하였다.

GoogleLeNet은 VGGNet 구조보다 복잡하고 첫번째 레이어를 지난 뒤의 이미지 크기가 너무 많이 감소된다.

6. 코드구현

Keras

import keras

def conv(x,f,k,s,n):
  for i in range(n):
    x = keras.layers.Conv2D(f,k,s,padding='same')(x)
    x = keras.activations.relu(x)
  return x

def vgg16():
  x = keras.layers.Input((224,224,3))

  L1 = conv(x,64,3,1,2)
  
  L2 = keras.layers.MaxPool2D(2,2)(L1)
  L2 = conv(L2,128,3,1,2)

  L3 = keras.layers.MaxPool2D(2,2)(L2)
  L3 = conv(L3,256,3,1,3)

  L4 = keras.layers.MaxPool2D(2,2)(L3)
  L4 = conv(L4,512,3,1,3)

  L5 = keras.layers.MaxPool2D(2,2)(L4)
  L5 = conv(L5,512,3,1,3)

  F = keras.layers.MaxPool2D(2,2)(L5)
  F = keras.layers.Flatten()(F)

  FC1 = keras.layers.Dense(4096)(F)
  FC1 = keras.activations.relu(FC1)
  
  FC2 = keras.layers.Dense(4096)(FC1)
  FC2 = keras.activations.relu(FC2)

  FC3 = keras.layers.Dense(1000)(FC2)
  FC3 = keras.activations.relu(FC3)
  
  output = keras.activations.softmax(FC3)

  return keras.Model(inputs=x,outputs=output,name='VGG16')
model = vgg16()
model.summary()

PyTorch

import torch.nn as nn
from torchsummary import summary


class VGGNet(nn.Module):
    def __init__(self):
        super(VGGNet,self).__init__()
        def conv(i,o,k,s,n):
            layers = []

            for count in range(n):
                if count == 0:
                    layers.append(nn.Conv2d(i,o,k,s,'same'))
                else:
                    layers.append(nn.Conv2d(o,o,k,s,'same'))
                layers.append(nn.ReLU())
            layers.append(nn.MaxPool2d(2,2))
            return nn.Sequential(*layers)
            
                
        self.l1 = conv(3,64,3,1,2)
        self.l2 = conv(64,128,3,1,2)
        self.l3 = conv(128,256,3,1,3)

        self.l4 = conv(256,512,3,1,3)

        self.l5 = conv(512,512,3,1,3)

        self.fc1 = nn.Sequential(
            nn.Linear(512*7*7,4096),
            nn.ReLU(),
        )

        self.fc2 = nn.Sequential(
            nn.Linear(4096,4096),
            nn.ReLU(),
        )

        self.output = nn.Sequential(
            nn.Linear(4096,1000)
        )

    def forward(self,x):
        l1 = self.l1(x)
        l2 = self.l2(l1)
        l3 = self.l3(l2)
        l4 = self.l4(l3)
        l5 = self.l5(l4)
        l5 = l5.view(l5.size(0),-1)

        fc1 = self.fc1(l5)
        fc2 = self.fc2(fc1)
        output = self.output(fc2)

        return output
        
summary(VGGNet(),(3,224,224))

profile
👋 인공지능을 통해 다음 세대가 더 나은 삶을 살도록

0개의 댓글