데이터사이언스/머신러닝

전이학습(Transfer learning)과 Fine-tuning

누군가의 이야기 2021. 11. 21. 18:38
728x90

 

인공지능론 프로젝트를 준비하면서

CNN을 활용한 분류 모델을 구축하려는데,

처음부터 가중치를 학습시키지 않고 전이학습을 통해 다른 모델의 가중치를 끌어다 쓴다고 한다.

생소한 개념이기에 아래 링크 내용을 정리해보았다.

실상 야매로 번역한 수준이니 링크를 직접 들어가보는게 보다 정확하다.

https://keras.io/guides/transfer_learning/

 

Keras documentation: Transfer learning & fine-tuning

Transfer learning & fine-tuning Author: fchollet Date created: 2020/04/15 Last modified: 2020/05/12 Description: Complete guide to transfer learning & fine-tuning in Keras. View in Colab • GitHub source Setup import numpy as np import tensorflow as tf fr

keras.io

 

전이 학습이란,

하나의 문제에 대해 학습한 특징을 취하여 유사한 새로운 문제에 활용하는 것으로 구성된다.

예를 들어 ImageNet 데이터셋을 학습한 모델의 특징은, 다른 분류 모델을 구축하는 데에 유용할 수 있다.

전이 학습은 일반적으로 새로운 데이터셋의 데이터가 너무 적어서 처음부터 전체 모델을 학습할 수 없을 때 사용되며, 이러한 경우 데이터 증가가 매우 중요하다.

통상 전이학습의 워크플로우는 아래와 같다.

 

1. 이전의 학습된 모델의 레이어를 가져온다.

2. 정보가 파괴되는 것을 방지하기 위해 고정시킨다.(Freezen)

3. 학습 가능한 층(trainable layers)을 고정시킨 층 위에 쌓는다. -> 현재 데이터셋을 고정된 모델에 적용시키기 위한 층

4. 학습시킨다.

 

전이학습을 활용하기 위해 trainable attribute를 먼저 이해할 필요가 있다.

각 층과 모델에는 3가지 가중치 특성이 존재하는데,

가중치(weight): 모든 층의 가중치변수를 의미함.

trainable_weights: 학습 중에 (경사하강법 등을 통해)오차를 줄이는 방향으로 업데이트 되는 가중치를 의미.

non_trainable_weights학습에 이용되지 않아야 하는 가중치들이며, 보통 forward pass 때 업데이트 된다.

 

케라스를 활용해서 전이학습에 대해 알아볼 예정

import numpy as np
import tensorflow as tf
from tensorflow import keras

 

예시) 2개의 trainable_weights를 갖는 dense layer

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 2
non_trainable_weights: 0

 

일반적으로 모든 가중치는 trainable_weights이며,

non_trainable_weights를 갖는 층은 배치정규화층 뿐이다. 

각 층과 모델은 trainable 특성을 가질 수 있는데, 이를 false로 두면 해당 층의 가중치는 학습할 수 없게 변하고

이를 Freezen(고정시키는 행위)이라 한다. 고정시키면 학습이나 경사하강 때 변화하지 않는다.

 

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights
layer.trainable = False  # Freeze the layer

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 0
non_trainable_weights: 2

 

위 개념이 케라스에서 전이학습 워크플로우를 실행하는데 필수 개념이라고 한다.

1. 기반이 될 모델과, 기존에 학습이 된 가중치를 가져온다.

2. trainable = false로 두면서 기존 모델을 freezen 시킨다.

3. 기존 모델 위에 새로운 층을 쌓는다.

4. 내가 하고자 하는 데이터셋을 사용하여 학습 시킨다.

 

위 방법보다 가벼운 수준의 워크플로우도 있다.

1. 기반이 될 모델과, 기존에 학습이 된 가중치를 가져온다.

2. 내가 사용할 데이터셋을 활용해 한 번 실행한 출력값을 기록한다. 이를 feature extraction이라 한다.

3. 출력값을 활용해 작은 모델을 구축한다.

이 개념의 핵심은 기존 모델을 한 번 실행한다는 것이고, 빠르고 비용 절감의 이점이 있다.

 

아래는 이해를 돕기 위한 코드를 활용한 예제

이전에 ImageNet 데이터를 통해 학습된 가중치를 불러오고 freezen 시킨다.

base_model = keras.applications.Xception(
    weights='imagenet',  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False)  # Do not include the ImageNet classifier at the top.
    
    base_model.trainable = False

이후 새로운 모델을 기존 모델 위에 쌓는다.

inputs = keras.Input(shape=(150, 150, 3))
# We make sure that the base_model is running in inference mode here,
# by passing `training=False`. This is important for fine-tuning, as you will
# learn in a few paragraphs.
x = base_model(inputs, training=False)
# Convert features of shape `base_model.output_shape[1:]` to vectors
x = keras.layers.GlobalAveragePooling2D()(x)
# A Dense classifier with a single unit (binary classification)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

새로운 데이터를 활용하여 학습시킨다.

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)

 

Fine-Tuning이란

위 과정을 거친 모델을 unfreezen 시키고 더 좋은 모델을 찾기 위해 학습시키는 과정이다.

여기서 작은 학습률(learning rate)을 사용하는 것이 매우 중요한데,

새로운 데이터는 데이터셋의 크기가 작기 때문에 오버피팅을 방지하기 위함이다.

아래는 fine-tune 하는 과정

# Unfreeze the base model
base_model.trainable = True

# It's important to recompile your model after you make any changes
# to the `trainable` attribute of any inner layer, so that your changes
# are take into account
model.compile(optimizer=keras.optimizers.Adam(1e-5),  # Very low learning rate
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])

# Train end-to-end. Be careful to stop before you overfit!
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)
728x90