Deep learning/모델 구현

5. 신경망 학습 (2) - 기울기

jwjwvison 2021. 4. 20. 00:05

이 포스팅은 밑바닥부터 시작하는 딥러닝을 공부하고 정리한것 입니다.


def function_2(x):
    return x[0]**2 + x[1]**2

 

기울기

 모든 변수의 편미분을 벡터로 정리한 것을 기울기(gradient)라고 한다.

def numerical_gradient(f,x):
    h=1e-4  #0.0001
    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

 그런데 이 기울기라는 게 의미하는 것이 무엇일까? 기울기 그림은 다음 그림처럼 방향을 가진 벡터(화살표)로 그려진다. 이 그림을 보면 기울기는 함수의 '가장 낮은 장소(최솟값)'를 가리키는 것 같다.

 위 그림에서 기울기는 가장 낮은 장소를 가리키지만, 실제는 반드시 그렇다고는 할 수 없다. 사실 기울기는 각 지점에서 낮아지는 방향을 가리킨다. 더 정확히 말하자면 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 크게 줄이는 방향이다. 

 

 

 1. 경사법(경사 하강법)

  기계학습 문제 대부분은 학습 단계에서 손실 함수가 최솟값이 될 때의 최적의 매개변수를 찾아낸다. 이런 어려운 상황 에서 기울기를 잘 이용해 함수의 최솟값(또는 가능한 한 작은 값)을 찾으려는 것이 경사법이다. 여기에서 주의할 점은 각 지점에서 함수의 값을 낮추는 방안을 제시하는 지표가 기울기라는 것이다. 그러나 기울기가 가리키는 곳에 정말 함수의 최솟값이 있는지, 즉 그쪽이 정말로 나아갈 방향인지는 보장할 수 없다. 실제로 복잡한 함수에서는 기울기가 가리키는 방향에 최솟값이 없는 경우가 대부분이다.

 기울어진 방향이 꼭 최솟값을 가리키는 것은 아니나, 그 방향으로 가야 함수의 값을 줄일 수 있다. 

 

 경사법은 현 위치에서 기울어진 방향으로 일정 거리만큼 이동한다. 그런 다음 이동한 곳에서도 마찬가지로 기울기를 구하고, 또 그 기울어진 방향으로 나아가기를 반복한다. 이렇게 해서 함수의 값을 점차 줄이는 것이 경사법(gradient method)이다.

 

 경사법을 수식으로 나타내보면 다음과 같다.

 

 기호 n(eta, 에타)는 갱신하는 양을 나타낸다. 이를 신경망 학습에서는 학습률(learning rate)라고 한다. 한 번의 학습으로 얼마만큼 학습해야 할지, 즉 매개변수 값을 얼마나 갱신하느냐를 정하는 것이 학습률이다.

 학습률 값은 0.01이나 0.001 등 미리 특정 값으로 정해두어야 하는데 일반적으로 이 값이 너무 크거나 작으면 좋은 장소를 찾아갈 수 없다.

 

 경사 하강법은 다음과 같이 간단하게 구현할 수 있다.

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

 f는 최적화 하려는 함수, init_x는 초깃값, lr는 learning rate를 의미하는 학습률, step_num은 경사법에 따른 반복 횟수를 뜻한다.

 이 함수를 사용하면 함수의 극솟값을 구할 수 있고 잘하면 최솟값을 구할 수도 있다.

 최종결과는 (0,0)에 가까운 결과가 나왔다. 경사법을 사용한 이 갱신 과정을 그림으로 나타내면 다음처럼 된다.

 

 

 2. 신경망에서의 기울기

 여기서 말하는 기울기는 가중치 매개변수에 대한 손실 함수의 기울기이다.

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

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)

x=np.array([0.6,0.9])
p=net.predict(x)
print(p)

print(np.argmax(p))

t=np.array([0,0,1])
net.loss(x,t)

 이어서 기울기를 구해보자. 지금까지처럼 numerical_gradient(f,x)를 써서 구하면 된다.

def f(W):
    return net.loss(x,t)
    
dw=numerical_gradient(f,net.W)
print(dw)

 numerical_gradient(f,x)의 인수 f는 함수, x는 함수 f의 인수이다. 그래서 여기에서는 net.W를 인수로 받아 손실 함수를 계산하는 새로운 함수 f를 정의했다.

 

 신경망의 기울기를 구한 다음에는 경사법에 따라 가중치 매개변수를 갱신하기만 하면 된다.