Tekhartha의 인공지능 기술블로그

MNIST-기본

|


딥 러닝 학습하는 사람들은 누구나 아는, 모르면 간첩인, MNIST 관련 사항을 정리해 본다. MNIST를 정리하면서, 딥 러닝 모델들에서 나올 수 있는 여러 가지 추가할 수 있는 옵션들을 모두 담아 보려고 한다.

[MNIST - 소개]

기본적으로 소개글은 텐서플로우 문서 한글 번역 사이트에 있는 MNIST 초급 페이지를 참고했음을 먼저 밝힌다.

MNIST는 Modified National Institute of Standards and Technology database의 약자로, 손으로 쓴 숫자들로 이루어진 대형 데이터베이스이다. 1995년 미국의 NIST(표준 및 기술협회)에서 취합했던 손글씨 데이터 샘플들을 재혼합 및 보정하여 만들어졌다. 각각의 데이터는 0~9 까지의 숫자를 쓴 그림들이며, 트레이닝 데이터는 60,000개, 테스트 데이터는 10,000개이다. 각 데이터는 28*28 = 784픽셀로 이루어져 있는 이미지 데이터이며, 클래스는 0~9까지 10개이다.

워낙에 유명한 데이터라서 텐서플로우에 아예 이 데이터를 받을 수 있게 하고 편집도 가능한 라이브러리가 존재한다(!)

아래에는 이 데이터셋을 로드하는 방법부터 시작해서 딥 러닝 모델을 이용해 분류하고 테스트 데이터를 예측까지 해 보는 과정을 정리했다.

[MNIST - 코드를 통한 이해]

import tensorflow as tf
import random
import matplotlib.pyplot as plt
tf.set_random_seed(777)  # for reproducibility

먼저 필요한 라이브리를 import한다.

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

위와 같은 코드로 mnist 데이터를 로드한다. 보다시피 텐서플로가 로딩을 지원하는 모습을 볼 수 있다. 이미지나 텍스트 파일로만 input을 받다가 이 코드를 처음 봤을 땐 굉장히 당황했다. 어..그럼 이 데이터가 어떤 모양으로 생겼는지는 어떻게 확인하지?

print(mnist.train.images.shape)
print(mnist.validation.images.shape)
print(mnist.test.images.shape)

위와 같이 메소드를 차근차근 찾다 보면 shape를 확인할 수 있는 메소드가 존재한다. 위 코드의 출력은 다음과 같다 :

(55000, 784)
(5000, 784)
(10000, 784)

train data는 55,000개, validation data는 5,000개, test data는 10,000개로 구성되어 있음을 알 수 있다.

(validation은 예비 test를 위한 데이터라고 보면 된다. 그냥 train data와 합쳐도 무관)

마찬가지 방법으로 mnist.train.labels.shape 같은 형식으로 label들의 shape를 찾아보면 10개의 클래스로 나누어져 있는 것을 확인할 수 있다.

그럼 데이터 하나가 어떻게 생겼는지를 자세히 볼까?

print(mnist.train.images[0])를 사용해서 첫 번째의 이미지를 출력해 보았더니…

이렇게 784개의 float형식의 숫자로 이루어진 리스트가 나타난다. 그러니까 28*28개의 각 픽셀들을 일렬로 쭈욱 나래비 세운 1차원 벡터 형식으로 저장되어 있다 이 말이다. (28*28 2차원 벡터가 아니다. 그러니까 인풋 데이터에는 줄 구분이 없다는 얘기다.)

그럼 각 숫자가 의미하는 바는? 가장 어두운 검정을 0, 가장 밝은 흰색을 1이라고 놓았을 때, 0~1 사이의 값으로 각 픽셀의 밝기를 나타내는 것이다.

그럼 이 784개의 숫자를 그림으로 나타내면 어떻게 보일까?

import matplotlib.pyplot as plt
plt.imshow(
        mnist.train.images[0].reshape(28, 28),
        cmap='Greys',
        interpolation='nearest')
plt.show()

위와 같이 28*28로 reshape하고 matplotlib를 사용하면 그림은 이렇게 그려지게 된다 :

(cmap 설정 안하면 0이 보라색으로, 1이 노란색으로 나오기 때문에 별로 예쁘지가 않다)

(interpolation은 보간법 또는 내삽법이라 불리는데 일종의 필터라고 생각하면 된다)

(* 참고로 일반적인 그림 파일의 형식은 이렇지 않다. 각 픽셀마다 RGB값이 숫자로 저장되어 있어서 보통 3차원 Numpy 행렬로 나타내어진다)


이제 데이터가 어떻게 생겼는지를 알았으니 다음으로 넘어가면,

nb_classes = 10

# MNIST data image of shape 28 * 28 = 784
X = tf.placeholder(tf.float32, [None, 784])
# 0 - 9 digits recognition = 10 classes
Y = tf.placeholder(tf.float32, [None, nb_classes])

W = tf.Variable(tf.random_normal([784, nb_classes]))
b = tf.Variable(tf.random_normal([nb_classes]))

nb_classes는 분류할 class의 개수(0~9까지 10개)를 나타내고,

X는 784개의 숫자로 이루어진 1차원 벡터를 한꺼번에 받는데, 한 번에 들어올 데이터의 개수(배치)를 None을 써서 사용자가 쉽게 바꿀 수 있도록 한다. 그러므로 X의 shape는 [None, 784]가 되어야 하고,

Y는 class 개수만큼 열을 설정해 주어야 한다. 마찬가지로 배치는 얼마나 될 지 설정할 수 있게끔 None을 사용한다. 그러므로 shape는 [None, nb_classes].

이번 코드에서는 Y = XW + b의 연산을 한 번만 수행할 것이므로 W와 b를 위와 같이 설정한다. W는 x에서 784개의 열로 된 데이터를 받아서 nb_classes만큼의 노드에 집어넣는다. 여기에 nb_classes 개수만큼의 숫자로 된 상수열 b를 추가한다.

# Hypothesis (using softmax)
hypothesis = tf.nn.softmax(tf.matmul(X, W) + b)
# cost (cross entropy)
cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(hypothesis), axis=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(cost)

그래프의 마지막을 softmax로 장식하기 위해 hypothesis 노드를 위와 같이 정의한다.

hypothesis는 [None, nb_classes]의 형태를 가질 것이고, cost는 cross entropy를 사용해서 값을 계산한다. cost는 배치의 각 데이터의 개별 cost들의 합이다. 이 cost를 gradient-descent(경사하강) 방식으로 업데이트 해나간다.

# Test model
is_correct = tf.equal(tf.arg_max(hypothesis, 1), tf.arg_max(Y, 1))
# Calculate accuracy
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

정확도를 측정해보기 위해 위와 같이 작성한다. is_correct는 hypothesis에서의 가장 확률이 높은 클래스와 실제 Y에서 가장 확률이 높은 클래스가 일치하는지를 본다. (Y는 one-hot encoding이므로 1인 클래스가 리턴될 것이다.)

accuracy에는 is_correct의 평균을 낸 값을 저장한다.

# parameters
training_epochs = 15
batch_size = 100

에폭(모든 데이터를 한 바퀴 도는 사이클)을 15, 배치 사이즈를 100으로 설정 후

with tf.Session() as sess:
    # Initialize TensorFlow variables
    sess.run(tf.global_variables_initializer())
    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0
        total_batch = int(mnist.train.num_examples / batch_size)

        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            c, _ = sess.run([cost, optimizer], feed_dict={X: batch_xs, Y: batch_ys})
            avg_cost += c / total_batch

        print('Epoch:', '%04d' % (epoch + 1),
              'cost =', '{:.9f}'.format(avg_cost))

    print("Learning finished")
    # Test the model using test sets
    print("Accuracy: ", accuracy.eval(session=sess, feed_dict={X: mnist.test.images, Y: mnist.test.labels}))

