Bag of Tricks for Image Classification with Convolutional Neural Networks

Bag of Tricks for Image Classification with Convolutional Neural Networks

2020, Jan 01    


  • 논문 : https://arxiv.org/abs/1812.01187
  • 참조 : https://www.youtube.com/watch?v=D-baIgejA4M&list=WL&index=34&t=0s
  • 참조 : https://www.youtube.com/watch?v=2yxsg_aMxz0
  • 참조 : https://hoya012.github.io/blog/Bag-of-Tricks-for-Image-Classification-with-Convolutional-Neural-Networks-Review/


목차



  • 19년도 CVPR에 발표된 Bag of Tricks 논문 입니다. 개인적으로 이런 논문은 너무 좋습니다. 블로그에서 핵심 내용만 뽑아준 것 같은 느낌이 들기 때문입니다.
  • 이 논문은 네트워크 구조를 고치지 않고 ResNet-50을 기준으로 Trick 들만 적용하여 Accuracy와 학습속도를 높인 방법론을 정리한 내용을 다룹니다.
  • 한번 쯤 들어본 내용들을 정리해서 다루었기 때문에 내용이 크게 어렵지 않을 것이라 생각 됩니다.


Drawing


  • Trick을 모두 적용하였을 때, 위 성능 지표와 같이 성능을 향상할 수 있었습니다.
  • 먼저 논문에서 사용한 다양한 전처리 등은 생략하도록 하겠습니다. 자세한 내용은 논문 또는 위의 참조 링크를 참조하시기 바랍니다.
  • 이 글에서 자세히 다루어 볼 것은 classification 문제 이외에도 충분히 사용할 만한 Trick 내용만 작성하였습니다.


Base line


Drawing



Drawing


  • 논문에서 사용된 기본적인 학습 절차 (base line)은 위 알고리즘을 따릅니다.


Linear scaling learning rate


  • batch size가 증가하게 되면 병렬성이 좋아지고 인/아웃 횟수가 줄어 오버헤드가 줄어드는 장점이 있는 반면 학습이 수렴하는 시간이 느려지는 단점이 있습니다. 따라서 batch size가 작은 학습 방법과 batch size가 큰 학습 방법의 accuracy 결과를 비교하면 batch size를 작게하여 학습하는 경우 더 좋은 성능을 얻는 것을 확인할 수 있습니다.
  • 하지만 batch size를 증가시키지 못하면 전체 학습 시간이 너무 오래 걸리므로 이 문제를 개선하기 위하여 연구가 진행되어 왔습니다.
  • Accurate, large minibatch SGD: training imagenet in 1 hour 논문에서는 batch size가 증가하게 되면, 그 만큼 linear 하게 learning rate를 증가시켜주는 것이 학습하는 데 좋다는 내용을 소개합니다.
  • 추천하는 방식으로는 initial learning rate' ← initial learning rate * batch size / 256입니다. 예를 들어 초기 learning rate가 0.1이고 batch size가 1024이면 0.1 * 1024 / 256 = 0.4로 초기 learning rate를 적용해야 함을 의미합니다. 이 방식을 통해 batch size가 커져서 성능이 떨어지는 문제를 개선할 수 있다고 설명합니다.


Learning rate warm-up


Drawing


  • 다음으로 Learning rate warm-up과 관련된 내용입니다. 보통 learning rate는 초기 설정한 값 (initial learning rate)을 기준으로 학습이 진행될수록 계속 줄여주는 방식을 많이 사용하고 있습니다.
  • 기존 방법과는 조금 다르게 초기에 learning rate를 0으로 설정하고 이를 일정 기간 동한 linear 하게 키워주는 방식을 learning rate warmup 방식으로 소개합니다.
  • 이와 같이 warm-up 과정을 거치는 이유는 학습을 처음에 시작할 때 random한 weight 값을 가지고 시작하게 되는데 이러한 random weight값을 이용하여 학습을 시작하면 학습이 안정화 되는데 불리하다는 점을 개선하기 위함입니다.
  • 위 그래프에서는 5 epoch 동안 조금씩 learning rate를 키워서 initial learning rate 값을 5 epoch에 0.4까지 도달하게 만든 후 점점 learning rate가 줄어들도록 변경하였습니다.
  • 위 방법은 이 글의 뒷부분에 제시된 Cosine Learning Rate Decay 부분과 혼합하여 사용할 수 있으며 그 코드는 다음 링크를 참조하시기 바랍니다.


