[Tensorflow]전이학습(Transfer Learning)과 미세조정(Fine-tuning)

신동혁·2022년 10월 4일
0

TensorFlow

목록 보기
4/4

1.전이학습이란?

전이학습이란 이미 학습되어 제공되는 모델을 활용하는 방법으로, 모델을 그대로 사용하지는 않고 모델의 feature extractor 부분만 사용하고 estimator는 다르게 사용하는 방법을 의미한다.

쉽게 말해서 개/고양이 분류 모델이 이미 학습까지 되어 제공되었을 때, 이를 이용해 여우/늑대를 분류하고 싶다면 개/고양이 분류에 대한 학습이 끝난 모델을 차용하되 모델에서 추론하는 부분은 차용하지 않고 직접 내가 가지고 있는 여우/늑대 데이터에 맞게 추론 부분을 구해주는 것이다.

2.Pretrained Model

위에서 언급한 미리 학습되어 제공되는 모델을 pretrained model이라고 한다. 이런 pretrained model을 그대로 사용하는 예제를 한 번 알아본다. 나는 keras에서 제공하는 pretrained model을 사용했다. keras의 pretrained model은 기본적으로 Image Net의 데이터셋으로 학습된 모델이다.

  • keras에서 제공하는 pretrained model
    • tensorflow.keras.applications 패키지를 통해 제공
    • Modules
      • 각 모델별 입력 Image 전처리 함수 제공
    • Functions
      • 각 모델 생성함수
    • 모델 생성함수의 주요 매개변수
      • weights: 모델의 학습된 weight 지정.
        • 기본값- 'imagenet'. ImageNet 데이터셋으로 학습된 weight를 가진 모델 생성
      • include_top: fully connected layer(분류기)를 포함할지 여부. True 포함시킴, False: 포함 안 시킴
        • False를 지정하면 Feature Extractor인 Convolution Layer들로만 구성된 모델이 생성된다.
      • input_shape: Input(입력) 이미지의 크기 shape. 3D 텐서로 지정. (높이, 너비, 채널). 기본값: (224,224,3)

예제 ) pretrained model 그대로 사용해보기( 전이학습 아님! )

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import applications
from tensorflow.keras.preprocessing.image import load_img, img_to_array

import numpy as np

vgg16 = applications.VGG16() # applications 패키지를 통해 pretrained 된 VGG16모델 불러오기

# pretrained model에 새로운 이미지 테스트하기 전에 전처리하는 과정 담은 함수
def get_input_tensor(path):
    """
    입력받은 path의 이미지를 읽어서 전처리한 뒤 반환.
    [parameter]
        path: str - 추론할 이미지 경로
    [return]
        Tensor: 전처리 결과
    """
    img = load_img(path, target_size=(224,224))
    img_arr = img_to_array(img)
    
    input_tensor = applications.vgg16.preprocess_input(img_arr) # VGG16을 학습하기 전에 전처리한 방식대로 전처리 해주는 함수
    return input_tensor[np.newaxis, :] # (h,w,c)이 아닌 (n,h,w,c) 형식이 되어야 하므로 np.newaxis로 n(1)값을 넣어줌.
    
# pretrained model에 테스트해볼 이미지 파일 경로 및 위에서 만든 함수로 해당 이미지 파일 불러와서 전처리하기
img_path = r"test_img/car1.jpg"
input_tensor = get_input_tensor(img_path) # 위에서 만든 함수로 이미지를 전처리하고 돌려받기

pred = vgg16.predict(input_tensor) # 전처리된 이미지를 pretrained model을 이용해 추론

label = np.argmax(pred,axis=-1) # 추론한 확률 중 가장 높은 확률의 index
label_name = applications.imagenet_utils.decode_predictions(pred, top=5) # 추론한 확률 중 가장 높은 확률 순 5위를 뽑아냄.
label, label_name

3.전이학습해보기

위에서 있는 코드는 pretrained model을 가져와 그대로 이용했다. 전이학습은 이와 다르게 pretrained model을 가져오긴 하지만 Feature extraction(Backbone, Base network) 부분만 그대로 가져와 사용하고 Estimator(classification) 부분은 직접 학습시켜 사용한다.

이때 Computer Vision 문제의 경우 Bottom 쪽의 Convolution Layer(Feature Extractor)들은 이미지에 나타나는 일반적인 특성을 추출하므로 다른 대상을 가지고 학습했다고 하더라도 재사용할 수 있다. 하지만 Top 부분 Layer 부분은 특히 출력 Layer의 경우 대상 데이터셋의 목적에 맞게 변경 해야 하므로 재사용할 수 없다.

예제 ) 개 고양이 분류 전이학습으로 구현해보기

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import applications
from tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array

import numpy as np

np.random.seed(0)
tf.random.set_seed(0)

# 하이퍼파라미터 상수값 설정
LEARING_RATE = 0.001
N_EPOCHS = 20
N_BATCHS = 100
IMAGE_SIZE = 224

# 개 고양이 이미지 로딩하고 전처리 후 추론하는 함수
def predict_cat_dog(img_path, model, preprocess_input):
  """
  이미지 경로, 모델을 받아서 추론한 뒤 그 결과를 반환하는 함수
  [parameter]
    img_path: 추론할 이미지의 경로
    model: 추론할 딥러닝 네트워크 모델 객체(학습된 모델)
    preprocess_input: 전처리 함수
  [return]
    tuple: (예측확률, 예측한 Label index, 예측한 Label name)
  """
  class_names = ["고양이", "개"]
  # 이미지 로딩
  img = load_img(img_path, target_size=(IMAGE_SIZE, IMAGE_SIZE))
  # ndarray로 변환
  img_arr = img_to_array(img)[np.newaxis, ...]
  # 전처리
  input_tensor = preprocess_input(img_arr)
  # 추론
  pred = model.predict(input_tensor)
  pred_proba = pred[0][0]
  pred_class = int(np.where(pred>=0.5, 1, 0))
  pred_class_name = class_names[pred_class]

  return pred_proba, pred_class, pred_class_name

# ImageDataGenerator 생성하는 함수
def get_generator(preprocess_input):
  """
  train/validation/test dataset ImageDataGenerator를 생성해서 변환하는 함수
  train set - Image Augmentation 적용
  validation/test set - Image Augmentation 적용안함
  [parameter]
    preprocess_input: 함수 - 모델의 전처리 함수(rescale 설정대신 사용)
  [return]
    Tuple: (train_dataset, validation_dataset, test_dataset)
  """
  train_dir="data/cats_and_dogs_small/train/"
  validation_dir="data/cats_and_dogs_small/validation/"
  test_dir="data/cats_and_dogs_small/test/"

  train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input, # 제너레이터로 이미지 제공할 때 어떻게 전처리 해줄지 설정
                                     rotation_range=30,
                                     width_shift_range=0.2,
                                     height_shift_range=0.2,
                                     horizontal_flip=True,
                                     fill_mode="constant"
                                     )
  val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
  test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)

  train_iter = train_datagen.flow_from_directory(train_dir, 
                                                 target_size=(IMAGE_SIZE, IMAGE_SIZE),
                                                 batch_size=N_BATCHS,
                                                 class_mode="binary")
  val_iter = val_datagen.flow_from_directory(validation_dir,
                                             target_size=(IMAGE_SIZE, IMAGE_SIZE),
                                             batch_size=N_BATCHS,
                                             class_mode="binary")
  test_iter = test_datagen.flow_from_directory(test_dir,
                                            target_size=(IMAGE_SIZE, IMAGE_SIZE),
                                            batch_size=N_BATCHS,
                                            class_mode="binary")
  return train_iter, val_iter, test_iter

# 위 ImageDataGenerator 생성하는 함수로 dataset들 생성
train_dataset, val_dataset, test_dataset = get_generator(applications.vgg16.preprocess_input)

