MNIST 는 softmax (multinomial logistic) regression 으로 풀 수 있는 가장 기초적인 문제 중에 하나입니다.
MNIST 의 각 이미지는 손으로 쓴 0~9 사이의 숫자이며 정답은 10가지 경우 중 하나이고
이미지의 각 픽셀 중 글자가 쓰여진 부분으로 학습을 하여 값을 구하기 위한 식(solution)의 변수가 일정한 값으로 수렴할 수 있기 때문에
regression analysis 로 풀 수 있으며 각 값이 0과 1 사이의 값으로 이루어지는 softmax regression 을 통해 어떤 특정 숫자일 가능성을
얻을 수 있습니다.
softmax regression 이란 linear regression 와 같이 딥러닝에서 사용되는 기본 개념으로 linear regression 은 여러 데이터들이 y = ax + b 와 같은 직선 일차 방정식에 회귀활 때 이를 x에 대한 y의 회기식이라 합니다. 다음으로 logistic regression 은 linear regression 와 같은 개념이지만 input이 주어졌을 때 얻어지는 y 값이 특정 값이 아닌 특정 분류로 나뉘는 것을 목표로 하는 점이 다릅니다. MNIST 에서는 어떤 이미지가 주어졌을 때 해당 이미지가 0~9 사이의 각 숫자 중 하나로 분류해야 되기 때문에 logistic regression 을 사용해야 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at http://tensorflow.org/tutorials/mnist/beginners/index.md """ import argparse import sys from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf FLAGS = None def main(_): # Import data mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True) # Create the model x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.float32, [None, 10]) # The raw formulation of cross-entropy, # # tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)), # reduction_indices=[1])) # # can be numerically unstable. # # So here we use tf.nn.softmax_cross_entropy_with_logits on the raw # outputs of 'y', and then average across the batch. cross_entropy = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) sess = tf.InteractiveSession() tf.global_variables_initializer().run() # Train for _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='/tmp/tensorflow/mnist/input_data', help='Directory for storing input data') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed) | cs |
위 소스는 TensorFlow 에서 제공하는 튜토리얼으로서 가장 단순한 수준의 softmax regression 모델입니다.
우선 input_data 을 import 합니다. input_data 은 MNIST 데이터를 관리해주는 함수이며 소스는 아래 링크에서 보실 수 있습니다.
input_data 을 통해 우리는 학습 데이터, 테스트 데이터, 검증 데이터를 얻을 수 있으며 각 데이터는
이미지와 그에 따른 라벨(0~9 사이의 값) 으로 구성되어 있습니다.
각 이미지는 28 * 28 픽셀이며 이 배열을 784개의 숫자로 이뤄진 백터로 표현할 수 있습니다.
TensorFlow 는 계산을 위한 데이터 흐름을 node 와 edge 를 사용한 방향 그래프로 표현하는데
node 는 계산과 데이터 입출력 등을 수행하고 edge 는 node 간의 데이터 입출력 관계를 나타냅니다.
여기서 데이터 입출력은 다차원 배열의 데이터 구조를 통해 전달되는데 이 다차원 배열을 tensor 라 부릅니다.
그래프 상의 각 node 는 하나 이상의 tensor 을 받아 계산을 수행하고 tensor 을 반환할 수 있으며 이 node를 operation (op) 라 부릅니다.
이제 계산을 수행하기 위해서 각 tensor 을 만들어야 됩니다. (일반적인 프로그래밍에서 변수를 선언하는 것과 비슷한거 같습니다)
x = tf.placeholder(tf.float32, [None, 784])
784 배열을 가진 각 이미지를 넣을 수 있도록 [None, 784] 크기의 배열로 x 를 만듭니다.
이때 placeholder 을 이용해서 직접 tensor 값을 전달 할 수 있도록 합니다. (TensorFlow 에서는 이를 feed 라고 합니다)
다음으로 이 문제를 풀기 위해 주어진 값들이 회귀한다는 가정하에 다음과 같은 회기식을 사용합니다.
y = Wx + b
여기서 W 는 weight, b 는 bias 라 합니다.
여기서 0~9 각각에 따른 확률을 구해야 되기에 얻어지는 식의 개수가 10개가 되어야되고 각 식은 x의 784개 좌표와 대응해야 됩니다.
이를 풀면
y1 = W1,1 * x1 + W1,2 * x2 + W1,3 * x3 ...... + W1,784 * x784 + b1
y2 = W2,1 * x1 + W2,2 * x2 + W2,3 * x3 ......+ W2,784 * x784 + b2
y3 = W3,1 * x1 + W3,2 * x2 + W3,3 * x3 ......+ W3,784 * x784 + b3
이제 W 와 b 를 구하기 위한 학습을 해야 됩니다.
학습을 하는 방법은 W 와 b 의 값을 바꿔가며 식을 만들어 얻어진 y 와 이미지와 매치되는 라벨의 값 즉 정답 y_ 를 비교하여 그 차이가
작은 모델이 좋은 모델이라 할 수 있습니다. 이런 y 와 y_ 의 차이를 cost 또는 loss 라 표현합니다.
이 값을 구하기 위해 위에서 언급한 y_ 을 위한 tensor 도 선언합니다.
y_ = tf.placeholder(tf.float32, [None, 10])
이제 cost 를 구하기 위해 nn.softmax_cross_entropy_with_logits 함수를 사용하게 되는데
이 함수를 이해하기 위해서는 우선 softmax classification 라는 개념을 이해해야 됩니다.
우선 여기서 classification 라는 개념은 위 식을 통해 얻어진 y 값을 분류하여 0~9 중 하나와 같은 값으로 도출해낸다는 의미입니다.
그리고 softmax 는 이 분류를 위해 얻어진 값을 0~1 사이의 확률로 만들어줍니다.
예를 들어 위 행렬곱을 통해
y1 = 700, y2 = -10, y3 = 0, ... , y10 = 163164
라는 값을 얻었다 할 때 이런 값으로는 학습이 제대로 되지 않기에 y 값을 학습에 용이한 값으로 바꾸는 것이 좋습니다.
이를 위한 여러 가지 Activation Function 중 여기서는 sigmoid 를 이용하였습니다.
https://en.wikipedia.org/wiki/Sigmoid_function
sigmoid 는 아래와 같은 식을 이용해 값을 얻을 수 있고
이 sigmoid 를 이용하면 값을 0~1 사이로 mapping 할 수 있습니다.
그리고 Softmax 는 후보군에 해당되는 sigmoid 값들을 다 더한 합으로 각각의 sigmoid 값을 나누어(normalize) 총합 1의 확률로 각각의 sigmoid 를 확률적으로 해석하고자 할 때 사용됩니다.
exp(xi)∑jexp(xj)
https://en.wikipedia.org/wiki/Softmax_function
다음으로 cross entropy 라는 개념이 있습니다. cross entropy 는 두 개의 값의 차이가 얼마나 되는지 나타내는 것으로 두 개의 값 p와 q에 대한 cross entropy 는 아래와 같은 식으로 구할 수 있습니다.
위 식에서 만약 p 와 q 의 차이가 클 수록 더 큰 값이 나오게 됩니다. 따라서 이 cross entropy 식을 이용해 p와 q 여기서는 예상한 y 값과 라벨 y 값의 차이가 작아지도록 학습을 시키면 원하는 결과를 얻을 수 있을 것입니다.
그리고 이런 모든 과정을 쉽게 해주는 함수가 바로 nn.softmax_cross_entropy_with_logits 입니다.
그래서 이제 cost 를 아래와 같이 정의하고 (reduce_mean 는 평균값을 구해주는 함수입니다)
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
그리고 이렇게 얻어진 cost 를 낮추기 위해 gradient descent 알고리즘을 사용합니다.
이 알고리즘은 cost function 여기서는 cross entropy 을 통해 얻어지는 값 즉 cost, error, loss 을 최소화 하는 방법으로
위와 같이 cost 를 미분한 후 learning rate 를 곱하여 빼는 과정을 반복하는 방법입니다.
https://en.wikipedia.org/wiki/Gradient_descent
그리고 이렇게 얻어진 값들을
을 이용해 cost 를 낮추기 위해 각 W 와 b 가 cost 에 어떤 영향을 주는지 학습할 수 있습니다. 그리고 역시 이 일련의 과정을 한 번에 처리해주는 것이 tensorflow 의
train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
함수입니다. 이 함수는 learning rate (여기서는 0.5) 을 파라미터로 받아
반복되는 학습 과정 중에 cross_entropy 값을 낮추는 방향으로 W 와 b 값을 조정할 것입니다.
여기까지를 구성단계라 하고 이제 session을 이용해 graph의 op을 실행시키는 실행 단계가 필요합니다.
그 중에서도 우선은 이 모델을 학습을 시켜야 됩니다. 이 학습을 통해 7840개의 W 값과 10개의 b 값이 정해지게 되겠지요.
sess = tf.InteractiveSession()
을 통해 session 을 시작하고
TensorFlow 에서는 변수(Variable) 을 사용하기 위해서는 아래와 같이 초기화가 필요합니다.
tf.global_variables_initializer().run()
for _ in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
위 코드는 input_data 를 통해 100개의 데이터를 가지고 와
batch_xs 변수에 각 이미지 데이터
batch_ys 에 각 이미지에 따른 라벨을 가져옵니다.
그리고 위에서 placeholder 로 선언한 x와 y_ tensor 에 각각 넣은 후 session 을 천 번 실행시킵니다.
이렇게 학습이 끝났고 이제 학습을 통해 얻은 모델에 테스트 이미지를 넣어 얼마나 정확한 모델인지 확인해 볼 차례입니다.
테스트를 통해 얻은 y 값은 0~1 사이의 값이기 때문에 이 중 가장 큰 값에 따른 숫자를 얻어야 되며
tf.argmax 함수를 통해 쉽게 얻을 수 있습니다.
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
위 코드는 정답 y_ 와 y 값이 얼마나 같은지 correct_prediction 에 넣고
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
을 통해 평균값을 얻습니다.
이제 마지막으로 아래 코드와 같이 테스트 데이터를 넣고 session 을 실행시킨 후 그 결과값을 출력합니다.
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))