본문 바로가기

Data Science/Machine Learning 구현

[ML] Tensorflow.Keras로 ImageDataGenerator 사용해보기

반응형

🔊 해당 포스팅은 권철민님의 CNN Fundamental 완벽 가이드 강의를 듣고 난 후 배운 내용을 정리하고자 하는 목적 하에 작성되는 포스팅입니다. 하단의 포스팅에서 사용되는 실습 코드 및 자료는 필자가 직접 재구성한 자료이며 권철민님의 자료를 그대로 인용하지 않았음을 필히 알려드립니다.

최근에 텐서플로우로 CNN의 기초를 다시 정립하고자 해당 강의를 듣게 되었다. 그러던 중 몰랐다가 알게 된 내용을 정리해서 재구성하여 배운 내용에 대해 기록하고자 한다. 이번 포스팅에서는 이미지 데이터를 증강하는 기법 즉 , Data Augmentation을 지원하는 Tensorflow.keras의 ImageDataGenerator에 대해 알아보고 CIFAR10 이미지 데이터로 실습코드까지 적용해보고자 한다.

ImageDataGenerator라는 발전기는 어떻게 동작할까?

1. Data Augmentation은 왜 필요할까?

이미지를 분류하는 CNN 모델도 정형 데이터를 예측하는 일반적인 머신러닝 모델과 마찬가지로 극복해야 할 문제점은 오버피팅(과적합) 문제이다. CNN 모델의 성능을 높이면서 오버피팅을 극복할 수 있는 가장 좋고 근본적인 해결책은 학습 데이터의 다양성을 늘리는 것이다. 물론 학습 데이터의 개수를 직접 늘리면 더할 나위 없이 좋을테지만 특히나 이미지 데이터를 추가로 구하는 것은 사람의 손을 필히 거쳐야 하며 비용도 시간도 많이 드는 일이다.

그래서 비용도, 시간도 적게 소요하면서 학습 데이터의 다양성을 늘릴 수 있는 방법이 무엇일까 고안하면서 나오게 된 방법이 Data Augmentation 즉, 데이터 증강 기법이다. 데이터 증강 기법은 쉽게 말해, 하나의 원본 이미지를 다양한 버전으로 만들어 학습시키는 것이다. 바로 아래 그림처럼 말이다.

여러가지 데이터 증강 기법


대표적인 데이터 증강 기법으로는 원본 이미지를 수평 또는 수직 반전 시키는 방법, 회전시키는 방법, 일부를 짜르는(crop) 방법, RGB 채널 순서를 바꾸거나 픽셀값을 변경하여 원본 이미지의 색조를 변경시키는 것들이 있다. 이 이외에도 수많은 증강 기법들이 존재한다. 이러한 원본 이미지 증강 기법을 Tensorflow.keras의 ImageDataGenerator가 대신해주는 것이다! 참고로 이 툴 이외에 Data Albumenation이나 ImgAug, Tensorflow Image Library 등 다른 데이터 증강 기법 툴들이 있기도 하다.

2. ImageDataGenerator의 역할

방금 위에서 원본 이미지의 데이터를 증강시켜 주는 것이 Tensorflow.keras의 ImageDataGenerator가 있다고 했다. 그런데 ImageDataGenerator는 이미지 데이터를 증강시켜줄 뿐만 아니라 Tensorflow 내에서 하나의 데이터 파이프라인을 구축하는 역할도 하게 된다. 정확히 말하면 ImageDataGenerator는 Tensorflow.keras의 preprocessing 모듈에 존재하는 클래스이다. 어쨌거나 ImageDataGenerator가 하는 역할을 이해하기 위해서 아래와 같은 사진 자료를 살펴보자.

ImageDataGenerator의 과정


