28. XOR을 위한 텐서플로우 딥네트웍 (lab 09-1)


Neural Network 코드를 작성해 볼 시간이다.


logistic regression을 사용해서 실행시킨 결과이다. 그림에 있는 것처럼 XOR 문제를 logistic regression으로 푸는 것은 불가능하다. Accuracy에 0.5라고 출력됐으니까, 50%의 정확도라는 뜻이다. 앤드류 교수님 수업에도 자주 나오는데, 여러 개의 logistic regression을 연결하지 않고, 단 한 개만으로 XOR 문제를 풀 수 없다고 말씀하셨다.


아래 코드에서 사용할 파일   07train.txt

import tensorflow as tf
import numpy as np

# 07train.txt
# # x1 x2 y
# 0 0 0
# 0 1 1
# 1 0 1
# 1 1 0

xy = np.loadtxt('07train.txt', unpack=True)

x_data = xy[:-1]
y_data = xy[-1]

X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

W = tf.Variable(tf.random_uniform([1, len(x_data)], -1.0, 1.0))

h = tf.matmul(W, X)
hypothesis = tf.div(1., 1. + tf.exp(-h))

cost = -tf.reduce_mean(Y * tf.log(hypothesis) + (1 - Y) * tf.log(1 - hypothesis))

rate = tf.Variable(0.1)
optimizer = tf.train.GradientDescentOptimizer(rate)
train = optimizer.minimize(cost)

init = tf.initialize_all_variables()

with tf.Session() as sess:
sess.run(init)

for step in range(1000):
sess.run(train, 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}), sess.run(W))
print('-'*50)

# Test model
correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)

#Calculate accuraty
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))
param = [hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy]
result = sess.run(param, feed_dict={X:x_data, Y:y_data})

for i in result:
print(i)
print('Accuracy :', accuracy.eval({X:x_data, Y:y_data}))

소스 코드가 없어서 직접 입력해서 만들었다.

음.. 거짓말. 앞에 나온 logistic regression 코드를 그대로 가져다가 사용했다. 달라진 점은 사용할 데이터 파일과 출력하는 형식 정도. 데이터 파일은 0과 1로 구성된 4개의 행을 갖고 있고, 결과는 XOR에 맞게 0, 1, 1, 0으로 되어 있다. 출력은 한 줄로 너무 길게 나와서 줄 단위로 보여줄 수 있도록 교수님의 코드를 조금 수정했다.


[출력 결과]
0 0.722337 [[-0.34294498 -0.4513675 ]]
200 0.693178 [[ 0.0063774 -0.02477646]]
400 0.693148 [[ 0.00423667 -0.00465424]]
600 0.693147 [[ 0.00126391 -0.0012734 ]]
800 0.693147 [[ 0.00036196 -0.00036215]]
--------------------------------------------------
[[0.5 0.49997401 0.50002599 0.5]] # hypothesis
[[1. 0. 1. 1.]] # tf.floor(hypothesis+0.5)
[[False False True False]] # correct_prediction
0.25 # accuracy
Accuracy : 0.25

hypothesis를 보면 결과가 0.5 부근에 몰려있는 것을 볼 수 있다. 동영상 캡쳐에서도 동일하다. 교수님은 50% 확률로 맞았지만, 나는 25%밖에 안 된다.

tf.floor 함수를 호출해서 결과를 0 또는 1로 만들었다. 0.5를 더한 다음에 소숫점 이하를 버렸다. 이걸 [0, 1, 1, 0]과 비교하면 첫 번째, 두 번째, 네 번째를 틀렸고, 세 번째에서 하나 맞았다. 결과는 [False, False, True, False]. XOR에 대해서 직선을 그어서 구분한다는 것 자체가 그냥 봐도 불가능하다.


앞의 코드를 살짝 바꾸니까 neural network 코드가 됐다. 그림에서는 198,000번 반복한 것 같은데.. 어찌 됐든 Accuracy가 1.0으로 잘 나왔다.


그림에서 보여지는 만큼만 코드가 바뀌었다. W1과 W2, b1과 b2를 초기화시키는 부분하고 logistic regression을 연결시키는 부분.

W1을 만들 때, 2x2 행렬이 들어간다. 이 값은 L2를 만들 때, matmul(X, W1)에서 사용된다. 그런데, 우리는 여전히 2x4 크기의 x_data를 갖고 있어서 에러가 발생한다. 동영상에서 이 부분이 누락됐다.

  x_data = np.transpose(xy[:-1])
  y_data = np.reshape(xy[-1], (4,1))

두 번의 행렬 계산이 일어난다. 첫 번째는 앞에서 설명한 matmul(X, W1). 이 부분은 (4행 2열) x (2행 2열)의 행렬 계산으로 처리되고 결과는 4행 2열로 나온다. 두 번째는 matmul(L2, W2)인데, (4행 2열) x (2행 1열)의 행렬 계산이 일어나고 최종 결과는 4행 1열로 나온다. 마지막에는 y_data와 비교해야 하니까 y_data도 4행 1열로 만들었다.


import tensorflow as tf
import numpy as np

xy = np.loadtxt('07train.txt', unpack=True)

x_data = np.transpose(xy[:-1])
y_data = np.reshape(xy[-1], (4, 1))

X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

W1 = tf.Variable(tf.random_uniform([2, 2], -1.0, 1.0))
W2 = tf.Variable(tf.random_uniform([2, 1], -1.0, 1.0))

b1 = tf.Variable(tf.zeros([2]))
b2 = tf.Variable(tf.zeros([1]))

