티스토리 뷰
위 책을 읽고 코드로 구현하기에 적합하도록 수식을 변경하여 정리했습니다.
keras, tensorflow 등의 라이브러리 없이 numpy 행렬 연산을 통해 직접 feed forward, back propagation 을 구현하여 학습을 할 수 있도록 코드를 구현했습니다.
Perceptron
다수의 입력을 받아서 모두 더한 뒤 특정한 함수를 통과시켜 얻은 결과는 아래와 같이 정리할 수 있습니다.
$ u = \sum_{i=1}^{3}{w_i \cdot a_i} + b $
$ z = f(u) $ f : activation function
Multiple Layer Perceptron
■ 다층 layer를 가지는 퍼셉트론을 도식화 하면 아래와 같은 그림으로 표현 가능합니다.
우선 진행에 앞서 혼동이 되지 않도록 수식에 대해 정의를 하고 넘어가겠습니다.
$ 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 들을 학습한 결과를 보면 어느정도 학습이 된 것을 확인 할 수 있습니다.
'머신러닝 > 기초' 카테고리의 다른 글
[수정 중] Feature selection (0) | 2019.08.03 |
---|---|
[ 기본 ] Neural network - feed forward (0) | 2017.06.30 |
[ 과제 2 ] Gauss-Newton Method (0) | 2017.06.24 |
함수 최적화 - Gradient Descent 에 대해서 (0) | 2017.06.18 |
[ 과제 1 ] Gradient Descent (0) | 2017.06.16 |
- Total
- Today
- Yesterday
- Residual Block
- keras
- backpropagation
- 네이버웹툰
- Digital watermarking
- gPRC
- implementation
- tensorflow serving
- DW
- 캡처방지
- numpy
- dct
- DWT-DCT
- flask serving
- SvD
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |