Backpropagation:Example
Overview
실제 Backpropagation를 계산 하는 방법에 대하여 정리한다.
Overview
두 개의 입력, 두 개의 숨겨진 뉴런, 두 개의 출력 뉴런을 구성한다. 또한 숨겨진 뉴런과 출력 뉴런은 바이어스(Bias)1를 포함한다.
Input / Output
<span style="color:red;">초기 가중치(initial weights), <span style="color:orange;">바이어스(the biases), <span style="color:blue;">훈련 입출력(Training inputs/outputs) 가중치, 바이어스, 입력, 출력에 해당하는 결과값은 우측 그림과 같다.
The Forward Pass
Perceptron 일반 Feedforward neural network와 같이 계산을 진행한다.
첫 번째 숨겨진 뉴런의 합성 함수(Combination function)는 아래와 같다.
$$ net_{h1} = (w_1 * i_1) + (w_2 * i_2) + (b_1 * 1) $$
이 공식을 입력값과 바이어스에 대입하면 아래와 같다.
$$ net_{h1} = (0.15 * 0.05) + (0.2 * 0.1) + (0.35 * 1) = 0.3775 $$
위의 합성 결과를 사용하여 숨겨진 뉴런의 활성함수 (Activation function)에 적용한다. 함수는 Sigmoid function을 사용하며 식은 아래와 같다.
$$ out_{h1} = \frac{1}{1+e^{-net_{h1}}} = \frac{1}{1+e^{-0.3775}} = 0.593269992 $$
동일하게 두 번째 숨겨진 뉴런(\(h_2\))도 적용하면 아래와 같은 결과가 나온다:
$$ out_{h2} = 0.596884378 $$
이제, 출력 뉴런을 계산한다. 계산 방법은 위와 동일하다.
$$ net_{o1} = (w_5 * out_{h1}) + (w_6 * out_{h2}) + (b_2 * 1) $$
$$ net_{o1} = (0.4 * 0.593269992) + (0.45 * 0.596884378) + (0.6 * 1) = 1.105905967 $$
$$ out_{o1} = \frac{1}{1+e^{-net_{h1}}} = \frac{1}{1+e^{-1.105905967}} = 0.75136507 $$
동일하게 두 번째 출력 뉴런(\(o_2\))도 적용하면 아래와 같은 결과가 나온다:
$$ out_{o2} = 0.772928465 $$
Calculating the Total Error
이제 손실 함수 (Loss function)2를 사용해, 손실률을 계산해야 한다. 사용할 손실함수는 Sum of squares (Mean squared error) (MSE)이다.
공식은 아래와 같다.
$$ E_{output} = \sum \frac{1}{2}(target - output)^{2} $$
\(target\)는 예상 출력값(0.01)이고 \(output\)은 실제 출력값(0.75136507)이다.
$$ E_{o1} = \frac{1}{2}(target_{o1} - out_{o1})^{2} = \frac{1}{2}(0.01 - 0.75136507)^{2} = 0.274811083 $$
동일하게 \(o_2\)도 구해준다.
$$ E_{o2} = 0.023560026 $$
이제, 전체 에러를 합산하여 최종 에러값(손실률)을 구한다.
$$ E_{total} = E_{o1} + E_{o2} = 0.274811083 + 0.023560026 = 0.298371109 $$
The Backwards Pass
Backwards Pass
역전파를 사용하여 가중치를 업데이트 한다. 업데이트 방법은 경사 하강법 (Gradient descent)을 사용한다.
$$ \mathbf{x}_{i+1} = \mathbf{x}_i - \gamma_i \nabla f(\mathbf{x}_i) $$
첫 번째 가중치 \(w_5\)를 갱신한\(w_5^{+}\)를 얻기 위해 변화율 \(\nabla f(w_5)\)을 구해야 한다. 따라서 가중치(\(w_5\))와 손실률(\(E_{total}\))과의 편미분 (Partial derivative)을 아래와 같이 구한다. (두 개 이상의 미지수에 대하여 변화율을 구하기 위해서 편미분을 사용한다)
$$ \frac{\partial E_{total}}{\partial w_{5}} $$
하지만 위의 두 변수 사이의 상관 분석 (Correlation and dependence)이 가능한 공식이 존재하지 않는다. (독립 변수(independent variable) 및 종속 변수(dependent variable)가 가중치(\(w_5\)) 또는 손실률(\(E_{total}\))에 해당하는 내용이 위 공식중에 없다) 따라서 독립/종속 변수가 존재하는 공식을 적용하기 위해 지금까지 계산한 내용을 확인해보자.
- \(E_{total} = E_{o1} + E_{o2}\)
- \(E_{o1} = \frac{1}{2}(target_{o1} - out_{o1})^{2}\)
- \(out_{o1} = \frac{1}{1+e^{-net_{o1}}}\)
- \(net_{o1} = (w_5 * h_1) + (w_6 * h_2) + (b_2 * 1)\)
순서 대로(Forward Pass) 계산된 과정은 아래와 같다.
$$ w_5 \rightarrow net_{o1} \rightarrow out_{o1} \rightarrow E_{o1} + E_{o2} \rightarrow E_{total} $$
인과 관계가 존재하지 않는 \(E_{o2}\)는 편미분 과정에서 탈락한다. 따라서 \(E_{total} = E_{o1}\)가 되므로 아래의 내용이 성립한다.
$$ w_5 \rightarrow net_{o1} \rightarrow out_{o1} \rightarrow E_{total} $$
위의 내용을 역순(Backward Pass)으로 계산하면 된다. 식의 성립을 위해 연쇄법칙 (Chain rule)을 적용하면 아래의 식이 된다.
$$ \frac{\partial E_{total}}{\partial w_{5}} = \frac{\partial E_{total}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial w_{5}} $$
Error(total) / Output(o1)
우선 \(\frac{\partial E_{total}}{\partial out_{o1}}\)를 구하기 위해 \(E_{total}\)를 재정의 한다.
$$ E_{o} = \frac{1}{2}(target_{o} - out_{o})^{2} $$
$$ E_{total} = E_{o1} + E_{o2} $$
$$ E_{total} = \frac{1}{2}(target_{o1} - out_{o1})^{2} + \frac{1}{2}(target_{o2} - out_{o2})^{2} $$
$$ E_{total} = \frac{1}{2}(target_{o1}^2 - 2target_{o1}out_{o1} + out_{o1}^2) + \frac{1}{2}(target_{o2}^2 - 2target_{o2}out_{o2} + out_{o2}^2) $$
$$ E_{total} = \frac{1}{2}target_{o1}^2 - target_{o1}out_{o1} + \frac{1}{2}out_{o1}^2 + \frac{1}{2}target_{o2}^2 - target_{o2}out_{o2} + \frac{1}{2}out_{o2}^2 $$
위 내용을 편미분 공식에 대입한다. (\(out_{o1}\)를 제외한 항목은 상수항으로 처리한다.)
$$ \frac{\partial E_{total}}{\partial out_{o1}} = -target_{o1}out_{o1}^{1 - 1} + {2}\frac{1}{2}out_{o1}^{2 - 1} $$
$$ \frac{\partial E_{total}}{\partial out_{o1}} = -target_{o1} + out_{o1} $$
$$ \frac{\partial E_{total}}{\partial out_{o1}} = -0.01 + 0.75136507 = 0.74136507 $$
Output(o1) / Network(o1)
\(\frac{\partial out_{o1}}{\partial net_{o1}}\)를 구하기 위해 \(out_{o1}\)를 확인한다.
$$ out_{o1} = \frac{1}{1+e^{-net_{o1}}} $$
\(out_{o1}\)은 Sigmoid function로, 이 함수의 미분(Logistic function#Derivative)형태는 아래와 같다.
$$ \frac{d}{dx}f(x) = f(x)\cdot(1-f(x)) $$
위에서 \(x\)는 \(net_{o1}\)이고, \(f(x)\)는 \(out_{o1}\)이므로 아래의 식이 성립된다.
$$ \frac{\partial out_{o1}}{\partial net_{o1}} = out_{o1}(1 - out_{o1}) = 0.75136507(1 - 0.75136507) = 0.186815602 $$
Network(o1) / Weight(5)
마지막으로 \(\frac{\partial net_{o1}}{\partial w_{5}}\)를 구하기 위해 \(net_{o1}\)를 확인한다.
$$ net_{o1} = w_5 * out_{h1} + w_6 * out_{h2} + b_2 * 1 $$
위 내용을 편미분 공식에 대입한다. (\(w_{5}\)를 제외한 항목은 상수항으로 처리한다.)
$$ \frac{\partial net_{o1}}{\partial w_{5}} = 1 * out_{h1} * w_5^{(1 - 1)} + 0 + 0 = out_{h1} = 0.593269992 $$
Error(total) / Weight(5)
최종 결과를 아래와 같이 도출한다.
$$ \frac{\partial E_{total}}{\partial w_{5}} = \frac{\partial E_{total}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial w_{5}} $$
$$ \frac{\partial E_{total}}{\partial w_{5}} = 0.74136507 * 0.186815602 * 0.593269992 = 0.082167041 $$
The modified weight(5)
오류를 수정하기 위해 경사 하강법 (Gradient descent)을 사용하여 수정된 가중치를 계산한다. 아래의 식에서 \(\eta\)는 학습속도(Learning rate)를 나타낸다. 여기에서는 0.5를 사용한다.
$$ w_5^{+} = w_5 - \eta * \frac{\partial E_{total}}{\partial w_{5}} = 0.4 - 0.5 * 0.082167041 = 0.35891648 $$
다른 값들도 위 방법으로 계산한다.
$$ w_6^{+} = 0.408666186 $$
$$ w_7^{+} = 0.511301270 $$
$$ w_8^{+} = 0.561370121 $$
Hidden Layer
Hidden Layer
계속해서 숨겨진 레이어의 가중치 \(w_1\), \(w_2\), \(w_3\), \(w_4\)를 계산해야 한다.
\(w_1\)의 경우 아래와 같다.
$$ \frac{\partial E_{total}}{\partial w_{1}} = \frac{\partial E_{total}}{\partial out_{h1}} * \frac{\partial out_{h1}}{\partial net_{h1}} * \frac{\partial net_{h1}}{\partial w_{1}} $$
Error(total) / Output(h1)
주의할 점으로, 숨겨진 레이어는 두 개의 출력 레이어에 영향을 줬다는 부분이다. 우측 그림을 참조하면 된다. 이 내용을 정리하면 아래와 같다.
$$ \frac{\partial E_{total}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial out_{h1}} + \frac{\partial E_{o2}}{\partial out_{h1}} $$
$$ \frac{\partial E_{o1}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial out_{h1}} $$ (\(out_{h1}\)을 도출하기 위한 전 단계가 \(net_{o1}\)이다. 위의 #Network(o1) / Weight(5)항목 참조)
\(\frac{\partial E_{o1}}{\partial net_{o1}}\)는 아래 식과 같이, 이미 계산된 결과를 사용할 수 있다. (\(\frac{\partial E_{o1}}{\partial out_{o1}}\)는 다시 계산해야 하지만 결과는 위에서 계산한 \(\frac{\partial E_{total}}{\partial out_{o1}}\)와 동일하다)
$$ \frac{\partial E_{o1}}{\partial net_{o1}} = \frac{\partial E_{o1}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} = 0.74136507 * 0.186815602 = 0.138498562 $$
\(\frac{\partial net_{o1}}{\partial out_{h1}}\)를 구하기 위해 \(net_{o1}\)를 확인한다.
$$ net_{o1} = w_5 * out_{h1} + w_6 * out_{h2} + b_2 * 1 $$
그리고 \(out_{h1}\)에 대하여 편미분을 진행한다. 결과는 \(w_5\)와 같다.
$$ \frac{\partial net_{o1}}{\partial out_{h1}} = w_5 = 0.40 $$
위의 계산 결과를 아래의 식에 대입한다.
$$ \frac{\partial E_{o1}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial out_{h1}} = 0.138498562 * 0.40 = 0.055399425 $$
동일한 방법으로 \(\frac{\partial E_{o2}}{\partial out_{o1}}\)을 계산한다.
$$ \frac{\partial E_{o2}}{\partial out_{h1}} = -0.019049119 $$
최종 결과를 아래와 같이 계산한다.
$$ \frac{\partial E_{total}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial out_{h1}} + \frac{\partial E_{o2}}{\partial out_{h1}} = 0.055399425 + -0.019049119 = 0.036350306 $$
Output(h1) / Network(h1)
\(\frac{\partial out_{h1}}{\partial net_{h1}}\)를 구하기 위해 \(out_{h1}\)를 아래와 같이 확인한다.
$$ out_{h1} = \frac{1}{1+e^{-net_{h1}}} $$
Sigmoid function의 미분(Logistic function#Derivative)형태를 참고하여 아래와 같이 계산한다.
$$ \frac{\partial out_{h1}}{\partial net_{h1}} = out_{h1}(1 - out_{h1}) = 0.59326999(1 - 0.59326999 ) = 0.241300709 $$
Network(h1) / Weight(1)
\(\frac{\partial net_{h1}}{\partial w_1}\)를 구하기 위해 \(net_{h1}\)를 확인한다.
$$ net_{h1} = w_1 * i_1 + w_2 * i_2 + b_1 * 1 $$
\(w_1\)에 대한 편미분을 구하면 결과적으로 \(i_1\)값과 동일해 진다.
$$ \frac{\partial net_{h1}}{\partial w_1} = i_1 = 0.05 $$
Error(total) / Weight(1)
최종 결과를 아래와 같이 도출한다.
$$ \frac{\partial E_{total}}{\partial w_{1}} = \frac{\partial E_{total}}{\partial out_{h1}} * \frac{\partial out_{h1}}{\partial net_{h1}} * \frac{\partial net_{h1}}{\partial w_{1}} $$
$$ \frac{\partial E_{total}}{\partial w_{1}} = 0.036350306 * 0.241300709 * 0.05 = 0.000438568 $$
The modified weight(1)
오류를 수정하기 위해 경사 하강법 (Gradient descent)을 사용하여 수정된 가중치를 계산한다.
$$ w_1^{+} = w_1 - \eta * \frac{\partial E_{total}}{\partial w_{1}} = 0.15 - 0.5 * 0.000438568 = 0.149780716 $$
위와 같이 \(w_2\), \(w_3\), \(w_4\)를 각각 계산한다.
$$ w_2^{+} = 0.19956143 $$
$$ w_3^{+} = 0.24975114 $$
$$ w_4^{+} = 0.29950229 $$
Python source code
# -*- coding: utf-8 -*-
import math
# INPUT1 -> WEIGHT1
# -> COMBINATION -> ACTIVATION -> OUTPUT
# INPUT2 -> WEIGHT2
def combination(w1, x1, w2, x2, bias):
return (w1 * x1) + (w2 * x2) + (1.0 * bias)
def sigmoid(x):
return 1.0 / (1.0 + math.exp(-1.0 * x))
def d_sigmoid(x):
return sigmoid(x) * (1.0 - sigmoid(x))
def cost(target, output):
return math.pow(target - output, 2.0) / 2.0
def back_error_output(target, output):
return output - target
def back_output_net(output):
return output * (1.0 - output)
def back_net_weight(input):
return input
def gradient_descent(weight, d_weight, eta=0.5):
return weight - (eta * d_weight)
class Layer(object):
def __init__(self):
self.x1 = 0.0
self.x2 = 0.0
self.w1 = 0.4
self.w2 = 0.3
self.bias = 0.5
self.threshold = 0.5
self.dw1 = 0.0
self.dw2 = 0.0
def setInput(self, x1, x2):
self.x1 = x1
self.x2 = x2
def setWeight(self, w1, w2):
self.w1 = w1
self.w2 = w2
def forward(self):
self.net = combination(self.w1, self.x1, self.w2, self.x2, self.bias)
self.output = sigmoid(self.net)
if self.output > self.threshold:
self.result = 1.0
else:
self.result = 0.0
def backward(self, target):
self.target = target
self.err = cost(self.output, target)
error_output = back_error_output(target, self.output)
output_network = back_output_net(self.output)
network_weight1 = back_net_weight(self.x1)
network_weight2 = back_net_weight(self.x2)
d_weight1 = error_output * output_network * network_weight1
d_weight2 = error_output * output_network * network_weight2
print ' - dw1: ' + str(d_weight1)
print ' - dw2: ' + str(d_weight2)
self.dw1 = gradient_descent(self.w1, d_weight1)
self.dw2 = gradient_descent(self.w2, d_weight2)
self.setWeight(self.dw1, self.dw2)
def __str__(self):
return '{}(w{}),{}(w{})={}(r{}/t{}) (e{})'.format(
self.x1, self.w1, self.x2, self.w2,
self.output, self.result, self.target, self.err)
if __name__ == '__main__':
layer = Layer()
predict = 0
while True:
layer.setInput(1, 1)
layer.forward()
layer.backward(predict)
print str(layer)
condition = raw_input('Input [q/y/n]: ')
if condition == 'q' or condition == 'Q':
break
elif condition == 'y' or condition == 'Y':
predict = 1
elif condition == 'n' or condition == 'N':
predict = 0
Backup
현재 위치의 페이지(문서)를 백업한다.
Download: Backpropagation_Example_-_Open_Source_Open_Mind.pdf
See also
Favorite site
- [추천] A Step by Step Backpropagation Example 3 (Backpropagation을 실제 예제로 보여준다)