TensorFlow 무작정 따라하기 3 : MNIST For ML Beginnesrs

이 글에서는 MNIST 데이터를 Softmax Regression을 통해 학습한다.
Multinomial Logistic Regression 이라고도 한다.

MNIST는 0부터 9까지의 손글씨 이미지를 가진 데이터베이스인데, 55,000개의 훈련용 이미지와 10,000개의 테스트용 이미지가 있다. 각종 머신러닝 기법들을 테스트하기 위해 많이 쓰이는 데이터이다. 각 이미지는 28 x 28 로 이루어져있다.

영상 처리 공부를 할 때 Lena 사진을 쓰는 것처럼 머신러닝 공부를 하다보면 MNIST를 사용하는 예제를 자주 볼 수 있다.

Lena

MNIST 데이터는 Yann LeCun's website에서 받을 수 있다. TensorFlow는 MNIST 데이터를 다운로드한 후 배열로 읽어들이는 코드를 제공하고 있다. 
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
위 코드를 실행하면 mnist에 MNIST 데이터가 들어가게 된다. 만약 위 코드를 실행할 때 UnicodeEncodeError 가 발생한다면 다음과 같은 방식으로 해결한다.

1. 현재 작성 중인 python 스크립트가 있는 폴더의 상위 폴더로 간다.
2. MNIST_data 라는 폴더를 만든다.
3. MNIST_data 폴더에 직접 MNIST데이터를 다운받은 후 압축을 풀지 않은 채 넣는다.

이렇게 하면 아무 에러없이 진행될 것이다.

MNIST 데이터 중 각 이미지가 어떤 숫자를 뜻하는지를 나타내는 label 데이터가 있다. 이 label 데이터를 우리는 one hot vector로 나타내기를 원한다. one hot vector 는 카테고리를 표현할 때 딱 하나의 요소만 1의 값을 나타내는 바이너리 벡터이다. 현재 label 데이터는 0~9를 의미하는데 만약 3을 의미한다면 [0,0,0,1,0,0,0,0,0,0] 로 나타내는 것이다. 따라서 label 데이터는 m x 10 크기를 가진다. 

이제 Softmax 모델을 구현할 차례이다. Softmax는 간단히 말해서 해당 데이터가 주어진 데이터 중에 차지하는 비율을 의미한다. 


위 식에 의해서 비율을 결정하게 된다. 식에 의해 모든 데이터들의 비를 합하면 1이 된다. 이 성질을 이용해서 각 데이터가 어떤 값일지 추측할 때 후보 추측 값들이 실제 값과 같은 확률을 표현한다. 그 중에 가장 높은 확률을 가진 값을 추측값으로 사용한다.

Softmax는 값을 표현하는 방식만 다를 뿐 Linear Regression의 하나이기 때문에 이전 글들에서 썼던 y=Wx+b 식을 그대로 이용한다. 이 식의 결과값을 softmax 함수의 인자로 넣어주면 된다. 

이제 전체 코드를 보면서 설명하겠다.
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot = True)

x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x,W)+b) # prediction label

y_ = tf.placeholder(tf.float32, [None, 10])

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=tf.matmul(x,W)+b))
train_step = tf.train.GradientDescentOptimizer(0.05).minimize(cross_entropy)

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

for _ in range(1000):
    batch_x, batch_y = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_x, y_: batch_y})

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

_accuracy = sess.run(accuracy, feed_dict={x:mnist.test.images, y_:mnist.test.labels})
print(_accuracy)
sess.close()
x = tf.placeholder(tf.float32, [None, 784]) 는 이미지의 개수를 정확하게 모르기 때문에 행을 None으로 두고 각 이미지는 28x28 이기 때문에 열을 784로 두었다.

cross entropy는 비용함수를 의미한다. 

식은 위처럼 정의가 되는데 이 식을 구현하는 것보다는 TensorFlow에서 제공하는 tf.nn.softmax_cross_entropy_with_logits함수를 사용하는 것을 권장한다.
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=tf.matmul(x,W)+b))
위 코드처럼 사용하면된다. labels에는 각 이미지의 실제 label 값을 넣어주고, logits에는 계산을 통해 추측한 값을 넣어준다.

sess = tf.InteractiveSession()
IneractiveSession 과 Session의 차이가 무엇일까? Session은 Tensor 데이터형의 계산을 하기 위해서 생성하는 객체인데 InteractiveSession을 하면 이후의 모든 계산을 한 Session에서 편하게 할 수가 있다. 만약 여러 다른 그래프에 대해 계산을 하려면 Session을 구분해서 쓰는게 좋다. 여기서는 한 그래프에 대해서 계산하니까 편하게 InteractiveSession을 썼다.

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
tf.argmax()는 해당 배열에서 가장 큰 값을 찾아내는 것이다. 첫 번째 인자는 배열이고, 두 번째 인자는 열 방향인지 행방향인지를 구분하는 것이다. 0은 열방향, 1은 행방향을 의미한다.

나머지 코드는 쉽게 이해가 되기 때문에 이것으로 글을 마치겠다.