오뚝이개발자

[CH6] 학습 관련 기술들 본문

AI/밑바닥딥러닝1

[CH6] 학습 관련 기술들

땅어 2020. 6. 21. 21:24
728x90
300x250

매개변수 갱신


신경망 학습의 목적은 손실함수의 값을 최대한 낮추는 매개변수를 찾는 것이었다. 이는 바로 최적의 매개변수를 찾는 최적화 문제이다. 앞 장에서 다루었던 매개변수의 기울기(미분)를 이용해 갱신하는 방식을 확률적 경사 하강법(SGD)라 한다. 최적화 방법에는 SGD말고도 모멘텀, AdaGrad, Adam 이라는 다른 방법들이 존재한다. (아래의 코드는 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]

GD와 SGD의 차이에 대해 조금 더 정확히 말하자면, SGD는 조금만 훑어보고(mini-batch) 빠르게 진행하는 것이다. '확률적'이란 말도 여기서 기인하는데, mini-batch를 구성하는 예가 무작위로 뽑혔다는 것을 의미한다.

GD와 SGD(출처: seamless tistory)

그러다 보니 둘은 최적값을 찾아가는 양상도 아래의 사진처럼 다르게 나타난다.

GD와 SGD가 최적값을 찾아가는 과정1(출처:seamless tistory)
GD와 SGD가 최적값을 찾아가는 과정2(출처: http://engmrk.com/mini-batch-gd/)

확실히 SGD는 GD가 모든 데이터를 대상으로 연산을 해 정확하긴 하나 매우 느린 단점을 보완하기 위해 탄생한 것이다. SGD는 GD와 달리 조금 해매지만 빠르게 진행할 수 있다.

 

SGD의 단점


하지만 이런 SGD에도 단점이 있다. 먼저, 미니배치를 통해서 학습을 시키므로 최적값으로 나아가기 위한 방향이 뒤죽박죽이다. 

똑같이 SGD를 사용했지만 방향 제각각(출처:seamless tistory)

뿐만 아니라 global optimal을 찾지 못할 가능성이 있다.

위와 같은 함수를 생각해보자. 이 함수의 그래프는 다음과 같다.

f(x,y)의 그래프(출처:밑바닥부터 시작하는 딥러닝)

이 함수의 그래프의 기울기를 화살표로 나타내보면 다음과 같다.

f(x,y)의 기울기(출처:밑바닥부터 시작하는 딥러닝)

f(x,y)는 (x,y)=(0,0)에서 최솟값이지만 기울기를 보면 대부분 (0,0)방향을 가리키지 않는다는 걸 알 수 있다. 정리하자면 SGD의 단점은 다음과 같다.

  • 미니배치를 통해 학습을 시키므로 최적의 값으로 나아가기 위한 방향 설정이 뒤죽박죽이다.
  • global optimal을 찾지 못할 가능성이 있다.

이런 단점을 개선해주는 모멘텀, AdaGrda, Adam과 같은 다른 최적화 방법들을 살펴보자. 참고로 아래 사진은 SGD를 사용해 해 f(x,y)의 최솟값을 찾는 것을 나타낸 것이다.

SGD에 의한 최적화 갱신 경로(출처:밑바닥부터 시작하는 딥러닝)

 

모멘텀


모멘텀은 운동량으로 실제 수식도 이러한 것을 반영한다. 마치 공이 굴곡진 경사면을 따라 내려가 최저점에 도달하는 것에 착안한 것이다.(아래 수식의 화살표는 우변의 값으로 좌변의 값을 갱신한다는 뜻) 실제로 모멘텀은 이러한 물리법칙에 착안하여 경사하강법에 가속을 부여하여 더 빠르게 최적의 값에 도달하도록 하는 최적화 알고리즘이다.

모멘텀 이미지(출처:밑바닥부터 시작하는 딥러닝)

class Momentum:
    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]
            

모멘텀에 의한 최적화 갱신 경로(출처:밑바닥부터 시작하는 딥러닝)

최적화 갱신 경로를 보면 SGD에 비해 '지그재그 정도'가 덜한 것을 알 수 있다. 모멘텀은 방향별 평균을 계산하여 가속을 부여하는데 세로방향의 경우 위아래로 왔다갔다하여 평균이 0에 가깝고 가로방향의 경우 오른쪽으로 계속해서 이동하고자 하므로 더 가속을 부여해준다. 따라서 GD를 사용하는 방법에 비해 지그재그 정도가 덜한 것이다. 따라서 모멘텀을 사용하면 경사하강법을 더 빠르게 진행시킬 수 있다.

 

AdaGrad(RMSProp 이라고도 한다)


신경망 학습에선 학습률이 중요한데 이를 정하는 효과적 기술로 학습률 감소(learning rate decay)가 있다. 처음엔 크게 학습하다가 조금씩 작게 학습한다는 것이다. 학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 전체의 학습률 값을 낮추는 것이지만, 이를 더욱 발전시킨 것이 AdaGrad다. AdaGrad는 각각의 매개변수에 맞춤형 값을 만들어준다.

 

