티스토리 뷰

 

Reference 오카타니 타카유키, 『딥러닝 제대로 시작하기』, jpub(2016), p58~p63

 

위 책을 읽고 코드로 구현하기에 적합하도록 수식을 변경하여 정리했습니다.

keras, tensorflow 등의 라이브러리 없이 numpy 행렬 연산을 통해 직접 feed forward, back propagation 을 구현하여 학습을 할 수 있도록 코드를 구현했습니다.

 

 

일반 적인 Perceptron 구조

 

Perceptron

다수의 입력을 받아서 모두 더한 뒤 특정한 함수를 통과시켜 얻은 결과는 아래와 같이 정리할 수 있습니다.


$ u = \sum_{i=1}^{3}{w_i \cdot a_i} + b $

$ z = f(u) $  f : activation function

 

 

Multiple Layer Perceptron


■ 다층 layer를 가지는 퍼셉트론을 도식화 하면 아래와 같은 그림으로 표현 가능합니다.

 

Multiple Layer Perceptron

 

우선 진행에 앞서 혼동이 되지 않도록 수식에 대해 정의를 하고 넘어가겠습니다.

$ u_{j}^{(l)} $  → l - layer 의 j-th 노드 (활성함수를 통과 전)

$ z_{j}^{(l)} $  → l - layer 의 j-th 노드 (활성함수를 통과 후)

$ w_{ij}^{(l)} $  → (l-1) layer 의 i-th 노드 에서 (l) layer 의 j-th 노드로 가는 가중치

$u_{i}^{(l-1)} = z_{i}^{(l-1)}$  → 입력층 노드는 활성함수가 없음

$u_{k}^{(l+1)} = z_{k}^{(l+1)}$ → 출력층 노드는 활성함수가 없음

 

Feed forward


앞 방향으로 나가는 결과 값을 계산하는 것은 아래처럼 쉽게 계산이 됩니다.

 

$ u_k = \sum_{j=1}^{3}{ w_{jk}^{(l+1)}z_{j}^{l} + b_{k}^{(l+1)} } = \sum_{j=0}^{3}{w_{jk}^{(l+1)}z_{j}^{(l)} } = \sum_{j=0}^{3}{w_{jk}^{(l+1)}f(u_{j}^{(l)}) } \leftarrow w_{0k}^{l+1}=b_k^{l+1} , z_0^{l}=1  $

 

Back-propagation


문제는 weight 들을 학습하기 위해 gradient descent 방식으로 업데이트 하는 과정이 간단하지 않아

이를 정리 하고자 아래와 같이 수식을 다시 정리해 보도록 하겠습니다.

 

여기서의 E (loss function)은 간단히 mse 로 정의하고 진행하겠습니다.

$ E = \sum_{k=1}^{3} {(y_k - d_k)}^2 $

 

${\large {\partial E \over \partial w_{jk}^{(l+1)}} = {\partial E \over \partial u_{j}^{(l+1)}}\cdot {\partial u_{j}^{(l+1)} \over \partial w_{jk}^{(l+1)}}{\partial E \over \partial w_{jk}^{(l+1)}} = {\partial E \over \partial u_{j}^{(l+1)}}\cdot {\partial u_{j}^{(l+1)} \over \partial w_{jk}^{(l+1)}} = (y_k -d_k)\cdot{\partial \sum_j{w_{jk}^{(l+1)} \cdot  {z_j^{(l)}} } \over \partial w_{jk}^{(l+1)}}
= (y_k -d_k)\cdot  {z_j^{(l)}}
}$

