머신러닝

3-4 XOR 로직 머신 러닝: 성냥개비 하나로 (0,1) 과 (1,0) 점을 (0,0)과 (1,1) 점을 분리하라!

coding art 2021. 7. 2. 16:13
728x90

이 제목을 풀어쓰면 classification이 불가능한 XOR 로직 perceptron 이 된다. 이미 AND 로직과 OR 로직 문제를 다루어 보았다. 이 두 문제는 쉽게 말하자면 성냥 한 개비를 사용하여 두 영역으로 분리하는 수준의 문제이다.

 

하지만 XOR 문제에서는 상황이 달라진다. (0,1)(1,0) 점을 포함해서 도려내려면 두 개의 선을 그어 파 내야 한다. 즉 두 개의 분리선이 당연히 필요하다.

머신 러닝에서 분리선 1개는 hypothesis = WX + b 이므로 즉 웨이트 벡터 W=(w1, w2) 1개와 바이아스 b1 1개가 필요하다는 뜻이다. 앞 예제에서 최소제곱법에 의해 계산했던 결과가 결국 웨이트 벡터와 바이아스 값이었다.

 

하지만 XOR 문제에서는 명백히 2개의 분리선이 필요하며 결국 웨이트 벡터 2개 및 각각의 편향(바이어스) 2개가 필요한 것이다. 하지만 별 대단한 문제가 아닌 듯한데 1969년에 민스키와 페파트 교수가 공동 저술한 Perceptron이란 저서에서 뉴론을 모델링하여 만들었던 단일 퍼셉트론은 AND OR 논리를 처리할 수는 있었으나 단일 퍼셉트론만으로는 배타논리인 XOR 처리가 불가능하다는 점을 지적하였다.

 

단일 퍼셉트론이란 점이 수학문제이기 때문에 극단적이기는 하지만 도대체 왜 퍼셉트론만으로 XOR 논리를 처리할 수 없는지 TensorFlow로 예제 계산을 통해 확인해보도록 한다.

 

배타논리는 위 그림에서처럼 x1 이나 x2 2개 중 1개만이 “1”일 때에 출력 y“1”이 되는 이상스런 논리이지만 그래도 대단히 중요한 논리 중의 하나이다.

 

그림의 좌표 평면에서 관찰해 보면 (0,0) (1,1) 이 라벨 값 “0”이며 (1,0)(0,1) 이 라벨 값 “1”이 되므로 서로 교차 하는 위치에 있으므로 2개를 그으면 분리가 가능할지도 모르겠지만 상식적으로 하나의 분리선을 긋기란 불가능하다.

 

그래도 못 미더운 부분이 있으므로 이러한 분리선을 과연 tensorFlow 학습에 의해 찾아낼 수 있는지 알아보기 위해서 입력 벡터와 웨이트 벡터를 도입하자.

XW에 의해 hypothesis를 다음과 같이 설정하자. hypothesis = X*W + b

hypothesis = x1*w1 + x2*w2 + b 이 성립한다.

이 분리선을 결정해보기 위해 TensorFlow 학습을 위한 코드 데이터를 아래와 같이 리스트 데이터 구조로 준비하자. ANDOR 로직 문제와 다 동일하지만 y-data 만 수정하면 된다.

x1_data = [0, 1, 0, 1], x2_data = [0, 0, 1,1 ], y_data = [0, 1, 1, 0]

 

learning_rate 값은 AND 로직의 경우에서처럼 동일하게 0.005 로 설정한다. 이 값은 여러 번 학습을 시도해 보고 결정해도 무방하다. 다음 결과는 라즈베리 파이 보드에서 계산한 결과이나 아나콘다에서 도일한 결과를 얻을 수 있다.

