1. Backpropagation(역전파 알고리즘)
Artificial neural network의 layer와 neuron의 수가 많아지고, 입력과 출력의 수가 많아질 수록
gradient descent를 통해 최적의 w와 b를 찾기 위한 계산량이 매우 많아집니다.
(가중치 개수 만큼의 cost function이 중복 계산되어야 함)
Cost function은 w와 b로 구성되어 있으므로, 출력 layer로부터 입력 layer쪽으로(역방향),
순차적으로 cost function에 대한 편미분을 구하고, 해당 편미분 값을 이용해 w와 b 값을 갱신합니다.
Backpropagation의 단계는 다음과 같습니다.
Forward propagation(정방향)과정을 통해 input을 마지막 output layer까지 전달해
cost function을 구합니다.
output layer에서 구한 실제 값과 예측 값의 차를 역방향으로 전파시켜 각 layer의
w와 b 값을 갱신합니다.
다음과 같은 perceptron이 있다고 가정하고 Backpropagation을 구해보겠습니다.
x1 a1(1)->z1(1) a1(1)->y1
a2(1)->z2(1)
x2 a3(1)->z3(1) a2(2)->y2
위 perceptron에서 activation function은 sigmoid이며, cost function은 MSE입니다.
cost function MSE는 다음과 같은 식으로 정의합니다.
$$J=\dfrac{1}{2}\sum _{i}\left( y_{i}-t_{i}\right) ^{2}$$
output layer에서 마지막 hidden layer쪽으로 w를 구합니다.
a1(2) = z1(1)w11(2) + z2(1)w12(2) + z3(1)w13(2)+b1(2)일 때 w를 구하면
아래와 같습니다.
$$\dfrac{aJ}{aw_{12}^{\left( 2\right) }}=\dfrac{\partial J}{\partial y_{1}}\cdot \dfrac{\partial y_{1}}{\partial a_{1}^{\left( 2\right) }}\cdot \dfrac{\partial a_{1}^{\left( 2\right) }}{\partial w_{12}^{\left( 2\right) }}$$
$$=\left( y_{1}-t_{1}\right) y_{1}\left( 1-y_{1}\right) z_{2}^{\left( 1\right) }$$
위 식은 체인룰을 사용한 것으로 J를 yi로 편미분한 식은 (yi-ti)가 되며, yi를 ai(2)로 미분한 것은 yi(1-yi)가 됩니다.
ai(2)를 wij(2)로 미분한 것은 zj(1)이 됩니다.
다음 hidden layer에서 input layer w를 구합니다.
$$\dfrac{dJ}{dw_{12}^{\left( 1\right) }}=\dfrac{dJ}{dz_{2}^{\left( 1\right) }}\cdot \dfrac{dz_{2}^{\left( 1\right) }}{da_{2}^{\left( 1\right) }}\cdot \dfrac{da_{2}^{\left( 1\right) }}{dw_{21}^{\left( 1\right) }}$$
$$\dfrac{\partial J}{\partial z_{2}^{\left( 1\right) }}=\sum ^{2}_{k=1}\dfrac{\partial J}{\partial y_{k}}\cdot \dfrac{\partial y_{k}}{\partial a_{k}^{\left( 2\right) }}\cdot \dfrac{2a_{k}^{\left( 2\right) }}{\partial z_{2}^{\left( 1\right) }}$$
$$=\sum ^{2}_{k=1}\dfrac{\partial J}{\partial y_{k}}\cdot \dfrac{\partial y_{k}}{\partial a_{k}^{\left( 2\right) }}\cdot w_{k,2}^{\left( 2\right) }$$
$$\dfrac{\partial J}{\partial y_{k}}\cdot \dfrac{\partial y_{k}}{\partial a_{k}^{\left( 2\right) }}=\left( y_{k}-t_{k}\right) y_{k}\left( 1-y_{k}\right)$$
즉 J를 z2(1)로 미분하면 다음과 같은 식을 도출할 수 있습니다.
$$\begin{aligned}\left( y_{1}-t_{1}\right) y_{1}\left( 1-y_{1}\right) w_{12}^{\left( 2\right) }+\left( y_{2}-t_{2}\right) y_{2}\left( 1-y_{2}\right) \ w_{22}^{\left( 2\right)} \end{aligned}$$
결국 outlayer를 제외한 모든 층의 backpropagation은 다음과 같은 식을 가집니다.
$$\begin{aligned}\left\{ \Sigma _{k=1}^{2}\left( y_{k}-t_{k}\right) y_{k}\left( 1-y_{k}\right) w_{ki}^{\left( 2\right) }\right\} z_{i}^{\left( 1\right) }\ \left( 1-z_{i}^{\left( 1\right) }\right) x_{j}\end{aligned}$$
이를 파이썬으로 구현해보겠습니다.
2. Python
먼저 필요한 라이브러리를 불러오겠습니다.
import numpy as np
import matplotlib.pyplot as plt
구현할 MLP는 input layer node의 수가 2개,
1st hidden layer node의 수가 5개,
2nd hidden layer node의 수가 3개,
output layer node의 수가 2개인 model입니다.
activation function은 sigmoid로 합니다.
def sigmoid(a):
return 1/(1 + np.exp(-a))
def d_sigmoid(a): # sigmoid의 미분 값
return sigmoid(a) * (1-sigmoid(a))
d_sigmoid는 sigmoid를 미분한 값입니다.
다음 mse 함수를 생성합니다.
def mse(y,t):
return 0.5 * np.mean((y-t)**2)
임의의 데이터를 생성합니다.
n = 100
n_class = 50
# class0: 평균이 (2,2)인 정규분포 데이터
# class1: 평균이(-2,-2)인 정규분포 데이터
x = np.random.randn(n,2)
x[n_class:,:] +=2
x[:n_class,:] -=2
# class0: [1,0]
# class1: [0,1]
t = np.zeros((n,2))
t[:n_class,:]=[1,0]
t[n_class:,:]=[0,1]
0과 1로 분류할 수 있는 데이터입니다.
다음 model의 sample 수와 input layer node의 수, hidden layer node의 수,
output layer node의 수를 변수로 생성합니다.
# sample의 수
num_data = x.shape[0] # x의 행
# input layer node의 수 (특징의 수)
input_node = x.shape[1] # x의 열
# hidden layer node의 수
hid_node1 = 5 # 임의의 수
hid_node2 = 3
# output layer node의 수 (class의 수)
output_node = t.shape[1]
w와 b의 값을 임의의 수로 생성합니다.
# w와 b는 임의의 값을 생성
# output layer와 2nd hidden layer 사이의 w,b
w3 = np.random.randn(output_node,hid_node2)
b3 = np.random.randn(output_node)
# 2nd hidden layer와 1st hidden layer 사이의 w,b
w2 = np.random.randn(hid_node2,hid_node1)
b2 = np.random.randn(hid_node2)
# 1st hidden layer와 input layer 사이의 w,b
w1 = np.random.randn(hid_node1,input_node)
b1 = np.random.randn(hid_node1)
예측 함수를 생성합니다.
def predict(x_in,w1,b1,w2,b2,w3,b3):
a1 = x_in.dot(w1.T)+b1
z1 = sigmoid(a1)
a2 = z1.dot(w2.T)+b2
z2 = sigmoid(a2)
a3 = z2.dot(w3.T)+b3
y = sigmoid(a3)
return a1,z1,a2,z2,a3,y
# a1: hidden layer의 weighted sum
# z1: hidden layer의 a1이 activation function(sigmoid)을 통과한 값
# a2: output layer의 weighted sum
# y: 예측값, output layer의 a2가 activation function(sigmoid)을 통과한 값
학습률과 학습 횟수, cost function의 값을 저장할 배열을 선언합니다.
learning_rate = 0.1
iter = 50000 # update 횟수
# update마다 cost 저장
cost = np.zeros((iter))
이제 MLP model을 학습시킵니다.
for i in range(iter):
a1,z1,a2,z2,a3,y = predict(x,w1,b1,w2,b2,w3,b3)
cost[i] = mse(y,t)
del_out = (y-t) * d_sigmoid(a3)
del_hid2 = del_out.dot(w3) * d_sigmoid(a2)
del_hid1 = del_hid2.dot(w2) * d_sigmoid(a1)
w3 = w3 - learning_rate * del_out.T.dot(z2) / num_data
b3 = b3 - learning_rate * np.sum(del_out,axis=0) / num_data
w2 = w2 - learning_rate * del_hid2.T.dot(z1) / num_data
b2 = b2 - learning_rate * np.sum(del_hid2,axis=0) / num_data
w1 = w1 - learning_rate * del_hid1.T.dot(x) / num_data
b1 = b1 - learning_rate * np.sum(del_hid1,axis=0) / num_data
예측을 통해 a1,z1,a2,z2,a3,y를 구하고
output layer에서 hidden layer의 cost function 편미분을 구합니다.
마찬가지로 2nd hidden layer -> 1st hidden layer,
1st hidden layer -> input layer의 cost function 편미분을 구합니다.
다음 구한 값을 토대로 w와 b를 업데이트 해줍니다.
마지막으로 예측, 타겟 값과 cost function의 값을 시각화합니다.
pred = y.argmax(axis=1).reshape(-1,1)
target = t.argmax(axis=1).reshape(-1,1)
np.concatenate((pred,target),axis=1)
왼쪽 열이 예측 값, 오른쪽 열이 타겟 값입니다.
정확하게 예측한 모습을 볼 수 있습니다.
plt.plot(cost[2:])
학습이 계속 될 수록 cost function의 값이 줄어듭니다.
'머신러닝' 카테고리의 다른 글
[파이썬] Ridge,Lasso Regression and Confusion matrix (21) | 2023.11.28 |
---|---|
[파이썬] 딥러닝 BGD_SGD (23) | 2023.11.20 |
[파이썬] Polynomial linear-regression (15) | 2023.11.01 |
[파이썬] Perceptron Gradient Descent (0) | 2023.10.25 |
[파이썬] 데이터 전처리 및 샘플링 (1) | 2023.10.24 |