혼자 아닌 혼자가 되고 싶은 나의 인생
파이썬을 이용한 뉴럴 네트워크 실전 - 경사하강법 (역전파) 본문
▶경사 하강법
● 손실 함수는 최대한의 정확도를 위한 매개변수를 위한 값을 찾기 위해 존재한다.
앞선 포스팅에서 경사하강법에 대해 설명한적 있다.
아래의 그림은 가중치 w에 따른 오차를 나타낸 함수이다.
즉, 오차는 가중치 w_ij의 함수로 나타내었으며, 경사하강법으로 최소한의 오차를 찾아내는 과정이다.
가중치가 단 한개일 경우 이렇게 쉽게 오차를 찾아낼 수 있다.
또한 가중치가 x0 그리고 x1 과 같이 2개라면 그림과 같이 찾아 낼 수 있을 것 이다.
만일 가중치가 여러개 일 경우라면 ?
Error = f(w_11,w_12,w_13 .... w 33) 일 경우, 즉 9개의 가중치에 의해 결정될 경우 9개의 축으로 이루어진 상상 할 수도 없이 복잡한 형상이 될 것 이다.
기계학습에서 문제의 대부분은 학습하는 과정에서 최적의 가중치와 편향(매개변수)을 를 찾아내는 과정이다. 최적이라는 뜻은 손실 함수값이 최소가 될 때 이다. 앞서 말한 것과 같이 손실 함수는 상상도 못할 정도로 복잡한 형태가 될 것이다. 이러한 상황에서는 기울기를 이용하여 최대한 작은 기울기를 찾기 위한 방법이 경사법이다.
▶경사 하강법 코드
경사 하강법을 위해서는 반드시 수치미분이 필요로 한다
전방차분 또는 후방차분의 오차를 줄이기 위한 중앙차분의 코드는 다음과 같다
----------------------------------------------------------------------------------
#중앙차분
def numerical_diff(f,x):
h = 1e-4 # 0.0001 정도가 제일 좋은 결과를 얻을 수 있는 값 이다. ( 그 이하의 값은 0으로 반올림되어버림 )
return ((f(x+h)-f(x-h))/(2*h)) # 중앙차분법이 비교적 오차가 적다
def function_a(x):
return (0.01*x**2+0.1*x)
import numpy as np
import matplotlib.pylab as plt
x = np.arange(0.0,50.0,0.1)
y = function_a(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x,y)
plt.show()
print(numerical_diff(function_a,2))
print(numerical_diff(function_a,5))
----------------------------------------------------------------------------------
앞서 보여준 코드는 한개의 변수만을 미분하는 코드이다.
둘이상의 변수를 가진 함수에서의 기울기를 구하기 위해서는 각각의 변수에 대한 편미분이 필요로 한다.
----------------------------------------------------------------------------------
#편미방
import numpy as np
def numerical_gradient(f,x): # 편미분 코드
h = 1e-4
grad = np.zeros_like(x) # x가 n개의 array라면 (n개의 변수로 된 편미분 방정식) n개의 array로 된 grad 라는 변수 생성
for idx in range(x.size): # x의 size (x.size) 만큼의 인덱스 (즉 0부터 x-1만큼) 생성 한다
# 또한 for문을 사용해서 x.size 의 개수만큼 미분을 반복하여 결과를 grad[] 로 반환한다.
tem_val = x[idx]
x[idx] = tem_val + h
fxh1 = f(x)
x[idx] = tem_val - h
fxh2 = f(x)
grad[idx] = (fxh1-fxh2)/(2*h)
x[idx] = tem_val
return grad
def function_b(x):
return x[0]**2+x[1]**3
print(numerical_gradient(function_b,np.array([1.0,2.0])))
print(numerical_gradient(function_b,np.array([2.0,4.0])))
print(numerical_gradient(function_b,np.array([3.0,6.0])))
----------------------------------------------------------------------------------
상단의 코드에서 미분한 x[0]^2 + x[1]^2 의 그래프는 위의 그림과 같이 그려진다.
그래프위에 수 많은 점이 있을 것 이며 그 점에서의 기울기 또한 위의 코드로 얻어질 수 있을 것 이다.
한가지 확실한 점은 우리는 에러값(error)이 제일 작아지는 중앙 (0,0)을 최정 목표로 지정해야 하며,
그 최종 목표지점을 찾아가기 위해서는 기울기라는 이정표를 잘 이용해야 한다는 점이다.
기울기라는 이정표로 원하는 점을 찾아가는 경사법(gradient method)을 나타내는 식은 다음과 같다
1. x_o점을 기준으로 한다.
2. x_o점의 기울기가 + 일 경우 x_o점을 기준으로 - 만큼 움직인다. (기울기가 줄어드는 방향으로 움직인다 - 2차함수 생각)
x_o점의 기울기가 - 일 경우 x_o점을 기준으로 + 만큼 움직인다. (기울기가 줄어드는 방향으로 움직인다)
=> 경사하강법 (gradient descent method) : 경사가 줄어드는 방향으로 움직인다
3. 위와 같은 과정을 여러번 반복한다면 결국에는 원하는 점에 도착한다.
----------------------------------------------------------------------------------
# 경사하강법 코드
import numpy as np
def numerical_gradient(f,x):
h = 1e-4
grad = np.zeros_like(x)
for idx in range(x.size):
tem_val = x[idx]
x[idx] = tem_val + h
fxh1 = f(x)
x[idx] = tem_val - h
fxh2 = f(x)
grad[idx] = (fxh1-fxh2)/(2*h)
x[idx] = tem_val
return grad
def gradient_descent(f,init_x,relaxation_f,iteration):
x = init_x
for i in range(iteration):
grad = numerical_gradient(f,x)
x -= relaxation_f*grad
print(x)
return x
def function_b(x):
return (x[0]**2+x[1]**2)
init_x = np.array([-3.0,4.0])
gradient_descent(function_b,init_x,0.1,50)
----------------------------------------------------------------------------------
▶경사 하강법을 위한 클래스 (TwoLayerNet)
경사하강법을 위한 클래스의 매서드 순서는 다음과 같다.
1. 가중치 초기화 -> Weight 그리고 bias 값을 랜덤하게 설정한다
2. 예측(추론) -> 인풋값인 x과 Weight 그리고 bias값을 이용하여 계산한다
3. 에러값을 계산 -> 교차 엔트로피 에러
4. 기울기를 계산
----------------------------------------------------------------------------------
# 경사하강법을 위한 클래스
import sys, os
sys.path.append(os.pardir)
from common.functions import * # 커몬펑션을 와일드카드 변수(*)를 이용하여 모든 변수를 전부 불러온다
from common.gradient import numerical_gradient # numerical gradient (경사하강법) 코드를 불러온다
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 1. 가중치 초기화
# Params 는 w와 b를 저장하는 딕셔너리 변수
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)
def predict(self, x):
# 2. 예측(추론)단계
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
def loss(self, x, t): # x : 입력 데이터, t : 정답 레이블
# 3. 에러률의 값 계산
y = self.predict(x)
return cross_entropy_error(y, t)
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
def numerical_gradient(self, x, t): # x : 입력 데이터, t : 정답 레이블
# 4. 기울기 계산
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):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}
batch_num = x.shape[0]
# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)
da1 = np.dot(dy, W2.T)
dz1 = sigmoid_grad(a1) * da1
grads['W1'] = np.dot(x.T, dz1)
grads['b1'] = np.sum(dz1, axis=0)
return grads
'컴퓨터 사이언스 > 신경망 기초' 카테고리의 다른 글
파이썬을 이용한 뉴럴 네트워크 실전 - 항등 함수와 소프트 맥스 함수 (순전파) (0) | 2022.01.06 |
---|---|
파이썬을 이용한 뉴럴 네트워크 실전 - 손실함수 (0) | 2022.01.06 |
뉴럴 네트워크(Neural Network)란? - 학습률, 가중치 업데이트 최종식 (0) | 2022.01.06 |
뉴럴 네트워크(Neural Network)란? - 데이터 준비하기 (0) | 2022.01.06 |
파이썬을 이용한 뉴럴 네트워크 실전 - 기초부터 3중신경망 구현 (순전파) (0) | 2022.01.06 |