Deep learning

[Supervised Learning / TensorFlow tutorial] MNIST For ML Beginners - Softmax regression

JaykayChoi 2017. 1. 24. 23:51

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([78410]))
  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 모델입니다.

https://github.com/tensorflow/tensorflow/blob/56fc8834c736878af34f00caa95e7d4a57ab01d2/tensorflow/examples/tutorials/mnist/mnist_softmax.py

우선 input_data 을 import 합니다. input_data 은 MNIST 데이터를 관리해주는 함수이며 소스는 아래 링크에서 보실 수 있습니다.

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/input_data.py

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/learn/python/learn/datasets/mnist.py


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

.
.
.
.
y10 = W10,1 * x1 + W10,2 * x2 + W10,3 * x3 ......+ W10,784 * x784 + b3

위와 같은 식이 샘플의 개수 즉 None개 만큼 필요할 것입니다.

위와 같은 다차원의 식을 일일이 코드로 작성할 수는 없기에 간편하게 행렬의 곱으로 표현을 할 수 있습니다.


위 설명만 놓고 보자면 
W 는 [10,784] x는 [784,None] 배열로 선언을 해야되지만 샘플의 개수를 고정시키지 않기 위해 x 의 배열을 [None, 784] W의 배열을 [784,10] 으로 선언하고
행렬 곱을 W * x 가 아닌 x * W 으로 하였습니다.
(2차원 배열을 선언할 때 메모리 할당을 위해 열의 크기는 반드시 주어져야 됩니다)

이제 위와 같은 이유로 W 와 b 의 변수 tensor 을 아래와 같이 선언합니다.

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

여기서 W 와 b 의 초기값은 학습을 통해 값을 얻을 것이기 때문에 중요치 않습니다.

그리고 y 를 아래와 같이 행렬곱으로 선언합니다.
y = tf.matmul(x, W) + b


이제 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 를 확률적으로 해석하고자 할 때 사용됩니다. 

enter image description here


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}))