PYTHON으로 딥러닝하기

파이썬으로 딥러닝하기 | 오차역전파란? & MNIST 신경망 구현하기

euni_joa 2018. 8. 31. 18:29
반응형


" You are going be fine! "


이전 포스팅에서 만든 신경망을 "더 좋은 신경망으로 만드는 방법"에 대해 알아보도록 하겠습니다.




오차 역전파란?


신경망 학습 처리에서 오차를 최소화하는 함수의 경사를


효율적으로 계산하기 위한 방법입니다.



< 비용함수(cost function)의 경사(기울기)를 계산하는 방법>



1. 수치미분


: 구현하기 쉽고 정확성이 높으나, 매우 느리다.



2. 오차 역전파


: 출력층으로부터 차례대로 역방향으로 각 층에 있는 노드의 오차를 계산하여,

그 오차로 함수의 경사를 계산합니다.


즉, 전파된 오차를 이용하여 가중치를 조정하는 것이 '오차역전파' 입니다.



< 계산 그래프 >


계산 그래프란, 순전파와 역전파에 계산 과정을 그래프로 나타내는 방법입니다.



왜 계산 그래프로 문제를 해결할까요?


1. 전체가 아무리 복잡해도 각 노드에서 단순한 계산에 집중하여

문제를 단순화 할 수 있습니다. ( 국소적 계산 )



2. 역전파를 통해서 미분을 효율적으로 계산할 수 있습니다.




< 합성함수 미분 구하기 >



우선 기본적인 미분 공식은 다음과 같습니다.



원리: 접선은 할선의 극한입니다.



하지만 위와 같은 진정한 미분은 h 가 0이 될 경우, 컴퓨터로 구현하기 어렵기 때문에


근사한 접선인 할선을 구하는 "수치미분"을 사용해야합니다.


따라서 수치미분에는 약간의 오차가 포함됩니다.



[ 파이썬으로 수치미분 구현하기 ]

def numerical_diff(f,x): h = 1e-4 # 0.0001 # 컴퓨터로 극한 값을 구하기 어려우므로 도함수를 구현한다. # return (f(x+h) - f(x-h)) / ((x+h) - (x-h)) return (f(x+h) - f(x-h)) / 2*h



< 편미분 >


변수가 2개 이상인 함수를 미분할 때 미분 대상 변수 외에 나머지 변수를

상수처럼 고정시켜 미분하는 것을 편미분이라고 합니다.



[ 파이썬으로 편미분 구현하기 ]


def numerical_gradient(f,x): h = 1e-4 grad = np.zeros_like(x) for idx in range(x.size): tmp_val = x[idx] # f(x+h) 계산 x[idx] = tmp_val + h fxh1 = f(x) # f(x-h) 계산 x[idx] = tmp_val - h fxh2 = f(x) grad[idx] = (fxh1 - fxh2) / 2*h x[idx] = tmp_val # 값 복원 return grad



< 신경망 학습을 시키기 위한 방법 >



(W: Weight, 기울기: 오차함수 미분한 값)




< 합성함수 미분 >



추가설명:

f(x) = ax + b


f(x)를 X로 보고 미분하고 (ax + b)를 속 미분하여 더한다.


혹시 증명을 알고 싶으신 분들은 구글에..ㅎㅎ



< 연쇄법칙 >


합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있습니다.


이것이 연쇄 법칙의 원리 입니다.




< 역전파 >


신경망의 역전파에서의 중요한 성질은

출력과 정답 레이블의 차이인 오차가 앞 계층에 전해지는 것입니다.


1. Affine / Relu 계층


Affine 계층이란,


신경망의 순전파 때 수행하는 행렬의 내적은 기하학에서는 어파인 변환이라고 합니다.


그래서 어파인 변환을 수행하는 처리를 'Affine 계층'이라는 이름으로 구현하도록 하겠습니다.



2. Softmax-with-Loss 계층


마지막으로 출력층에서 사용하는 함수로


신경망의 학습의 목적인 신경망의 출력(Softmax의 출력)이

정답 레이블과 가까워지도록 가중치 매개변수의 값을 조정하는 역할을 합니다.




< MNIST 신경망 구현하기 >


드디어 역전파를 Python으로 구현해보도록 하겠습니다.



[ 2층 신경망 구현하기 ]


import numpy as np from common.layers import * from common.gradient import numerical_gradient # 수치미분 함수 from collections import OrderedDict class TwoLayerNet: def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): # 가중치 초기화 self.params = {} self.params['W1'] = weight_init_std * \ np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * \ np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size) # 계층생성 self.layers = OrderedDict() # 순전파 순서의 반대로 역전파가 된다. self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) self.layers['Relu1'] = Relu() self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) self.lastLayer = SoftmaxWithLoss() # 예측 수행 def predict(self, x): for layer in self.layers.values(): x = layer.forward(x) return x # x: 입력데이터, t: 레이블 def loss(self, x, t): y = self.predict(x) return self.lastLayer.forward(y,t) def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1) if t.ndim != 1: t= np.argmax(t, axis=1) accuracy = np.sum(y == t) / float(x.shape[0]) return accuracy # x: 입력데이터, t: 레이블 def numerical_gradient(self, x, t): loss_W = lambda W: self.loss(x,t) grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) return grads # 오차 역전파 def gradient(self, x, t): # 순전파 self.loss(x,t) # 역전파 dout = 1 dout = self.lastLayer.backward(dout) layers = list(self.layers.values()) layers.reverse() for layer in layers: dout = layer.backward(dout) # 결과 저장 grads = {} grads['W1'] = self.layers['Affine1'].dW grads['b1'] = self.layers['Affine1'].db grads['W2'] = self.layers['Affine2'].dW grads['b2'] = self.layers['Affine2'].db return grads


※ 참고


수치미분과 오차역전파법의 결과를 비교하면 오차역전파법의 구현에 잘못이 없는지 확인할 수 있습니다.



[ MNIST 데이터 학습 구현하기 ]


import numpy as np from dataset.mnist import load_mnist # 데이터 읽기 (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) iters_num = 10000 train_size = x_train.shape[0] batch_size = 100 learning_rate = 0.1 train_loss_list = [] train_acc_list = [] test_acc_list = [] iter_per_epoch = max(train_size / batch_size, 1) for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 오차역전파법으로 기울기를 구한다. grad = network.gradient(x_batch, t_batch) # 갱신 for key in ('W1', 'b1','W2','b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print(train_acc, test_acc)


※ 추가


문제. 위의 2차 신경망으로 3차 신경망을 구하고 정확도를 확인하시오



코드 출처: Deep Learning from Scratch

데이터: 한빛 미디어 깃 허브 저장소




오늘은 여기까지!


다음 포스팅은 학습 관련 기술들에 대해 더 자세히 알아보도록 합시다!!

반응형