NIPS 2012. [Paper]
Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton
University of Toronto

이미지 넷 모델 리뷰

  1. AlexNet(2012 우승)
  2. VGGNet(2014 준우승)
  3. GoogleNet(Inception)(2014 우승)
  4. ResNet (2015 우승)

Introduction

AlexNet은 딥러닝 부흥기의 시작점으로 볼 수 있을 정도로 인공지능 역사에 큰 영향을 미친 논문이다. 지금 이시점에서 이 논문을 읽으면서 오늘날 너무 당연한게 배우고 있는 개념을 제시하고 있다. 특히 해당 논문을 인공지능을 처음 배우는 사람의 시작점으로 많이 추천되는 논문임에 중요도가 높다고 볼 수 있다. 해당 논문은 LSVRC-2012에서 top-5 test error rate가 15.3%로, 2등인 26.2%보다 월등히 뛰어난 성능으로 우승하였다. 이는 CNN, ReLU, Dropout, GPU 병렬연산 등을 사용하였고, 해당 코드들을 공개하는 기여를 하였다.

ImageNet은 기본적으로 1500만장의 고해상도 이미지를 22,000개의 클래스로 분류한다. 이때 Large-Scale Visual Recognition Challenge(ILSVRC)는 해당 데이터 셋을 이용하여, train 120만 장, valid 5만장, test 15만장만을 이용해 1000개의 클래스로 분류하는 대회를 의미한다. ImageNet 대회는 가변적인 이미지가 존재한다. 저자가 제안할 논문에서는 256x256의 고정 이미지만을 지원하기에, 이미지 중 짧은 부분을 256으로 하고 중앙을 기준으로 256만 남기고 잘라내는 전처리 과정을 진행했다.

Architecture

저자는 제안한 아키텍처의 중요한 4가지 특징을 중요한 순서대로 기술했다.

1. ReLU Nonlinearity

저자는 gradient descent를 통한 학습과정에서 tanh나 시그모이드같은 saturating nonlinearities는 학습이 오래걸린다고 한다. 그래서 저자는 ReLU와 같은 non-saturating nonlinearity를 통해 학습을 시켰다고 한다. 여기서 saturating하다는 건 한국어로 번역하면 포화라는 것인데, 여기서는 출력값이 수렴한다로 보면 된다. 즉 기울기를 계산했을 때 해당 값이 0에 가까워져 학습이 잘 진행되지 않는 vanishing gradient problem이 발생하는 함수들을 말한다. 이때 ReLU의 경우 양수부분이 $f(x) = x$ 이므로 해당 문제가 발생하지 않는다.

또한 저자는 ReLU와 기존의 nonlinearity의 차이에 대해서도 언급한다. linearity $f(x) = \left\vert tanh(x) \right\vert$의 경우 Caltech-101 데이터셋에서 좋은 성능을 보였다. 하지만 해당 데이터 셋에서는 overfitting을 방지하는게 주 목적이었다. 하지만 ReLU의 경우 학습 속도가 빨라 ImageNet과 같은 거대한 데이터셋을 학습할 때 효율적인 언급을 하고 있다.

위 Figure는 CIFAR-10 데이터셋을 학습 시킬때 tanh(점선)보다 ReLU(실선)이 6배 빠른 학습 속도를 보여준다는 것을 보여준다. (근데 figure가 손으로 그린거 같은 느낌이 든다…)

2. 다중 GPU 사용

해당 모델을 학습시킬 때 GTX580을 사용했지만, GPU의 메모리가 3GB밖에 되지 않아 모델을 학습하는데 문제가 있었다. 그래서 저자는 네트워크를 2개의 GPU로 나누어 학습시켰다. 학습과정에서 커널을 절반으로 나눠 각각 학습시키도록 하였다. 이때 호스트의 메모리에 접근하지 않고, gpu끼리 통신할 수 있게 하여 속도를 높였다.

