본문 바로가기
컴퓨터비전/CNN

[딥러닝] 전이 학습(Transfer Learning)

by PIAI 2022. 3. 15.

전이 학습이란?

 

일반적으로 CNN 기반의 딥러닝 모델을 제대로 훈련시키려면 많은 수의 데이터가 필요합니다. 근데 그것은 쉽지 않은 일이지요. 방법도 어려울뿐더러 시간의 제약도 많이 있습니다.

 

이러한 현실적인 문제를 해결한 것이 전이학습입니다. 전이학습이란 이미 충분한 데이터와 여러 연구와 실험으로 만들어진 모델을 학습한 가중치를 가지고와 우리 모델에 맞게 재보정해서 사용하는 것입니다.

 

  • 이 글에서는 Keras Applications으로 예시를 들건데 사용 가능한 Pretrained 모델은 대부분 ImageNet 데이터 셋으로 사전 훈련된 모델입니다.
  • ImageNet은 1000개의 class들에 대한 이미지를 분류합니다.
  • 최종 Classification layer가 1000개의 unit의 softmax형태로 되어 있습니다.
  • 입력 이미지는 대부분 244 x 244 입니다.

아래에서는 이제 Pretrained된 모델 하나 VGG를 살펴보겠습니다.

 

VGG16

from tensorflow.keras.applications import VGG16

model = VGG16()
model.summary()

VGG16의 16의 의미는 16개의층(Conv2D(13개) + Dense(3개))가 있다는 의미입니다. 파라미터를 살펴보겠습니다.

  • include_top: (True, False), Classification Layer의 포함여부입니다.
  • weights: ('imagenet', None) imagenet의 가중치의 포함여부입니다.
  • input_tensor: tensor형식으로 input데이터를 넣을지 여부입니다.
  • input_shape: tensor가 아닌 튜플형식으로 ex) (x, y, 3) 
model = VGG16(input_shape=(32, 32, 3), include_top=False, weights='imagenet')
model.summary()

include_top=False로 해서 Classification Layer를 제거하였고, input_shape를 기존 모델의 (244, 244, 3)이 아닌 (32, 32, 3)으로 삽입하였습니다. 모델은 Funtional API이고 model.output을 하면 마지막 출력 결과가 반환됩니다.

 

그럼 이제 input_shape을 (32, 32, 3)이고, classification layer를 제거하고 커스터마이징하고, 가중치는 imagenet으로 전이학습하고, 마지막 출력을 softmax로 10가지분류를 하겠습니다.

 

from tensorflow.keras.layers import GlobalAveragePooling2D, Dense
from tensorflow.keras.models import Model

model = VGG16(input_shape=(32, 32, 3), include_top=False, weights='imagenet')
output = model.output

x = GlobalAveragePooling2D()(output)
x = Dense(50, activation='relu')(x)
output = Dense(10, activation='softmax', name='output')(x)

model = Model(inputs=model.input, outputs=output)
model.summary()

이제 전이학습하고 커스터마이징하여 모델을 만들었으니 Dataset로 훈련시킬일만 남았습니다. Dataset로 쓸 예제는 keras.datasets에 내장되어있는 cifar10을 쓸것입니다. 훈련데이터 50000개와, 테스트데이터 10000개, 분류 10개로 이루어져 있습니다.

 

cifar10

먼저 cifar10의 데이터세트를 불러와 원핫인코딩 시키고 훈련세트를 검증세트와 분리 시키겠습니다. 원핫인코딩 시키는 이유는 나중에 모델을 컴파일할때 loss에서 categorical_crossentropy를 하기 위함입니다.

from tensorflow.keras.datasets import cifar10

(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
print(train_images.shape, train_labels.shape, test_images.shape, test_labels.shape)

import numpy as np
import pandas as pd

from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

train_oh_labels = to_categorical(train_labels)
test_oh_labels = to_categorical(test_labels)
tr_images, val_images, tr_oh_labels, val_oh_labels = train_test_split(train_images, train_oh_labels, test_size=0.15)

print(tr_images.shape, tr_oh_labels.shape, val_images.shape, val_oh_labels.shape, test_images.shape, test_oh_labels.shape)

keras의 ImageDataGenerator로 훈련데이터를 데이터 증강시켜 학습하도록 하겠습니다. 

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping

train_gen = ImageDataGenerator(
    horizontal_flip=True,
    rescale=1/255.0
)
# 검증용 데이터 세트는 scale만 바꾸어주고 augmentation 적용하면 안됨
val_gen = ImageDataGenerator(rescale=1/255.0)
test_gen = ImageDataGenerator(rescale=1/255.0)

flow_tr_gen = train_gen.flow(tr_images, tr_oh_labels, batch_size=64, shuffle=True)
flow_val_gen = val_gen.flow(val_images, val_oh_labels, batch_size=64, shuffle=False)
flow_test_gen = test_gen.flow(test_images, test_oh_labels, batch_size=64, shuffle=False)

model.compile(optimizer=Adam(0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# 3번 반복내에 validation loss가 줄어들지 않으면 learning rate를 0.2 감소
lr_cb = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=3, mode='min', verbose=1)
# 5번 반복내에 validation loss가 줄어들지 않으면 강제종료
st_cb = EarlyStopping(monitor='val_loss', patience=5, mode='min', verbose=1)

result = model.fit(flow_tr_gen, epochs=50, validation_data=flow_val_gen, callbacks=[lr_cb, st_cb])

model.evaluate(flow_test_gen)

15, 19번째 epoch에서 learning rate가 줄어들다가 21번째에서 early stopping이 되었습니다.

댓글