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]))

Keras - 선형 회귀 구현

|

Linear Regression

Keras를 이용해서 선형 회귀를 구현하는 예제입니다. 학습에 사용된 데이터는 보스턴 집값입니다.

기존에 공부했던 코드들은 결과값이 0 또는 1, 또는 여러 범주 중 하나를 선택하는 코드들이었습니다. 이번 코드는 주어진 데이터를 이용해서 집값을 선형으로 예측하는 예제입니다.

0 또는 1의 값이 아니기 때문에 모델의 마지막 출력(output) 계층에서 활성화 함수(Activation Function)를 지정할 필요가 없습니다.


예제 코드

from keras.models import Sequential
from keras.layers import Dense
from sklearn.model_selection import train_test_split

import pandas as pd

df = pd.read_csv('house_price.csv', delim_whitespace=True, header=None)

data_set = df.values
X = data_set[:, 0:13]
Y = data_set[:, 13]

X_train, X_validation, Y_train, Y_validation = train_test_split(X, Y,
                                                                test_size=0.2)

model = Sequential()
model.add(Dense(30, input_dim=13, activation='relu'))
model.add(Dense(6, activation='relu'))
model.add(Dense(1))

model.compile(loss='mean_squared_error', optimizer='adam')

model.fit(X_train, Y_train, epochs=200, batch_size=10)

Y_prediction = model.predict(X_validation).flatten()

for i in range(10):
  real_price = Y_validation[i]
  predicted_price = Y_prediction[i]
  print('Real Price: {:.3f}, Predicted Price: {:.3f}'.format(real_price,
                                                             predicted_price))

Keras - 최적 모델 찾기

|

와인 감별 머신 러닝

와인의 속성을 체크하여 레드 와인과 화이트 와인을 구분하는 예제입니다. 학습에 사용하는 데이터 세트는 여기에서 받을 수 있습니다.


기본 예제 코드

먼저 아주 기본적인 머신러닝 코드를 구현합니다.

from keras.models import Sequential
from keras.layers import Dense

import pandas as pd

df = pd.read_csv('wine.csv', header=None)
df = df.sample(frac=1)
data_set = df.values

X = data_set[:, 0:12]
Y = data_set[:, 12]

