MNIST 데이터 셋 다중클래스 분류

About the MNIST data set

Modified National Institute of Standards and Technology(이하, MNIST) 데이터는 손으로 쓴 숫자들로 이루어진 데이터 셋으로 기계학습에 적합하도록 Anti-Aliasing 및 normalizing 처리되어 Yann Lecun이 공유한 자료이다. 다중클래스 분류(multiclass classification)의 필수 예제로 활용되고 있으며 Yann Lecun의 웹사이트에는 linear classifier, SVM, MLP, CNN 등 다양한 알고리즘을 MNIST 데이터에 적용한 결과를 소개하고 있다.

TensorFlow에서도 MNIST 데이터를 제공하고 있으며 다음 코드를 입력하면 data 폴더에 데이터가 자동으로 저장된다.

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("data/", one_hot=True)

위 코드를 실행하면 아래와 같은 경고 메세지가 뜬다(TensorFlow 1.8.0 기준).

Please use alternatives such as official/mnist/dataset.py from tensorflow/models.

위 코드를 이용해서 데이터 다운 방법을 지양하고 있으며 이후 버전에서 삭제한다고 한다. 하지만 이 글에서는 이 방법으로 데이터를 불러온다.

MNIST 데이터는 다음과 같이 3개로 나눠져있다(참고: Avoid overfitting).

  • mnist.train: 모델 학습에 쓰이는 55,000개 학습데이터
  • mnist.validation: overfitting을 막기 위해 학습 횟수를 결정하는 학습에 쓰이지 않는 5,000개 확인데이터(또는 검증데이터)
  • mnist.test: 모델의 성능을 평가하는 10,000개 테스트데이터

각각의 데이터 셋은 손글씨 숫자 이미지 \(X\)와 해당 이미지의 라벨 \(Y\)로 구성되어있다. 숫자 이미지 \(X\)는 28픽셀(pixels) \(\times\) 28픽셀(pixels)의 바운딩 박스 형태를 \(28\times 28=784\) 차원의 벡터로 펼쳤으며(flatten) 픽셀값은 \([0,1]\) 사잇값으로 normalized 되어있다. 이 이미지 \(X\)를 다시 (28,28) 차원의 array(또는 행렬)로 사이즈를 변경하면 다음과 같은 형태를 띠고 있다.

\(Y\)는 해당 \(X\) 이미지의 실제 숫자값 0~9를 나타내고 위 코드에서

one_hot=True

으로 설정했기 때문에 \(Y\)는 다음과 같이 10차원의 one hot vector로 표현된다.

\[0 =\left(\begin{array}{cc} 1 \\ 0 \\ \vdots\\ 0\end{array}\right), \quad 1 = \left(\begin{array}{cc} 0 \\ 1 \\ \vdots\\ 0\end{array}\right),\quad \ldots \quad 9 = \left(\begin{array}{cc} 0 \\ 0 \\ \vdots\\ 1\end{array}\right)\]

학습데이터의(55,000개) 이미지 \(X\)와 라벨 \(Y\)는 다음과 같이 구성되어있다.

MNIST multiclass classification

MNIST 데이터의 숫자를 분류하는 MLP(Multilayer Perceptron) 모델을 만들어 보자. Adam optimizer를 사용해서 학습을 진행했다. 학습데이터는 batch size 100으로 데이터를 분할했고 확인데이터(validation)와 테스트데이터는 분할하지 않고 전체를 대입했다(batch size로 나눠서 분할하는 함수는 라이브러리에서 제공). 여기서, 이전 포스트의 모델과 다른점은 weight와 bias를 tf.Variable로 선언하지 않고

tf.layers.dense

를 이용해 간단하게 만들었다(hidden layer도 동일하게 만들 수 있다). tf.layers.denseinput, layer의 차원, 그리고 activation function만 정해주면 매번 weight와 bias를 정의할 필요 없다.

Hidden layer 없는 모델

