ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [NLP] 신경망 학습에서 증말 중요한 가중치의 초기값 정하기↗
    ML/NLP 2020. 4. 21. 04:17

     

     

     

     

     

    이번 포스팅에서는 신경망 학습에서 특히 중요한 가중치의 초깃값에 대해 설명하겠다.

     

     

     

     

    초깃값을 0으로 하면?


    오버피팅을 억제해 범용 성능을 높이는 테크닉인 가중치 감소(weight decay) 기법을 소개하겠다.

     

    가중치 감소는 간단히 말하자면 가중치 매개변수의 값이 작아지도록 학습하는 방법이다.

     

    가중치 값을 작게 하여 오버피팅이 일어나지 않게 하는 것이다.

     

     

     

    가중치를 작게 만들고 싶으면 초깃값도 최대한 작은 값에서 시작하는 것이 맞겠다.

     

    그러면 가중치의 초깃값을 모두 0으로 설정하면 어떻게 될까?

     

    실제로 가중치 초깃값을 0으로 하면 학습이 올바로 이뤄지지 않는다.

     

     

    초기값을 모두 0으로 하면 안되는 이유는, 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.

     

    가중치들은 같은 초깃값에서 시작하고 갱신을 거쳐도 여전히 같은 값을 유지하는 것이다.

     

    이는 가중치를 여러 개 갖는 의미를 사라지게 한다.

     

    이 '가중치가 고르게 되어버리는 상황'을 막으려면 초깃값을 무작위로 설정해야 한다.

     

     

     

     

     

     

    은닉층의 활성화값 분포


    은닉층의 활성화값 (활성화 함수의 출력 데이터)의 분포를 관찰하면 중요한 정보를 얻을 수 있다.

     

    가중치의 초깃값에 따라 은닉층 활성화값들이 어떻게 변화하는지 실험을 해보겠다.

     

     

    활성화 함수로 시그모이드 함수를 사용하는 5층 신경망에 무작위로 생성한 입력 데이터를 흘리며 각 층의 활성화값 분포를 히스토그램으로 그려보자.

     

     

    import numpy as np
    import matplotlib.pyplot as plt
    
    
    def sigmoid(x):
        return 1 / (1 + np.exp(-x))
    
    x = np.random.randn(1000, 100) # 1000개의 데이터
    node_num = 100  # 각 은닉층의 노드(뉴런) 수
    hidden_layer_size = 5  # 은닉층이 5개
    activations = {}  # 이곳에 활성화 결과를 저장
    
    for i in range(hidden_layer_size):
        if i != 0:
            x = activations[i-1]
            
        w = np.random.randn(node_num, node_num) * 1
        a = np.dot(x, w)
        z = sigmoid(a)
        activations[i] = z
        
        
    # 히스토그램 그리기
    for i, a in activations.items():
        plt.subplot(1, len(activations), i+1)
        plt.title(str(i+1) + "-layer")
        if i != 0: plt.yticks([], [])
        plt.hist(a.flatten(), 30, range=(0,1))
    plt.show()

    Out:

    그림 1

     

    층이 5개이고, 각 층의 뉴런은 100개씩이다. 

     

    입력 데이터로서 1,000개의 데이터를 정규분포로 무작위로 생성하여 이 5층 신경망에 흘린다.

     

    이 코드에서는 가중치의 분포에 주의해야 한다.

     

    이번에는 표준편차가 1인 정규분포를 이용했는데, 표준편차를 바꿔가며 활성화값들의 분포가 어떻게 변화하는지 관찰하는 것이 이 실험의 목적이다.

     

     

     

    코드의 결과로 나온 [그림 1]을 보자.

     

    각 층의 활성화값들이 0과 1에 치우쳐 분포되어 있다.

     

    여기에서 사용한 시그모이드 함수는 그 출력이 0에 가까워지자(또는 1에 가까워지자) 그 미분은 0에 다가간다.

     

    그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다.

     

    이것이 기울기 소실(gradient vanishing)이라 알려진 문제이다.

     

    층을 깊게 하는 딥러닝에서는 기울기 소실은 더 심각한 문제가 될 수 있다.

     

     

     

     

    이번에는 가중치의 표준편차를 0.01로 바꿔서 해보겠다.

     

    앞의 코드에서 가중치 초깃값 설정 부분을 다음과 같이 바꾸면 된다.

    # w = np.random.randn(node_num, node_num) * 1
    w = np.random.randn(node_num, node_num) * 0.01

     

    히스토그램 결과를 보면 다음과 같다.

     

    그림 2

     

    이번에는 0.5 부근에 집중되었다.

     

    앞의 예처럼 0과 1로 치우치진 않았기 때문에 기울기 소실 문제는 일어나지 않았다.

     

    하지만 활성화값들이 치우쳤다는 것은 표현력 관점에서는 큰 문제가 있는 것이다.

     

    다수의 뉴런이 거의 같은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 없어진다.

     

    그래서 활성화값들이 치우치면 표현력을 제한한다는 관점에서 문제가 된다.

     

     

     

     

    Xavier 초기값


    사비에르 글로로트(Xavier Glorot)와 요슈아 벤지오(Yoshua Bengio)의 논문에서 권장하는 가중치 초깃값인 Xavier 초깃값을 사용해보겠다.

     

    현재 Xavier 초기값은 일반적인 딥러닝 프레임워크들이 표준적으로 이용하고 있다.

     

    논문에서는 각 층의 활성화값들을 광범위하게 분포시킬 목적으로 가중치의 적절한 분포를 찾고자 했다.

     

    결과적으로 앞 계층의 노드가 n개라면 표준편차가 1/√n인 분포를 사용하면 된다는 결론을 이끌었다.

     

     

     

    Xavier 초깃값을 사용하면 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 퍼진다.

     

    이제 직접 Xavier 초기값을 사용해보자.

     

    다음과 같이 코드를 고치면 된다.

    # w = np.random.randn(node_num, node_num) * 1
    # w = np.random.randn(node_num, node_num) * 0.01
    w = np.random.randn(node_num, node_num) / np.sqrt(node_num)

    Out:

    그림 3

     

    Xavier 초깃값을 사용한 결과는 [그림 3]처럼 된다.

     

    이 결과를 보면 층이 깊어지면서 형태가 다소 일그러지지만, 앞에서 본 방식보다는 확실히 넓게 분포됨을 알 수 있다.

     

    각 층에 흐르는 데이터는 적당히 퍼져 있으므로, 시그모이드 함수의 표현력도 제한받지 않고 학습이 효율적으로 이뤄질 것이다.

     


    [그림 3]은 오른쪽으로 갈수록 일그러진다. 이는 sigmoid 함수 대신에 tanh 함수(쌍곡선 함수)를 이용하면 개선된다. 
    tanh 함수도 sigmoid 함수와 같은 'S'자 모양 곡선 함수이다.
    다만 tanh 함수가 원점 (0, 0)에서 대칭인 반면, sigmoid 함수는 (x,y) = (0, 0.5)에서 대칭인 S 곡선이다.
    활성화 함수로는 원점에서 대칭인 함수가 바람직하다고 알려져 있다.

     

     

     

     

     

    ReLU를 사용할 때의 가중치 초깃값


    Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과이다.

     

    sigmoid 함수와 tanh 함수는 좌우 대칭이라 중앙 부근이 선형인 함수로 볼 수 있어서 Xavier 초깃값이 적당하다.

     

    반면 ReLU를 이용할 때는 ReLU에 특화된 He 초깃값을 이용하라고 권장한다.

     

    He 초깃값은 앞 계층의 노드가 n개일 때, 표준편차가 √(2/n)인 정규분포를 사용한다.

     

     

    Xavier 초깃값이 √(1/n)이었던과 달리, ReLU는 음의 영역이 0이라서 더 넓게 분포시키기 위해 2배의 계수가 필요하다고 해석할 수 있겠다.

     

     

    활성화 함수로 ReLU를 이용한 경우의 활성화값 분포를 보자.

     

    [그림 4]는 표준편차가 0.01인 정규분포(std = 0.01), Xavier 초깃값, ReLU 전용 He 초깃값일 때의 실험 결과를 차례로 보여준다.

     

    그림 5

     

    결과를 보면 std = 0.01일 때의 각 층의 활성화값들은 아주 작은 값들이다.

     

    신경망에 작은 데이터가 흐은다는 것은 역전파 때 가중치의 기울기 역시 작아진다는 뜻이다. 이는 중대한 문제이며, 실제로도 학습이 거의 이뤄지지 않는다.

     

     

    Xavier 초깃값을 보면 층이 깊어지면서 치우침이 조금씩 커진다.

     

    실제로 층이 깊어지면 활성화값들의 치우침도 커지고, 학습할 때 '기울기 소실' 문제를 일으킨다.

     

     

    마지막으로 He 초깃값은 모든 층에서 균일하게 분포되었다.

     

    층이 깊어져도 분포가 균일하게 유지되어 역전파 때도 적절한 값이 나올 것으로 기대 가능하다.

     

     

    이 실험의 결과를 바탕으로, 활성화 함수로 ReLU를 사용할 때는 He 초깃값을, sigmoid나 tanh 등의 S자 모양 곡선일 때는 Xavier 초깃값을 쓰겠다. 

     

     

     

     

     

    MNIST 데이터셋으로 본 가중치 초깃값 비교


    이번에는 실제 데이터를 가지고 가중치의 초깃값을 주는 방법이 신경망 학습에 얼마나 영향을 주는지 보겠다.

     

    그림 6

     

    이 실험은 층별 뉴런 수가 100개인 5층 신경망에서 활성화 함수로 ReLU를 사용했다.

     

    [그림 6]에서 보듯 std = 0.01일 때는 학습이 전혀 이뤄지지 않는다.

     

    앞서 활성화값의 분포에서 본 것처럼 순전파 때 너무 작은 값(0근처로 밀집한 데이터)가 흐르기 때문이다.

     

    그로 인해 역전파 때의 기울기도 작아져 가중치가 거의 갱신되지 않는 것이다.

     

     

     

    반면 Xavier과 He 초깃값의 경우 학습이 제대로 이뤄지고 있다.

     

    학습 진도는 He 초깃값 쪽이 더 빠르다.

     

     

    가중치 초깃값이 중요함을 다시 한번 강조하며 이번 포스팅을 마치겠다.

     

     

     

     

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

    댓글

dokylee's Tech Blog