텐서플로 케라스(Tf.keras)에서 전이학습(Transfer Learning) 하기
Nov. 1, 2022, 5:57 p.m.
1. 전이학습이란
어느 분야에 딥러닝을 사용하기 위해선 모델을 구축하고 학습에 사용할 데이터 셋을 준비해야 합니다. 그러나 보통은 데이터가 많이 부족합니다. 그래서 다양한 리샘플링 과정을 통해 데이터를 늘려서 학습을 진행하죠.
그러나 세상에는 이미 엄청나게 큰 데이터셋으로 학습이 완료된 모델들이 많습니다. ImageNet을 통해 1000가지의 이미지를 분류하는 VGG, LeNet 등등이 이것이죠.
이 모델들은 이미지를 분류하기 위해서 이미지의 다양한 특징을 추출하는 방법을 학습했습니다. 다른 이미지 분류 문제를 해결하기 위해 이러한 모델을 사용해볼 순 없을까요? 그 해법에 대해서 수년간 연구가 진행되었고 그것이 전이학습입니다.
전이학습이란 어떤 도메인에서 충분한 데이터로 학습이 완료된 모델을 fine-tuning 해서 비슷하지만 다른 도메인에 적용하는 것을 말합니다.
이번 포스팅에서는 CNN 기반 이미지 분류 모델의 전이학습에 대해서 알아보려 합니다.
2. CNN 신경망의 특징 추출 기능
대부분의 CNN 신경망의 마지막은 FCN, 완전 연결 계층입니다. 이 계층을 최종 출력이 나오게 되죠. 이것을 예측 계층이라고 하겠습니다.
이 마지막 계층을 제거한다면 어떨까요? 그렇다면 CNN 신경망은 예측 계층에 전달할 이미지의 특징을 추출한 결과를 반환 할 것입니다. 이것을 이미지의 고차원 특징 맵이라고 할 수 있습니다.
즉 마지막 계층을 제거한 신경망은 이미지의 특징을 파악해서 고차원의 벡터로 출력하는 특징 추출기의 역할을 할 수 있습니다.
.
이것에 얕은 분류기를 연결하여 다른 도메인에서의 이미지 학습에 사용할 수도 있겠죠.
또한 꼭 마지막 계층만 제거하는 것이 아니라 필요에 의해 원하는 만큼의 계층만 남겨 진행할 수도 있겠습니다.
이것은 기존 신경망이 학습한 도메인과 내가 적용하려는 도메인 간의 차이가 얼마나 되는지, 훈련할 샘플이 얼마나 많은지에 따라서 결정을 하면 됩니다.
도메인이 많이 유사하면 기존 CNN의 학습된 정보를 많이 이용할 수 있겠죠. 그러면 최종 계층만 제거해서 학습해도 무방할 것입니다.
.
그러나 만약 도메인이 많이 다르다면 기존 CNN의 고차원적인 학습 정보는 도움이 안될 것입니다. 대신에 초반 계층에서 이미지의 윤곽선이나 형태들을 추출하는 정도는 재사용할 수 있겠죠. 이럴때는 계층을 많이 제거하고 초반 계층만 이용할 수도 있습니다.
.
그러나 이미지 분류를 위해 학습된 기존 CNN을 아예 다른 음성인식 같은 도메인에 사용한다면 전이학습의 효과를 전혀 볼 수 없을 것입니다.
.
3. 사전 학습된 모델 계층 제거하기
먼저 케라스에서 사전 훈련된 모델을 가져와 보겠습니다.
model = tf.keras.applications.efficientnet.EfficientNetB3(weights='imagenet', classes=1000,
input_shape=(300 , 300, 3))
weights를 imagenet으로 설정하므로써 사전 학습된 가중치를 가져왔습니다.
이 모델의 레이어를 살펴보겠습니다.
model.summary()
Model: "efficientnetb3"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 300, 300, 3 0 []
)]
rescaling (Rescaling) (None, 300, 300, 3) 0 ['input_1[0][0]']
normalization (Normalization) (None, 300, 300, 3) 7 ['rescaling[0][0]']
stem_conv_pad (ZeroPadding2D) (None, 301, 301, 3) 0 ['normalization[0][0]']
stem_conv (Conv2D) (None, 150, 150, 40 1080 ['stem_conv_pad[0][0]']
)
.
.
.
top_activation (Activation) (None, 10, 10, 1536 0 ['top_bn[0][0]']
)
avg_pool (GlobalAveragePooling (None, 1536) 0 ['top_activation[0][0]']
2D)
top_dropout (Dropout) (None, 1536) 0 ['avg_pool[0][0]']
predictions (Dense) (None, 1000) 1537000 ['top_dropout[0][0]']
==================================================================================================
Total params: 12,320,535
Trainable params: 12,233,232
Non-trainable params: 87,303
__________________________________________________________________________________________________
effnet은 굉장히 레이어를 많이 쌓기 때문에 길어서 중간에 생략했습니다. 마지막 레이어를 보시면 Dense 레이어로 1000개의 분류를 위한 출력을 하는 것을 볼 수 있습니다.
전이학습에 사용하기 위해서 마지막 Dense 레이어를 제거해 보겠습니다.
다양한 방법이 있지만 여기서는 케라스 모델의 함수형 선언을 사용해 보겠습니다.
feature_extractor = tf.keras.Model(inputs=model.input, outputs=model.get_layer('avg_pool').output)
model.summary()를 통해서 레이어의 이름을 확인하고 model의 input을 input층으로, 마지막 Dense 레이어와 Dropout 전의 avg_pool 레이어를 최종 출력층으로 하는 특징 출력기를 만들 수 있습니다.
다시 확인해 보면,
feature_extractor.summary()
.
.
.
top_bn (BatchNormalization) (None, 10, 10, 1536 6144 ['top_conv[0][0]']
)
top_activation (Activation) (None, 10, 10, 1536 0 ['top_bn[0][0]']
)
avg_pool (GlobalAveragePooling (None, 1536) 0 ['top_activation[0][0]']
2D)
==================================================================================================
Total params: 10,783,535
Trainable params: 10,696,232
Non-trainable params: 87,303
__________________________________________________________________________________________________
보시면 마지막 계층이 avg_pool 인 것을 확인할 수 있습니다.
이런식으로 모델의 계층에서 원하는 만큼만 취해서 사용할 수 있는데 사실 케라스에서 제공하는 기본 신경망은 include_top 매개변수를 이용해서 바로 특징 추출기로 가져올 수 있습니다.
feature_extractor = tf.keras.applications.efficientnet.EfficientNetB3(weights='imagenet', include_top=False,
classes=1000, input_shape=(300, 300, 3))
feature_extractor.summray()
.
.
.
top_conv (Conv2D) (None, 10, 10, 1536 589824 ['block7b_add[0][0]']
)
top_bn (BatchNormalization) (None, 10, 10, 1536 6144 ['top_conv[0][0]']
)
top_activation (Activation) (None, 10, 10, 1536 0 ['top_bn[0][0]']
)
==================================================================================================
Total params: 10,783,535
Trainable params: 10,696,232
Non-trainable params: 87,303
__________________________________________________________________________________________________
보시면 include_top을 False로 했을때 마지막 CNN 레이어의 출력까지만 포함되어 있는 것을 볼 수 있습니다.
이런 식으로 사전 학습된 모델의 계층을 일부 제거해서 재사용 할 수 있습니다.
.
4. 계층 제거한 모델에 새 계층 이식하기
그럼 이제 새 도메인에 적용하기 위해서 새로운 계층을 특징 추출기 뒤에 추가할 차례입니다.
역시 케라스의 함수형 모델 선언을 이용하도록 하겠습니다.
간단하게 Dense층을 새로 추가하겠습니다.
new_dense = tf.keras.layers.Dense(12)(feature_extractor.output)
new_model = tf.keras.Model(feature_extractor.input, new_dense)
new_model.summary()
.
.
.
top_activation (Activation) (None, 10, 10, 1536 0 ['top_bn[0][0]']
)
avg_pool (GlobalAveragePooling (None, 1536) 0 ['top_activation[0][0]']
2D)
dense (Dense) (None, 12) 18444 ['avg_pool[0][0]']
==================================================================================================
Total params: 10,801,979
Trainable params: 10,714,676
Non-trainable params: 87,303
__________________________________________________________________________________________________
마지막에 Dense가 추가되었네요.
정말로 편리한 케라스의 함수형 선언 덕분에 1000개의 카테고리로 이미지를 분류하는 모델을 가중치를 유지한 채로 12개로 분류하는 모델로 쉽게 변형 할 수 있습니다.
.
4. 계층 고정을 통한 선택적 학습
이제 모델을 학습할 준비가 완료되었습니다. 여기서 그냥 학습을 실행하게 된다면 기껏 가져온 가중치가 훼손되어 전이학습의 의미가 없어질 수도 있습니다. 그러므로 계층 고정이 필요합니다.
각 계층은 layer.trainable 특성을 통해 계층을 고정할 지 말지 정할 수 있습니다.
계층의 목록은 model.layers로 가져올 수 있으며 for문을 돌면서 각 계층을 고정할 지 말지 정해주시면 되겠습니다.
.
이상으로 케라스로 전이학습을 하는 방법에 대해 알아봤습니다. 전이학습은 초대용량 데이터와 리소스로 사전 학습된 모델을 효과적으로 새로운 도메인에 적용할 수 있는 방법이므로 다양한 문제에 모델을 적용하고 싶을 때 꼭 고려해볼만 한 솔루션입니다. 또한 이미지 도메인에 국한되어 있지 않고 모든 분야의 딥러닝에서 사용할 수 있으니 알아두는게 좋다고 생각합니다.
케라스 인공지능 텐서플로 keras tensorflow 전이학습