논문 리뷰, 구현

3.VGG 논문 리뷰,구현

jwjwvison 2022. 6. 29. 20:26

이번 포스팅에서는 VGG 논문을 리뷰하고 구현해 보겠다.

VGG16 구조

 

Abstract

 논문 저자들의 주되 기여는 매우 작은 (3x3) 합성곱 필터를 가진 구조를 사용하여 깊이가 증가하는 네트워크를 철저히 평가하는 것이다. 이는 깊이를 16~19층으로 구현하면서 선행 기술 구성의 상당한 향상을 달성할 수 있다는 것을 보여주었다. 이 저자들의 팀은 2014년 ImageNet challenge에서 locaslisation과 classification에서 각각 1등과 2등을 차지했다.

 

Introduction

 이 논문에서 저자들은 ConvNet의 깊이 구조에 초점을 맞추었다. 이를 위해서 읻르은 5가지의 파라미터를 고정시켰고, 합성곱 층을 더 추가하면서 network의 깊이만 증가시켰는데 이는 매우 작은 (3x3) 합성곱 필터를 모든 층에서 사용했기 때문에 실현 가능할 수 있었다.

 

ConvNet Configurations

 논문의 저자는 이들의 ConvNet은 대부분 AlexNet의 원칙을 따랐다고 밝혔다.

 

 1. Architecture 

 Input층은 224x224 사이즈의 RGB이미지를 입력 받는다.

 

 전처리는 RGB 평균값을 training set의 각각의 픽셀에 빼주었다.

 

 합성곱 필터의 사이즈는 모두 3x3이고, 1x1 필터를 사용한 부분도 있는데 이는 input channels의 선형변환으로 볼 수 있다. 합성곱층 필터의 strides=1 이고 same padding을 사용했다. Pooling 층은 maxpooling을 사용했고 모든 층이 아닌 5개의 층만 사용되었다. 필터 사이는 2x2 이고 stride=2이다.

 

 합성곱층 뒤에 완전연결층이 따라온다. 처음 2개의 완전연결층은 4096channel, 세번째 층은 1000개의 channel을 가지고 있는 softmax layer이다. 모든 hidden layer 에는 활성화 함수로 ReLU를 사용했다.

 

 2. Configurations

 Table 1에 깊이를 서로 달리한 모델 A~E의 배치가 나와있다. 11~19 깊이로 실험이 진행되었고, 넓이(channels)는 각 max-pooling layer 이후에 2배씩 증가하여 512에 도달한다. Table2는 각 배치의 parameter 수가 나와있다.

 

3. Discussion

 이들의 ConvNet의 구성은 이전에 좋은 성능을 내었던 모델들의 구성과 상당히 다르다. 큰 사이즈의 필터를 사용하는 대신 3x3 사이즈의 stride=1 인 작은 필터를 모든 층에 걸쳐서 사용하는데 이는 모든 픽셀이 합성되어지는 결과를 야기한다.

 

 3x3 사이즈의 필터를 쓰는 합성곱층 3개를 쌓은것이 왜 7x7 사이즈의 필터를 쓰는 한개의 합성곱층 보다 좋을까?

 

 첫번째 이유는, 이 논문의 저자들은 하나의 ReLU 함수 대신 세개의 ReLU함수를 사용했는데 이는 decision function을 더 차별적으로 만들어준다.  

 두번째 이유는, 파라미터의 수를 줄일 수 있다. 예를 들어 C개의 channel를 가진 3개의 3x3 filter를 이용하면 연산량은 3(3^2 * C^2) = 27가 되고, C 개의 channel를 가진 1개의 7x7 filter를 이용하면 연산량은 =49C2가 된다.대략 81% 정도 더 줄일 수 있다.

 

1x1 conv layer는 비선형성을 부여하기 위한 용도이다. 입력과 출력의 channels를 동일하게 하고 1x1 conv layer를 이용하면 relu 함수를 거치게 되어 추가적인 비선형성이 부여된다.

 

 

Classification FrameWork

 이 섹션에서는 ConvNet의 training과 evaluation에 대한 디테일을 설명한다.

 

