Keras - Model.fit 옵션

|

Keras 학습 함수 fit()

Keras에서는 모델 학습을 위해 fit() 함수를 사용합니다.

model.fit(X, Y, batch_size=100, epochs=10)
  • X : 입력 데이터
  • Y : 결과(Label 값) 데이터
  • batch_size : 한 번에 학습할 때 사용하는 데이터 개수
  • epochs : 학습 데이터 반복 횟수


batch_size

batch_size는 학습할 때 문제를 몇 번 풀고 정답을 확인하는 지를 결정하는 값입니다. 만약 batch_size10이라면, 총 10개의 데이터를 학습한 다음 가중치를 1번 갱신하게 됩니다.

100문제를 풀고 가중치를 갱신하는 것과, 1문제를 풀고 가중치를 갱신하는 것은 학습 결과에 큰 차이가 있습니다. 사람이 문제집으로 공부를 하는 것과 비슷합니다. 100문제를 풀고 한 번에 채점하면서 학습하는 것과, 1문제를 풀고 채점한 다음 다음 문제를 푸는 것과 비슷합니다.

batch_size 값이 크면 클수록 여러 데이터를 기억하고 있어야 하기에 메모리가 커야 합니다. 그대신 학습 시간이 빨라집니다. batch_size 값이 작으면 학습은 꼼꼼하게 이루어질 수 있지만 학습 시간이 많이 걸립니다.


epochs

학습 데이터 전체셋을 몇 번 학습하는지를 의미합니다. 동일한 학습 데이터라고 하더라도 여러 번 학습할 수록 학습 효과는 커집니다. 하지만, 너무 많이 했을 경우 모델의 가중치가 학습 데이터에 지나치게 최적화되는 과적합(Overfitting) 현상이 발생합니다.

다양한 학습 데이터를 이용해서 학습하는 것이 제일 좋지만, 현실적으로 학습 데이터를 다량으로 획득하는 것이 어렵기 떄문에 동일 데이터를 반복해서 학습하는 것은 피하기 어렵습니다. 따라서 과적합이 발생하지 않는 적절한 학습 횟수를 찾는 것도 딥러닝이 가진 숙제 중 하나입니다.

Keras - CNN(Convolution Neural Network) 예제

|

CNN on Keras

Keras에서 CNN을 적용한 예제 코드입니다. MNIST 손글씨 데이터를 이용했으며, GPU 가속이 없는 상태에서는 수행 속도가 무척 느립니다.


예제 코드

from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Conv2D, MaxPooling2D, Dropout, Flatten
from keras.callbacks import ModelCheckpoint, EarlyStopping

import matplotlib.pyplot as plt
import os
import numpy

MODEL_SAVE_FOLDER_PATH = './model/'

if not os.path.exists(MODEL_SAVE_FOLDER_PATH):
  os.mkdir(MODEL_SAVE_FOLDER_PATH)

model_path = MODEL_SAVE_FOLDER_PATH + 'mnist-' + '{epoch:02d}-{val_loss:.4f}.hdf5'

cb_checkpoint = ModelCheckpoint(filepath=model_path, monitor='val_loss',
                                verbose=1, save_best_only=True)

cb_early_stopping = EarlyStopping(monitor='val_loss', patience=10)

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_validation = X_validation.reshape(X_validation.shape[0], 28, 28, 1).astype('float32') / 255

Y_train = np_utils.to_categorical(Y_train, 10)
Y_validation = np_utils.to_categorical(Y_validation, 10)

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(X_train, Y_train,
                    validation_data=(X_validation, Y_validation),
                    epochs=3, batch_size=200, verbose=0,
                    callbacks=[cb_checkpoint, cb_early_stopping])

print('\nAccuracy: {:.4f}'.format(model.evaluate(X_validation, Y_validation)[1]))

y_vloss = history.history['val_loss']
y_loss = history.history['loss']

x_len = numpy.arange(len(y_loss))
plt.plot(x_len, y_loss, marker='.', c='blue', label="Train-set Loss")
plt.plot(x_len, y_vloss, marker='.', c='red', label="Validation-set Loss")

plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()


예제 코드 설명

Keras에서 Convolution Layer를 추가하는 코드는 다음과 같습니다.

model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))

Conv2D() 함수의 인자로

  • 첫 번째 숫자인 32는 32개의 필터를 적용하겠다는 의미입니다.
  • kernel_size는 필터의 크기를 의미합니다.
  • input_shape는 (행, 열, 색상)을 의미합니다. 흑백의 경우 1의 값을 가집니다. RGB의 경우 3의 값을 가집니다.


model.add(MaxPooling2D(pool_size=2))

MaxPooling2D는 풀링 기법 중 가장 많이 사용하는 맥스 풀링을 적용하는 함수입니다. 맥스 풀링은 정해진 영역 안에서 가장 큰 값만 남기고 나머지는 버리는 방식입니다. pool_size는 풀링 윈도우 크기이며 2의 값은 전체 크기를 절반으로 줄입니다.

model.add(Dropout(0.25))

Dropout()는 특정 노드에 학습이 지나치게 몰리는 것을 방지하기 위해 랜덤하게 일부 노드를 꺼주는 역할을 합니다. Dropout을 통해 과적합을 조금 더 효과적으로 회피할 수 있습니다.

model.add(Flatten())

지금까지 작업했던 이미지는 2차원 배열인데, Flattern() 함수를 통해 1차원 배열로 바꿔줄 수 있습니다.


실행 결과

Image

실행 결과입니다. 현재 사용하고 있는 노트PC의 GPU 성능이 낮아 GPU 가속을 받을 수 없는 상황이라 epoch 값을 3으로 했더니 위와 같은 결과가 나왔습니다. 환경이 좋은데서는 epoch 값을 더 늘려서 좀 더 성능이 좋은 모델을 얻을 수 있을 것입니다.

CNN(Convolution Neural Network)

|

여기를 보면 좀 더 자세한 내용을 볼 수 있습니다.

CNN

CNN(Convoluion Neural Network)은 딥러닝에서 이미지 인식의 꽃이라고 불릴 정도로 강력한 성능을 가진 기법입니다.

CNN은 입력된 이미지에서 특징을 좀 더 잘 뽑아내기 위해서 그 위에 필터(마스크, 윈도우, 커널 등으로 불려짐)를 입히는 기법입니다. 그 이후 풀링(Pooling)이라고 불리우는 다운 샘플링(Down-sampling) 과정을 거쳐 신경망에 데이터를 입력합니다.

Image


이미지 인식

사람은 이미지를 눈으로 보고 바로 인식을 할 수 있지만, 컴퓨터는 이미지를 각 픽셀에 입력되어 있는 숫자값들의 배열로 인식합니다.

Image


필터 사용

아래 그림처럼 포토샵 등에서 다양한 필터를 적용해서 이미지를 변형하는 것을 보신 경험이 있을겁니다. 어떤 필터는 이미지를 더 부드럽게 만들기도 하고, 어떤 필터는 더 날카롭게, 경계면을 뚜렷하게 만드는 필터들도 있습니다.

Image

CNN에서도 이미지 인식을 보다 잘할 수 있도록 필터를 사용합니다.

Image

원본 이미지와 필터간 합성곱(Convolution) 연산을 통해 새로운 Layer를 생성하고, 이를 Convolution Layer라고 합니다. 이러한 필터를 여러 개 사용할 경우 여러 개의 Convolution이 만들어집니다.


필터 사용 예제

필터를 이용해서 원본 이미지와 Convolution 연산을 하는 예제를 살펴보겠습니다.

Image

위 이미지와 같은 필터가 있다고 가정합니다.

Image

그리고 위 이미지에서 노란색 박스 부분을 필터로 검출하게 되면 아래와 같은 결과가 나오게 됩니다.

Image

위 이미지의 아랫 부분에 Multiplication and Summation 결과를 보면 상당히 큰 크기의 값이 도출되는 것을 알 수 있습니다.

Image

