티스토리 뷰
1. 경사법(경사 하강법)
기계학습 문제 대부분은 학습 단계에서 최적의 매개변수를 찾아냅니다. 신경망 역시 최적의 매개변수(가중치와 편향)을 학습 시에 찾아야 합니다.
여기에서 최적이란 손실 함수가 최솟값이 될 때의 매개변수 값입니다.
일반적인 문제의 손실 함수는 매우 복잡하죠 매개변수 공간이 광대하여 어디가 최솟값이 되는 곳인지를 짐작할 수 없습니다.
이런 상황에서 기울기를 잘 이용해 함수의 최솟값을 찾으려는 것이 경사법입니다.
여기서 주의할 점은 각 지점에서 함수의 값을 낮추는 방안을 제시하는 지표가 기울기라는 것입니다.
경사하강법은 현 위치에서 기울어진 방향으로 일정 거리만큼 이동합니다. 그런 다음 이동한 곳에서도 마찬가지로 기울기를 구하고, 또 기울어진 방향으로 나아가기를 반복합니다. 이렇게 해서 함수의 값을 점차 줄이는 것이 경사 하강법입니다.
인수 f는 최적화하려는 함수, init_x는 초기값, lr은 learning rate를 의미하는 학습률, step_num은 경사법에 따른 반복 횟수.
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f,x)
x -= lr * grad
return x
2. 신경망에서의 기울기
신경망 학습에서도 기울기를 구해야 하는데 여기서 말하는 기울기는 가중치 매개변수에 대한 손실 함수의 기울기입니다.
예를 들어 형상이 2 * 3, 가중치가 W, 손실 함수가 L인 신경망을 생각해보면 수식으로는 다음과 같습니다.
1행 1번째 원소인 W11을 조금 변경했을 때 손실 함수 L이 얼마나 변화하느냐를 나타내고 여기서 중요한 점은 편미분 한것이 W와 같다는 것입니다.
소스코드 구현
import sys, os
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
#from common.functions import softmax, cross_entropy_error
#from common.gradient import numerical_gradient
def softmax(x):
if x.ndim == 2:
x = x.T
x = x - np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x - np.max(x) # 오버플로 대책
return np.exp(x) / np.sum(np.exp(x))
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
# 훈련 데이터가 원-핫 벡터라면 정답 레이블의 인덱스로 반환
if t.size == y.size:
t = t.argmax(axis=1)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val # 값 복원
return grad
def f(W):
return net.loss(x, t)
class simpleNet:
def __init__(self):
self.W = np.random.randn(2,3) # 정규분포로 초기화
def predict(self, x):
return np.dot(x, self.W)
def loss(self, x, t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y, t)
return loss
net = simpleNet()
print(net.W)
#[[ 2.13924928 -1.53187348 1.3352016 ]
[-1.52693031 -0.43417503 -0.28699196]]
x = np.array([0.6, 0.9])
p = net.predict(x)
print(p)
#[-0.09068772 -1.30988162 0.54282819]
t = np.array([0, 0, 1])
print(net.loss(x, t))
#0.5232684141105788
dW = numerical_gradient(f, net.W)
print(dW)
#[[ 0.21924763 0.14356247 -0.36281009]
[0.32887144 0.2153437 -0.54421514]]
편미분 한 w11은 대략 0.2입니다. 이는 w11을 h만큼 늘리면 손실 함수의 값은 0.2h 만큼 증가한다는 의미이고 편미분한 w23은은 대략 -0.5이니
h만큼 늘리려면 손실 함수의 값은 0.5h만큼 감소하는 것입니다.
그래서 손실 함수를 줄인다는 관점에서는 w23은 양의 방향으로 갱신하고 w11은 음의 방향으로 갱신해야 합니다.
'딥러닝' 카테고리의 다른 글
딥러닝 - 06. 손실함수 (0) | 2019.03.14 |
---|---|
딥러닝 - 05. MNIST 데이터 셋 / 추론 배치 처리 (0) | 2019.03.07 |
딥러닝 - 04. 신경망 (3) 출력층 설계 (0) | 2019.02.21 |
딥러닝 - 04. 신경망 (2) 3층 신경망 구현 (0) | 2019.02.19 |
딥러닝 - 04. 신경망 (1) 활성화, 계단, 시그모이드 함수 (0) | 2019.02.19 |