티스토리 뷰

매개변수 갱신

SGD의 단점 

SGD는 단순하고 구현도 쉽지만, 비등방성 함수(방향에 따라 성질, 즉 여기에서는 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적이라는 것입니다. 이럴 때 무작정 기울어진 방향으로 진행하는 단순한 방식보다 더 영리한 방법이 필요하고. 지그재그로 탐색하는 근본 원인은 기울어진 방향이 본래의 최솟값과 다른 방향을 가리켜서라는 점도 생각해 볼 수 있습니다.

이제 SGD의 단점을 개선해주는 모멘텀, AdaGrad, Adam에 대해 설명해 보겠습니다.

모멘텀

모멘텀은 운동량을 뜻하는 단어로, 물리와 관련이 있습니다. 모멘텀 수식은 다음과 같습니다.

여기서 v는 물리에서 속도에 해당하는 부분입니다. 기울기 방향으로 힘을 받아 물체가 가속된다는 물리 법칙을 나타냅니다. av항은 물체가 아무런 힘을 받지 않을 때 서서히 하강시키는 역할을 합니다. 물리에서는 지면 마찰이나 공기 저항에 해당됩니다. 그리고 속도가 크게 나올수록 기울기가 크게 업데이트 되어 SGD의 단점을 개선할 수 있습니다.


아래는 모멘텀을 구현한 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
 
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]
cs
인스턴스 변수 v가 물체의 속도입니다. v는 초기화 때는 아무 값도 담지 않고, 대신 update()가 처음 호출될 때 매개변수와 같은 구조의 데이터를 딕셔너리 변수로 저장합니다.


오른쪽이 SGD이고, 왼쪽이 모멘텀을 이용했을 때 갱신하는 모습입니다. 모멘텀을 사용할 때 지그재그 정도가 덜할 것을 볼 수 있습니다. 이는 x축의 힘은 아주 작지만 방향은 변하지 않아서 한 방향으로 일정하게 가속하기 때문입니다. 거꾸로 y축의 힘은 크지만 위아래로 번갈아 받아서 상충하여 y축 방향의 속도는 안정적이지 않습니다.


AdaGrad

신경망에서 학습률을 정해주는 것은 매우 중요합니다. 이 학습률을 정하는 효과적 기술로 학습률 감소가 있습니다. 이는 학습을 진행하면서 학습률을 점차 줄여가는 것입니다. 처음에는 크게 학습하다가 조금씩 작게 학습한다는 얘기로, 실제 신경망 학습에서 자주 쓰입니다.

학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 전체의 학습률 값을 일괄적으로 낮추는 방법이 있습니다. 이를 더욱 발전시킨 것이 AdaGrad입니다. 이는 각각의 매개변수에 알맞는 값을 만들어줍니다.

AdaGrad는 개별 매개변수에 적응적으로 학습률을 조정하면서 학습을 진행합니다. 아래는 수식을 나타낸 것입니다.

여기서 h는 기존 기울기 값을 제곱하여 게속 더해줍니다. 그리고 매개변수를 갱신할 때 루트 h분의 1을 곱해 학습률을 조정합니다. 매개변수의 원소 중에서 많이 움직인(크게 갱신된) 원소는 학습률이 낮아진다는 뜻인데, 이는 학습률 갑소가 매개변수의 원소마다 다르게 적용됨을 뜻합니다.


AdaGrad를 코드로 구현해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
 
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)
cs

여기에서 주의할 점은 마지막 줄에서 1e-7라는 작은 값을 더하는 부분입니다. 이 작은 값은 self.h[key]에 0이 담겨 있다 해도 0으로 나누는 사태를 막아줍니다. 대부분의 딥러닝 프레임워크에서는 이 값도 인수로 정할 수 있습니다.

그래프를 보면 최솟값을 향해 효율적으로 움직이는 것을 알 수 있습니다. y축 방향은 기울기가 커서 처음에는 크게 움직이지만, 그 큰 움직임에 비레해 갱신 정도도 큰 폭으로 작아지도록 조정합니다. 그래서 y축 방향으로 갱신 강도가 빠르게 약해지고, 지그재그 움직임이 줄어듭니다.


여기서 AdaGrad의 문제는 과거의 기울기를 제곱하여 계속 더해가기 때문에 학습을 진행할수록 갱신 강도가 약해집니다.  실제로 무한히 계속 학습한다면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않게 됩니다. 이 문제를 개선한 기법으로 RMSProp라는 방법이 있습니다. 이것은 과거의 모든 기울기를 균일하게 더해가는 것이 아니라, 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영합니다. 이를 지수이동평균이라 하여, 과거 기울기의 반영 규모를 기하급수적으로 감수시킵니다.


Adam

Adam은 모멘텀과 AdaGrad의 이점만 합친 기법입니다. Adamd은 하이퍼파라미터의 '편향 보정'이 진행된다는 점도 Adam의 특징입니다. 


Adam의 갱신 과정도 그릇 바닥을 구르듯 움직입니다. 모멘텀과 다른 점은 좌우 흔들림이 적습니다. 이는 학습의 갱신 강도를 적응적으로 조정해서 얻는 혜택입니다.


이제 여기서 드는 생각은 SGD, AdaGrad, 모멘텀은 옛날 기법이고, Adam을 애용하면 된다고 생각할지 모르지만 사실 어떤 기법이 무조건 좋다는 것은 없습니다. 각자 장단점이 다르기 때문이죠.  그래서 상황에 알맞게 적절한 학습법을 고르는 것도 능력입니다.(학습 속도는 AdaGrad, Adam, Momentom, SGD

순서대로 빠르다)



'AI > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글

오버피팅(가중치 감소, 드롭아웃)  (0) 2018.09.30
가중치 초기화, 배치 정규화  (0) 2018.09.30
오차역전파(2)  (0) 2018.09.18
오차역전파법(1)  (0) 2018.09.17
신경망 학습  (0) 2018.09.13
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함