Seeyong's Blog

그동안 우리는 특정 숫자를 도출해내거나, 0 또는 1 중의 한 숫자를 예측하는 방법들을 살펴봤다. 사실 현실에서는 그 중간에 위치한 변수들이 너무나도 많다. 대학에서 평점을 매길 때 A부터 F까지 한정된 개수의 점수를 나타내는 것이 대표적이다. 0 또는 1만으로는 나타내기 힘들고, 100점 만점의 점수만으로 평기하기에도 애매하다. 이제 우리는 Softmax Classifiaction이라는 방법을 사용해 이 문제를 해결할 수 있다.

import tensorflow as tf

x_data = [[1, 2, 1, 1],
         [2, 1, 3, 2],
         [3, 1, 3, 4],
         [4, 1, 5, 5],
         [1, 7, 5, 5],
         [1, 2, 5, 6],
         [1, 6, 6, 6],
         [1, 7, 7, 7]]

# One-hot-encoding
y_data = [[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 0], [0, 1, 0], [0, 1, 0], [1, 0, 0], [1, 0, 0]]

X = tf.placeholder('float', [None, 4])
Y = tf.placeholder('float', [None, 3])
nb_classes = 3

W = tf.Variable(tf.random_normal([4, nb_classes]), name = 'weight')
b = tf.Variable(tf.random_normal([nb_classes]), name = 'bias')

위의 x_data와 같은 변수와 y_data라는 결과물을 토대로 학습을 진행하려고 한다. 먼저 알아야 할 기본 개념은 One-hot-encoding이다. 일종의 약속을 코드로 구현했다고 보면 된다. 예를 들어 A라는 점수는 [1, 0, 0]으로, B[0, 1, 0]으로, C[0, 0, 1]이라는 코드로 치환해서 사용한다는 약속이다. 단 하나의 숫자만 1로 표현한다고 해서 One-hot-encoding이라고 부른다. 위의 y_data는 이와 같은 개념을 사용한 것이다. hypothesis를 정의하는 기본 논리는 같다. X라는 데이터에 W라는 변수를 곱해 Y라는 결과물을 예측하는 것이다. 다만 이제는 0 또는 1의 단 두 결과만 나오거나 무한한 임의의 숫자를 도출해내는 것이 아니라 한정된 결과물만 나타내야 하기 때문에 더욱더 차원에 대한 고려가 필요하다.

위 그림에서 결과물은 세 개만 나올 수 있다. 그리고 만약 세 개의 결과물에 대한 예측 점수가 각각 2.0, 1.0, 0.1으로 나올 경우, 전체 합이 1인 확률로 표시하는 것이 Softmax Classification의 골자다.

그리고 그림에 나타나 있는 식은 물론 이해하면 좋지만, 몰라도 사용할 수 있다. 구글에서 이미 TensorFlow안에 구현해두었기 때문에.

# tf.nn.softmax computes softmax activations
# exp(logits) / reduce_sum(exp(logits), dim)
hypothesis = tf.nn.softmax(tf.matmul(X, W) + b)

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

tf.nn.softmax라는 메소드를 사용해서 한 줄의 코드로 저 위의 복잡한 수식을 코드로 구현할 수 있다. cost의 경우 기존의 방식과는 달라진 것을 발견할 수 있다. 실제 결과물인 Yhypothesis의 결과물과의 차이가 아닌 곱으로 나타내고 있다. 이또한 수학적인 부분이기 때문에 자세한 공식은 사용하지 않겠지만, 결과적으로 예측이 틀릴 경우 cost가 증가하고 반대의 경우 cost0이 되는 수식으로 이해하면 된다.

# Launch graph
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    
    for step in range(2001):
        sess.run(optimizer, feed_dict={X: x_data, Y: y_data})
        if step % 200 == 0:
            print(step, sess.run(cost, feed_dict={X: x_data, Y: y_data}))

학습을 하는 방식은 동일하다. 전체 Graph를 그리고 변수를 초기화한 후에 optimizer를 실행하기 위해 x_datay_data를 넣는다.

0 2.2636468
200 0.6693883
400 0.56024593
600 0.4671715
800 0.3768742
1000 0.28712988
1200 0.23116957
1400 0.20998713
1600 0.19221275
1800 0.17709729
2000 0.16409844

위와 같이 출력이 되는 것을 볼 수 있다. 학습을 진행할수록 cost0을 향해 줄어드는 것을 확인할 수 있다.

    # Testing & One-hot-encoding
    a = sess.run(hypothesis, feed_dict={X: [[1, 11, 7, 9],
                                           [1, 3, 4, 3],
                                           [1, 1, 0, 1]]})
    print(a, sess.run(tf.argmax(a, 1)))

실제로 학습 결과가 잘 나오는지 테스트를 해본다. 여기서 argmax라는 새로운 메소드를 사용한다. 첫번째 그림에서 보았듯이 가장 확률이 높은 결과물을 예측치로 설정하기 위해, 여러 확률 중에서 max 값을 찾아내는 방법이다. 그 결과는 아래와 같이 나온다.

[[8.8530891e-03 9.9113691e-01 1.0071689e-05]
 [8.2685965e-01 1.5776965e-01 1.5370723e-02]
 [2.3587551e-08 4.0142154e-04 9.9959856e-01]]
 
 [1 0 2]

첫번째 리스트에 담긴 결과물들은 각 결과물에 대한 score를 나타낸다. 이를 확률로 변환해서 가장 높은 확률의 결과물을 찾아낸 것이 두번째 리스트에 담긴 숫자들이다. 학습한 내용들을 토대로 다른 임의의 x_data들에 대해 새로운 예측치를 나타내는 것이다.