반응형


" 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

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




오늘은 여기까지!


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

반응형
반응형

안녕하세요!



오늘은


신경망에서 오차 역전파에서 사용되는


지수함수와 로그함수에 대해 알아보겠습니다.





고등학교 수학시간때 들었지만


저는 기억이 가물가물... 열심히 하지 않았나봐요..



그래서 딥러닝을 위해 다시 공부해보도록 하겠습니다!




우선,


지수란?


2 x

 

이러한 거듭제곱의 형태를 말합니다.



그렇다면,


지수함수란?


y = 2 x


위와 같이 y= 을 붙여서 짝을 지어주는 것을 말합니다.


이것을 밑이 2인 지수함수라고 합니다.



이러한 지수함수는 밑이 1보다 큰 경우와


밑이 0보다 크고 1보다 작은 경우로 나눌 수 있습니다.



그래프로 비교해 볼까요?




(1 < 밑)  y =  x  함수의 그래프





(0 < 밑 < 1) y =  (1/2) x 함수의 그래프




따라서


 y =  x 그래프와 y = (1/2) x 그래프는 Y축 대칭입니다.


 y =  x 는 증가함수 그래프의 형태를


y = (1/2) x 는 감소함수 그래프의 형태를 띄게됩니다.


(위의 그래프는 구글에서 직접 그려보실 수 있습니다.)




로그 함수란?


y = loga x (a > 0, a != 1, x > 0)


이것을 a를 밑으로 하는 로그함수라고 말합니다.


여기서 x는 진수입니다.


로그함수는 지수함수의 역함수입니다.



이러한 로그함수도 밑이 1보다 큰 경우와


밑이 0보다 크고 1보다 작은 경우로 나눌 수 있습니다.




그래프로 확인해 볼까요?



(1 < 밑) 일 때, 아래의 그래프 형태를 띕니다.




(0 < 밑 < 1) 일 때, 아래의 그래프 형태를 띕니다.





따라서 


y = logx  그래프와  y =  a x 그래프는 Y = X축 대칭입니다.


y = x 라는 것은 x 대신 y, y 대신 x 의 값을 갖는 다는 것입니다.





오늘은 여기까지!


이 원리를 이용하는 신경망 오차 역전파 방법은 파이썬에서 활용해보도록 해보겠습니다.

반응형

+ Recent posts