1. Training

 ConvNet의 training 절차는 일반적으로 AlexNet을 따른다(mult-scale training-images로 부터 input crop를 sampling 하는 것을 제외하고). 즉, 미니 배치 gradient descent와 mometum을 사용하여 다항 로지스틱 회귀 분석 목표를 최적화함으로써 training을 수행했다.

 배치 사이즈는 256, mometum은 0.9, L2규제(5*10^-4) 를 사용했다. 첫번째, 두번째 F.C 에는 dropout(0.5)를 사용했다.

 학습률은 초기에는 10^-2로 설정되었다가 검증 세트의 정확도가 더이상 상승하지 않으면 10배씩 줄였다. 결국에는 학습률은 3번 줄었고 370회 반복후 (74epoch) 학습은 멈추었다.

 

 이 논문의 저자들은 AlexNet에 비해 매개변수의 수와 신경망의 깊이는 더 많고 깊지만, 이 신경망은 더 깊고 더 작은 합성곱 신경망 필터 크기에 의해 생기는 암묵적 정규화, 특정 층의 pre-initialisation로 인해 수렴하는데 더 작은 epochs가 필요했다고 추측했다.

 

 신경망 weight의 initialisation은 중요한데 왜냐하면 좋지 않은 initialisation은 깊은 신경망에서 gradient의 불안정성 때문에 학습을 방해할 수 있다. 이 문제를 피하기 위해 얕은 구조A (Table 1)를 훈련하는 것으로 시작했다. 그리고, 더 깊은 구조를 학습시킬때는 이들은 처음 4개의 합성곱층과 마지막 3개의 F.C층을 A의 가중치로 초기화시켰다(중간 층들은 랜덤하게 초기화되었다).

 랜덤 초기화(해당하는 경우)의 경우, 이들은 평균이 0이고, 분산이 10^-2를 갖는 정규 분포에서 가중치를 표본 추출했다. 편향은 0으로 초기화했다.

 

 VGG 모델은 3가지의 data augmentation이 적용되었다.

 (1) crop된 이미지를 무작위로 수평 뒤집기

 (2) 무작위로 RGB 값 변경하기

 (3) image rescaling

 

2. Testing

 test image를 다양하게 rescale 하여 trained ConvNet에 입력으로 이용했다. 다양하게 rescale 함으로써 성능이 개선되었다고 한다. 또, 수평 뒤집기를 적용하여 test set을 증가시켜주었고 최종 점수를 얻기 위해 동일한 이미지에 data augmentation이 적용된 이미지들의 평균 점수를 이용했다고 한다.

 

 

VGG16 구현

class VGG16(Sequential):
  def __init__(self):
    super().__init__()

    self.add(Conv2D(64,3,1,padding='same',activation='relu',input_shape=(224,224,3)))
    self.add(Conv2D(64,3,1,padding='same',activation='relu'))

    self.add(MaxPool2D(2,2))
    self.add(Conv2D(128,3,1,padding='same',activation='relu'))
    self.add(Conv2D(128,3,1,padding='same',activation='relu'))

    self.add(MaxPool2D(2,2))
    self.add(Conv2D(256,3,1,padding='same',activation='relu'))
    self.add(Conv2D(256,3,1,padding='same',activation='relu'))
    self.add(Conv2D(256,3,1,padding='same',activation='relu'))

    self.add(MaxPool2D(2,2))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))

    self.add(MaxPool2D(2,2))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))
    self.add(Conv2D(512,3,1,padding='same',activation='relu'))

    self.add(MaxPool2D(2,2))
    self.add(Flatten())

    self.add(Dense(4096,activation='relu'))
    self.add(Dropout(0.5))
    
    self.add(Dense(4096,activation='relu'))
    self.add(Dropout(0.5))

    self.add(Dense(1000,activation='softmax'))

    self.optimizer=keras.optimizers.Adam(learning_rate=0.001)
    

    self.compile(optimizer=self.optimizer,loss='sparse_categorical_crossentropy',metrics='accuracy')

출처:https://arxiv.org/abs/1409.1556v6