NeuroWhAI의 잡블로그

[Keras] ImageDataGenerator 사용해서 학습 데이터 개수 늘리기 본문

개발 및 공부/라이브러리&프레임워크

[Keras] ImageDataGenerator 사용해서 학습 데이터 개수 늘리기

NeuroWhAI 2018. 4. 1. 08:29


※ 이 글은 '코딩셰프의 3분 딥러닝 케라스맛'이라는 책을 보고 실습한걸 기록한 글입니다.


keras.preprocessing.image.ImageDataGenerator는 데이터를 이리저리 변형시켜서 새로운 학습 데이터를 만들어줍니다.
변형의 예시는 회전, 이동 등등 매우 다양합니다.

아래 코드는 CNN을 이용해서 MNIST 이미지를 분류하는 예시인데 일부러 데이터 수를 줄이고
ImangeDataGenerator를 통해서 데이터를 늘린 뒤 학습을 진행할겁니다.

코드:
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import numpy as np
import keras
from keras import layers, models, datasets, backend
from keras.utils import np_utils
import matplotlib.pyplot as plt
from sklearn import model_selection
from keras.preprocessing.image import ImageDataGenerator
 
class CNN(models.Sequential):
    def __init__(self, input_shape, num_classes):
        super().__init__()
 
        self.add(layers.Conv2D(32, kernel_size=(33),
                               activation='relu',
                               input_shape=input_shape))
        self.add(layers.Conv2D(64, (33), activation='relu'))
        self.add(layers.MaxPooling2D(pool_size=(22)))
        self.add(layers.Dropout(0.25))
        self.add(layers.Flatten())
        self.add(layers.Dense(128, activation='relu'))
        self.add(layers.Dropout(0.5))
        self.add(layers.Dense(num_classes, activation='softmax'))
 
        self.compile(loss=keras.losses.categorical_crossentropy,
                     optimizer='rmsprop', metrics=['accuracy'])
 
class DataML():
    def __init__(self):
        (x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()
        
        _, x_train, _, y_train = model_selection.train_test_split(x_train,
                                                                 y_train,
                                                                 test_size=0.02)
 
        y_train = np_utils.to_categorical(y_train)
        y_test = np_utils.to_categorical(y_test)
 
        img_rows, img_cols = x_train.shape[1:]
 
        if backend.image_data_format() == 'channels_first':
            x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
            x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
            input_shape = (1, img_rows, img_cols)
        else:
            x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
            x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
            input_shape = (img_rows, img_cols, 1)
 
        x_train = x_train.astype('float32')
        x_test = x_test.astype('float32')
        x_train /= 255.0
        x_test /= 255.0
 
        self.input_shape = input_shape
        self.num_classes = 10
        self.x_train, self.y_train = x_train, y_train
        self.x_test, self.y_test = x_test, y_test
        self.generator = ImageDataGenerator(rotation_range=20,
                                           width_shift_range=0.05,
                                           height_shift_range=0.05)
 
def plot_loss(history):
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('Model Loss')
    plt.ylabel('Loss')
    plt.xlabel('Epoch')
    plt.legend(['Train''Test'], loc=0)
 
def plot_acc(history):
    plt.plot(history.history['acc'])
    plt.plot(history.history['val_acc'])
    plt.title('Model accuracy')
    plt.ylabel('Accuracy')
    plt.xlabel('Epoch')
    plt.legend(['Train''Test'], loc=0)
 
def main():
    batch_size = 100
    epochs = 10
 
    data = DataML()
    model = CNN(data.input_shape, data.num_classes)
    
    data_flow = data.generator.flow(data.x_train, data.y_train,
                                    batch_size=batch_size)
 
    history = model.fit_generator(data_flow, epochs=epochs, steps_per_epoch=10,
                                  verbose=2, validation_data=(data.x_test, data.y_test))
 
    performance_test = model.evaluate(data.x_test, data.y_test, batch_size=100,
        verbose=0)
    print('\nTest Result ->', performance_test)
 
    plot_acc(history)
    plt.show()
    plot_loss(history)
    plt.show()
 
if __name__ == '__main__':
    main()
cs

결과:
Epoch 1/10
- 1s - loss: 1.9590 - acc: 0.3270 - val_loss: 0.9760 - val_acc: 0.7187
Epoch 2/10
- 1s - loss: 1.2538 - acc: 0.5900 - val_loss: 0.6712 - val_acc: 0.7739
Epoch 3/10
- 1s - loss: 0.9830 - acc: 0.6830 - val_loss: 0.4238 - val_acc: 0.8914
Epoch 4/10
- 1s - loss: 0.7643 - acc: 0.7590 - val_loss: 0.2997 - val_acc: 0.9202
Epoch 5/10
- 1s - loss: 0.6685 - acc: 0.7890 - val_loss: 0.2433 - val_acc: 0.9354
Epoch 6/10
- 1s - loss: 0.6084 - acc: 0.8190 - val_loss: 0.2183 - val_acc: 0.9407
Epoch 7/10
- 1s - loss: 0.4792 - acc: 0.8610 - val_loss: 0.2281 - val_acc: 0.9350
Epoch 8/10
- 1s - loss: 0.4556 - acc: 0.8620 - val_loss: 0.1744 - val_acc: 0.9513
Epoch 9/10
- 1s - loss: 0.4424 - acc: 0.8770 - val_loss: 0.1609 - val_acc: 0.9543
Epoch 10/10
- 1s - loss: 0.3911 - acc: 0.8830 - val_loss: 0.1381 - val_acc: 0.9579

Test Result -> [0.13811885924078524, 0.9579000061750412]



아래는 코드 설명.

sklearn 라이브러리의 model_selection.train_test_split 함수를 통해서 학습용 데이터의 2%만 뽑아냈습니다.
이 2%의 데이터로만 학습시키니 약 93%의 정확도가 나오더군요.

generator = ImageDataGenerator(rotation_range=20, width_shift_range=0.05, height_shift_range=0.05)
로 이미지 생성기를 만들었는데 이름을 보시면 아시겠지만
이미지를 최대 20도 회전시킬 수 있고 좌우, 상하 이동은 최대 5% 비율로 하겠다는 말입니다.
더 많은 변형 옵션은 공식문서에서 확인할 수 있습니다.

data_flow = generator.flow(data.x_train, data.y_train, batch_size=batch_size)
로 이미지를 생성해서 반환하는 일종의 Iterator(반복자)를 만듭니다.
이 반복자는 x, y를 가지고 한번에 batch_size만큼의 랜덤하게 변형된 학습 데이터를 만들어줍니다.

model.fit_generator(data_flow, epochs=epochs, steps_per_epoch=10, ...)
로 이미지 생성기를 이용해 학습을 진행할 수 있습니다.
꼭 위 함수를 쓸 필요는 없지만 쓰지 않으면 생성기로부터 직접 변형된 데이터를 얻어서 사용해줘야 합니다.
steps_per_epoch는 한 세대마다 몇 번 생성기로부터 데이터를 얻을지를 나타내는 값입니다.
고로 실제로 한 세대마다 사용되는 학습데이터의 수는 steps_per_epoch * batch_size가 됩니다.

끝!






Comments