머신러닝

4-9 multi-layer NN(뉴럴 네트워크)의 Wide Deep Learning의 한계는 어디까지인가?

coding art 2021. 7. 6. 14:22
728x90

XOR 로직을 처리하기 위해 구성되는 다음의 가장 기본 적인 형태의 NN을 가지고 사용자가 원하는 규모의 은닉 층을 넣어서 처리해 보도록 하자. 은닉 층의 자유도를 10개 정도면 충분할 것이다. 아울러 중간에 위치시킬 은닉 층수를 이미 다루어 보았던 4에서 순차적으로 10까지 증가시켜 보자. 코드에서 레이어 하나씩 증가시키기란 그다지 어렵지 않다.

은닉층 수를 증가시켜 본 결과 8개까지는 별 문제가 없었고 9개에서부터 결과가 나오기도 하나 제대로 된 결과가 나오지 않는 빈도수가 증가하였다. 이와 같이 앞단의 입력 노드들과 이어지는 다음 단의 노드들과 빠짐없이 모조리 연결되는 경우를 “Fully connected layers” 라고 하며 이 용어는 CNN(Convolutional NN)의 마지막 레이어 부분에서 사용된다. 다음은 10개의 은닉 층을 가지는 코딩 사례이다. 은닉 층의 수와 은닉 층의 자유도 수는 임의로 설정이 가능하다.

이 은닉 층이 10개인 신경망 코드를 사용하여 learning rate = 0.1, 학습횟수 10,000회로 두어 xor 로직 문제를 계산해 보자. 다음의 표는 계산 결과 사례이다. Hypothesis 0.5로 계산되면 Failed 를 나타냄을 알 수 있다.

충분한 수의 자유도와 은닉층을 사용하여 코딩했음에도 불구하고 악화된 결과를 주는 이유는 무엇일까? 주된 원인은 초기 단계에서 랜덤한 웨이트와 편향(바이어스) 값에 대해 hypothesis가 대단히 불안정하기 때문인 것으로 보인다. 특히 Sigmoid를 계속 중복해서 처리하게 되면 출력 값이 1보다 작아지기 시작하면서 은닉 층을 많이 거칠수록 시그날 값이 점차 들어들며(Vanishing Gradient) 실패한 결과를 주게 된다. 다시 말하자면 back-propagation 에 의한 편미분 계산 과정의 어느 중간 layer에 이르러서는 거의 “0” 의 값이 곱해지게 되어 나머지 layer들에 있어서 편미분 값이 의도치 않게 모조리 “0”d이 되어 더 이상 웨이트 업데이트에 실패하는 결과를 낳게 된다.

 

결론적으로 NN을 적용함에 있어 자유도가 크고 은닉층 수가 많다고 해서 반드시 바람직한 결과를 주지 않는다는 점이다. NN 의 코딩 구조 자체가 문제가 되는 것이 아니라 코드를 구성하는 Sigmoid 함수의 지나친 과다 사용으로 인한 것이므로, Sigmoid 함수를 바꾸어 은닉층을 많이 거쳐도 시그날 값이 줄어들지 않고 유지가 되는 Relu 함수를 사용하여 개량하도록 해보자. Relu 함수는 0 보다 작은 경우 0으로 처리되며 0 보다 큰 경우 일차함수 형태의 값으로 처리함으로 인해서 Sigmoid 함수의 중복 사용에 따른 수치 값의 축소를 방지할 수 있다. 이렇게까지 많은 은닉층을 사용하고자 하는 이유는 CNN(Convolutional NN)을 중심으로 구성되는 image classification에서 10개 또는 그 이상의 수많은 층의 레이어를 사용할 수 있어야 하기 때문이다.

 

xor_nn_01 코드를 10개의 은닉층을 가지도록 변형한 코드를 첨부하였으니 다운 받아 실행해 보자.

 

#wide_deep_10th.py

import tensorflow as tf
import numpy as np

tf.set_random_seed(777) # reproducibility
learning_rate = 0.1

x_data = [[0, 0],
[0, 1],
[1, 0],
[1, 1]]
y_data = [[0],
[1],
[1],
[0]]
x_data = np.array(x_data, dtype=np.float32)
y_data = np.array(y_data, dtype=np.float32)

X = tf.placeholder(tf.float32, [None, 2])
Y = tf.placeholder(tf.float32, [None, 1])

W1 = tf.Variable(tf.random_normal([2, 10]), name='weight1')
b1 = tf.Variable(tf.random_normal([10]), name='bias1')
layer1 = tf.sigmoid(tf.matmul(X, W1) + b1)

W2 = tf.Variable(tf.random_normal([10, 10]), name='weight2')
b2 = tf.Variable(tf.random_normal([10]), name='bias2')
layer2 = tf.sigmoid(tf.matmul(layer1, W2) + b2)

W3 = tf.Variable(tf.random_normal([10, 10]), name='weight3')
b3 = tf.Variable(tf.random_normal([10]), name='bias3')
layer3 = tf.sigmoid(tf.matmul(layer2, W3) + b3)

W4 = tf.Variable(tf.random_normal([10, 10]), name='weight4')
b4 = tf.Variable(tf.random_normal([1]), name='bias4')
layer4 = tf.sigmoid(tf.matmul(layer3, W4) + b4)

W5 = tf.Variable(tf.random_normal([10, 10]), name='weight5')
b5 = tf.Variable(tf.random_normal([10]), name='bias5')
layer5 = tf.sigmoid(tf.matmul(layer4, W5) + b5)

W6 = tf.Variable(tf.random_normal([10, 10]), name='weight6')
b6 = tf.Variable(tf.random_normal([10]), name='bias6')
layer6 = tf.sigmoid(tf.matmul(layer5, W6) + b6)

W7 = tf.Variable(tf.random_normal([10, 10]), name='weight7')
b7 = tf.Variable(tf.random_normal([10]), name='bias7')
layer7 = tf.sigmoid(tf.matmul(layer6, W7) + b7)

W8 = tf.Variable(tf.random_normal([10, 10]), name='weight8')
b8 = tf.Variable(tf.random_normal([10]), name='bias8')
layer8 = tf.sigmoid(tf.matmul(layer7, W8) + b8)

W9 = tf.Variable(tf.random_normal([10, 10]), name='weight9')
b9 = tf.Variable(tf.random_normal([10]), name='bias9')
layer9 = tf.sigmoid(tf.matmul(layer8, W9) + b9)

W10 = tf.Variable(tf.random_normal([10, 1]), name='weight10')
b10 = tf.Variable(tf.random_normal([1]), name='bias10')

hypothesis = tf.sigmoid(tf.matmul(layer9, W10) + b10)

#cost/loss function
cost = -tf.reduce_mean(Y * tf.log(hypothesis) + (1 - Y) *
tf.log(1 - hypothesis))

train = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cost)

#Accuracy. computation
#True if hypothesis>0.5 else False
predicted = tf.cast(hypothesis > 0.5, dtype=tf.float32)
accuracy = tf.reduce_mean(tf.cast(tf.equal(predicted, Y), dtype=tf.float32))

#Launch graph
with tf.Session() as sess:
#initialize TensorFlow variables
sess.run(tf.global_variables_initializer())

for step in range(10001): sess.run(train, feed_dict={X: x_data, Y: y_data}) if step % 100 == 0: print(step, sess.run(cost, feed_dict={ X: x_data, Y: y_data}), sess.run([W1, W2])) #Accuracy report h, c, a = sess.run([hypothesis, predicted, accuracy], feed_dict={X: x_data, Y: y_data}) print("\nHypothesis: ", h, "\nCorrect: ", c, "\nAccuracy: ", a)

'''
'''