Transfer Learning
사전 학습된 모델 'Inception V3'를 불러와서 차량 파손 여부를 분류하는 문제에 적용 해 본다.
이번에는 image_dataset_from_directory를 이용해 training set, validation set을 만들어 본다.
또한 Image Preprocessing Layer, Image Augmentation Layer를 통해 이미지를 변형시켜 성능을 높여본다.
1. Image_dataset_from_directory
https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image_dataset_from_directory
Image_dataset_from_directory는 디렉토리의 이미지 파일에서 tf.data.data.data 집합을 생성해 준다.
즉, 디렉토리 안의 이미지 파일들을 데이터셋 형식으로 만들어 준다. 여기서 image_size, seed 등을 지정해 줄 수 있고, validation_set을 나눠줄 수도 있다.
directory 구조
/content/Car_Images/
├── abnormal/
└── normal/
import tensorflow as tf
from keras.preprocessing import image_dataset_from_directory
# 데이터셋 경로 설정
dataset_dir = "/content/Car_Images/"
Image Preprocessing
# 훈련 및 검증 데이터셋 생성 전에 정규화 하는 Rescaling 레이어 추가
preprocessing_model = tf.keras.Sequential([
tf.keras.layers.Rescaling(1./255)
])
데이터셋 생성
※ TensorFlow의 image_dataset_from_directory 함수는 디렉터리의 구조에 따라 레이블을 자동으로 생성한다.
레이블은 알파벳 순으로 인덱싱되므로 폴더 이름에 따라 레이블이 할당되기 때문에 위와 같은 폴더 구조에서 abnormal은 0, normal은 1이 된다.
하지만 차량 파손 여부를 찾는 문제에서 결론적으로 찾고자 하는 것은 파손된 차량이기 때문에 abnormal이 1이 되는 것이 좋다. 여기서 image_dataset_from_directory를 사용할 때 임의로 레이블을 변경하는 방법은 두 가지가 있다.
첫째, 단순히 파일 이름을 바꾸는 것이다. 예를 들면 01_normal, 02_abnormal과 같은 식으로 바꿔서 abnormal이 뒤로 오게 하는 것이다.
둘째, 'class_names' 매개변수를 사용하여 normal과 abnormal에 명시적으로 레이블을 할당하는 방법이다. 이는 데이터셋을 생성할 때 " class_names=['normal', 'abnormal'] "와 같은 코드를 넣어주면 된다.
# 훈련 데이터셋 생성
train_dataset = image_dataset_from_directory(
dataset_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(128, 128),
batch_size=32,
class_names=['normal', 'abnormal']
).map(lambda x, y: (preprocessing_model(x), y))
# 검증 데이터셋 생성
valid_dataset = image_dataset_from_directory(
dataset_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(128, 128),
batch_size=32,
class_names=['normal', 'abnormal']
).map(lambda x, y: (preprocessing_model(x), y))
Found 605 files belonging to 2 classes.
Using 484 files for training.
Found 605 files belonging to 2 classes.
Using 121 files for validation.
확인
# 데이터셋 구성 확인
for image_batch, labels_batch in train_dataset:
print(image_batch.shape)
print(labels_batch.shape)
break # 하나의 배치만 확인
(32, 128, 128, 3)
(32,)
# input_shape : feature 수 도출
nfeatures = image_batch.shape[1:]
nfeatures
TensorShape([128, 128, 3])
2. Transfer Learning
1) Inception V3 모델 불러와서 저장하기
• include_top = False로 설정하여 분류기를 제외하고 미리 학습된 가중치 imagent를 로드
• .trainable = False로 설정하여 불러온 모델의 모든 레이어들을 Frozen 시켜 가중치를 고정
from keras.applications import InceptionV3
from keras.layers import GlobalAveragePooling2D, Dense
from keras.models import Model
from keras import Sequential
from keras.layers import Input, Flatten, Dense
from keras.layers import RandomFlip, RandomRotation
from keras.callbacks import EarlyStopping
# InceptionV3 모델 불러오기
base_model = InceptionV3(weights='imagenet', include_top=False, input_shape=nfeatures)
base_model.trainable = False # 모델의 모든 레이어를 Frozen 시킴
# 불러온 모델 확인
base_model.summary()
2) Inception V3 모델과 연결하여 구조 설계
• Inception V3를 불러온 후 다른 레이어와 연결
• Image Augmentation Layer를 만들어 데이터 증강
# 데이터 증강 레이어
data_augmentation = Sequential([
RandomFlip('horizontal'),
RandomRotation(0.2),
])
# 새 모델 구조 설계
inputs = Input(shape=nfeatures)
x = data_augmentation(inputs)
x = base_model(x, training=False)
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
outputs = Dense(1, activation='sigmoid')(x)
model = Model(inputs, outputs)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 모델 구조 확인
model.summary()
3) 학습
# 학습곡선 함수
def dl_history_plot(history):
plt.figure(figsize=(10,6))
plt.plot(history['loss'], label='train_err', marker = '.')
plt.plot(history['val_loss'], label='val_err', marker = '.')
plt.ylim([0, 3])
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend()
plt.grid()
plt.show()
# EarlyStopping 설정
min_de = 0
pat = 10
es = EarlyStopping(monitor = 'val_loss', min_delta = min_de, patience = pat)
# 학습
hist = model.fit(train_dataset, epochs=100, validation_data=valid_dataset,
callbacks=[es]).history
dl_history_plot(hist)
4) 성능 평가
※ Image_dataset_from_directory로 데이터셋을 만들면서 test 데이터셋을 만들지 못했다.
성능 평가를 위해 앞서 CNN 모델링에서 만든 test 데이터셋을 사용했다.
하지만 train에 test 데이터가 들어있을 수 있다는 점을 참고해서 봐주길 바란다. 이를 해결하기 위해 새로운 test 이미지를 수집해 데이터셋을 만드는 것을 추천한다.
※ 물론 위와 같은 오점이 있지만, 학습을 통한 학습 곡선, 마지막 epoch의 validation accuracy 등을 통해 충분히 좋은 성능을 가진다고 판단할 수 있다.
# 테스트 데이터셋 준비가 필요
# 예측
predictions = model.predict(x_test)
predictions = np.round(predictions).astype(int)
# 평가
print(confusion_matrix(y_test, predictions))
print(classification_report(y_test, predictions))
import numpy as np
import matplotlib.pyplot as plt
import random as rd
# labels 딕셔너리
labels = {0: 'normal', 1: 'abnormal'}
# 모델의 예측 결과 확인 해 보기
def check_pred(model, labels, x_test, y_test):
predictions = model.predict(x_test)
id = rd.randrange(0, len(x_test))
print(f'id = {id}')
print(f'다음 그림은 {labels[y_test[id]]} 입니다.')
prob_abnormal = np.floor(predictions[id][0]*100).tolist()
prob_normal = np.floor((1 - predictions[id][0])*100).tolist()
prob_dict = {
labels[0]: prob_normal,
labels[1]: prob_abnormal
}
print('모델의 카테고리별 확률 : ')
print(prob_dict)
print()
# 정답 여부 확인
single_pred_test = (predictions > 0.5).astype(int).flatten()
if y_test[id] == single_pred_test[id]:
print('정답입니다')
else:
print('틀렸어요')
plt.imshow(x_test[id])
plt.show()
check_pred(model, labels, x_test, y_test)
확인 결과 test 이미지에서 틀린 이미지는 아래 두 장이다.
첫 번째 사진은 사람인 내가 봐도 구분하기 어렵다고 생각한다. 그냥 오염된 것처럼 보이기도..
두 번째 사진은 누가봐도 정상인데 틀렸다. 그래도 abnormal일 확률이 다른 이미지에 비해 그리 높지는 않다.
그 외의 나머지 경우에는 잘 예측한 것을 볼 수 있다.
정리
여기까지 3개의 글에 걸쳐 차량 공유 플랫폼의 차량 파손 여부를 확인하는 모델을 만들어 봤다.
첫 번째 글에서는 데이터 전처리, 두 번째 글에서는 CNN 모델링, 세 번째에서는 Transfer Learning을 해 보았다.
나는 공부를 하는 입장에서 간단하게 구현 해 보았지만, 현업에서 Inception V3 모델 말고도 더 다양한 모델, 더 다양한 CNN 모델 구조, 전처리 등을 실험하여 성능 개선을 통해 이 기술이 적용되면 효율적이고 편리한, 필요한 기술이 될거라고 생각한다.
전체 코드는 깃허브에서 확인할 수 있다.
https://github.com/suetudy/Sues_Projects
'프로젝트' 카테고리의 다른 글
인공위성 데이터로 도시 환경 개선하기 - 쿨루프 시공 대상 여부 분류 (0) | 2024.04.15 |
---|---|
차량 공유 업체의 차량 파손 여부 분류하기(2) - CNN 모델링 (0) | 2024.04.09 |
차량 공유 업체의 차량 파손 여부 분류하기(1) - 데이터 확인 및 전처리 (0) | 2024.04.09 |
YOLOv8와 Roboflow를 활용한 Object Detection - 클라이밍 이미지에서 얼굴, 손, 발 detection(2) (0) | 2024.04.08 |
YOLOv8와 Roboflow를 활용한 Object Detection - 클라이밍 이미지에서 얼굴, 손, 발 detection(1) (0) | 2024.04.08 |