( ⨀기호는 행렬의 원소별 곱셈)

class 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)

AdaGrad에 의한 최적화 갱신 경로(출처:밑바닥부터 시작하는 딥러닝)

갱신 경로를 보면 SGD나 모멘텀에 비해 훨씬 효율적으로 움직이는 걸 알 수 있다.

 

Adam


모멘텀과 AdaGrad를 융합한 방식이 바로 Adam이다. 여기선 이에 대해 깊게 설명하진 않겠다. 하지만 요즘 대부분의 사람들이 Adam optimizer에 만족하면서 사용하고 있다.

Adam에 의한 최적화 갱신 경로(출처:밑바닥부터 시작하는 딥러닝)

 

최적화 기법으로 무엇을 이용하는 것이 좋을까?


이에 대한 답은 없다. 모든 문제에서 뛰어난 최적화 기법이란 건 아직까지 없다. 다만 각자의 장단이 있어 잘푸는 문제와 서툰 문제가 있을 뿐이다.

 

가중치의 초깃값


가중치의 초깃값을 잘 설정하는 것은 학습의 성패를 가르기도 하는 중요한 일이다. 그렇다면 가중치의 초깃값을 모두 0으로 하면 어떨까? 답은 "안된다"이다.

정확히는 모든 가중치를 동일한 값으로 설정해서는 안된다. 그 이유는 역전파 시에 결국 똑같이 갱신되므로 가중치를 여러 개 갖는 의미가 사라진다. 그러니 중요한 것은 (가중치의 대칭적인 구조를 무너뜨리려면) 초깃값을 무작위로 설정해야 한다는 것이다.

 

은닉층의 활성화값 분포


가중치를 표준편차가 1인 정규분포로 초기화할 때 각 층의 활성화값 분포(출처:밑바닥딥러닝)

5개의 층으로 이루어져 있고, 활성화 함수로 시그모이드를 사용하는 어떤 신경망의 각 층의 활성화값들 분포가 위와 같다고 해보자. 각 층의 활성화값들은 0과 1에 치우쳐 분포되어 있다. 시그모이드 함수의 경우 그 출력이 0이나 1에 가까워지면 미분값은 0에 가까워진다. 그래서 이처럼 데이터가 0과 1에 치우쳐 분포하게 되면 연전파의 기울기 값이 점차 작아지다 사라진다. 이것이 기울기 소실(gradient vanishing)이다. 

가중치를 표준편차가 0.01인 정규분포로 초기화할 때 각 층의 활성화값 분포(출처:밑바닥딥러닝)

표준편차를 0.01로 바꾸었더니 위와 같은 분포를 얻었다. 앞의 예처럼 0과 1에 치우치진 않아지만 0.5부근에 집중되어 있다. 이 경우 기울기 소실 문제는 없지만, 표현력 관점에서는 문제가 있다. 뉴련 100개가 다 똑같은 값을 출력한다면 뉴련 1개짜리와 다를 게 없는 것이다. 그래서 활성화값들이 치우치면 표현력을 제한하게 된다.

결론적으로 신경망에서 각 층의 활성화값은 적당히 골고루 분포되어야 한다.

이를 위해 가중치 초깃값을 잘 설정토록 도와주는 방법에는 아래의 두 가지가 대표적이다.

  • Xavier 초깃값(사비에르 초깃값)
  • He 초깃값

 

Xavier 초깃값


앞 계층의 노드가 n개라면 표준편차가 1/sqrt(n)인 분포를 사용

Xavier 초깃값(출처:밑바닥딥러닝)

은닉층의 활성화값 분포에서와 동일한 문제에 Xavier 초깃값을 적용시킨 결과 아래와 같이 넓게 분포된다.

Xavier 초깃값 적용시킬 때의 각 층의 활성화값 분포(출처:밑바닥딥러닝)

 

cf) tanh 함수도 시그모이드와 같은 S자 곡선 함수이다. 다만 tanh은 원점대칭, 시그모이드는 (0,0,5)에서 대칭이다. 사실 활성화 함수용으로는 원점대칭 함수가 바람직하다고 알려져있다. 위의 그림에서 우측으로 갈수록 일그러지는 것은 tanh를 쓰면 개선된다.

 

He 초깃값


Xavier 초깃값은 시그모이드나 tanh를 활성화 함수로 사용할 때 적합하다. 반면, ReLU를 사용할 때는 이에 특화된 He 초깃값을 사용하는 것이 좋다. He 초깃값은 앞 계층의 노드가 n개일 때, 표준편차가 sqrt(2/n)인 정규분포를 사용한다.(Xavier 초깃값이 sqrt(1/n)이었는데, ReLU는 음의 영역이 0이라 더 넓게 분포시키기 위해 2배의 계수가 필요하다고 생각하면 직관적으로 이해가 간다.)

활성화 함수로 ReLU를 사용한 경우 가중치 초깃값에 따른 분포(출처:밑바닥딥러닝)

 

배치 정규화(Batch Normalization)


