[골빈해커의 3분 딥러닝] 04_01_classification

2019. 6. 4. 19:55machine learning

골빈 해커님이 쓰신 3분 딥러닝을 코드 위주로 설명한 포스팅.

 

4장 01에서는 2개의 특징 데이터(털, 날개)를 가지고 [기타, 포유류, 조류]를 분류하는 모델을 설명하고 있으며, 전체 코드는 아래와 같다.

import tensorflow as tf
import numpy as np
x_data = np.array([[0, 0], [1, 0], [1, 1], [0, 0], [0, 0], [0, 1]])
y_data = np.array([
[1, 0, 0], # 기타
[0, 1, 0], # 포유류
[0, 0, 1], # 조류
[1, 0, 0],
[1, 0, 0],
[0, 0, 1]
])
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)
# tf.random_uniform : 정규분포 난수를 생성하는 함수로 배열의 shape, 최소, 최대 값을 파라미터로 받는다.
# -1 ~ 1 사이의 난수를 2x3개 생성한다.
W = tf.Variable(tf.random_uniform([2, 3], -1., 1.))
b = tf.Variable(tf.zeros([3]))
# ex) [1, 0] x [[0.2, 0.3, 0.4] + [0.1, 0.2, 0.3] = [0.3, 0.5, 0.7]
# [0.1, 0.2, 0.1]]
# 1x2 matmul 2x3 + 1x3 = 1x3
L = tf.add(tf.matmul(X, W), b)
L = tf.nn.relu(L)
model = tf.nn.softmax(L)
cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(model), axis=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train_op = optimizer.minimize(cost)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
for step in range(100):
sess.run(train_op, feed_dict={X: x_data, Y: y_data})
if (step + 1) % 10 == 0:
print(step + 1, sess.run(cost, feed_dict={X: x_data, Y: y_data}))
prediction = tf.argmax(model, 1)
target = tf.argmax(Y, 1)
print('예측값:', sess.run(prediction, feed_dict={X: x_data}))
print('실제값:', sess.run(target, feed_dict={Y: y_data}))
is_correct = tf.equal(prediction, target)
print(sess.run(is_correct, feed_dict={X: x_data, Y: y_data}))
print(sess.run(accuracy, feed_dict={X: x_data, Y: y_data})) # 2/6
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))
print('정확도: %.2f' % sess.run(accuracy * 100, feed_dict={X: x_data, Y: y_data}))

step by step으로 하나씩 살펴보자.

 

import tensorflow as tf
import numpy as np

텐서플로우와 numpy를 import 하고 있다. 참고로 실행 환경에서의 텐서플로우와 numpy 버전은 각각 1.13.0, 1.16.3을 사용하였다.

 

x_data = np.array([[0, 0], [1, 0], [1, 1], [0, 0], [0, 0], [0, 1]])
y_data = np.array([ [1, 0, 0], # 기타
                              [0, 1, 0], # 포유류
                              [0, 0, 1], # 조류
                              [1, 0, 0], [1, 0, 0], [0, 0, 1] ])
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

x와 y 데이터를 각각 선언하고, train 시에 x와 y 데이터를 담을 수 있는 placeholder도 선언한다. 

x data는 [털, 날개] 여부를 가지는 2차원 데이터로 나타나고, y data는 [기타,포유류,조류]를 각각 one-hot encoding으로 표현하였다.

 

W = tf.Variable(tf.random_uniform([2, 3], -1., 1.))
b = tf.Variable(tf.zeros([3]))

weight와 bias를 선언한다. weight는 -1 ~ 1 사이의 정규분포를 가지는 2x3 행렬의 난수로 표현이 되고, bias는 1x3의 0값으로 초기화가 된다.

input data가 1x2 행렬이기 때문에 wx + y = [1x2] * [2x3] + [1x3] 으로 행렬 계산이 되고 최종 결과 값은 [1x3]의 행렬 값이 나타난다.

 

 

L = tf.add(tf.matmul(X, W), b)
L = tf.nn.relu(L)
model = tf.nn.softmax(L)

input data에 weight를 곱한 후 bias를 더한 식을 L로 정의하고, L에 대해 relu를 적용한다. 

그리고 마지막으로 L에 대해 softmax를 적용한다. softmax는 간단히 설명하자면 각각의 가중치 데이터를 총 합이 1이 되게 확률값을 만들어 주는 기능을 한다.

 

cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(model), axis=1))

모델을 정의 했다면 이번에는 cost 함수를 정의한다. cost 함수는 cross entropy로 정의한다. Y 값 * log(모델의 예측확률)의 전체 합이 cross entropy가 되며 마지막에 reduce_mean을 해주는 이유는 해당 함수가 batch size 입력을 받기 때문에 해당 batch에 대한 평균 loss를 구하기 위해 reduce_mean이 필요하다.

 

reduce_sum의 axis가 헷갈리는 부분인데, axis가 없는 경우는 전체 합, axis가 0일 경우에는 열단위 합, axis가 1일 경우에는 행단위 합을 진행한다고 생각하면 된다. 

아래의 예를 보면, 이해가 쉽다.

 

x = [[0,1,2], [3,4,5]]
print(sess.run(tf.reduce_sum(x))) # 전체 계산 15
print(sess.run(tf.reduce_sum(x, axis=0))) # 열단위 계산 [3,5,7]
print(sess.run(tf.reduce_sum(x, axis=1))) # 행단위 계산 [3, 12]

 

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train_op = optimizer.minimize(cost)

optimizer로 gradient descent optimizer를 사용하고 learning rate 를 0.01로 주자.

 

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

for step in range(100):
    sess.run(train_op, feed_dict={X: x_data, Y: y_data})
    if (step + 1) % 10 == 0:
        print(step + 1, sess.run(cost, feed_dict={X: x_data, Y: y_data}))

이제 만들어진 모델을 학습해보자. 

먼저 tf.global_variables_initializer()를 통해 기존 선언한 변수들에 대한 초기화를 진행하자.

session을 선언한 후 변수 초기화를 실행 시키자.

 

range 100만큼 돌면서 x_data와 y_data를 집어 넣어서 train_op를 실행시키자.

10 단위로 cost 함수를 실행시켜서 cross_entropy date를 출력해 보자.

 

prediction = tf.argmax(model, 1)
target = tf.argmax(Y, 1)
print('예측값:', sess.run(prediction, feed_dict={X: x_data}))
print('실제값:', sess.run(target, feed_dict={Y: y_data}))
is_correct = tf.equal(prediction, target)
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))
print('정확도: %.2f' % sess.run(accuracy * 100, feed_dict={X: x_data, Y: y_data}))

마지막으로 모델에 대한 성능 평가를 진행한다.

model에서 행단위로 가장 높은 값을 가지는 index를 리턴하는 함수를 prediction에 정의한다.

그리고 현재 y_data 중 가장 높은 값 (1인 데이터) 의 index 를 리턴하는 함수를 target에 정의한 후 각각 sess.run을 실행시켜서 결과를 확인해 보자.

출력해보면, 예측 값 : [2 2 2 2 2 2], 실제값 : [0 1 2 0 0 2] 로 나타나게 된다.

두 개의 리스트를 tf.equal을 통해 비교하자. 일치하면 True, 틀리면 False를 리턴하게 될 것이다. ex) [False, False, True, False, False, True]

True, False 리스트를 정수로 변환한 후 reduce_mean을 함으로써 평균을 구하자. 

약 33%의 정확도를 확인해 볼 수가 있으며, 이 결과가 지금 현재의 모델 성능임을 확인해 볼 수가 있다.