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

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