가중치의 초깃값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습이 잘 된다는 점을 배웠다. 그럼 각 층이 활성화값을 적당히 퍼뜨리도록 '강제'하면 어떨까? 이러한 아이디어에서 출발한 것이 배치 정규화이다.

이는 배치 정규화 계층(Batch Norm)을 신경망에 삽입해 데이터 분포가 덜 치우치도록 한다.

배치 정규화를 사용한 신경망(출처:밑바닥딥러닝)

구체적으로는 데이터 분포의 평균이 0, 분산이 1이 되도록 정규화 한다. 수식으로는 다음과 같다.

배치정규화가 학습속도를 높인다.(출처:밑바닥딥러닝)

배치정규화의 장점

  • 학습 속도 향상
  • 초깃값에 크게 의존하지 않는다.
  • 오버피팅 억제

 

오버피팅


신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태를 말한다. 오버피팅은 주로 다음의 두 경우에 일어난다.

  • 매개변수가 많고 표현력이 높은 모델
  • 훈련 데이터가 적음

훈련데이터와 시험데이터의 에폭별 정확도 추이(출처:밑바닥딥러닝)

그래프를 보면 훈련 데이터에 대해서는 100에폭을 지나는 때부터 정확도가 거의 100%이다. 반면 시험 데이터에 대해서는 정확도가 떨어지고 제대로 대응하지 못하고 있다. 이러한 오버피팅을 억제하기 위해선 다음과 같은 방법들을 사용한다.

  • 가중치 감소
  • 드롭아웃

 

가중치 감소(weight decay)=L2 regularization


학습 과정에서 큰 가중치에 대해서는 그에 상응하는 큰 페널티를 부과해 오버피팅을 억제하는 방법이다. 원래 오버피팅은 가중치 매개변수의 값이 커서 발생하는 경우가 많기 때문이다. 

일반적으로 L2 법칙에 따른 가중치 감소는 1/2 * λ * W^2가 되고 이를 가중치 각각의 손실함수에 더해준다. 이를 적용한 결과 아래 그래프처럼 훈련데이터와 시험데이터 간의 차이가 줄고, 앞에서와 달리 훈련 데이터에 대한 정확도가 100%에 도달하지 못한 점도 주목해야 한다.

가중치 감소를 이용한 정확도 추이(출처:밑바닥딥러닝)

 

드롭아웃(dropout)


가중치 감소는 간단히 구현할 수 있으나 모델이 복잡해지면 가중치 감소만으로는 대응이 어렵다. 이 때 흔히 드롭아웃을 사용한다. 드롭아웃이란 훈련 때 은닉층의 뉴런을 무작위로 골라 삭제해 이들 뉴런이 신호를 전달하지 않게 하는 것이다. 이를 통해 의도적으로 과적합을 방지한다.

드롭아웃의 개념(출처:밑바닥딥러닝)
왼쪽:드롭아웃없이, 오른쪽:드롭아웃적용한 결과(출처:밑바닥딥러닝)

기계학습에선 앙상블 학습(ensemble learning)을 애용한다. 이는 개별적으로 학습시킨 여러 모델의 출력을 평균내어 추론하는 방식이다. 드롭아웃의 원리도 이와 유사하다.

 

적절한 하이퍼파라미터 값 찾기(Hyperparameter optimization)


적절한 하이퍼파라미터 값을 찾는 것도 중요하다. 여기엔 각 층의 뉴련 수, 배치 크기, 학습률 등이 있다. 이러한 값들을 효율적으로 탐색하는 방법을 알아보자.

주의할 점은 하이퍼 파라미터의 성능을 평가할 때는 시험 데이터를 사용해선 안된다는 것이다. 시험 데이터를 사용하여 하이퍼파라미터를 조정하면 하이퍼파라미터의 값이 시험 데이터에 오버피팅 되기 때문이다. 따라서 하이퍼파라미터 전용 확인 데이터가 필요한데 이를 검증 데이터(validation data)라 한다. 일반적으로 훈련 데이터 중 20%를 분리해 사용한다.

하이퍼 파라미터를 최적화하는 과정은 다음과 같다.

0단계 : 하이퍼파라미터 값의 범위를 설정

1단계 : 설정된 범위에서 하이퍼파라미터의 값을 무작위로 추출

2단계 : 1단계에서 샘플링한 하이퍼파라미터 값을 사용해 학습하고, 검증 데이터로 정확도 평가(단, 에폭은 작게 설정해 1회 평가에 걸리는 시간을 단축)

3단계 : 1단계와 2단계를 특정 횟수(100회 등) 반복하며, 그 정확도의 결과를 보고 하이퍼파라미터의 범위를 좁힌다.

위의 과정을 반복해 좁아진 범위에서 값을 골라내는 것이다.

 

 

 

 

728x90
300x250

'AI > 밑바닥딥러닝1' 카테고리의 다른 글

[CH8] 딥러닝  (0) 2020.06.23
[CH7] 합성곱 신경망(CNN)  (0) 2020.06.23
[CH5] 오차역전파법  (0) 2020.06.20
[CH4] 신경망 학습  (0) 2020.06.19
[CH3] 신경망  (0) 2020.06.13
Comments