이때 한가지 트릭을 사용하는데, 두 GPU끼리는 특정레이어에서만 통신한다는 점이다. 즉 특정레이어를 제외하면 각각의 GPU에서 학습하고 있는 커널만 보고 학습을 하는 것이다. 해당 방식을 통해 커널의 숫자를 늘릴 수 있어 error rate는 1.7%,1.2%(top-1, top-5) 감소하였다. 하지만 학습 시간의 경우 조금 감소하게 되는데 이는 GPU간의 통신 속도 때문이다.

3. Local Response Normalization

\[\begin{equation} b_{x,y}^i = \frac{a_{x,y}^i}{\left( k + \alpha \sum_{j=\max(0, i-n/2)}^{\min(N-1, i+n/2)} \left( a_{x,y}^j \right)^2 \right)^\beta} \end{equation}\]

ReLU의 경우 non-saturating이기 때문에 정규화 단계가 필요하지 않지만, 저자는 Local Nomalization이 generalization에 도움이 된다는 사실을 알아냈다. 이는 lateral inhibition(측면억제)의 구현으로 실제 뉴런의 동작에서 모방한 것이다. 한 뉴런에서 큰 활동을 하면 다른 뉴런에서 이를 억제하려고 하는 특징을 갖고 있는데 해당한다.

수식의 구성요소

  • $a_{x,y}^i$ : ($x,y$)에서 커널 $i$에서 뉴런들에 의해 계산 한 후 ReLU가 적용된 출력값, 즉 정규화 전 값
  • $b_{x,y}^i$ : $a_{x,y}^i$에 정규화가 적용된 값
  • 분모 $\left( k + \alpha \sum \dots \right)^\beta$ : 동일한 위치에서
  • N : 레이어의 총 커널 수의 합
  • n, k, $\alpha , \beta$는 하이퍼 파라미터
    • n : 같은 spatial position에 있는 인접한 커널의 수
    • k : 분모가 0이 되는 것을 방지하는 기본값(bias)
    • $\alpha , \beta$ : 정규화 강도와 관련

4. Overlapping Pooling

오늘날 CNN을 계산할 때는 당연하게도 overlapping이 가능하다고 생각하지만 논문이 나온 시점에서는 그렇지 않았다보니 이를 수식으로 표현하고 있다. pooling할 때의 간격을 s라 하고, 하나의 픽셀이 z x z를 pooling 했다고 하자. 이때 s = z가 되면 overlapping 되지 않는 과거의 pooling 방식이 된다. 하지만 s < z 가 되면 pooling이 오버랩된다. 이를 통해 error rate는 조금 감소하였고, overfitting 또한 소폭 감소하였다.

5. Overall Architecture

전체적인 아키텍처는 다음과 같다. 총 5개의 convolutional layer와 3개의 fully connected layer로 구성되었다. 숫자가 써진 네모박스가 커널이고, 그 사이에 연결되는 선을 layer로 이해하면 좀 더 편하다. 출력층에서는 1000개의 클래스로 분류를 위해 소프트 맥스를 사용하였다.

또한 위의 2번 다중 GPU 사용에서 언급했듯, 2,4,5번 Conv 레이어는 단일 gpu연결이고, 3번째 레이어만 2번째 레이어의 모든 커널과 연결되어 있다. 위에서 언급한 정규화 레이어의 경우 첫번째와 두번째 레이어 뒤에 붙어 있다. 또한 Max Pooling의 경우 정규화 다음과 5번째 레이어 뒤에 붙게 된다. 그리고 모든 레이어 끝에는 ReLU가 적용되어 있다.

Reducing Overfitting

제안된 모델 아키텍처는 약 6000만개의 파라미터가 있지만, 데이터셋의 클래스는 1000개로 각각의 이미지를 분류해야 하다보니 overfitting을 마주칠 수 밖에 없다. 그래서 해당 논문에서는 2개의 기법을 이용한다.

1. 데이터 증강

가장 쉽고 보편적인 방법으로 데이터를 증강하는 방법을 사용한다. 라벨은 유지한 채 사진만 변형시켜 데이터를 증가시키는데, 이때 컴퓨팅 능력이 크게 필요하지 않기 때문에 GPU에서 다른 배치를 처리중일 때 CPU에서 변형된 이미지를 생성하여 컴퓨팅적으로 효율적으로 학습을 시켰다고 한다.