model = Sequential()
model.add(Dense(30, input_dim=12, activation='relu'))
model.add(Dense(12, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

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

model.fit(X, Y, epochs=200, batch_size=200)

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


모델의 저장과 불러오기

모델은 다음 코드를 이용해서 저장하고 불러올 수 있습니다.

from kera.models import load_model

# 모델 저장
model.save('snowdeer_model.h5')

# 모델 불러오기
model = load_model('snowdeer_model.h5')


모델 체크포인터 콜백

ModelCheckpoint 콜백 함수는 Keras에서 모델을 학습할 때마다 중간중간에 콜백 형태로 알려줍니다. 다음과 같은 코드를 이용해서 사용할 수 있습니다.

from keras.callbacks import ModelCheckpoint
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 + '{epoch:02d}-{val_loss:.4f}.hdf5'

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

# ...

model.fit(X, Y, validation_split=0.2, epochs=200, batch_size=200, verbose=0,
          callbacks=[cb_checkpoint])

ModelCheckpoint의 속성으로 verbose는 해당 함수의 진행 사항의 출력 여부, save_best_only는 모델의 정확도가 최고값을 갱신했을 때만 저장하도록 하는 옵션입니다.

전체 코드는 다음과 같습니다.

from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import ModelCheckpoint
import os
import pandas as pd

MODEL_SAVE_FOLDER_PATH = './model/'

df = pd.read_csv('wine.csv', header=None)
df = df.sample(frac=1)
data_set = df.values

X = data_set[:, 0:12]
Y = data_set[:, 12]

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

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

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

model = Sequential()
model.add(Dense(30, input_dim=12, activation='relu'))
model.add(Dense(12, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

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

model.fit(X, Y, validation_split=0.2, epochs=200, batch_size=200, verbose=0,
          callbacks=[cb_checkpoint])

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

실행해보면 ./model 폴더 아래에 수많은 .hdf5 파일들이 생겼음을 확인할 수 있습니다. 파일명은 epoch 값과 val_loss(오차율)이므로 가장 최종적으로 생긴 파일이 가장 성능이 좋은 모델이 됩니다.


학습 자동 중단

반복문을 돌면서 최고 성능의 모델을 찾아낼 때, 초반에 최고 성능의 모델이 찾아져서 그 보다 더 좋은 성능의 모델이 더 이상 발견되지 않는 경우가 있습니다. 이런 경우 학습을 중단하도록 하는 기능을 Keras에서는 EarlyStopping()이라는 함수로 제공하고 있습니다.

from keras.callbacks import EarlyStopping

# ...

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

# ...

model.fit(X, Y, validation_split=0.2, epochs=5000, batch_size=500,
          callbacks=[EarlyStopping])

위와 같은 코드를 이용해서 구현하면 epoch 횟수가 총 5,000번이지만, 중간에 더 좋은 성능의 모델이 100번동안 발견이 되지 않으면 학습을 멈추게 됩니다.

Keras - Overfitting 회피하기

|

Overfitting

과적합(Overfitting)은 머신러닝에 자주 등장하는 용어입니다. 학습 데이터에 과하게 최적화하여, 실제 새로운 데이터가 등장했을 때 잘 맞지 않는 상황을 의미합니다.

Image

학습 데이터에도 Noise나 예외가 섞인 데이터들이 포함되어 있을 수 있고, 신경망의 층이 너무 많거나 변수가 복잡해서 Overfitting이 발생하기도 합니다.

과적합을 방지하는 건 머신러닝의 큰 숙제였고, Regularization과 Validation 등을 이용해서 과적합을 회피하는 방법들이 많이 연구되었습니다.


Regularization, Validation

Regularization은 학습 데이터를 조금 나누어서 테스트 데이터로 활용하는 방법입니다. 예를 들어 학습 데이터의 80%만 학습을 시키고, 나머지 20%는 학습을 시키지 않고 테스트에 활용하는 방법입니다. 나머지 20%로 검증을 하는 것을 Validation이라고 합니다.

Image

그리고 아래 이미지와 같이 검증용 데이터를 고정하지 않고 무작위로 바꿔가면서 사용하는 ‘K겹 교차검증(K-fold Cross Validation)’ 기법도 있습니다.

Image


학습 데이터와 테스트 데이터로 분리하는 예제 코드

sklearn 모듈을 이용하면 주어진 데이터를 학습 데이터와 테스트 데이터로 쉽게 나눌 수 있습니다.

from sklearn.model_selection import train_test_split

...

# 주어진 데이터를 학습 데이터와 테스트 데이터로 분리
X_train, X_validation, Y_train, Y_validation = train_test_split(X, Y, test_size=0.2)

...

# 학습 데이터를 이용해서 학습
model.fit(X_train, Y_train, epochs=100, batch_size=5)

# 테스트 데이터를 이용해서 검증
print('\nAccuracy: %.4f' % (model.evaluate(X_validation, Y_validation)[1]))


전체 코드는 다음과 같습니다. (기존의 아이리스 품종 분류 코드에 적용했습니다.)

from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split

import pandas as pd

df = pd.read_csv('iris.csv',
                 names=["sepal_length", "sepal_width", "petal_length",
                        "petal_width", "species"])

data_set = df.values
X = data_set[:, 0:4].astype(float)
obj_y = data_set[:, 4]

encoder = LabelEncoder()
encoder.fit(obj_y)
Y_encodered = encoder.transform(obj_y)

Y = np_utils.to_categorical(Y_encodered)

# 주어진 데이터를 학습 데이터와 테스트 데이터로 분리
X_train, X_validation, Y_train, Y_validation = train_test_split(X, Y,
                                                                test_size=0.2)

model = Sequential()

model.add(Dense(16, input_dim=4, activation='relu'))
model.add(Dense(3, activation='softmax'))

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

# 학습 데이터를 이용해서 학습
model.fit(X_train, Y_train, epochs=100, batch_size=5)

# 테스트 데이터를 이용해서 검증
print('\nAccuracy: {:.4f}'.format(model.evaluate(X_validation, Y_validation)[1]))


K겹 교차 검증 예제 코드

K겹 교차 검증도 sklearn 모듈을 이용해서 쉽게 적용할 수 있습니다.

from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import KFold

import pandas as pd

df = pd.read_csv('iris.csv',
                 names=["sepal_length", "sepal_width", "petal_length",
                        "petal_width", "species"])

data_set = df.values
X = data_set[:, 0:4].astype(float)
obj_y = data_set[:, 4]

encoder = LabelEncoder()
encoder.fit(obj_y)
Y_encodered = encoder.transform(obj_y)

Y = np_utils.to_categorical(Y_encodered)

# 주어진 데이터를 학습 데이터와 테스트 데이터로 분리
skf = KFold(n_splits=5, shuffle=True)

accuracy = []

for train, validation in skf.split(X, Y):
  model = Sequential()

  model.add(Dense(16, input_dim=4, activation='relu'))
  model.add(Dense(3, activation='softmax'))

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

  # 학습 데이터를 이용해서 학습
  model.fit(X[train], Y[train], epochs=100, batch_size=5)

  # 테스트 데이터를 이용해서 검증
  k_accuracy = '%.4f' % (model.evaluate(X[validation], Y[validation])[1])
  accuracy.append(k_accuracy)

# 전체 검증 결과 출력
print('\nK-fold cross validation Accuracy: {}'.format(accuracy))