세션을 열고 epoch만큼 for문을 돌면서 그 안에서 (전체 데이터 개수/배치 크기) 만큼 또 for문을 돌리면서 batch 수만큼 데이터를 그래프에 공급한다. avg_cost에는 배치마다의 평균 cost의 합을 넣는다. 15번의 epoch마다 cost를 출력하고 학습을 완료한다.

마지막의 print(“Accuracy : “)… 문장에서 모든 테스트 데이터들에 대한 평가를 수행한다. 그런데 이게 GPU메모리에 모든 데이터를 한 방에 넣고 돌리는 형식이라서 본인 컴퓨터에선 안 돌아간다. (지포스 750ti 기준 안됨)

성능이 딸리는 컴퓨터에서 하고 싶으면 테스트도 트레이닝 같이 배치로 나누어서 돌리면 된다.

돌아가는 컴퓨터에서는 대략 정확도가 0.88가량이 나온다고 한다.

   # Get one and predict
    r = random.randint(0, mnist.test.num_examples - 1)
    print("Label: ", sess.run(tf.argmax(mnist.test.labels[r:r + 1], 1)))
    print("Prediction: ", sess.run(tf.argmax(hypothesis, 1), feed_dict={X: mnist.test.images[r:r + 1]}))

랜덤하게 1개의 데이터를 뽑아서 실제 Y값을 출력해보고, 학습된 모델로 Prediction을 해본다.


쉬운 듯 어렵고, 어려운 듯 쉬운 텐서플로우 모델. 간단한 Y=WX+b의 모델만으로도 0.88의 정확도를 보여주는데, cnn 등 좀더 깊은 신경망을 사용하면 거의 100%에 이르는 정확도를 보인다고 한다.

아래에는 전체 코드이다.

import tensorflow as tf
import random
import matplotlib.pyplot as plt
tf.set_random_seed(777)  # for reproducibility

from tensorflow.examples.tutorials.mnist import input_data
# Check out https://www.tensorflow.org/get_started/mnist/beginners for
# more information about the mnist dataset
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

nb_classes = 10

# MNIST data image of shape 28 * 28 = 784
X = tf.placeholder(tf.float32, [None, 784])
# 0 - 9 digits recognition = 10 classes
Y = tf.placeholder(tf.float32, [None, nb_classes])

W = tf.Variable(tf.random_normal([784, nb_classes]))
b = tf.Variable(tf.random_normal([nb_classes]))

# Hypothesis (using softmax)
hypothesis = tf.nn.softmax(tf.matmul(X, W) + b)

cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(hypothesis), axis=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(cost)

# Test model
is_correct = tf.equal(tf.arg_max(hypothesis, 1), tf.arg_max(Y, 1))
# Calculate accuracy
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

# parameters
training_epochs = 15
batch_size = 100

with tf.Session() as sess:
    # Initialize TensorFlow variables
    sess.run(tf.global_variables_initializer())
    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0
        total_batch = int(mnist.train.num_examples / batch_size)

        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            c, _ = sess.run([cost, optimizer], feed_dict={X: batch_xs, Y: batch_ys})
            avg_cost += c / total_batch

        print('Epoch:', '%04d' % (epoch + 1),
              'cost =', '{:.9f}'.format(avg_cost))

    print("Learning finished")
    # Test the model using test sets
    print("Accuracy: ", accuracy.eval(session=sess, feed_dict={X: mnist.test.images, Y: mnist.test.labels}))

    # Get one and predict
    r = random.randint(0, mnist.test.num_examples - 1)
    print("Label: ", sess.run(tf.argmax(mnist.test.labels[r:r + 1], 1)))
    print("Prediction: ", sess.run(tf.argmax(hypothesis, 1), feed_dict={X: mnist.test.images[r:r + 1]}))

* 본 포스트는 김성훈 교수님의 “모두를 위한 딥러닝” 강좌를 참고하여 작성되었습니다.

김성훈 교수님의 “모두를 위한 딥러닝”

Comments