3-4 Hinton교수의 ReLU 대신 ZSigmoid(z)를 사용하는 Wide Deep Learning 뉴럴 네트워크(NN)의 Vanishing Gradient의 새로운 해결방안
Sigmoid 함수의 중복적인 사용으로 인한 Vanishing Gradient 문제로 인해 Hinton 교수가 제안한 ReLU 함수가 대안으로 폭 넓게 사용되면서 문제의 해결이 이루어졌지만 한편 Sigmoid 함수의 사용이 대단히 위축 되었다. 하지만 ReLU 함수의 약점 중의 하나가 꺽어지는 부분에서 미분계수 값을 가지지 못하는 단점이 있다. 이러한 약점을 보강하면서 변수가 ∓∞에 접근할 경우 ReLU와 거동이 유사해지면서 Sigmoid 함수를 포함하는 간단한 형태의 연속함수를 제안한다. 이 연속함수 zSigmoid(z)는 별도의 새로운 함수 도입없이 코드상에서 간단히 한줄로 구현이 가능하며 Sigmoid 함수를 배제할 필요가 없다. 머신 러닝의 여러 종류의 문제에 대해서 verification 이 되어야겠지만 대표적으로 Wide Deep NN 문제에 적용해 보았으며 Hinton 교수의 ReLU 함수와 별 차이가 없었다. 머신 러닝 연구하시는 분들께서는 반드시 확인해보시고 괜찮다면 많이 이용해 바란다. 반드시 문헌 참고에 @codingart 와 스팀잇 블로그 주소를 명기해 주시기 바란다.
Learning rate = 0.1, 학습횟수 10,000회 조건 하에서 XOR 로직 문제를 Depth가 10인 즉 10개의 layer들을 가지면서 각 layer당 10개의 노드를 가지는 Fully connected NN(뉴럴 네트워크)을 적용해서 실패한 결과를 주었던 Wide deep NN 코드를 1단과 2단에 Sigmoid 함수 대신 ReLU 함수를 사용하여 성공적으로 계산이 가능하다. Wide Deep NN의 실패 원인은 NN 레이아웃의 구조적 문제가 아니라 layer 사이에 도입되는 Sigmoid 함수의 특성에 기인하는 것이란 점을 지적하였다.
각 layer 별로 어떤 노드에서 값이 마이너스(-)의 값을 가지게 되면 Sigmoid 처리 결과 거의 ∼0에 가까운 값을 주며 반면에 값이 아무리 큰 양의 값이라 해도 1.0 보다 작은 값을 가지게 되므로 어쨌든 결국 1.0 보다 작은 값을 10번까지 계속 곱하다 보면 거의 ∼0이 될 가능성이 대단히 높다.
특히 Backpropagation 계산 과정에서 Sigmoid 함수의 (편)미분 값을 사용해야 하는데 아래 그래프에서 볼 수 있듯이 Sigmoid 함수의 미분은 종 모양으로서 최대값이 0.25 이다. 즉 10개의 은닉층에 대해서 Backpropagation 계산을 위한 미분 계수들이 거듭 곱해지게 된다면 10의 –6승 이하가 되므로 웨이트 업데이트 과정이 멈추게 되는 것이나 다름 없어진다. tanh(z) 의 경우도 Sigmoid처럼 종 모양 미분계수의 최대값이 1.0 이라서 Sigmoid 보다 다소 낳지만 원리상 Sigmoid처럼 은닉층이 많아지게 되면 동일한 거동을 보인다. 실제로 10개 또는 그 이상의 layer를 가지는 NN 계산 결과가 실패하게 되는 원인이 된다.
반면에 ReLU = max( 0, z) 를 사용하게 되면 어떤 노드에서 값이 마이너스(-)의 값을 가지게 되면 물론 0 으로 처리 되지만 반면에 값이 0과 1 사이라면 그대로 반영이 되고 특히 큰 양의 값이면 있는 그대로 계산에 반영이 되어 1.0 보다 작아질 가능성은 없다. 하지만 ReLU 의 가장 큰 약점은 z=0에서 미분계수 연속적이지 못하다는 점이다.
ReLU 함수 도입 이후로도 z가 음의 영역에서도 0.0이 아닌 적절한 함수 값을 가지도록 하기 위하여 새로운 함수 도입에 따른 많은 시도가 있었다. 하지만 여기서는 새로운 함수의 도입 없이 Sigmoid 함수와 변수를 결합한 간단한 형태의 함수를 사용하여 ReLU를 대체해 보기로 한다.
함수 Sigmoid(z) 에 z를 곱한 함수 형태를 다음과 같이 작도해 보자. z 값이 커지면 ReLU 와 같아지게 되며 음의 z 값에 대해서는 –1.2 근처에서 한번 변곡 후 0.0 에 가까워지게 된다. 이 함수의 미분계수는 이미 앞 그림의 비교에서 볼 수 있듯이 z 값의 양의 영역에서 0.5에서 1.0 이상으로 증가했다가 1.0에 수렴하는 연속적인 거동을 보인다.
10개의 은닉층을 가지는 Wide deep NN을 대상으로 learning rate = 0.1, 학습횟수 10,000 회로 XOR 로직 문제에 적용해 보도록 한다.
10개의 은닉 층 모두 적용할 필요는 없으며 ReLU에서처럼 1,2 은닉층에만 적용해도 충분히 만족스러운 결과를 확인할 수 있을 것이다.
ReLU를 포함하여 많은 기법들이 이미 발표되어 사용되고 있으며 여기서 제시된 기법 자체도
형태로 일반화가 가능하므로 n=1,2,3∙∙∙ 경우를 대상으로 사용자가 테스트 해 본 후 적합한 형태를 선택하여 적용이 가능하다. 그 중에서도 n=1 인 경우가 간단하며 컴퓨팅 부담도 그다지 크지 않은 편이다.
즉 wide_deep_10th.py 코드에서 선두 부분에 Sigmoid(z)를 zSigmoid(z)로 변경한 코드를 첨부하였으니 ReLU 함수가 없으며 10개의 은닉층에 Sigmoid 함수가 그대로 들어있는 파이선 파일을 다운 받아 실행해 보기 바란다.
#z_wide_deep_10th.py
import tensorflow as tf
import numpy as np
tf.set_random_seed(777) #for 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.nn.relu(tf.matmul(X, W1) + b1)
layer1 =tf.multiply(tf.matmul(X, W1) + b1,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.nn.relu(tf.matmul(layer1, W2) + b2)
layer2 =tf.multiply(tf.matmul(layer1, W2) + b2,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(1001):
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)
'''
Hypothesis:
[[0.04372542]
[0.9461495 ]
[0.94905233]
[0.04614163]]
Correct: [[0.]
[1.]
[1.]
[0.]]
Accuracy: 1.0
'''







짱짱맨 호출에 응답하여 보팅하였습니다.