$ \begin{align*}{\partial E \over \partial w_{ij}^{(l)}} & =  {\partial E \over \partial u_{j}^{(l)}} {\partial u_{j}^{(l)} \over \partial w_{ij}^{(l)}} = {\partial E \over \partial u_{j}^{(l)}} \cdot z_i^{l-1} = {\sum_{k=0}^{3}{ {\partial E \over \partial u_{k}^{(l+1)}} {\partial u_{k}^{(l+1)} \over \partial u_{j}^{(l)}} } } \cdot z_i^{l-1}  \\ & = 
\left (  f'(u_j^{l}) \sum_{k}{w_{jk}^{(l+1)}(u_k^{(l+1)} - d_k)}\right )z_i^{(l-1)} \end{align*} $

$\begin{align*} {\partial E \over \partial w_{ij}^{(l)}} = {\partial E \over \partial u_{j}^{(l)}}{\ \partial u_{j}^{(l)} \over \partial w_{ij}^{(l)}} =\delta_{j}^{(l)}{\ \partial u_{j}^{(l)} \over \partial w_{ij}^{(l)}} \quad \leftarrow \quad {\partial E \over \partial u_{j}^{(l)}} = \delta_{j}^{(l)} \end{align*}$


$ \begin{align*} {\partial E \over \partial u_{j}^{(l)}} & = \sum_{k}{{\partial E \over \partial u_{k}^{(l+1)}} 
{\partial u_{k}^{(l+1)} \over \partial u_{j}^{(l)}} } = \sum_{k}{{\partial E \over \partial u_{k}^{(l+1)}} \cdot
\left ( w_{jk}^{(l+1)} f'(u_j^{(l)})\right )} \\ & = \sum_{k}{\delta_k^{(l+1)}\left ( w_{jk}^{(l+1)} f'(u_j^{(l)})\right )} \quad l=2, 3,\dots ,L-1 \end{align*} $

 

여기서 중요한 것은 델타 함수가 출력층에서 멀어져도 출력층을 이용하여

전파되는 양을 수식으로 정의할 수 있게 되었습니다. 

$\delta_{j}^{(l)}=\sum_{k}{\delta_k^{(l+1)}\left ( w_{jk}^{(l+1)} f'(u_j^{(l)})\right )}$

 

Back-propagation matrix form



import numpy as np
import matplotlib.pyplot as plt

class MLP(object):
    """
    Using Keras and tensorflow, it is easy to learn. 
    However, it is implemented by using only numpy for feed forward and back propagation.

    Args:
        layers : [xs_dim, node, ... , node, out_dim]    

    mr.xeros@gmail.com [roadk]
    """
    
    @staticmethod
    def sigmoid(x):
        return 1/(1+np.power(np.e, -x))

    @staticmethod
    def identity(x):
        return x

    @staticmethod
    def inv_sig(x):
        tmp = MLP.sigmoid(x)
        return tmp*(1-tmp)

    def __init__(self, **kwargs):


        self.layers = kwargs['layers']
        self.activate = [MLP.identity]
        self.weights = [1]
        self.bias = [0]
        self.lr = 0.002

        for i in range(1, len(self.layers)):
            self.weights.append(np.random.normal(0, 0.5, (self.layers[i-1],self.layers[i])))
            self.bias.append(np.random.normal(0, 0.5, self.layers[i]))

            if i != len(self.layers) -1:
                self.activate.append(MLP.sigmoid)
            else:
                self.activate.append(MLP.identity)

    def feed_forward(self, xs):

        self.U = [xs]
        self.Z = [xs]

        for i in range(1, len(self.layers)):
            u = self.Z[i-1].dot(self.weights[i]) + self.bias[i]
            z = self.activate[i](u)
            self.U.append(u)
            self.Z.append(z)

        return self.Z[-1]


    def back_propagate(self, xs, ys):

        pred_ys = self.feed_forward(xs)
        self.D = []

        for i in reversed(range(1,len(self.layers))):
            if i == len(self.layers) - 1:
                d = pred_ys - ys
            else:
                d = self.inv_sig(self.U[i])*(self.D[-1].dot(self.weights[i+1].T))

            dW = self.Z[i-1].T.dot(d)
            db = np.sum(d, axis=0)

            self.weights[i] -= self.lr*dW
            self.bias[i] -= self.lr*db

            self.D.append(d)
        return

    def evaluate(self, xs, ys):
        pred_ys = self.feed_forward(xs)
        d = pred_ys - ys
        return np.mean(np.sqrt(d**2))
    

if __name__ == '__main__':
    
    model = MLP(layers=[1,5,5,5,1])
    
    train_x = np.linspace(-5,5,100)
    train_y = np.sin(train_x)

    xs = train_x.reshape(-1,1)
    ys = train_y.reshape(-1,1)

    pred_ys = model.feed_forward(xs)

    for i in range(50000):
        model.back_propagate(xs, ys)

        if (i+1) % 5000 == 0:
            error = model.evaluate(xs, ys)
            print('ITER={:05d}, RMSE={:.4f}'.format(i+1, error))
            
    pred_ys = model.feed_forward(xs)
    
    plt.plot(xs.ravel(), pred_ys.ravel(), label='prediction')
    plt.plot(xs.ravel(), train_y.ravel(), label='original')
    plt.legend()    
            
        

 

Naive Gradient descent 와 sigmoid 를 사용하였고, numpy 사용하여 weights 들을 학습한 결과를 보면  어느정도 학습이 된 것을 확인 할 수 있습니다.

 

Numpy 만 사용하여 학습한 결과 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함