본문 바로가기
인공지능

컴퓨터비전 전이학습 Computer Vision, Transfer Learning - Fine Tuning

by 피키타임즈 2023. 10. 8.

컴퓨터비전 전이학습 Computer Vision Transfer Learning - Fine Tuning

Fine Tuning (미세조정)

  • Pretrained Network 위에 새로운 Layer를 추가해서 Model을 구현
  • Pretrained Network 부분을 동결
  • 일단 학습을 진행, Model 을 1차적으로 완성
  • Pretrained Network의 일부 layer를 동결 해제 (Filter update)
  • 다시 학습을 진행
  • 총 2번의 학습이 진행됨 (한번에 진행하지 않는 이유는 한번에 학습시 기존의 filter정보가 손상될까봐 나눠서 한다.
# train: 2000, validation : 1000

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.applications import VGG16
import matplotlib.pyplot as plt

train_dir = '/content/train' # 2000장
validation_dir = '/content/validation' # 1000장

# imageDataGenerator 생성
train_datagen = ImageDataGenerator(
    rescale=1/255,
    rotation_range=30,
    shear_range=0.2,
    width_shift_range=0.1,
    height_shift_range=0.1,
    zoom_range=0.2,
    horizontal_flip=True
)
validation_datagen = ImageDataGenerator(rescale=1/255)

train_generator = train_datagen.flow_from_directory(
    train_dir,
    classes=['cats', 'dogs'], # cats folder로부터 나온 이미지 cats=0, dogs=1
    target_size=(150, 150), # image resize
    batch_size=20,
    class_mode='binary'
)

validation_generator = validation_datagen.flow_from_directory(
    validation_dir,
    classes=['cats', 'dogs'], # cats folder로부터 나온 이미지 cats=0, dogs=1
    target_size=(150, 150), # image resize
    batch_size=20,
    class_mode='binary'
)

# Transfer Learning(전이학습) 하려고 한다.
# Pretrained Network 사용하려고 함

model_base = VGG16(weights='imagenet',
                   include_top=False,
                   input_shape=(150, 150, 3))

# Pretrained Network 안의 모든 layer를 동결 (update가 되지 않도록 처리)
model_base.trainable = False

# 우리 모델 구현
model = Sequential()
model.add(model_base)
model.add(Flatten(input_shape=(4 * 4 * 512,)))
model.add(Dense(units=256,
                activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(units=1,
                activation='sigmoid'))
model.summary()

model.compile(optimizer=Adam(learning_rate=1e-4),
              loss='binary_crossentropy',
              metrics=['accuracy'])

history = model.fit(train_generator,
                    steps_per_epoch=100, # 2000개 데이터 batch = 20, 1 epoch당 100번 돌아야 한다
                    epochs=30,
                    validation_data=validation_generator,
                    validation_steps=50, # validation 데이터 1000장 -> batch=20, 20 * 50 = 1000장
                    verbose=1)

# Fine Tuning (미세조정)
# Pretrained Network 에서 일부 layer를 동결에서 해제
# 동결을 해제한 층을 같이 학습에 이용해서 다시 학습을 진행

model_base.trainable = True # 동결에서 해제 : 이렇게 하면 잘 만들어진 필터가 손상될 수 있다. 그래서 일부분만 해제할것

model_base.summary()
model_base.trainable = True
for layer in model_base.layers:
	# 맨 아래의 3개 layer만 train가능하도록 수정
    if layer.name in ['block5_conv1', 'block5_conv2', 'block5_conv3']:
        layer.trainable = True # 동결해제
    else:
        layer.trainable = False # update가 안되게 동결

model_base.summary()

# 재 학습시 일반적으로 learning_rate을 줄여서 처리
model.compile(optimizer=Adam(learning_rate=1e-5),
              loss='binary_crossentropy',
              metrics=['accuracy'])

history = model.fit(train_generator,
                    steps_per_epoch=100, # 2000개 데이터 batch = 20, 1 epoch당 100번 돌아야 한다
                    epochs=20,
                    validation_data=validation_generator,
                    validation_steps=50, # validation 데이터 1000장 -> batch=20, 20 * 50 = 1000장
                    verbose=1)

train_acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
train_loss = history.history['loss']
val_loss = history.history['val_loss']

plt.plot(train_acc, 'bo', color='r', label='training accuracy')
plt.plot(val_acc, 'b', color='b', label='validation accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.show()

plt.plot(train_loss, 'bo', color='r', label='training loss')
plt.plot(val_loss, 'b', color='b', label='validation loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

여기서 더 나은 모델을 만들려면 Pretrained Network를 조금 더 좋은 모델로 변경하여 학습하면 됩니다.

EfficientNet을 사용해서 Fine Tuning 진행

EfficientNet B4 사용 (모바일 친화적인 모델)

# keras에 현재는 포함되어 있지만, 여기서는 외부 라이브러리로 사용 예정
# tensorflow.keras.applications.EfficientnetB4 에 있음
pip install efficientnet
pip install tensorflow-addons
# EfficientNet B4를 이용한 Fine Tuning

import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import efficientnet
import efficientnet.tfkeras as efn
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping
import matplotlib.pyplot as plt
import cv2 # opencv (이미지 처리의 대표적인 library)

train_dir = '/content/train'
validation_dir = '/content/validation'

IMAGE_SIZE = 256 # Pretrained Network에 들어가는 이미지의 가로, 세로 크기
BATCH_SIZE = 8   # ImageDataGenerator로 한번에 가져오는 이미지의 개수

# 이미지 전처리 함수를 하나 만든다
def image_preprocessing(img):
  # 이미지 전처리 코드가 여기에 들어올 수 있다.
  # 이미지 반전, 이미지 흑백처리, 이미지 크롭처리, 이미지 크기변경처리 등
  img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE))
  return img
  
  
train_datagen = ImageDataGenerator(
    rescale=1/255,
    rotation_range=40,
    shear_range=0.2,
    width_shift_range=0.1,
    height_shift_range=0.1,
    zoom_range=0.2,
    horizontal_flip=True,
    vertical_flip=True,
    fill_mode='nearest',
    preprocessing_function=image_preprocessing
)

validation_datagen = ImageDataGenerator(rescale=1/255, 
                                        preprocessing_function=image_preprocessing)

train_generator = train_datagen.flow_from_directory(
    train_dir,
    classes = ['cats', 'dogs'],
    batch_size = BATCH_SIZE,
    class_mode = 'binary'
)

validation_generator = validation_datagen.flow_from_directory(
    validation_dir,
    classes = ['cats', 'dogs'],
    batch_size = BATCH_SIZE,
    class_mode = 'binary'
)

# Pretrained Network

pretrained_model = efn.EfficientNetB4(
    weights='imagenet',
    include_top=False,
    input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)
)

pretrained_model.summary()

... 중간 생략 ...

pretrained_model.trainable = False

model = Sequential()
model.add(pretrained_model)
model.add(Flatten(input_shape=(8 * 8 * 1792,)))
model.add(Dense(units=256,
                activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(units=1,
                activation='sigmoid'))

# EarlyStopping
es = EarlyStopping(monitor='val_loss',
                   verbose=1,
                   patience=5) # 최소 loss 보다 loss가 5번 상승할 때까지 참는다

model.compile(optimizer=Adam(learning_rate=1e-5),
              loss='binary_crossentropy',
              metrics=['accuracy'])

history = model.fit(train_generator,
                    steps_per_epoch=2000 // BATCH_SIZE, # batch_size=8, 전체 train 데이터 2000개
                    epochs=50,
                    validation_data=validation_generator,
                    validation_steps = (1000 // BATCH_SIZE),
                    callbacks=[es],
                    verbose=1)

validation accuracy가 0.0947로 early stopping으로 epoch 16에서 학습이 중단되었습니다.

위의 학습이 종료 된 후, 일부 layer 동결을 해제한 후 아까와 같은 방법으로 상위 layer 재학습을 통해 Fine Tuning을 진행 할 수도 있습니다.

 

반응형

댓글