이전 포스팅 Convolutional Neural Network - 1에 이어서 케라스에서 제공하는 MNIST를 이용해서 각각 다층 퍼셉트론 Multilayer perceptron, 컨볼루션 신경망 Convolutional Neural Network 깊은 컨볼루션 신경망 Deep Convolutional Neural Network 모델로 구성하고 학습시켜 보겠습니다. 예제 코드는 블록과 함께 하는 파이썬 딥러닝 케라스 을 바탕으로 작성하였습니다.

데이터 전처리

학습 데이터 불러오기

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, width * height).astype('float32') / 255.0
x_test = x_test.reshape(10000, width * height).astype('float32') / 255.0

케라스에서 load_data 함수는 해당 데이터를 무작위로 섞고, 학습에 사용할 데이터와 테스트에 사용할 데이터를 제공합니다. x_train은 60,000개의 행으로 구성되고 width*height 개의 값을 가지도록 설정하면, 입력 계층에는 60,000개의 데이터가 이미지의 각 픽셀에 해당하는 총 width*height 개의 뉴런에 대한 뉴런이 있는 것이고, 각 픽셀은 MNIST 이미지 중 하나라고 생각하면 됩니다. 그리고 60000*(width*height) 형태로 변환된 행렬을 float32 타입으로 정규화하고 255로 나누는데, 이는 케라스에서 데이터를 0에서 1 사이의 값으로 구동할 때 최적의 성능을 보입니다. 따라서 현재 0~255 사이의 값으로 이루어진 값을 0~1 사이의 값으로 바꿔줍니다(각 픽셀의 강도를 최대 강도 값인 255로 나눔).

학습 데이터에서 검증 데이터 분리하기

x_val = x_train[50000:]
y_val = y_train[50000:]
x_train = x_train[:50000]
y_train = y_train[:50000]

앞에서 불러온 학습 데이터를 다시 50,000개의 학습 데이터와 검증에 사용할 데이터 10,000개로 분리합니다.


모델 구성하기

Multilayer perceptron

