Skip to content

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

References


  1. 고정된 상수 값 이다. 

  2. 또는 비용 함수 (Cost function) 

  3. A_Step_by_Step_Backpropagation_Example_–_Matt_Mazur.pdf