4개의 y 출력 값 (0, 1, 1, 0) 에 대응하여 분리가 곤란한 (0.5, 0.5, 0.5, 0.5) 가 얻어졌다. 아울러 이 계산 결과를 잘 살펴보면 웨이트 w1, w2 은 거의 0 의 값이 얻어지며 바이어스 b 는 상수 값 0.5가 얻어졌다. 즉 아무리 많이 학습을 해도 hypothesis 가 거의 0.5에 가까운 상수가 얻어졌다는 점은 결국 activation function 개념에서 HIGH 또는 LOW 로 분리 식별이 불가능하다는 의미일 것이다. 이 문제는 방법을 바꾸어서 softmax Sigmoid를 사용하여 풀어도 마찬가지 결과가 얻어진다는 점에 유의하자. 즉 방법이나 기법 상의 문제가 아니다. 성냥개비로 풀어도 마찬가지인 것이 하나로는 답이 없다.

내용을 요약 해보면, 2개의 입력 값을 처리하여 1개의 출력 값을 계산하는 퍼셉트론 구조로는 더 이상 XOR 논리 학습이 이루어지지 않는다는 점이다.

 

생명체에서 오직 하나의 신경세포만으로 특별한 논리를 처리할 수 있도록 구성 되었을 리는 만무하며 어떻게 해서든 수많은 뉴론들을 엮어서 필요한 처리를 하고 있음에 비추어 보아 결국 우리도 신경망을 어떻게 구성해야 할지 그 방법을 모색해야 할 것이다. 즉 성냥개비 1개비로 안되며 2개를 써서 문제를 풀어야 할 것이다. 다시 말하면 또 다른 웨이트 벡터가 적어도 하나 필요한 시점으로서 여기서 부터가 뉴럴네트워크(NN: Neural Network)의 시작점이다. 머신러닝에서 특히 구글과 딥러닝이 작당하여 지나칠 정도의 흥행을 통해 엄청난 자본을 성공적으로 펀딩했는지는 모르겠으나 너무 스포(spoiled)가 되어 가장 재미없는 분야이기도 하다. 그래도 어쩔 수 없이 딥러닝을 어느 정도 살펴 본 후 CNN(Convolutional Neural Network) 으로 넘어갈 필요가 있다.

 

CNN 단계에서 카메라에서 캡츄어 한 이미지를 분석하여 이놈은 고양이 저놈은 개 ∙∙∙ 로 분리를 넘어 식별하는 단계가 있다고 얘기는 들었는데 해보지는 못했음. 그래서 미리 미리 준비 차원에서 거금을 투자하여 5만원짜리 라즈베리 파이 보드에 3만원짜리 파이 카메라를 준비하고 대기하고 있음. 이 정도 단계가 90년대 초반 정도 수준이라는데 훌쩍 넘어가야겠죠.

 

첨부된 파이선 코드를 실행해 보자. session = tf.Session() 이하 영역에서 indentation 이 무너진 부분을 반드시 복구하여 실행하기 바란다.

 

#multi_variable_linear_reg_XOR.py

import tensorflow as tf
tf.set_random_seed(777) # for reproducibility

x1_data = [0., 1., 0., 1.]
x2_data = [0., 0., 1., 1.]

y_data = [0., 1., 1., 0.]

#placeholders for a tensor that will be always fed.
x1 = tf.placeholder(tf.float32)
x2 = tf.placeholder(tf.float32)

Y = tf.placeholder(tf.float32)

w1 = tf.Variable(tf.random_normal([1]), name='weight1')
w2 = tf.Variable(tf.random_normal([1]), name='weight2')

b = tf.Variable(tf.random_normal([1]), name='bias')

hypothesis = x1 * w1 + x2 * w2 + b
print(hypothesis)

#cost/loss function
cost = tf.reduce_mean(tf.square(hypothesis - Y))

#Minimize. Need a very small learning rate for this data set
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.005)
train = optimizer.minimize(cost)

#Launch the graph in a session.
sess = tf.Session()
#Initialize global variables in the graph.
sess.run(tf.global_variables_initializer())

for step in range(4001):
cost_val, hy_val, _ = sess.run([cost, hypothesis, train],
feed_dict={x1: x1_data, x2: x2_data, Y: y_data})
if step % 400 == 0:
print(step, cost_val, hy_val)