만약 위 그림처럼 필터와 비슷한 부분이 거의 없는 영역에 Convolution 계산을 하면 아랫쪽의 계산 결과처럼 0이 나왔음을 확인할 수 있습니다.

이렇게 필터를 이용하면 이미지에서 특징점들을 쉽게 찾아낼 수 있습니다.


딥러닝에 Convolution을 적용

Image

딥러닝에 Convolution을 적용하면 위 그림과 같은 구조가 됩니다. 각 Layer에서 Convolution 연산을 한 후 Activation Function을 거쳐 다음 Layer에서 또 Convolution 연산을 하는 계층 형태로 되어 있습니다.

Keras - Epoch와 오차(Loss)간 관게를 그래프로 확인하기

|

MNIST

이전 포스팅에서 다룬 MNIST 손글씨 인식 결과를 이용해서 그래프로 확인하는 예제입니다.

예제 코드

from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import ModelCheckpoint, EarlyStopping

import matplotlib.pyplot as plt
import os
import numpy

MODEL_SAVE_FOLDER_PATH = './model/'

if not os.path.exists(MODEL_SAVE_FOLDER_PATH):
  os.mkdir(MODEL_SAVE_FOLDER_PATH)

model_path = MODEL_SAVE_FOLDER_PATH + 'mnist-' + '{epoch:02d}-{val_loss:.4f}.hdf5'

cb_checkpoint = ModelCheckpoint(filepath=model_path, monitor='val_loss',
                                verbose=1, save_best_only=True)

cb_early_stopping = EarlyStopping(monitor='val_loss', patience=10)

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

X_train = X_train.reshape(X_train.shape[0], 784).astype('float64') / 255
X_validation = X_validation.reshape(X_validation.shape[0], 784).astype('float64') / 255

Y_train = np_utils.to_categorical(Y_train, 10)
Y_validation = np_utils.to_categorical(Y_validation, 10)

model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam',
              metrics=['accuracy'])

history = model.fit(X_train, Y_train, validation_data=(X_validation, Y_validation),
          epochs=30, batch_size=200, verbose=0,
          callbacks=[cb_checkpoint, cb_early_stopping])

print('\nAccuracy: {:.4f}'.format(model.evaluate(X_validation, Y_validation)[1]))

y_vloss = history.history['val_loss']
y_loss = history.history['loss']

x_len = numpy.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c='red', label="Validation-set Loss")
plt.plot(x_len, y_loss, marker='.', c='blue', label="Train-set Loss")

plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()


실행 결과

Image

그래프를 보면 학습 데이터에 대한 오차는 학습을 계속할수록 줄어들지만, 실제 검증용 데이터에 대한 오차는 일정 수준 이상부터는 큰 차이가 없는 것을 알 수 있습니다.

Keras - MNIST 손글씨 인식하기

|

MNIST

MNIST 손글씨 인식은 머신러닝의 ‘Hello, World’라고 불리울정도로 기본이 되는 예제입니다.

Image

미국 국립표준기술원(NIST)에서 고등학생과 인구조사국 직원 등이 쓴 손글씨를 수집하여 70,000개의 숫자 손글씨 데이터셋으로 만들었습니다.

MNIST 데이터는 이미지 형태의 데이터다보니 머신러닝의 입력 데이터로 변환하는 전처리 작업을 해줘야 합니다.


다운로드 및 전처리

MNIST 데이터는 워낙 유명하다보니, Keras에서 기본적으로 쉽게 불러올 수 있는 기능을 제공하고 있습니다.

MNIST 데이터는 학습용 데이터 60,000개, 검증용 데이터 10,000개로 이루어져 있습니다.

from keras.datasets import mnist

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

위 코드로 MNIST 데이터를 네트워크에서 다운받아서 각각의 변수에 불러오도록 수행합니다. 다운로드는 처음에 한 번만 수행하며, 그 뒤로는 이미 다운받은 데이터를 활용해서 불러오기를 수행합니다.


MNIST 데이터는 이미지로 되어 있어서 컴퓨터에서 인식할 수 있도록 전처리 작업을 해주어야 합니다. 손글씨 한 장의 이미지는 28 x 28 = 784개의 픽셀로 이루어져 있습니다.

