본 포스팅은 다음 과정을 정리 한 글입니다.
Custom Models, Layers, and Loss Functions with TensorFlow
www.coursera.org/specializations/tensorflow-advanced-techniques
지난 시간 리뷰
컴퓨터 비전을 위한 강력한 모델 중에 하나는 ResNet-18인데요. 이것은 매우 깊은 네트워크의 훈련을 용이하기 위해 Residual 방법을 사용하여 설계된 네트워크입니다.
이번 시간에는 ResNet-18이 어떻게 설계되어 있는지와 subclass를 통해 이를 구축하는 방법에 대해 살펴보겠습니다.
ResNet구조는 아래와 같습니다. 7x7 컨볼루션(Convolution)으로 시작하여 배치 정규화(Batch Norm)된 레이어와 3x3 최대 풀링(Max Pooling)이 이어집니다.
그 후에는 파란색으로 표시된 Identity ResNet 블록이라고 하는 논리 블록이 반복됩니다.
보시다시피 컨볼루션을 통과하는 main 경로가 있고 shortcut이 있는 Residual 네트워크 입니다.
배치 정규화된 레이어의 결과와 X가 그대로 합쳐져서 Relu를 통과합니다.
Identity라는 이름은 입력 x를 전혀 변경하지 않은 shortcut이 있다는 사실에서 나온 것입니다. 모든 레이어들을 통과하지 않고 identity transformation을 수행한 것이라고 볼 수 있습니다.
또한 거의 동일하지만 1x1 컨볼루션 레이어를 통과하고 오는 shortcut이 있는 주황색 유형이 있습니다.
따라서 파란색 resnet 블록이 2번 연달아 오고 주황색 resnet 블록과 파란색 resnet 블록이 연결된 블록이 3번 반복됩니다.
크고 복잡한 모델이므로 identity 블록만 사용하는 Mini ResNet을 먼저 만들어 보려고 합니다.
이를 사용하여 간단한 분류기를 만들어보겠습니다.
Identity 블록의 코드는 아래와 같습니다.
여기서는 모델 클래스를 확장하여 이 코드가 우리가 원할 경우 실제로 독립형 모델로 동작할 수 있도록 하고자 합니다.
init 함수에서 각 레이어를 정의합니다. 여기서는 전체 모델 아키텍처 자체가 아니라 레이어 각각을 정의하고 선언하는 것뿐입니다.
3x3 컨볼루션, 모델 시작 부분의 레이어 및 배치 정규화가 여기에 정의됩니다.
필터 수와 커널 크기를 매개 변수화 하여 받아옵니다.
3x3 컨볼루션 및 배치 정규화는 다음은 activation function이 와야 하는데 relu를 사용하여 레이어를 활성화할 수 있습니다.
여기에서 activation function을 정의하고 call에서 self.act를 호출해 봅시다.
우리가 만드는 것은 Residual 네트워크 이므로 main경로와 shortcut경로가 추가가 되어야 합니다. 여기에 add 레이어를 추가하여 이 것을 구현할 수 있습니다.
그런 다음 call function에서 functional API를 사용하여 레이어의 순서를 연결해 줍니다.
먼저 입력 (input_tensor)로 시작을 해줍니다.
그다음에는 첫 번째 컨볼루션 레이어, 첫 번째 배치 정규화, 그리고 첫 번 째 Activation 레이어가 이어집니다. self.act라는 relu 레이어를 어떻게 재사용하는지 잘 보시기 바랍니다.
다시 Mini Residual 모델을 살펴보겠습니다.
방금 코드에서 생성한 IdentityBlock이 파란색으로 표시가 됩니다.
이제 나머지 컨볼루션, 풀링, 배치 정규화와 같은 나머지 모델을 정의해야 합니다. 이는 IdentityBlock 외부에서 연결되어야 합니다.
그럼 이제 전체 ResNet을 완성시켜 봅시다.
모델 내의 각 레이어를 정의하는 init 함수부터 시작해봅시다.
이전과 마찬가지로 모델 클래스를 확장하도록 합시다. (tf.keras.Model)
초기 7x7 컨볼루션 레이어를 다음과 같이 정의해 줍니다.
self.bn에 배치 정규화를 정의해주고
IdentityBlock에서 했던 것처럼 self.act에 Activation 'relu'를 정의해 줍니다.
그다음 3x3 최대 풀링을 또한 정의해 줍니다.
그다음으로 아까 정의한 IdentityBlock을 정의해주는데 매개변수 필터로 64, 커널로 3을 넣어줍니다.
그러면 IdentityBlock의 init이 호출될 때 filter 64, kernel이 3으로 설정된 레이어들을 정의하게 될 것입니다.
그런 다음 글로벌 평균 풀링을 정의해 줍니다.
이 모델은 여러 클래스에 대한 분류기의 역할을 할 것이기 때문에 몇 개를 분류할지에 대한 num_classes값을 매개변수로 받습니다.
예를 들어, 10개의 클래스가 있는 MNIST를 분류하도록 훈련시키려는 경우 최종 레이어를 10개의 뉴런이 있는 Dense 레이어로 하드 코딩하는 것이 아니라 매개 변수로 전달받은 num_classes의 값만큼 뉴런을 생성하는 것입니다.
이러한 방식은 매우 유연한 모델을 만드는 정말 좋은 방법입니다.
call에서 이제 최종적으로 아키텍처를 구성합니다.
inputs을 받아서 7x7 컨볼루션에 연결하고 배치 정규화, 최대 풀링 계층, 2개의 IdentityBlock으로 이어집니다. 그리고 글로벌 평균 풀링을 거쳐 분류에 사용되는 Dense 레이어까지 전달이 됩니다.
그럼 이제 이 모델을 호출하는 방법에 대해 살펴봅시다.
우리가 구현한 Mini ResNet을 호출하는 방법입니다.
MNIST가 10개의 클래스를 사용하기 때문에 매개 변수로 10을 전달을 합니다.
tfds에서 로드할 수 있는 MNIST 데이터 세트를 사용하여 훈련을 진행하고
ResNet은 모델의 subclass 이므로 model.fit을 사용할 수 있는데, 따라서 resnet.fit을 호출하여 모델을 학습시킵니다.
이것이 바로 model API를 사용하여 사용자 지정 모델을 구성하는 방법입니다.
긴 글 읽어주시느라 고생하셨습니다.
감사합니다.
댓글