일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 데이터 증강
- ResNet
- 태블로 실습
- cnn
- sparse coding
- object detection
- Data Augmentation
- 데이터과학
- data analysis
- SRCNN
- k-fold cross validation
- 파이썬 구현
- Computer Vision
- 데이터 분석
- 태블로
- 논문 리뷰
- Deep Learning
- kt희망나눔재단
- super resolution
- 개 vs 고양이
- Tableau
- 딥러닝
- 논문리뷰
- 머신러닝
- 데이터 시각화
- VGG
- VGG16
- 장학프로그램
- Semantic Segmentation
- kt디지털인재
- Today
- Total
기억의 기록(나의 기기)
[VGG16] 예제를 통한 transfer learning, fine-tuning 설명 본문
VGG16 - transfer learning, fine-tuning
안녕하세요, 데이터과학을 공부하고 있는 황경하입니다.
오늘은 pretrained model 그중 VGG16 모델을 이용한 transfer learning, fine-tuning을 공부해보려 합니다.
Google Colab 환경에서 작업하였습니다. 바로 가시죠!
코드: https://colab.research.google.com/drive/18ulYxLmpESpf-MDvvGIfBXVkDxR05Ih4?usp=sharing
VGG16?
VGG16은 2014년 ILSVRC대회에서 준우승을 한 모델입니다. ILSVRC 모델은 세계에서 가장 큰 Image 경진대회로 평범한 학부생 수준의 공모전이 아닌 세계적인 기업들이 참여하는 경진대회입니다.
당시에 Google Net이 1등을 차지했는데, 이상하게 2등을 한 VGG16이 매우 큰 각광을 받았습니다. 그 이유는, 신경망을 구성하는 가장 기초적인 Convolution Layer, MaxPooling Layer, DropOut Layer만 사용했기 때문인데요. 기본적인 신경망 구조를 깊게 쌓아 신경망을 만들었고, 그렇기에 우리 같은 배우는 사람 입장에서 매우 쉽게 다가갈 수 있다는 장점이 있습니다. VGG16 모델은 keras에 이미 구현되어 있고, imagenet 데이터로 사전 학습되어 있어 우리가 따로 학습을 시키지 않아도 바로 사용할 수 있습니다. 그러나, 내가 가지고 있는 데이터에 맞게 모델의 정확도를 높이고 싶다면 지금부터 배울 transfer learning과 fine-tuning 과정이 필요합니다.
Data Load for Test VGG16
간단히 vgg16모델의 정확도를 살펴보기 위하여 4장의 사진만 다운로드합니다. 원래는 4장의 이미지에 대한 정확도를 다 보여드리고 싶었으나, 글만 길어질 것 같아 하나만 테스트해 보겠습니다.
데이터를 zip 파일로 다운로드하고 unzip 합니다.
!pip install --upgrade --no-cache-dir gdown
import gdown, zipfile, os
if not os.path.isfile('VGG16_test.zip'):
gdown.download(id='11LZAFSFVtDsdKdLcFR9E-MaDoar6C3R5', output='VGG16_test.zip')
VGG16_test = zipfile.ZipFile('VGG16_test.zip')
VGG16_test.extractall()
VGG16_test.close()
|
사진 중 golden.jpg라는 사진을 하나 출력해 보겠습니다. 귀여운 레트리버 사진이네요.
import matplotlib.pyplot as plt
import matplotlib.image as image
img = image.imread("golden.jpg")
print(img.shape)
plt.imshow(img)
plt.axis('off')
plt.show()
|
이제 이 데이터를 vgg16에 맞게 resize해주고, 전처리해 주어 vgg16 모델의 예측 top5 범주를 확인해 보겠습니다.
- 위에서 언급했지만, VGG16 모델은 ImageNet 데이터로 학습되어 있기 때문에 입력 이미지가 (224,224) shape을 가져야 합니다. 또한, ImageNet이 1000종류의 범주로 되어있어 1000차원의 확률 분포를 출력합니다.
- tf.keras.applications.vgg16.preprocess_input 함수는 4차원 텐서를 입력받으며 vgg16에 맞게 전처리 해줍니다.
- out에는 모델의 예측값이 담기며 -를 붙여서 가장 큰 값을 가장 작게 만들어주고 sort 해줌으로써 가장 큰 확률의 index를 저장합니다.
import tensorflow as tf
import numpy as np
with open('imagenet_classes.txt') as f:
labels = [line.strip() for line in f.readlines()]
img = tf.image.resize(img,(224,224))
out=vgg16(tf.keras.applications.vgg16.preprocess_input(tf.expand_dims(img,axis=0)))
top_5 = np.argsort(-out[0])[:5]
for idx in top_5:
print(f"{labels[idx]} : {out[0,idx]*100:.3f}%")
|
보시다시피 매우 높은 확률로 골든 레트리버라고 정확히 예측했네요. 이로써 VGG16 모델이 사전학습된 모델임을 확인했습니다.
Transfer Learning?
Transfer Learning은 전이학습이라고 합니다. 이 과정은 모델에 새로운 데이터를 학습시키는 것인데요. vgg16 모델은 이미 학습되어 있는 모델이기에 왜 또 학습을 시키는지 궁금하신 분들이 계실 것 같습니다. 맞습니다. 이미 학습이 되어있죠. 하지만, 우리가 프로젝트를 하거나 연구를 할 때에 내 데이터셋에 이 모델이 높은 성능을 보일지는 알 수 없습니다. 그래서, 모델에 내 데이터셋을 학습시키는 것이죠. 다만, 우리가 생각해야 할 것이 있습니다. 신경망에 대한 이해가 필요한 부분인데, vgg16 모델의 특성 추출기 부분은 이미 잘 학습되어 있습니다. 즉, 아래 사진의 가위 기준 왼쪽 층들은 모두 학습이 잘 되어있다는 것이죠. 이 부분에 우리가 새로운 데이터를 넣고 학습을 시킨다면, gradient가 매우 커져 이미 학습된 파라미터들을 다시 초기화시키는 것이나 다름없습니다. 따라서, 우리는 앞에 특성추출기 부분은 학습시키지 않을 것입니다. 또한, 추출기(가위 기준 오른쪽 층들)는 이미 input_shape = (224,224)로 고정되어 있는데, 우리가 사용할 데이터는 (180,180) shape을 가져 맞기 않기 때문에 자르고 가지고 오겠습니다.
conv_base = keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(180, 180, 3))
conv_base.trainable = False
|
말로는 길게 설명했는데, 코드는 매우 짧네요.
- 위에서 vgg16 모델을 불러온 코드랑 똑같은 함수를 사용하되, 파라미터를 조정합니다.
- include_top: 추출기 부분은 가져오지 않게 설정합니다.
- input_shape: 우리가 사용할 데이터는 (180,180,3) shape을 가지므로 그에 맞게 설정합니다.
- trainable: 특성 추출기 부분만 가져왔기 때문에 학습되지 않게 바꿔줍니다.
* 참고로, include_top = True로 설정한 경우 input_shape이 설정되지 않습니다. 추출기 부분은 이미 (224,224,3)으로 고정되어 있기 때문이죠. 그래서, include_top = False로 설정한 경우에만 변경할 수 있습니다.
Data Load for Transfer Learning & Create Dataset
이제는 전이학습 시킬 데이터셋을 불러오겠습니다. 데이터에 대한 설명은 이전 글을 참고해 주세요.
import pathlib
from tensorflow.keras.utils import image_dataset_from_directory
if not os.path.isdir('cats_vs_dogs_small'):
gdown.download(id='1z2WPTBUI-_Q2jZtcRtQL0Vxigh-z6dyW', output='cats_vs_dogs_small.zip')
cats_vs_dogs_small = zipfile.ZipFile('cats_vs_dogs_small.zip')
cats_vs_dogs_small.extractall()
cats_vs_dogs_small.close()
base_dir = pathlib.Path("cats_vs_dogs_small")
train_dataset = image_dataset_from_directory(
base_dir / "train",
image_size=(180, 180),
batch_size=32)
validation_dataset = image_dataset_from_directory(
base_dir / "validation",
image_size=(180, 180),
batch_size=32)
test_dataset = image_dataset_from_directory(
base_dir / "test",
image_size=(180, 180),
batch_size=32)
|
Build Model & Training
모델을 먼저 만들어보죠. 다시 말씀드리지만, 추출기 없이 모델을 가져왔기 때문에 우리가 직접 추출기를 구현해주어야 합니다.
from keras.layers import Flatten, Dense, Dropout
def build_model(input_shape, num_out):
inputs = keras.Input(shape=(180, 180, 3))
x = keras.applications.vgg16.preprocess_input(inputs)
x = conv_base(x)
x = Flatten()(x)
x = Dense(256)(x)
x = Dropout(0.5)(x)
outputs = Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
return model
model = build_model((180,180,3), 1)
|
이제 모델의 학습환경을 구성하고 학습시켜 보죠.
model.compile(loss="binary_crossentropy",
optimizer="rmsprop",
metrics="accuracy")
callbacks = [keras.callbacks.ModelCheckpoint(
filepath="feature_extraction.h5",
save_best_only=True,
monitor="val_loss")]
history = model.fit(
train_dataset,
epochs=50,
validation_data=validation_dataset,
callbacks=callbacks)
|
test_model = keras.models.load_model("feature_extraction.h5")
(test_loss, test_acc) = test_model.evaluate(test_dataset)
print(f"test_loss: {test_loss}")
print(f"test_acc: {test_acc}")
|
정확도가 약 97% 정도 나오네요. 이렇게 분류기 부분만 학습을 시켜봤습니다. 이제는 우리의 데이터셋을 넣어도 높은 정확도를 보임을 알 수 있죠. 하지만, 이것이 우리의 목적은 아닙니다. 우리의 목적은 분류기의 학습이 아닌 특성 추출기를 포함한 모델 전체의 전이 학습이었죠. 따라서, 이 작업은 분류기의 학습을 통해 우리의 데이터셋에 대해 모델이 큰 gradient를 가지지 않게 하기 위해서 작업한 것입니다. 그래야 특성 추출기의 파라미터들이 크게 변동하지 않을 테니까요. 따라서, 지금부터는 특성 추출기 부분의 trainable을 True로 하고 학습시켜 보겠습니다. 이 작업을 fine-tuning이라고 합니다.
Fine-Tuning
conv_base.trainable = True
for layer in conv_base.layers[:-4]:
layer.trainable = False
print("동결 해제층 :")
for layer in conv_base.layers:
if layer.trainable:
print(layer.name)
|
이렇게 특성추출기의 학습을 허용하지만, 보시다시피 마지막 block은 제외하고 다시 False로 변경했습니다. 즉, 마지막 block(4개의 층)만 학습시키겠다는 것인데요. 그 이유는 하위 층의 필터는 로컬 한 특징을 인코딩하고 상위 층의 필터는 글로벌한 특징을 인코딩하기 때문입니다. 즉, 하위 층은 굉장히 작은 영역의 특징을 찾아내는데 이것은 이미지의 입장에서 봤을 때 겨우 눈썹, 콧구멍 정도의 크기입니다. 크게 다른 사진들과 다르지 않겠죠. 따라서, 파라미터의 수만 늘어나기 때문에 학습을 시키지 않습니다. 이에 비해, 상위층은 눈, 코, 입 같은 큰 영역의 특징을 찾습니다. 따라서, 다른 사진들과 크게 다른 특징을 찾을 수 있기 때문에 학습을 시킵니다.
model.compile(loss="binary_crossentropy",
optimizer=keras.optimizers.RMSprop(learning_rate=1e-5),
metrics=["accuracy"])
callbacks = [keras.callbacks.ModelCheckpoint(
filepath="fine_tuning.h5",
save_best_only=True,
monitor="val_loss")]
history = model.fit(
train_dataset,
epochs=30,
validation_data=validation_dataset,
callbacks=callbacks)
|
이렇게 다시 학습을 시키되 크게 변하지 않게 learning_rate를 10의 -5승으로 매우 작은 값을 잡아줍니다
그 결과, 약 0.15%의 정확도 향상을 기록했네요. 이제는 나의 데이터셋에 모델이 높은 정확도를 보인다고 이야기할 수 있을 것 같습니다.
마치며..
이렇게 오늘은 Transfer Learning, Fine-tuning에 대해 알아봤습니다. 개와 고양이를 분류하는 간단한 예제임에도 코드가 쉽지 않죠. 사실, 이 예제는 전이 학습과 미세 조정에 대한 이해도를 높이기 위한 예제일 뿐입니다. 더 복잡한 데이터도 이러한 구조로 학습시킬 수 있죠. 다음에는 이와 비슷한 ResNet에 대한 설명으로 돌아오겠습니다.
오늘도 읽어주셔서 감사합니다.
'딥러닝' 카테고리의 다른 글
[Sparse Coding] 희소 코딩 기법에 대한 그림을 통한 쉬운 설명 (0) | 2024.07.12 |
---|---|
[Data Preprocessing] Duplicate Cleaner 소개 및 사용법 (1) | 2024.07.03 |
[ResNet] 예제를 통한 설명 및 함수형 API 구현하기 (1) | 2024.06.25 |
[Data Augmentation] 예제를 통한 데이터 증강 효과 설명 (0) | 2024.06.24 |
[Data Augmentation] 강아지, 고양이, MNIST 예시를 통한 설명 (0) | 2024.06.24 |