Zero Gamma in BatchNorm


  • 먼저 Batch Normalization에 대한 글을 읽고 오시길 추천 드립니다.
  • Batch Normalization을 사용하면 대표적으로 다음과 같은 장점이 있습니다.
    • ① 학습 속도를 빠르게 할 수 있습니다.
    • ② weight initialization에 대한 민감도를 감소 시킬 수 있습니다.
    • ③ regularization 효과가 있습니다.


Drawing


  • Batch Normalization을 적용할 때, 위 식과 같이 \(\gamma, \beta\)가 사용되며 학습하면서 결정되는 파라미터입니다. 이 값은 scaleshift를 변경해주는 값으로 입력 차원이 \(k\)이면 두 파라미터의 차원도 \(k\)를 가집니다.
  • 두 파라미터를 사용하는 이유 중의 하나로 Batch Normalization로 인한 activation function의 non-linearity 영향력 감소 문제를 개선하기 위함이 있습니다.


Drawing


  • 예를 들면 위 그림과 같은 시그모이드 함수를 activation function으로 사용하였을 때, Batch Normalization의 결과에 scale과 shift를 적용하지 않으면 위 그림의 빨간색 영역만 그대로 사용할 수 있습니다. 그러면 non-lineariry의 효과가 줄어들게 됩니다. 이 문제를 해결하는 역할로 \(\gamma, \beta\)가 사용됩니다.
  • 이 값을 일반적으로 \(\gamma = 0, \beta = 1\)로 초기화 하고 학습을 하는데 이 논문에서는 \(\gamma = 0\)로 초기화 하는 것을 추천합니다.


Drawing


  • 만약 \(\gamma = 0\)으로 두면 학습 시작할 때, 위 그림과 같은 ResNet 구조에서 Residual Block 내부의 weight에 0이 곱해지므로 무시하게 됩니다. 따라서 Identity mapping (Shortcut)으로만 weight가 전달되어 Identity mapping만 사용하는 매우 간단한 네트워크를 일시적으로 사용할 수 있습니다.
  • 이와 같은 방법을 사용하면 학습 시작할 때 네트워크의 구조를 단순화 함으로써 네트워크의 복잡도를 일시적으로 낮출 수 있고, backpropagation을 통해 의미 있는 weight update가 발생하면 그 때 부터 Residual Block 내부가 사용됨과 동시에 원래 설계한 네트워크의 복잡도를 사용할 수 있습니다.
  • Pytorch의 BatchNorm2d는 다음 링크에서 사용 방법을 알 수 있습니다.
  • BatchNorm2d 함수의 파라미터에 Gamma를 직접 제어하는 파라미터는 없으므로 Gamma에 직접 접근하여 값을 변경해주면 됩니다. 다음과 같습니다.


def SetZeroGammaInBatchNorm2d(bn):
    bn.weight.data = torch.zeros(bn.weight.data.shape, requires_grad=True)
    return bn

bn = nn.BatchNorm2d(3, affine=True)
print(bn.weight, bn.bias)
# Parameter containing:
# tensor([1., 1., 1.], requires_grad=True)
# tensor([0., 0., 0.], requires_grad=True)

bn = SetZeroGammaInBatchNorm2d(bn)
print(bn.weight, bn.bias)
# Parameter containing:
# tensor([0., 0., 0.], requires_grad=True) ← Changed
# tensor([0., 0., 0.], requires_grad=True)


No bias decay


  • 학습을 할 때, L2 Regularization 방법을 통하여 weight의 크기를 줄이는 방법을 많이 사용 합니다. 이 때, weight는 weightbias를 모두 포함하지만 이 논문에서는 오직 weight에만 decay를 주는 것을 권장합니다. (이 또한 휴리스틱한 방법으로 접근하였습니다.)
  • bias의 경우 weight에 비해 파라미터 수가 매우 적으므로 Regularization을 해주지 않아도 overfitting에 대한 문제를 방지할 기여도가 적고 반대로 bias가 잘못 적용될 경우 underfitting이 날 가능성이 있으므로 사용하지 않도록 소개하니다.
  • 참조 논문은 Highly scalable deep learning training system with mixed-precision: Training imagenet in four minutes.입니다.


Low-precision training


  • 참조 논문 : https://arxiv.org/abs/1710.03740
  • NVIDIA의 GPU를 사용할 때, 기본적으로 FP32 (32-bit Floating Point)를 기본적인 데이터 타입으로 사용하고 있습니다. 하지만 FP16을 기본 데이터 타입으로 사용하면 모델이 가벼워져서 학습 시간을 단축할 수 있습니다.
  • V100 GPU의 경우 FP32를 사용할 때, 14 TFLOPS(Tera Floating Operations per Second)의 연산 속도를 가지는 반면 FP16을 사용할 때, 100 TFLOPS의 연산속도를 가지는 것을 확인할 수 있었습니다. 실제 학습을 할 때에도 보통 2 ~ 3배 정도 연산 속도가 빨라지는 것을 경험할 수 있습니다.


Drawing


  • 하지만 딥러닝에서 사용하는 weight의 경우 매우 작은 값을 가지므로 단순히 FP16만을 사용하면 accuracy 성능에 문제가 발생할 수 있습니다.
  • 따라서 위 그림과 같이 Mixed Precision이라는 방법을 이용하여 FP16FP32를 섞어서 사용하며 이는 Pytorch에서 AMP(Automatic Mixed Precision) 이라는 패키지로 지원합니다.
  • 위 그림을 보면 Forward, Backward, Activation 등 계산하여 gradient를 구할 때에는 FP16을 사용하고 optimizer.step()을 이용하여 실제 weight update가 발생할 때에는 FP32를 사용하여 저장합니다. 이 때, FP32로 저장되는 weight를 master weight라고 합니다.
  • 이 때, FP16FP32 값의 범위 차이가 발생하기 때문에, FP16 값이 FP32 값과 같은 precision 범위에서 계산이 될 수 있도록 별도 scaling factor를 따로 저장하고 weight update 시 이 값을 계산 결과인 FP16에 곱해주어 FP32와 값의 범위가 유사해지도록 만들어 주는 방법을 사용합니다.


Cosine Learning Rate Decay


  • Cosine Learning Rate Decay의 형태를 살펴보면 아래 그림과 같이 양의 값을 가지는 Cosine 함수 형태를 가집니다.


Drawing


  • Cosine값은 처음에 천천히 감소하다가 점점 감소량이 커지고 마지막에 로컬 미니멈에 빠지게 되었을 떄, 다시 천천히 감소하게 되는 형태를 나타냅니다.
  • 위 방식을 제안한 논문은 SGDR : Stochastic Gradient Descent with Warm Restarts라는 논문으로 Cosine Learning Rate 스케줄러를 계속 반복시키면서 학습하여 로컬 미니멈에 빠졌을 때, 빠져나올 수 있도록 스케쥴러를 설계하였습니다.
  • 또한 앞에서 다룬 Learning rate warm-up과 함께 Cosine 형태의 Learning rate 스케쥴러를 사용하는 방법을 제시하였습니다.


Drawing


  • 위 그림과 같이 SGDR 또는 이 논문을 참조한 다른 논문에서는 restart는 하지 않고 warm-up + cosine scheduler만 사용하는 경우도 많이 있었습니다.
  • Pytorch에서 SGDR과 같이 Warm-up + Cosine + Restart 를 모두 사용한 코드를 확인하고 싶으시면 아래 링크를 참조해 주시기 바랍니다.


  • 이 이외에도 ResNet-50의 최적화 방법, Knowledge distillation 및 Data Augmentation 방법이 있습니다. 다루지 않는 내용은 논문을 참조해 보시기 바랍니다.