Softmax classification : Multinomial classification
Softmax 함수를 구하기 전에, 간단히 이론을 보면,
Multinomial classification은 각각을 Linear 한 계산을 한번에 해준다고 보면된다.
[w1,w2,w3] * x1 = A
, [w1,w2,w3] * x2 =B
, [w1,w2,w3] * x3 = C
위와 같이, A냐 아니냐를 아는 weight와 x, B,C도 각각을 나눠주어 계산하는데, 이를 좀더 편하게 하기위해, 3*3 행렬
을 이용해 한번에 계산한다.
그리고 그 결과값을 0과 1사이의 수로 만들어, 전체의 합이 1인 확률로 만드는데, 그게 softmax 함수
다.
그 결과를 One-hot encoding
을 할때는, 가장 확률이 높은 값을 1, 나머지를 0으로 만들면 된다.
그리고, cost function을 알아봐야 하는데, Cross-entropy라는 것을 쓴다.
cross-entropy의 구현과정은 개인적 학습에 맞기고, Logistic cost
와 cross entropy
는 사실상 같다는 것만 알고 있자.
그럼 코드를 작성해보자.
import tensorflow as tf
# 4개의 element
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
# 4개(1,2,1,1)가 들어왔을때 마지막 꺼만 1(0,0,1)
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]);
# Y의 갯수가 Label의 갯수
nb_classes = 3
# X(4개) * w = b (3개)
W = tf.Variable(tf.random_normal([4, nb_classes]), name='weight');
b = tf.Variable(tf.random_normal([nb_classes]), name='bias');
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);
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}));
#Testing & One-hot encoding
a = sess.run(hypothesis, feed_dict={X: [[1,11,7,9], [1,3,4,3],[1,1,0,1]]})
# argument max인 값의 index를 출력
print(a, sess.run(tf.arg_max(a, 1)));
code의 설명은 주석으로 처리해 두었다.
그럼 이를 좀더 Fancy하게 보도록 하자.
softmax_cross_entropy_with_logits
라는 함수를 소개할 것이다.
logits = tf.matmul(X, W) + b
hypothesis = tf.nn.softmax(logits)
를 알아두자.
우리는 이전에, cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(hypothesis), axis=1));
를 사용해 계산했다.
이를 조금더 간단히 하기 위해,
cost_i = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=Y_one_hot)
cost = tf.reduce_mean(cost_i)
를 사용한다.
그럼 이걸 가지고 하나를 구현해보자.
data는 data-04-zoo.csv를 사용한다.
위 데이터는 동물을 특징(꼬리, 날개 등등)에 따라 분류한 데이터다. 그 데이터는 약 7가지의 분류가 된다.
따라서 Y데이터가 7이 된다.
그런데 One-hot encoding
으로 바꿔주어야 한다.
그럴때 사용하는 것이 tf.one_hot
이다.
파라미터로는 Y값과 몇개의 갯수가 있는지가 주어져야 한다.
그런데 여기서 중요한 것은, reshape
다.
데이터가 [[0],[3]]
라 할 때, one-hot을 하면, [[[1000000]],[[0001000]]]
이 된다.
즉, 한 차원이 더 증가된다.
따라서 reshape를 쓴다.
Y_one_hot을 위 one_hot 함수를 쓴 것이라 할 때, ((?, 1, 7)의 형태 즉, 3차원)
Y_one_hot = tf.reshape(Y_one_hot, [-1, nb_classes])
를 해준다.((?, 7)의 형태 즉, 2차원)
-1은 everything을 의미한다.
import tensorflow as tf
import numpy as np
xy = np.loadtxt('data-04-zoo.csv', delimiter=',', dtype=np.float32);
x_data = xy[:, 0:-1]
y_data = xy[:, [-1]]
nb_classes = 7 #0~6
# 특징 데이터 16개
X = tf.placeholder(tf.float32, [None, 16])
# Y는 한개 0~6
Y = tf.placeholder(tf.int32, [None, 1])
Y_one_hot = tf.one_hot(Y, nb_classes); #one-hot
Y_one_hot = tf.reshape(Y_one_hot, [-1, nb_classes])
W = tf.Variable(tf.random_normal([16, nb_classes]), name='weight')
b = tf.Variable(tf.random_normal([nb_classes]), name='bias')
logits = tf.matmul(X, W) + b
hypothesis = tf.nn.softmax(logits)
cost_i = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=Y_one_hot)
cost = tf.reduce_mean(cost_i)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(cost)
# 예측값 hypothesis probability의 최대값의 index를 뽑아 0~6 값으로 만듦
# 두번째 파라미터는 적용할 차원을 알려주는 매개변수, 0이면 2차원에서 열을 비교 즉, 한개만 비교하면 1개밖에 나오지 않는다. 1이면 2차원의 행을 비교
prediction = tf.argmax(hypothesis, 1)
# 그 값이, Y_one_hot으로 바꾸기 전 값과 비교
correct_prediction = tf.equal(prediction, tf.argmax(Y_one_hot, 1))
# 그 값을 모아서 평균을 냄
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for step in range(2000):
sess.run(optimizer, feed_dict={X: x_data, Y: y_data})
if step % 100 == 0:
loss, acc = sess.run([cost, accuracy], feed_dict={X: x_data, Y: y_data})
print("Step : {:5}\tLoss: {:.3f}\tACC: {:.2%}".format(step, loss, acc))
# 예측한 값과 비교
pred = sess.run(prediction, feed_dict={X: x_data})
# y 데이터를 [[1],[0]] -> [1,0] flatten 시켜, 각각의 list의 값을 각각 p와 y로 넘김
for p, y in zip(pred, y_data.flatten()):
print("[{}] Prediction: {} True Y: {}".format(p==int(y), p, int(y)))
이후는 다음에 진행하도록 하자.