아래 코드로 픽셀 정보를 눈으로 확인할 수 있습니다.

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

for x in X_train[0]:
  for i in x:
    print('{:3} '.format(i), end='')
  print()

픽셀 정보는 다음과 같습니다.

  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   3  18  18  18 126 136 175  26 166 255 247 127   0   
  0   0   0   0   0   0  30  36  94 154 170 253 253 253 253 253 225 172 253 242 195  64   0  
  0   0   0   0   0  49 238 253 253 253 253 253 253 253 253 251  93  82  82  56  39   0   0   
  0   0   0   0   0  18 219 253 253 253 253 253 198 182 247 241   0   0   0   0   0   0   0  
  0   0   0   0   0   0  80 156 107 253 253 205  11   0  43 154   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0  14   1 154 253  90   0   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0 139 253 190   2   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0  11 190 253  70   0   0   0   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0  35 241 225 160 108   1   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0   0  81 240 253 253 119  25   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0  45 186 253 253 150  27   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0   0  16  93 252 253 187   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0 249 253 249  64   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0  46 130 183 253 253 207   2   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0  39 148 229 253 253 253 250 182   0   0   0   0   0   
  0   0   0   0   0   0   0   0  24 114 221 253 253 253 253 201  78   0   0   0   0   0   0   
  0   0   0   0   0   0  23  66 213 253 253 253 253 198  81   2   0   0   0   0   0   0   0   
  0   0   0   0  18 171 219 253 253 253 253 195  80   9   0   0   0   0   0   0   0   0   0   
  0   0  55 172 226 253 253 253 253 244 133  11   0   0   0   0   0   0   0   0   0   0   0    
  0   0 136 253 253 253 212 135 132  16   0   0   0   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  
  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  


손글씨 정보를 다음과 같은 순서로 변환해줍니다.

  • 가로28, 세로28개의 배열을 1차원 784개의 배열로 변환
  • 각 픽셀당 0~255의 값을 가지는데, 이를 Normalization (255로 나누어주면 됨)
  • 결과값에 대해서는 One-hot-Encoding 적용

코드로는 다음과 같습니다.

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

X_train = X_train.reshape(X_train.shape[0], 784).astype('float64') / 255
X_validation = X_validation.reshape(X_validation.shape[0], 784).astype(
    'float64') / 255

Y_train = np_utils.to_categorical(Y_train, 10)
Y_validation = np_utils.to_categorical(Y_validation, 10)

그 다음부터는 다른 딥러닝 코드들과 비슷한 흐름으로 흘러갑니다.


전체 소스

ModelCheckpointEarlyStopping 모듈까지 적용한 전체 소스입니다.

from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import ModelCheckpoint, EarlyStopping

import os

MODEL_SAVE_FOLDER_PATH = './model/'

if not os.path.exists(MODEL_SAVE_FOLDER_PATH):
  os.mkdir(MODEL_SAVE_FOLDER_PATH)

model_path = MODEL_SAVE_FOLDER_PATH + 'mnist-' + '{epoch:02d}-{val_loss:.4f}.hdf5'

cb_checkpoint = ModelCheckpoint(filepath=model_path, monitor='val_loss',
                                verbose=1, save_best_only=True)

cb_early_stopping = EarlyStopping(monitor='val_loss', patience=10)

(X_train, Y_train), (X_validation, Y_validation) = mnist.load_data()

X_train = X_train.reshape(X_train.shape[0], 784).astype('float64') / 255
X_validation = X_validation.reshape(X_validation.shape[0], 784).astype('float64') / 255

Y_train = np_utils.to_categorical(Y_train, 10)
Y_validation = np_utils.to_categorical(Y_validation, 10)

model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam',
              metrics=['accuracy'])

model.fit(X_train, Y_train, validation_data=(X_validation, Y_validation),
          epochs=30, batch_size=200, verbose=0,
          callbacks=[cb_checkpoint, cb_early_stopping])

print('\nAccuracy: {:.4f}'.format(model.evaluate(X_validation, Y_validation)[1]))