Python, TensorFlow Image Classification(이미지 분류) 모델 생성 방법 및 학습 확인 검증 (Machine learning, Deep learning) for window
이번에는 이미지를 분류하는 모델을 생성하고 학습하여, 해당 모델을 테스트 해 보는 방법을 포스팅 하려고 합니다.
이미지 분류에 관한 튜토리얼 API문서는 다음과 같습니다.
https://www.tensorflow.org/tutorials/images/classification?hl=ko
이미지 분류 | TensorFlow Core
TensorFlow가 5월 14일 Google I/O로 돌아왔습니다! 지금 등록하세요 이미지 분류 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 튜토리얼은 tf.keras.Sequential 모
www.tensorflow.org
저는 총 1332개의 이미지를 준비했고,
제가 분류를 하고자 하는 이미지는 다음과 같습니다.
standing 서있는 자세
standing_bent_over 서있으면서, 허리를 구부린 자세
standing_back 서있으면서, 뒤를 돌아본 자세
squatting 스쿼트 자세
sitting 앉아있는 자세
lying 누워있는 자세
kneeling 무릎꿇은 자세
['kneeling', 'lying', 'sitting', 'squatting', 'standing', 'standing_back', 'standing_bent_over']
분류에 대한 총 7개의 클래스(카테고리)로 나누었습니다. 이러한 데이터를 통하여 해당 모델을 학습 할 예정 입니다.
먼저 필요한 lib 패키지를 다운받습니다.
pip list
pip list를 통해 현재 패키지를 확인 합니다. 그리고 필요한 라이브러리는 다음과 같습니다.
import lib
import PIL.Image
import matplotlib.pyplot as plt
import numpy as np
import PIL
import tensorflow as tf
import pathlib
import datetime
plt.rcParams['font.family'] ='Malgun Gothic'
plt.rcParams['axes.unicode_minus'] =False
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
만약 이미지가 없다면 tensorflow 공식 문서에 지원하는 학습용 이미지 파일이 있습니다. (공식 참고)
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)
위 코드는 위 압축파일을 get_file() 호출하여 해제 합니다. 이미지 파일이 생성되는 경로는 다음과 같습니다.
C:\Users\이름\.keras\datasets
저는 자체 이미지로 학습을 진행하기에 코드는 다음과 같습니다.
# 학습 할 이미지 파일 경로
education_images_path = "C:\\Users\\사용자이름\\.keras\\datasets\\education_images"
data_dir = pathlib.Path(education_images_path)
해당 경로에 있는 이미지 데이터를 dataset 형식으로 변환합니다.
이미지의 총 카운트를 체크합니다.
# 이미지 카운트
image_count = len(list(data_dir.glob('*/*.png')))
print("총 이미지 수: " + str(image_count))
총 이미지 수: 1332
실제 이미지를 오픈하여 파일에 이상이 없는지 확인 할 수 있습니다.
standing_img = list(data_dir.glob('standing/*'))
img = PIL.Image.open(str(standing_img[1]))
#img.show()
데이터 로더 설정과 학습, 검증 설정에 대한 코드를 작성 합니다.
# 데이터 로더
batch_size = 32
img_height = 240
img_width = 180
print("\n학습")
# 학습 설정
train_ds = tf.keras.utils.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
print("\n검증")
# 검증 설정
val_ds = tf.keras.utils.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
여기서 batch_size는 epochs에 대해 전체 데이터세트를 반복하여 모델을 학습 합니다. batch_size가 클수록 해당 모델에 많은 데이터를 제공할 수 있지만, 메모리의 저장 공간이 넘칠 수 있습니다. 대부분 2^(2의 제곱)으로 값을 초기화 합니다. 저는 2^5(2의 5제곱)의 값을 batch_size 변수에 초기화 했습니다. 이미지의 크기는 각각 240, 180으로 설정 하였습니다.
학습과 검증 코드에서 data_dir는 위 학습 이미지 폴더인 education_images의 전체 하위 폴더의 경로를 가지고 있습니다.
ex) [ "../education_images/standing", "../education_images/standing_bent_over", ... ]
validation_split의 값이 0.2이면, 전체 데이터 중 80%는 학습하고, 20%는 test데이터로 모델을 검증합니다. 모델을 개발 할 때는 검증 분할을 사용하는 것이 좋다고 합니다. subset 의 값이 "training"이면 하위 집합을 로드 하도록 지시한다는 것을 말한다고 합니다. 학습할 때의 데이터세트는 위 standing, standing_bent_over 폴더를 로드하도록 지시합니다.
"validation"은 검증 목적의 하위 집합만 로드하도록 지시합니다. seed의 경우 epoch데이터를 고정 시킵니다. seed는 개발자가 편한 값으로 설정해도 무방 합니다.
실제 학습 객체의 하위 카테고리를 확인 할 수 있습니다.
# 학습한 클래스 네임 (한 카테고리를 담고있는 디렉토리)
class_names = train_ds.class_names
# ["standing", "standing_bent_over", "standing_back", "squatting", "sitting", "lying", "kneeling"]
print(class_names)
해당 학습 데이터를 확인 할 수있는 창을 생성합니다.
# 데이터 확인하기 plt (창 사이즈)
plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(20):
for i in range(9):
# 캔버스
ax = plt.subplot(3, 3, i+1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
plt의 figure를 통해 학습된 이미지를 캔버스에 그려 확인 할 수 있습니다.
데이터 세트중 성능을 좀 더 보완할 수 있는 코드가 있는데
# 성능 업 데이터 세트 구성하기
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
cache()는 버퍼링된 프리페치를 사용하여 입 출력을 차단하지 않고 메모리에 유지한다고 합니다.
prefetch() 학습중 데이터를 캐시에 보관한다고 합니다. (만약 학습에 대한 CPU나 GPU의 메모리 사용량이 높으면 에러가 발생할 수 있습니다.)
데이터 표준화 하기
# 데이터 표준화 하기
normalization_layer = layers.Rescaling(1./255)
normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
normalization_layer는 RGB값 표준화 한다고 합니다. RGB 값은 [0, 255] 범위로 되어 있는데 이것은 신경망에 이상적이지 않다고 합니다. 일반적으로 값을 작게 만들어 [0, 1]로 표준화 해야한다고 합니다.
모델 생성
# Model 생성
num_classes = len(class_names)
model = Sequential([
layers.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
layers.Conv2D(16, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(32, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(num_classes)
])
Sequential() 호출하면 모델을 생성 해 줍니다. Rescaling은 [0, 1] 이미지 크기, 3(컬러) 입니다. Conv2D 메소드는 2D 컴볼루션 레이어이며 16은 filters매개변수로 int, 출력 공간의 차원 필터 수 입니다. 매개변수 3은 kernel_size로 2개의 정수로 구성된 튜플/리스트 컨볼루션의 창의 크기를 지정합니다. padding='same'는 대소문자 구분 하지 않고, 입력의 왼쪽/오른쪽 또는 위/아래로 균일하게 패딩 됩니다(strides=1 출력은 입력과 동일한 크기를 갖습니다) activation='relu'은 활성화 기능 입니다.
모델 컴파일 코드는 다음과 같습니다.
# Model 컴파일
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
# 모든 레이어를 확인한다.
model.summary()
epochs = 8
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[tensorboard_callback]
)
만약 텐서보드를 사용하여 학습 결과를 확인하고 싶다면 model.fit() 메소드를 호출하기 전 아래 코드를 추가하면 됩니다.
# 텐서보드
print("텐서보드 로그 생성")
log_dir = "./logs/"+datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)
학습이 진행되어 생성된 Log를 확인 할 수 있습니다.
이 Log 파일에 대한 기록은 텐서보드 웹페이지를 접속하면 확인 할 수 있습니다.
window의 텐서보드 서버 실행 방법은 다음과 같습니다.
tensorboard --logdir C:\tensor\logs (위 사진 처럼 생성된 로그 경로)
커맨드 입력 후 http://localhost:6006/ 접속
실제로 모델 컴파일 테스트를 진행하면 model.summary() 로그를 확인 할 수 있고
최종 진행 로그를 확인 할 수 있습니다.
Total params: 5,431,335
Trainable params: 5,431,335
Non-trainable params: 0
epoch를 진행합니다. loss와 accuracy 등 손실과 정확성에 관한 내용을 확인 할 수 있습니다. 손실은 낮을 수록 정확도는 높을수록 좋습니다. epoch를 반복하면 점점 좋아지는 것을 확인 할 수 있습니다. 아래 사진의 경우는 검증 부분에 손실이 올라갔다 내려갔다 하는것을 알 수 있습니다.(왜 이러지? 사진 문제인가..)
plt를 이용하여 figure로 확인 할 수있습니다.
# 훈련 결과 확인하기
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
# epochs 사이즈
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='훈련 정확도')
plt.plot(epochs_range, val_acc, label='검증 정확도')
plt.legend(loc='lower right')
plt.title('훈련 및 검증 정확도')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='훈련 손실')
plt.plot(epochs_range, val_loss, label='검증 손실')
plt.legend(loc='upper right')
plt.title('훈련 및 검증 손실')
plt.show()
print("\n종료")
확인 결과
(이미지의 경우 AI 프로그램을 이용하여 데이터를 생성 하였습니다. 혹시나 저작권 문제가 있을 수 있어, 모자이크 처리하였습니다.)
학습의 경우 점점 좋아지는 것을 확인 할 수 있고, 검증의 경우 결과가 아쉽게 확인 됩니다.