Data augmentation은 Network의 robustness를 높이기 위해 거의 default로 적용하는 방법이다. 케라스에서는 ImageDataGenerator 클래스의 몇 가지 클래스 함수들로 이미지 로드 뿐만 아니라 augmentation을 지원한다.
- flow( )
- flow_from_directory( )
- flow_from_dataframe( )
이번 글에선 flow_from_directory( ) 함수를 사용해서 augmentation을 하는 방법에 대해 정리하고자 한다. 그러기 위해선 일단 사용할 이미지가 필요하다. 저작권 문제에서 자유로운 https://pixabay.com/ 에서 마음에 드는 고양이와 강아지 사진을 각각 2장씩 골라 아래와 같은 폴더 구조로 저장하자. 여기서 이렇게 폴더명에 맞춰서 이미지를 저장하는 이유는 이미지를 불러올 때 폴더명에 맞춰서 자동으로 labelling하는 flow_from_directory( ) 함수의 기능을 활용하기 위함이다.
example folder
└ dog folder : dog1.jpg / dog2.jpg
└ cat folder : cat1.jpg / cat2.jpg
이제 셋팅은 끝났으니 코드단으로 넘어가서 사용법을 알아보도록 하자.
import tensorflow as tf
import numpy as np
import cv2
np.random.seed(15)
path = 'example'
generator = tf.keras.preprocessing.image.ImageDataGenerator(
rotation_range = 20,
width_shift_range = 0.2,
height_shift_range = 0.2,
rescale = 1. / 255)
batch_size = 4
iterations = 5
images = []
- [ Line 5 ]
Keras의 Random Seed 설정은 np.random.seed( )를 통해 해줄 수 있다.
재현성 잡힌 Image Load & Augmentation 결과를 얻고 싶다면 np.random.seed( )를 설정해주도록 하자. - [ Line 9 ~ 13 ]
ImageDataGenerator 인스턴스를 generator라는 변수명으로 생성하자.
생성시에 파라미터를 설정하면 어떻게 augmentation를 진행할지 지정할 수 있다. - [ Line 15 ~ 16 ]
- example folder에 있는 이미지 4장을 한 번에 읽어들이기 위해 batch_size = 4 로 설정
- augmentatoin을 5번 적용하기 위해 iterations = 5 로 설정
이렇게 생성한 generator 인스턴스의 flow_from_directory( ) 함수를 사용하는 방식에는 두 가지가 있다.
- next( ) 함수 사용
- for 문에서 flow_from_directory( ) 호출
각 케이스 별 사용법에 대해 자세히 알아보도록 하자.
그 전에 광고 하나 보고 가자!
(1) next( ) 함수 사용 방법
obj = generator.flow_from_directory(
path,
target_size = (150, 150),
batch_size = batch_size,
class_mode = 'binary')
for i in enumerate(range(iterations)):
img, label = obj.next()
n_img = len(label)
base = cv2.cvtColor(img[0], cv2.COLOR_RGB2BGR) # keras는 RGB, openCV는 BGR이라 변경함
for idx in range(n_img - 1):
img2 = cv2.cvtColor(img[idx + 1], cv2.COLOR_RGB2BGR)
base = np.hstack((base, img2))
images.append(base)
img = images[0]
for idx in range(len(images) - 1):
img = np.vstack((img, images[idx + 1]))
cv2.imshow('result', img)
- [ Line 5 ]
class_mode는 어떤 방식으로 폴더명에 따른 labelling을 진행할 수 있는 파라미터이다.
'binary'로 설정하면 0 or 1로 labelling이 진행된다. - [ Line 9 ]
next( ) 함수 사용 방법을 제대로 보여주는 코드 한 줄이다.
for문에서 obj.next( )를 한 번 호출할 때 마다
(1) obj는 설정된 경로에서
(2) batch_size에 맞춰서
(3) 이미지를 target_size로 resizing 한 다음에
(4) 폴더명을 기반으로 'binary' 방식에 맞춰 labelling까지 진행해서 이미지를 불러온다.
따라서 여기에선 obj.next( ) 한 번 호출하면
4장의 이미지를 150 x 150 size로 label 정보와 함께 볼러오게 된다.
( iterations x batch_size )의 형태로 사진이 합성되어 있는 걸 확인할 수 있다. 4개의 이미지를 5번 이미지 증식해 불러왔기 때문이다. 또한 사진을 보시면 원본 이미지에 비해 회전, x축 이동, y축 이동이 된 형태로 변형되어 생성됐다는 걸 알 수 있다. 추가로 위에서 random_seed를 고정해뒀기 때문에 여러번 실행을 반복해도 결과 이미지에는 변함 없는 것을 확인할 수 있다.
(2) for문 선언 과정에서 flow_from_directory( ) 바로 사용
obj = generator.flow_from_directory(
path,
target_size = (150, 150),
batch_size = batch_size,
class_mode = 'binary')
for i, (img, label) in enumerate(obj):
n_img = len(label)
base = cv2.cvtColor(img[0], cv2.COLOR_RGB2BGR)
for idx in range(n_img - 1):
img2 = cv2.cvtColor(img[idx + 1], cv2.COLOR_RGB2BGR)
base = np.hstack((base, img2))
images.append(base)
if i is iterations - 1:
break
img = images[0]
for idx in range(len(images) - 1):
img = np.vstack((img, images[idx + 1]))
cv2.imshow('result', img)
- [ Line 7 ]
첫 번째 방법과 큰 차이는 없지만 거의 유일하게 다른 한 줄이다.
obj.next( )를 직접 호출했던 첫 번째 방법과는 다르게 for문 선언 라인에서 바로 obj를 사용했다.
for문이 자동으로 obj.next( )를 호출한다는 이용한 방식이다. - [ Line 16, 17 ]
하지만 이 방법을 쓰면 for문의 반복 횟수를 직접적으로 제한할 길이 막혀서
해당 코드를 추가해서 반복을 제한했다. - 마찬가지로 이미지를 띄워서 결과를 확인해보면 위와 같은 결과 이미지가 생성되는 것을 확인할 수 있다.
지금까지 flow_from_directory를 통해 이미지를 불러오고 그와 동시에 augmentation 적용까지 해버리는 방법에 대해 알아 봤다. 설명이 틀린 곳이 있을 수 있으니.. 의심 되는 부분은 댓글로 알려주시면 감사하겠습니다.
'Deep Learning > Keras & Tensorflow' 카테고리의 다른 글
How to get reproducible results with Keras? (케라스 재현성 잡는 방법) (0) | 2021.11.04 |
---|---|
Generative Adversarial Network (GAN) 설계 시 고려할 부분 (0) | 2021.10.08 |
keras.models.Model( ) (0) | 2021.08.31 |
MNIST 분류 모델, 조금 다르게 실행해보자 / get_tensor_by_name( ) (0) | 2021.08.31 |
RNN과 CNN 동시 사용 모델(RCNN / CRNN)의 개념 및 구현 (0) | 2021.08.31 |