총 2가지 증강 기법을 사용하는데 첫번째는 256x256이미지에서 랜덤하게 224x224 패치를 추출하고 이를 수평적으로 반사(대칭)시켜 학습 시킨다. 이를 통해 한개의 이미지가 2048배 커지지만, 이미지가 상호 의존적이게 된다. 그리고 테스트할 때 또한 이미지에서 랜덤하게 224x224를 5개를 추출하여 예측의 결과를 평균내는 방식으로 진행한다.

두번째 증강기법은 이미지의 RGB의 강도를 변경하는 것이다. 여기서 RGB 픽셀의 값에 PCA를 적용한다. 정규 분포를 따르는 랜덤 값을 eigenvalues(고유값)에 곱한 principal components(주성분)의 값을 더해준다. 이를 설명하는 수식은 다음과 같다.

\([p_1, p_2, p_3][\alpha_1\lambda_1,\alpha_2\lambda_2,\alpha_3\lambda_3]^T\)

  • $p$ : eigenvactor
  • $\alpha$ : 랜덤한 값
  • $\lambda$ : eigenvalues

다시 설명하자면 3x3 공분산행렬에서 추출된 eigenvactor과 eigenvalues를 통해 위 수식을 통해 계산한 값을 원본이미지에 더해주는 방식으로 진행된다.

논문에 나와있진 않았지만 공분산 행렬(covariance matrix)가 뭔지 모르겠어서 검색을 통해 이해한 내용은 다음과 같다. 원본 이미지의 픽셀의 갯수가 N = W x H개 있다고 가정할 때 이미지는 행렬로 다음과 같이 나타낼 수 있다.

\[X = \begin{bmatrix} R_1 & G_1 & B_1 \\ R_2 & G_2 & B_2 \\ \vdots & \vdots & \vdots \\ R_N & G_N & B_N \end{bmatrix}\]

이제 해당 행렬에 대해 각 RGB마다 정규화를 진행하고 아래와 같은 수식을 통해 공분산 행렬을 구할 수 있다.

\[\Sigma = \frac{1}{N} X_{\text{norm}}^T X_{\text{norm}}\] \[\Sigma = \begin{bmatrix} \text{Var}(R) & \text{Cov}(R, G) & \text{Cov}(R, B) \\ \text{Cov}(G, R) & \text{Var}(G) & \text{Cov}(G, B) \\ \text{Cov}(B, R) & \text{Cov}(B, G) & \text{Var}(B) \end{bmatrix}\]

이제 구해진 공분산 행렬을 통해 PCA기법으로 증강을 진행하면 된다.

2. Dropout

다양한 모델의 예측을 결합하는 것은 error를 줄이는데 굉장히 좋은 방법이지만, 큰 모델을 학습시키는데 너무 긴 시간이 든다. 이에 학습동안에 dropout기법을 활용하면 굉장히 효율적으로 모델 결합이 가능하다. 0.5의 확률로 hidden neuron의 출력값을 0으로 만들어준다. 이방법을 통해서 해당 뉴런은 forward도 backpropagation에도 관여하지 않는다. 따라서 매 학습마다 다른 아키텍처이지만, 같은 가중치를 갖는 아키텍처들이 된다. 이를 통해서 모델이 예측할 때 특정한 뉴런에 의해 의존해서 예측하는게 불가능해져, 뉴런들이 골고루 학습할 수 있게 된다. 그리고 테스트할 때는 모든 뉴런을 사용하는 대신 모든 뉴런의 출력값을 0.5를 곱해줘 계산을 한다.

Details of Learning