model = Sequential()
model.add(Dense(256, input_dim=width*height, activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(10, activation='softmax'))

딥러닝이란 퍼셉트론 위에 숨겨진 퍼셉트론 층을 차곡차곡 쌓은 형태입니다. 이 층들은 케라스에서 Sequential 을 통해 쉽게 구현할 수 있습니다. Sequential로 모델을 선언해 놓고, model.add라는 라인을 추가하면 새로운 층이 만들어집니다. 데이터에서 input_dim 개의 값을 받아 은닉층의 256개 노드로 보내겠다는 뜻인데, 여기서는 입력층을 따로 만들지 않고 첫 번째 Dense가 은닉층+입력층 역할을 겸하도록 설정합니다. 은닉층의 각 노드는 input_dim 개의 입력 값으로부터 임의의 가중치를 가지고 각 노드로 전송되어 활성화 함수를 거치고, 활성화 함수를 거친 결과 값이 출력층으로 전달됩니다.

Convolutional Neural Network

model = Sequential()
# input_shape(행, 열, 색상 또는 흑백)
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(width, height, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dense(10, activation='softmax'))

CNN에서도 마찬가지로 먼저 층을 쌓기 위해 Sequential을 호출합니다. 두 번째 줄의 코드는 32개의 차원을 갖고 각 필터(컨볼루션 필터)의 크기가 3 x 3 인 Convolutional layer를 추가하는 것인데, width x height 이미지에 3 x 3 Convolution을 적용해 32개(차원)의 출력 채널을 만드는 것을 의미합니다.

cnn5

Dropout

Convolutional Layer에서 구한 특징 맵 Feature map 을 하나의 출력 값으로 결합하기 위해 Pooling Layer를 추가합니다. 여기서는 일반적으로 사용되는 MaxPooling을 2 x 2 크기로 적용하였습니다.

이제 풀링된 Pooled feature를 다시 앞에서 만들었던 기본 층(Dense)에 연결해야 합니다. Convolutional layer나 Pooling layer은 주어진 이미지를 2차원 배열로 다루기 때문에 이를 1차원으로 바꿔주는 Flatten 함수를 적용하여 RELU 계층을 보냅니다.

Deep Convolutional Neural Network

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

Deep CNN에서는 계층의 필터 수를 32에서 64로 증가시켰습니다. Deep layer의 필터 수를 늘리는 것은 딥러닝에서 사용하는 일반적인 기술입니다.

dropout

Dropout

노드가 많아지거나 층이 많아진다고 해서 학습이 무조건 좋아지는 것이 아니라는 점을 과적합을 통해 알 수 있습니다. 이를 효과적으로 피해가는 방법이 드롭아웃(Dropout) 기법인데, 은닉층에 배치된 노드 중 일부를 비활성화 시킨다고 보면 됩니다. 이렇게 랜덤하게 노드를 비활성화시켜서 학습 데이터에 지나치게 치우쳐서 학습되는 과적합을 방지할 수 있습니다.


Model compile

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

모델을 컴파일 할 때 어떤 오차 함수, 최적화 방법 그리고 metrics 함수를 지정해 줍니다. 먼저, 오차함수에는 평균 제곱 오차 계열과 교차 엔트로피 계열이 있습니다.

  • 평균 제곱 오차(Mean squared error): 수렴하기까지 속도가 많이 걸린다.
  • 교차 엔트로피(Cross entropy): 출력 값에 로그를 취해서, 오차가 커지면 수렴 속도가 빨라지고 오차가 작아지면 속도가 감소한다.

metrics 함수는 모델이 컴파일될 때 모델 수행 결과를 나타내게끔 설정하는 부분입니다. 정확도를 측정하기 위해 사용되는 테스트 샘플을 학습 과정에서 제외시킴으로써 과적합 문제(over fitting, 특정 데이터에서는 잘 작동하나 다른 데이터에서는 잘 작동하지 않는 문제)를 방지하는 기능을 담고 있습니다.


모델 학습하기

hist = model.fit(x_train, y_train, epochs=30, batch_size=32, validation_data=(x_val, y_val))

학습과정 살펴보기

%matplotlib inline
import matplotlib.pyplot as plt

fig, loss_ax = plt.subplots()

acc_ax = loss_ax.twinx()

loss_ax.plot(hist.history['loss'], 'y', label='train loss')
loss_ax.plot(hist.history['val_loss'], 'r', label='val loss')
loss_ax.set_ylim([0.0, 0.5])

acc_ax.plot(hist.history['acc'], 'b', label='train acc')
acc_ax.plot(hist.history['val_acc'], 'g', label='val acc')
acc_ax.set_ylim([0.8, 1.0])

loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
acc_ax.set_ylabel('accuray')

loss_ax.legend(loc='upper left')
acc_ax.legend(loc='lower left')

plt.show()

multilayer_perceptron

Multilayer perceptron

convolutional

Convolutional Neural Network

deep_convolutional

Deep Convolutional Neural Network

모델 평가하기

loss_and_metrics = model.evaluate(x_test, y_test, batch_size=32)
print('## evaluation loss and_metrics ##')
print(loss_and_metrics)

multilayer_perceptron2

Multilayer perceptron

convolutional2

Convolutional Neural Network

deep_convolutional2

Deep Convolutional Neural Network

모델 사용하기

yhat_test = model.predict(x_test, batch_size=32)

%matplotlib inline
import matplotlib.pyplot as plt

plt_row = 5
plt_col = 5

plt.rcParams["figure.figsize"] = (10,10)

f, axarr = plt.subplots(plt_row, plt_col)

cnt = 0
i = 0

while cnt < (plt_row*plt_col):

    if np.argmax(y_test[i]) == np.argmax(yhat_test[i]):
        i += 1
        continue

    sub_plt = axarr[cnt/plt_row, cnt%plt_col]
    sub_plt.axis('off')
    sub_plt.imshow(x_test[i].reshape(width, height))
    sub_plt_title = 'R: ' + str(np.argmax(y_test[i])) + ' P: ' + str(np.argmax(yhat_test[i]))
    sub_plt.set_title(sub_plt_title)

    i += 1    
    cnt += 1

plt.show()

multilayer_perceptron3

Multilayer perceptron

convolutional3

Convolutional Neural Network

deep_convolutional3

Deep Convolutional Neural Network

Reference