과정을 코드와 밀접하게 연결시켜서 이해하기 위해서 각 과정마다 실습코드를 덧붙여서 자료를 만들어 보았다. 특이한 점은 ImageDataGenerator를 사용하지 않고 단순한 과정을 사용하면 모델을 학습시키는 fit 메소드에 배치사이즈, 셔플 유무, X, y 설정을 넣었지만 ImageDataGenerator를 사용하게 되면 ImageDataGenerator 객체를 생성한 후 flow 메소드에 배치사이즈, 셔플 유무, X, y 설정을 인자로 넣어주어야 한다. 이렇게 하는 이유는 이미지 사이즈가 커질수록 한 번에 이미지를 CPU, GPU에 올릴 수 없기 때문에 배치 사이즈 크기 설정 같은 처리를 미리 ImageDataGenerator가 처리를 하는 것이다. 결국 ImageDataGenerator 과정을 간단화해보면 다음과 같은 단계로 구성될 수 있다.

  1. ImageDataGenerator 객체를 생성하면서 증강을 수행할 유형들 지정
  2. 1단계에서 생성한 ImageDataGenerator 객체를 생성한 후 flow 메소드로 X(이미지), y(레이블), 이미지를 한 번에 몇개 업로드시킬지에 대한 배치 사이즈 설정, 데이터 셔플 유무를 지정. flow 메소드로 생성한 객체는 Numpy Array Iterator 객체로 우리가 흔히 아는 Python iterator 처럼 loop 문이나 next() 함수를 사용해 iterator 안의 데이터를 하나씩 호출할 수 있다.
  3. flow 메소드로 만들어진 이터레이터를 설계된 모델의 fit() 메소드 또는 fit_generator() 메소드에 입력시킨 후 epoch, callbacks 설정을 입력시켜준다. 참고로 fit() 메소드에 X, y를 설정해주지 않고 flow 메소드로 만들어진 이터레이터를 넣어주는 것은 위에서 사진에도 보다시피 이터레이터를 next() 함수로 호출하면 X, y 모두 반환이 되기 때문이다.

자, 이제 ImageDataGenerator 동작 과정에 대해 알아보았으니 CIFAR10 데이터로 간단히 테스트 후 모델링을 진행해보자.

3. CIFAR10 데이터셋에 ImageDataGenerator 적용해보기

하단의 코드를 실행하기 위해 필요한 CIFAR10 데이터 전처리, 모델 설계 클래스는 여기 코드를 참고하자. 하단의 코드는 전처리, 모델 클래스를 정의했다고 가정하고 ImageDataGenerator 코드만을 작성하였다.

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping

# 데이터 로드
datasets = PreprocessData(valid_size=0.15, random_state=42, scaling=False)
tr_images, tr_ohe_labels, val_images, val_ohe_labels, test_images, test_ohe_labels = datasets.preprocess_data()
# 모델 설계
model = CnnModel.create_model(verbose=True)

# ImageDataGenerator 객체 생성 -> 적용할 증강 기법 설정
tr_gen = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, rescale=1/255.0, rotation_range=0.45,
                           zoom_range=[0.5, 1.5])
val_gen = ImageDataGenerator(rescale=1/255.0)

# Numpy Array Iterator 객체 생성하여 모델 인풋으로 배치 사이즈만큼 집어넣을 준비
flow_tr_gen = tr_gen.flow(x=tr_images, y=tr_ohe_labels, batch_size=64, shuffle=True)
flow_val_gen = val_gen.flow(x=val_images, y=val_ohe_labels, batch_size=65, shuffle=False)

# Callaback 설정
rlr_call = ReduceLROnPlateau(monitor='val_loss', mode='min', factor=0.1, patience=4, verbose=1)
es_call = EarlyStopping(monitor='val_loss', mode='min', patience=7, verbose=1)

# 모델 compile
model.compile(optimizer=Adam(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# 학습
tr_hist = model.fit(flow_tr_gen, epochs=20, validation_data=flow_val_gen, callbacks=[rlr_call, es_call])

# 평가
test_gen = ImageDataGenerator(rescale=1/255.0)
flow_test_gen = test_gen.flow(x=test_images, y=test_ohe_labels, batch_size=32, shuffle=False)
test_hist = model.evaluate(flow_test_gen)






반응형