위 사진은 첫번째 레이어에서 추출된 96개의 커널들. 위의 48개는 첫번째 GPU에서 아래 48개는 두번째에서 추출된 것임

  • optimizer : stochastic gradient descent(SGD)
  • batch size : 128
  • momentum : 0.5
  • weight decay : 0.0005
  • weight 초기화 방법 : 평균 0, 표준편차 0.01인 정규분포
  • 2,4,5번째 Conv 레이어와 FC 레이어의 bias를 1로 설정(나머지는 0으로)
    • 이는 초기에 ReLU의 양수 입력값을 제공해 빠른 학습을 제공하기 위함
  • 모든 레이어에서 같은 learning rate 사용하고, 학습중 직접 조정
    • 0.01에서 시작해서 valid error가 개선될 때까지 10으로 계속 나눔
  • 모든 이미지에 대해 90 cycles(에폭 말하는건가?)
  • GTX 580 x 2대로 6일

Results

저자는 ILSVRC-10과 ILSVRC-12에서 해당 모델을 돌렸다. ILSVRC-10에서 해당 모델은 위 표와 같은 성적을 달성했다. SIFT + FVs방법이 당시 우승한 방법인데 해당 방법보다 더 좋은 성적을 보였다는 것을 보여준다.

다음은 ILSVRC-12에서의 결과이다. ILSVRC-10에서는 실제로 나가진 않고 프로토타입에 불과하다면 이건 실제로 나가서 우승을 했다. 맨위에 있는 SIFT + FVs기법을 사용했을 때의 error rate이 26.2%인데, 저자의 가장 좋은 성능을 보인 결과는 error rate가 15.4%로 압도적으로 좋은 성능을 보였다고 한다.

Discussion

해당 모델들에서 하나의 Conv 레이어를 제거하면 성능이 저하된다고한다. 이를 통해 모델의 깊이가 굉장히 중요하다는 사실을 알 수 있다.

추가적으로 실험을 단순화하기 위해 비지도학습이 더 좋은 결과를 보일 것임을 알고 있음에도 사용하지 않았다고 한다.

그리고 궁극적으로 deep convolution network를 비디오에도 적용하고 싶다고 하였는데, 현재는 이게 너무나도 쉽게 되고 있다.

구현 코드 보기

class AlexNet(nn.Module):
    def __init__(self, num_classes: int = 1000, dropout: float = 0.5) -> None:
        super().__init__()
        _log_api_usage_once(self)
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(p=dropout),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

해당 코드는 파이토치의 torchvision에 구현되어 있는 alexnet의 구조이다. 사실 직관적이고 쉬운 코드로 구성되어 있기에 쉽게 이해할 수 있는 부분에 대한 부분은 생략하도록 하겠다. torchvision에 구현된 모델들에는 _log_api_usage_once(self)라는 코드가 생성자에 있다. 이는 파이토치 내부에서 해당 클래스가 호출됐을 때 해당 모델이 사용했음을 로깅하기 위해 사용된다. 따라서 따로 모델 성능에 영향을 주지 않는다.

우선 CNN을 통해 features를 추출하는 과정에서 논문에 써져있는 채널의 수가 다르다. 이는 추출할 feature의 수가 달라질 뿐, 근본적인 아키텍처가 달라지는 것은 아니므로 넘어갈 수 있다.

두번째로 다른 점을 보자면, 컴퓨팅 능력이 향상됨에 따라 이제 더이상 gpu를 병렬적으로 연산하는 부분이 빠졌다.

세번째로는 Local Response Normalization (LRN)가 제거되었다. 이는 이후에 batch nomalization와 같은 더 향상된 기법이 등장함에 따라 자연스럽게 제거된 것으로 보인다.

그리고 conv layer의 마지막에 논문에서는 max pooling으로 동일하게 하고 있지만 해당 논문에서는 average pooling을 한다는 것을 알 수 있다. 이는 추측컨데 max pooling은 정보 손실이 발생할 수 있어, 마지막 레이어에서는 정보들을 취합하는 과정이 있어야하기에 바뀐것으로 추정된다.

전체적으로 2012년 당시에는 처음부터 구현을 해야하는 것들이 사용하기 쉽게 구현되어 전체적으로 직관적으로 구현되어 있어 쉽게 이해할 수 있음을 알 수 있다.