ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [NLP]신경망 최적화 함수들 Optimizer: SGD, Momentum, AdaGrad, Adam
    ML/NLP 2020. 4. 20. 05:02

     

     

     

    신경망 학습의 핵심 개념인 가중치 매개변수의 최적값을 탐색하는 최적화 방법에 대해 포스팅 하겠다.

     

     

    신경망 학습의 목적은 손식 함수의 값을 낮추는 매개변수를 찾는 것이고, 이는 곧 매개변수의 최적값을 찾는 문제이며, 이러한 문제를 푸는 것을 최적화(optimization)이라 한다.

     

     

    특히 이번 포스팅에서는 최적화를 가능하게 하는 기법들 중 SGD, Momentum, AdaGrad, Adam에 대해 설명하도록 하겠다.

     

     

     

     

     

    확률적 경사 하강법(SGD)


     

    SGD는 다음과 같은 수식으로 쓸 수 있다.

     

    그림 1

    여기서 W는 갱신할 가중치 매개변수고 L/W는 W에 대한 손실 함수의 기울기이다.

     

    η은 학습률을 의미하는데, 실제로는 0.01이나 0.001과 같은 값을 미리 정해서 사용한다.

     

    수식에서 보듯 SGD는 기울어진 방향으로 일정 거리만 가겠다는 단순한 방법이다.

     

    이를 클래스로 구현해보자. 

     

     

     

     

    class SGD:
        def __init__(self, lr=0.01):
            self.lr = lr
            
        def update(self, params, grads):
            for key in params.keys():
                params[key] -= self.lr * grads[key]

    update 메서드의 인수인 params와 grads는 params['W1'], grads['W1'] 등과 같이 각각 가중치 매개변수와 기울기를 저장하고 있다.

     

    이처럼 최적화를 담당하는 클래스를 분리해 구현하면 기능을 모듈화하기 좋다.

     

     

    • SGD의 단점

    SGD는 단순하고 구현도 쉽지만, 문제에 따라서는 비효율적일 때가 있다.

     

    SGD의 단점을 알아보기 위해 다음 함수의 최솟값을 구하는 문제를 생각해보겠다.

     

     

    그림 2

     

    그림 3

     

    [그림 2] 함수의 기울기를 그려보면 [그림 3]처럼 된다. 

     

    이 기울기는 y축 방향은 크고 x축 방향은 작다는 것이 특징이다. (y축 방향은 가파른데 x축 방향은 완만하다)

     

    또 주의할 점으로는 [그림 2]가 최솟값이 되는 장소는 (x, y) = (0, 0)이지만 [그림 3]이 보여주는 기울기 대부분은 (0, 0) 방향을 가리키지 않는다는 것이다.

     

     

     

     

     

    이제 [그림 2] 함수에 SGD를 적용해보겠다.

     

    탐색을 시작하는 장소(초깃값)는 (x,y) = (-7.0, 2.0)으로 하겠다.

     

    그림 4

    SGD는 [그림 4]와 같이 심하게 굽이진 비효율적 움직임을 보여준다.

     

    즉, SGD의 단점은 비등방성(anisotropy) 함수(=방향에 따라 성질, 즉 여기서는 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적이라는 것이다.

     

    또한 SGD가 지그재그로 탐색하는 근본 원인기울어진 방향이 본래의 최솟값과 다른 방향을 가리켜서라는 점도 생각해야 한다.

     

     

    이러한 SGD의 단점을 개선해주는 모멘텀(momentum), AdaGrad, Adam이라는 세 방법을 소개할 것이다.

     

     

     

     

    모멘텀 momentum


    모멘텀 기법은 수식으로 다음과 같이 쓸 수 있다.

     

    그림 5

     

    W는 갱신할 가중치 매개변수, L/W는 W에 대한 손실 함수의 기울기, η는 학습률이다.

     

    v라는 변수가 새로 나오는데, 이는 물리에서 말하는 속도(velocity)에 해당한다.

     

    [그림 5]의 첫번째 수식은 기울기 방향으로 힘을 받아 물체가 가속된다는 물리 법칙을 나타낸다. 

     

    모멘텀의 이미지는 공이 그릇의 곡면(기울기)을 따라 바닥을 구르는 듯한 움직임을 보여줄 것이다.

     

    αv항은 물체가 아무런 힘도 받지 않을 때 서서히 하강시키는 역할도 한다. (α는 0.9등의 값으로 설정한다).

     

     

    다음은 모멘텀의 구현이다.

    class Momentum:
    
        """모멘텀 SGD"""
    
        def __init__(self, lr=0.01, momentum=0.9):
            self.lr = lr
            self.momentum = momentum
            self.v = None
            
        def update(self, params, grads):
            if self.v is None:
                self.v = {}
                for key, val in params.items():                                
                    self.v[key] = np.zeros_like(val)
                    
            for key in params.keys():
                self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
                params[key] += self.v[key]

     

    이제 모멘텀을 사용해서 [그림 2] 함수의 최적화 문제를 풀어보자. 결과는 [그림 6]과 같다.

     

     

    그림 6

     

    [그림 6]에서 보듯 모멘텀의 갱신 경로는 공이 그릇 바닥을 구르듯 움직이다.

     

    SGD와 비교했을 때 '지그재그 정도'가 덜한 것을 알 수 있다.

     

     

    x축으로는 힘이 작지만, 방향은 변하지 않아서 한 방향으로 일정하게 가속하게 되고

     

    y축으로는 힘은 크지만, 위아래로 번갈아 받아서 상충하여 y축 방향의 속도는 안정적이지 않게 되기 때문에

     

    전체적으로 SGD보다 x축 방향으로 빠르게 다가가 지그재그 움직임이 줄어들게 된다.

     

     

     

     

     

     

     

    AdaGrad


    신경망 학습에서는 학습률(수식에서는 η로 표기) 값이 중요하다. 

     

    이 값이 너무 작으면 학습 시간이 너무 길아지고, 반대로 너무 크면 발산하여 학습이 제대로 이뤄지지 않는다.

     

     

     

    이 학습률을 정하는 효과적 기술로 학습률 감소(learning rate decay)가 있다.

     

    이는 학습을 진행하면서 학습률을 점차 줄여가는 방법이다.

     

     

     

    AdaGrad는 개별 매개변수에 적응적으로 학습률을 조정하면서 학습을 진행한다.

     

    AdaGrad의 갱신 방법 수식은 다음과 같다.

     

     

    그림 7

    W는 갱신할 가중치 매개변수, L/W는 W에 대한 손실 함수의 기울기, η는 학습률이다.

     

    새로 h라는 변수가 등장하는데, h는 [그림 7]의 첫번째 수식에서 보듯 기존 기울기 값을 제곱하여 계속 더해준다.

     

    (⊙기호는 행렬의 원소별 곱셈을 의미한다)

     

     

     

    그리고 매개변수를 갱신할 때 1/√h을 곱해 학습률을 조정한다. 

     

    매개변수의 원소 중에서 많이 움직인 (크게 갱신된) 원소는 학습률이 낮아진다는 뜻인데, 다시 말해 학습률 감소가 매개변수의 원소마다 다르게 적용됨을 뜻한다.

     


    AdaGrad는 과거의 기울기를 제곱하여 계속 더해가기 때문에, 학습을 진행할수록 h의 값이 커져 갱신 강도가 약해진다.
    이를 개선한 기법으로는 RMSProp이 있고, 이 방법은 먼 과거의 기울기는 점차 잊고 새로운 기울기 정보를 크게 반영한다.

    이를 지수이동평균(Exponential Moving Average, EMA)이라 하여, 과거 기울기의 반영 규모를 기하급수적으로 감소시킨다.

     

    구현을 해보겠다.

    class AdaGrad:
    
        """AdaGrad"""
    
        def __init__(self, lr=0.01):
            self.lr = lr
            self.h = None
            
        def update(self, params, grads):
            if self.h is None:
                self.h = {}
                for key, val in params.items():
                    self.h[key] = np.zeros_like(val)
                
            for key in params.keys():
                self.h[key] += grads[key] * grads[key]
                params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

     

    여기에서 주의할 것은 마지막 줄에서 1e-7이라는 작은 값을 더하는 부분이다.

     

    이 작은 값은 self.h[key]에 0이 담겨 있어도 0으로 나누는 사태를 막아준다.

     

     

     

    이제 AdaGrad을 사용해서 [그림 2] 함수의 최적화 문제를 풀어보자. 결과는 [그림 8]과 같다.

     

    그림 8

     

    [그림 8]을 보면 최솟값을 향해 효율적으로 움직이는 것을 볼 수 있다.

     

    y축 방향은 기울기가 커서 처음에는 크게 움직이지만, 그 큰 움직임에 비례해 갱신 정도도 큰 폭으로 작아지도록 조정된다.

     

    그래서 y축 방향으로 갱신 정도가 빠르게 약해지고, 지그재그 움직임이 줄어든다.

     

     

     

     

     

     

     

    Adam


     

    모멘텀과 AdaGrad을 융합해보자는 생각을 기반으로 하여 출발한 기법이 Adam이다.

     

    두 방법의 이점을 조합했다면 매개변수 공간을 효율적으로 탐색해줄 것을 기대할 수 있다.

     

    또한, 하이퍼파라미터의 '편향 보정'이 진행된다는 점도 Adam의 특징이다.

     

    여기서는 더 깊게 파고들지 않겠다.

     

     

     

     

    그럼 Adam을 사용해서 [그림 2] 함수의 최적화 문제를 풀어보자. 결과는 [그림 9]와 같다.

     

    그림 9

     

    [그림 9]와 같이 Adam 갱신 과정도 그릇 바닥을 구르듯 움직인다. 

     

    모멘텀과 비슷한 패턴인데, 모멘텀 때보다 공의 좌우 흔들림이 적다.

     

    이는 학습의 갱신 강도를 적응적으로 조정해서 얻는 혜택이다.

     

     

     

     

     

     

    어느 갱신 방법을 이용할 것인가?


    그림 10

     

    앞서 본 4개의 갱신 방법을 비교해보자.

     

    [그림 10]만 보면 AdaGrad가 가장 나은 것 같은데, 사실 그 결과는 풀어야 할 문제가 무엇이냐에 따라 달라지므로 주의해야 한다.

     

    또한 학습률과 같은 하이퍼파라미터를 어떻게 설정하느냐에 따라서도 결과가 바뀐다.

     

     

    문제의 상황을 고려해 여러 가지로 시도해봐야 한다.

     

     

     

     

     

     

    MNIST 데이터셋으로 본 갱신 방법 비교


    손글씨 숫자 인식을 대상으로 지금까지 설명한 네 기법을 비교하자. 이는 [그림 11]과 같다.

     

    그림 11

    이 실험은 각 층이 100개의 뉴런으로 구성된 5층 신경망에서 ReLU를 활성화 함수로 사용해 측정했다.

     

    [그림 11]의 결과를 보면 SGD의 학습 진도가 가장 느리다.

     

    나머지 세 기법의 진도는 비슷하지만, AdaGrad가 조금 더 빠른 것 같다.

     

    이 실험에서 주의할 점은 하이퍼파라미터인 학습률과 신경망의 구조(층 깊이 등)에 따라 결과가 달라진다는 것이다.

     

     

    다만 일반적으로 SGD보다 다른 세 기법이 빠르게 학습하고, 때로는 최종 정확도도 높게 나타난다.

     

     

     

     

     

     

     

    밑바닥부터 시작하는 딥러닝 1
    국내도서
    저자 : 사이토 고키 / 이복연(개앞맵시)역
    출판 : 한빛미디어 2017.01.03
    상세보기

     

     

    댓글

dokylee's Tech Blog