첫 번째로 만들 모델은 히든레이어(hidden layer)가 없는 784차원 \(X\)에서 10차원의 \(H\)로 바로 가는 모델이다. 중간에 히든레이어가 없기 때문에 output인 \(H\)의 input은 \(X\)이고 차원(node 개수)은 라벨 \(Y\)의 차원과 같은 10으로 설정하였다. 그리고 다중클래스 분류이기 때문에 \(H\)의 activation function은 softmax로 설정했다.

# Set variables and layers
X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])

H = tf.layers.dense(X, 10, activation=tf.nn.softmax)

결과는 다음과 같다(코드 실행시 결과는 조금 상이할 수 있다).

Accuracy of Test data : 0.9149

Hidden layer 있는 모델

두 번째로 모델은 히든레이어(hidden layer)가 5개 있는 모델이다(\(H0,H1,\ldots,H4\)). Input과 output 레이어 사이의 중간에 있는 히든레이어의 차원은 모두 256이고 activation은 sigmoid로 설정하였다(차원과 activation 모두 같은 필요 없다). 모델의 순서를 보면 다음과 같다.

\[\underbrace{X}_{784} \longrightarrow \underbrace{H0}_{256} \longrightarrow \underbrace{H1}_{256} \longrightarrow \underbrace{H2}_{256} \longrightarrow \underbrace{H3}_{256} \longrightarrow \underbrace{H4}_{256} \longrightarrow \underbrace{H}_{10}\]
# Set variables and layers
X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])

H0 = tf.layers.dense(X, 256, activation=tf.nn.sigmoid)
H1 = tf.layers.dense(H0, 256, activation=tf.nn.sigmoid)
H2 = tf.layers.dense(H1, 256, activation=tf.nn.sigmoid)
H3 = tf.layers.dense(H2, 256, activation=tf.nn.sigmoid)
H4 = tf.layers.dense(H3, 256, activation=tf.nn.sigmoid)

H = tf.layers.dense(H4, 10, activation=tf.nn.softmax)

결과는 다음과 같다(코드 실행시 결과는 조금 상이할 수 있다). 컴퓨터 속도가 느린 컴퓨터에서는 오랜 시간 걸린다.

Accuracy of Test data : 0.9765

TensorFlow code

히든레이어가 있는 모델은 레이어 설정 code 부분만 간단히 수정하면 되므로 히든레이어 없는 모델의 코드를 작성했다.

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow.examples.tutorials.mnist import input_data
%matplotlib inline

# Load mnist data set
mnist = input_data.read_data_sets('data/', one_hot = True)

training_img = mnist.train.images
training_lab = mnist.train.labels
validation_img = mnist.validation.images
validation_lab = mnist.validation.labels
test_img = mnist.test.images
test_lab = mnist.test.labels

# Set variables and layers
X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])

H = tf.layers.dense(X, 10, activation=tf.nn.softmax)

loss = - tf.reduce_mean(tf.reduce_sum(Y * tf.log(H), reduction_indices=[1]))
train = tf.train.AdamOptimizer(0.001).minimize(loss)

correct_prediction = tf.equal(tf.argmax(H,1), tf.argmax(Y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

sess = tf.Session()
sess.run(tf.global_variables_initializer())

cost_list = []
val_list = []
acc_list = []

batch_size = 100
n_epoch = 10

for i in range(n_epoch):
    for j in range(55000//batch_size):
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        _, cost = sess.run([train, loss], feed_dict={X: batch_xs, Y: batch_ys})
        cost_list.append(cost)
    cost_ = sess.run(loss, feed_dict={X: mnist.validation.images, Y: mnist.validation.labels})
    acc = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels})
    val_list.append(cost_)    
    acc_list.append(acc)


plt.figure(figsize=(12,10))
plt.subplot(221)
plt.title("Cost of Trining data")
plt.xlabel("Steps")
_ = plt.plot(cost_list, "c")

plt.subplot(222)
plt.title("Cost of Validation data")
plt.xlabel("Steps")
_ = plt.plot(val_list, "b")

plt.subplot(223)
plt.title("Accuracy of Test data")
plt.xlabel("Steps")
_ = plt.plot(acc_list, "k--")

print("Accuracy of Test data : %s" %acc)

Reference

  • https://www.tensorflow.org