ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3. 신경망 학습(4)
    AI 모델(딥러닝 기초)/3. 신경망 학습 2023. 1. 9. 14:47
    728x90

    ※ 기울기(Gradient)

    - 모든 변수의 편미분을 벡터로 정리한 것을 의미함

    import numpy as np
    import matplotlib.pylab as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    def _numerical_gradient_no_batch(f, x):
        h = 1e-4 # 0.0001
        grad = np.zeros_like(x) # x와 형상이 같은 배열을 생성
        
        for idx in range(x.size):
            tmp_val = x[idx]
            
            # f(x+h) 계산
            x[idx] = float(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

     

    편미분에 대한 간단한 gradient 자체를 구현해 보았다.

     

    1. 기울기는 함수의 가장 낮은 장소(최솟값)을 가리킨다.

    2. 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 크게 줄이는 방향이다. 

     

    책에서는 기울기에 대한 정의를 이러한 두 가지 말로 표현한다.이 말이 대체 무슨 말인지 잘 이해가 안 될 수도 있다. 이 말의 뜻을 이해해 보기 위해 그림을 그려보자.

     

    import numpy as np
    import matplotlib.pylab as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    def _numerical_gradient_no_batch(f, x):
        h = 1e-4 # 0.0001
        grad = np.zeros_like(x) # x와 형상이 같은 배열을 생성
        
        for idx in range(x.size):
            tmp_val = x[idx]
            
            # f(x+h) 계산
            x[idx] = float(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
        
    def numerical_gradient(f, X):
        if X.ndim == 1:
            return _numerical_gradient_no_batch(f, X)
        else:
            grad = np.zeros_like(X)
            
            for idx, x in enumerate(X):
                grad[idx] = _numerical_gradient_no_batch(f, x)
            
            return grad
            
    def function_2(x):
        if x.ndim == 1:
            return np.sum(x**2)
        else:
            return np.sum(x**2, axis=1)
        
    def tangent_line(f, x):
        d = numerical_gradient(f, x)
        print(d)
        y = f(x) - d*x
        return lambda t: d*t + y
        
    if __name__ == '__main__':
        x0 = np.arange(-2, 2.5, 0.25)
        x1 = np.arange(-2, 2.5, 0.25)
        X, Y = np.meshgrid(x0, x1)
        
        X = X.flatten()
        Y = Y.flatten()
        
        grad = numerical_gradient(function_2, np.array([X, Y]) )
        
        plt.figure()
        plt.quiver(X, Y, -grad[0], -grad[1],  angles="xy",color="#666666")#,headwidth=10,scale=40,color="#444444")
        plt.xlim([-2, 2])
        plt.ylim([-2, 2])
        plt.xlabel('x0')
        plt.ylabel('x1')
        plt.grid()
        plt.legend()
        plt.draw()
        plt.show()

     

    이 코드를 통해서 그림을 그려볼 수 있다.

     

     

     

    이러한 형태의 그림이 그려진다. 편미분을 미분해서 3D로 표현해보면 안장점이 생기면서 0.0 부근에서 가라앉는 것을 확인할 수 있다. 그와 연계해서 살펴보면 기울기 화살표가 모두 0.0으로 향하는 것이 아래쪽을 향하고 있다는 것을 파악할 수 있을 것이다. 이를 통해 Gradient 기울기 값은 최솟값을 향하고 있다는 말 뜻을 정확히 이해해 볼 수 있다.

     

     

     

    ※ 경사법(경사 하강법)

    위에서 살펴본 Gradient를 활용해서 함수의 최솟값을 찾는 방식을 살펴보자. 최적의 매개변수를 찾고자 하는 것이 신경망의 목표이지만 매우 복잡한 손실 함수와 광대한 매개변수 공간을 통해 최솟값 지점을 한 눈에 찾기란 거의 불가능하다. 이때 우리는 Gradient를 활용한 경사 하강법을 사용한다.

     

    하지만, 기울기가 모든 것을 결정짓는 것은 아니다. 기울기는 단순한 지표일 뿐이다. 기울어진 방향이 꼭 최솟값을 가리키는 것은 아닐 수 있기 때문이다. 하지만, 그 방향으로 나아갔을 때 함수의 값을 줄일 수 있다는 것은 팩트이다. 기울기 정보를 단서로 나아갈 방향 정도는 특정 지을 수 있는 것이다.

     

    경사법은 현재 위치에서 기울어진 방향으로 일정 거리만큼씩 이동하고 또 기울기를 구하고, 또 기울어진 방향으로 일정 거리만큼 나아가는 것을 반복하여 함수의 값을 점차 줄이는 방식이다. 경사 하강법, 경사 상승법 두 가지의 방식이 있지만 딥러닝에서는 최솟값을 찾는 경사 하강법을 주로 택한다.

     

    경사법 자체를 수식으로 표현해 보았다. 이 수식에서는 n과 비슷한 모양의 에타(학습률)이 등장한다. 학습률은 얼마만큼 학습해야 할지, 매개변수 값을 얼마나 갱신할지를 정하는 것이다. 

     

     

    ■ 적절한 학습률

     

    학습률은 너무 크지도, 너무 작지도 않은 적절한 값을 찾을 필요가 있다.

    init_x = np.array([-3.0, 4.0])    
    
    lr = 10.0
    step_num = 100
    x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num)
    
    print(x)
    [-2.58983747e+13 -1.29524862e+12]

    학습률이 너무 큰 예시이다. 값 자체가 모두 발산해버리는 것을 확인할 수 있다.

     

    init_x = np.array([-3.0, 4.0])    
    
    lr = 1e-10
    step_num = 100
    x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num)
    
    print(x)
    [-2.99999994  3.99999992]

     

    학습률이 너무 작은 예시이다. 값이 거의 갱신되지 않은 채 그냥 끝나버리는 것을 파악할 수 있다.

     

    학습률과 같은 하이퍼파라미터는 신경망의 가중치 매개변수처럼 자동으로 획득되지 못해서 사람이 직접 설정해야 한다. 여러가지 값들 중 반복적인 경험을 통해 가장 잘 학습하는 학습률 매개변수 값을 찾는 것이 성능을 높이는 중요한 요인이 된다.

     

    마지막으로 적절한 학습률을 가지고 경사하강법의 과정을 보여주는 그림을 구현해 보자.

     

    import numpy as np
    import matplotlib.pylab as plt
    
    def gradient_descent(f, init_x, lr=0.01, step_num=100):
        x = init_x
        x_history = []
    
        for i in range(step_num):
            x_history.append( x.copy() )
    
            grad = _numerical_gradient_no_batch(f, x)
            x -= lr * grad
    
        return x, np.array(x_history)
    
    
    def function_2(x):
        return x[0]**2 + x[1]**2
    
    init_x = np.array([-3.0, 4.0])    
    
    lr = 0.1
    step_num = 100
    x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num)
    
    print(x)
    
    plt.plot( [-5, 5], [0,0], '--b')
    plt.plot( [0,0], [-5, 5], '--b')
    plt.plot(x_history[:,0], x_history[:,1], 'o')
    
    plt.xlim(-3.5, 3.5)
    plt.ylim(-4.5, 4.5)
    plt.xlabel("X0")
    plt.ylabel("X1")
    plt.show()
    [-6.11110793e-10  8.14814391e-10]
    

     

    기존에 구현했던 코드의 연장선으로 0.1의 학습률과 100번의 반복횟수를 가진 function_2 함수로 얻어지는 x값과 경사하강법으로 어떻게 0에 가까운 수가 나올 수 있었는지를 그림으로 나타내어 보았다

     

     

     

    728x90

    'AI 모델(딥러닝 기초) > 3. 신경망 학습' 카테고리의 다른 글

    3. 신경망 학습(6)  (0) 2023.01.15
    3. 신경망 학습(5)  (0) 2023.01.10
    3. 신경망 학습(3)  (0) 2023.01.07
    3. 신경망 학습(2)  (0) 2023.01.07
    3. 신경망 학습(1)  (2) 2023.01.05
Designed by Tistory.