L2 = tf.sigmoid(tf.matmul(X, W1) + b1)
hypothesis = tf.sigmoid(tf.matmul(L2, W2) + b2)

cost = -tf.reduce_mean(Y * tf.log(hypothesis) + (1 - Y) * tf.log(1 - hypothesis))

rate = tf.Variable(0.1)
optimizer = tf.train.GradientDescentOptimizer(rate)
train = optimizer.minimize(cost)

init = tf.initialize_all_variables()

with tf.Session() as sess:
sess.run(init)

for step in range(10000):
sess.run(train, feed_dict={X: x_data, Y: y_data})
if step % 1000 == 999:
# b1과 b2는 출력 생략. 한 줄에 출력하기 위해 reshape 사용
r1, (r2, r3) = sess.run(cost, feed_dict={X: x_data, Y: y_data}), sess.run([W1, W2])
print('{:5} {:10.8f} {} {}'.format(step+1, r1, np.reshape(r2, (1,4)), np.reshape(r3, (1,2))))
print('-'*50)

# Test model
correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)

#Calculate accuraty
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))
param = [hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy]
result = sess.run(param, feed_dict={X:x_data, Y:y_data})

print(*result[0])
print(*result[1])
print(*result[2])
print( result[-1])
print('Accuracy :', accuracy.eval({X:x_data, Y:y_data}))

역시 코드를 직접 입력해서 만들어 보았다. logistic regression처럼 1,000번만 돌렸더니, 결과가 안 나왔다. 2,000번을 돌렸는데도, 결과가 좋지 않게 나왔다. 그래서, 10,000번을 반복하고 있다. 앞에 설명했던 것처럼 코드를 일부만 수정했고, 추가로 print 함수의 형식을 조금 바꿨다.


[출력 결과]
1000 0.68227279 [[-1.24171805 0.06442181 -0.91778374 0.46263656]] [[-0.98959821 -0.52660322]]
2000 0.59741580 [[-2.62564516 0.24180347 -2.59751081 0.63329911]] [[-2.69694042 -0.66693091]]
3000 0.46362704 [[-4.15857601 0.92896783 -4.20671129 1.22253466]] [[-4.52800512 -1.86550784]]
4000 0.19515765 [[-5.1121006 2.5385437 -5.1532464 2.54854703]] [[-6.11614418 -4.89787388]]
5000 0.08121696 [[-5.61055708 3.47669911 -5.63592672 3.47990155]] [[-7.34246731 -6.90866327]]
6000 0.04758544 [[-5.89690733 3.94383311 -5.91585779 3.94633341]] [[-8.16183186 -8.0021019 ]]
7000 0.03300261 [[-6.09076691 4.23373413 -6.1062789 4.23588562]] [[-8.75467396 -8.72057724]]
8000 0.02506309 [[-6.23520756 4.43864489 -6.24855947 4.4405632 ]] [[-9.21470547 -9.24997044]]
9000 0.02012194 [[-6.34940481 4.59504986 -6.3612566 4.59679461]] [[-9.58913803 -9.66739845]]
10000 0.01676891 [[-6.44336367 4.72052336 -6.45410633 4.72213221]] [[ -9.90425777 -10.01130104]]
--------------------------------------------------
[ 0.01334288] [ 0.981884] [ 0.9818663] [ 0.01691606]
[ 0.] [ 1.] [ 1.] [ 0.]
[ True] [ True] [ True] [ True]
1.0
Accuracy : 1.0

출력 결과를 보니까, Accuracy에 1.0이 나왔고, cost 또한 제대로 감소하고 있다는 것을 볼 수 있다. []로 감싸여 있는 W1과 W2 또한 값이 잘 바뀌는 것 같다. 그러나, 여전히 cost가 감소하고 있기 때문에 반복 횟수를 늘려도 된다. 원하는 결과가 나오지 않았다면, 당연히 20,000번으로 수정해야 한다.



Neural Network 모델에서 layer를 추가했다. 첫 번째 layer를 input layer, 가운데 layer를 hidden layer, 마지막 layer를 output layer라고 부른다. hidden layer의 갯수에는 제한이 없다. 다만 너무 많으면 overfitting 문제가 발생할 수 있다.

W1, W2, W3에서 중요한 점은 행렬 곱셈이 적용되기 때문에 행렬 곱셈을 할 수 있는 형태로 layer가 추가되어야 한다는 점이다. 첫 번째가 2행 5열이라면 두 번째는 5행으로 시작해서 5행 4열, 세 번째는 4행으로 시작해서 4행 1열. 입력과 출력 데이터는 갯수가 정해져 있기 때문에 바꿀 수 없다. 파일에서 가져왔을 때, 열의 갯수가 이미 정해져 있기 때문에 바꿀 수 없다. 다만 열 전체를 사용하지 않거나 조합해서 새로운 열을 만드는 방식으로 feature 갯수를 바꿀 수는 있다. 이 코드에서는 2행 5열이므로 x열은 2개, 4행 1열이니까 y열은 1개가 된다.

hypothesis를 계산하는 것은 이전과 달라지지 않았다. L2를 L3에 전달하고, L3를 hypothesis에 전달하면 된다.그냥 갯수만 많아질 뿐, 복잡하지는 않다.


이번 그림에서 출력된 W1은 10행 2열의 결과를 보여준다. W1 출력에 포함된 갯수를 세면 된다. 이번 코드는 텐서보드와 관련이 있어서 여기서 설명하지 않고, 다음 글에서 텐서보드와 함께 보도록 한다.