from tensorflow.keras import layers
# Feature extraction(backbone)은 그대로 가져오고 estimator만 새롭게 학습하는 모델을 정의한다.
def create_model_1(backbone):
  """
  Feature Extract 네트워크 모델을 받아서 고양이/개를 분류하는 모델을 생성
  backbone/Feature extract/Conv base -> 특징추출하는 네트워크를 말한다. => 여기가 pretrained model 사용하는 부분
  head/estimator -> 추론을 담당하는 네트워크 => 여기가 우리가 구현하는 부분
  [parameter]
    backbone: Model - Pretrained backbone 네트워크모델
  [return]
    model 객체 - 완성된 모델을 반환
  """
  model = keras.Sequential()

  backbone.trainable = False # Feature extraction(backbone)은 그대로 가져와야 하므로 train되지 않도록 False로 설정
  model.add(backbone) # 위에서 trainable에 대한 설정이 끝난 후 backbone 추가
  model.add(layers.GlobalAveragePooling2D()) # 가중치가 너무 많아질 수 있으므로 feature map을 1x1 형태로 만들어 가중치가 채널수로만 이루어지도록 함.
  model.add(layers.Dense(units=1, activation="sigmoid"))
  return model
 
backbone = applications.VGG16(include_top=False, input_shape=(IMAGE_SIZE,IMAGE_SIZE,3))
model1 = create_model_1(backbone)


# 컴파일
model1.compile(optimizer=keras.optimizers.Adam(LEARING_RATE),
               loss="binary_crossentropy",
               metrics=["accuracy"])

# 콜백설정
import os
base_path = "/drive/Mydrive/딥러닝_소스/saved_models"
save_path = os.path.join(base_path, "cat_dog_vgg16")
mc_cb = keras.callbacks.ModelCheckpoint(save_path, save_best_only=True, monitor="val_loss",verbose=2)

# 학습
hist = model1.fit(train_dataset,
                  epochs=N_EPOCHS,
                  validation_data=val_dataset,
                  steps_per_epoch=len(train_dataset),
                  validation_steps=len(val_dataset),
                  callbacks=[mc_cb])
# 평가
loss, acc = model1.evaluate(test_dataset)
loss, acc

# 새로운 이미지로 추론(위에서 만들어 놓은 함수 이용)
pred = predict_cat_dog(새로운 이미지 경로, model1, applications.vgg16.preprocess_input)
print(pred)

4.Fine-tuning(미세조정)이란?

위에서 설명한 전이학습은 pretrained된 모델을 불러와 backbone은 그대로 사용했다. 하지만 이때 backbone도 내 데이터에 맞게 학습시키고 싶다면 어떻게 할까? 이때 사용하는 것이 fine-tuning이다. fine-tuning은 pretrained된 모델의 backbone에서 bottom layer(input layer와 가까운 layer)는 frozen 상태로 고정(학습시키지 않음)시켜놓고 top layer는 고정시키지 않고 학습하는 방법과 pretrained된 모델에서 backbone과 추론기(classifier) 모두를 학습하는 방법이 존재한다.

코드는 위에서 설명한 전이학습과 거의 유사하다. 특정 layer만 Frozen시키는 방법만 추가했다고 보며 된다. 해당 부분의 코드만 확인해본다.

def create_model2():
    '''
    backbone으로 VGG16 모델을 사용. 
    block5_conv3 레이어는 trainable하게 설정하고 나머지는 Frozon 시킨 뒤 fine tuning 한다.
    '''
    # backbone network 설정
    backbone = applications.VGG16(include_top=False, input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3))
    
    is_trainable = False  # Trainable 설정할 bool 값.
    
    for layer in backbone.layers: # backbone에서 layer들 하나씩 꺼내서 순회
        if layer.name == 'block5_conv3':
            is_trainable = True
        layer.trainable = is_trainable

    # 모델 정의
    model = keras.Sequential()
    model.add(backbone)
    model.add(layers.GlobalAveragePooling2D())
    model.add(layers.Dense(units=1, activation='sigmoid'))

    return model
profile
개발취준생

0개의 댓글