소소하지만 소소하지 않은 개발 공부/밑바닥부터 시작하는 딥러닝

Chapter6.2 가중치의 초깃값, DL from Scratch, 파이썬

still..epochs 2022. 11. 11. 23:08

6.2 가중치의 초깃값

신경망 학습에서 특히 중요한 것은 가중치의 초깃값이다. 가중치의 초깃값을 무엇으로 설정하느냐가 신경망 학습의 성패가 가르는 일이 있다. 이번 절에서는 권장 초깃 값에 대해서 설명하고 실험을 통해 실제로 신경망 학습이 신속하게 이뤄지는 모습을 확인하겠다.

 

6.2.1 초깃값을 0으로 하면?

가중치 감소(weight decay) 기법 : 가중치 매개변수의 값이 작아지도록 학습하는 방법이다.

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

 

그렇다면 가중치의 초깃값을 모두 0으로 설정한다면 어떨까? 이는 나쁜 아이디어인데, 실제로 가중치 초깃값을 0으로 하면 학습이 올바로 이뤄지지 않는다. 그 이유는 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.

 

6.2.2 은닉층의 활성화값 분포

가중치의 초깃값에 따라 은닉층 활성화값들은 어떻게 변화할까?

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

 

import numpy as np
import matplotlib.pyplot as plt


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def ReLU(x):
    return np.maximum(0, x)


def tanh(x):
    return np.tanh(x)
    
input_data = np.random.randn(1000, 100)  # 1000개의 데이터
node_num = 100  # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5  # 은닉층이 5개
activations = {}  # 이곳에 활성화 결과를 저장

x = input_data

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]

    # 초깃값을 다양하게 바꿔가며 실험해보자!
    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(1.0 / node_num)
    # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)


    a = np.dot(x, w)


    # 활성화 함수도 바꿔가며 실험해보자!
    z = sigmoid(a)
    # z = ReLU(a)
    # z = tanh(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.xlim(0.1, 1)
    # plt.ylim(0, 7000)
    plt.hist(a.flatten(), 30, range=(0,1))
plt.show()

 

층이 5개이고, 각 층의 뉴런은 100개씩이다. 입력 데이터로서 1000개의 데이터를 정규분포로 무작위로 생성하여 이 5층 신경망에 흘린다. 활성화 함수로는 시그모이드 함수를 이용하고, 각 층의 활성화 결과를 activations 변수에 저장한다. 이번에는 표준편차가 1인 정규분포를 이용했는데, 이 분포된 정도(표준 편차)를 바꿔가며 활성화값들의 분포가 어떻게 변화하는지 관찰하는 것이 목표이다.

- 각 층의 활성화값들이 0과 1에 치우쳐 분포되어 있다. 여기에서 사용한 시그모이드 함수는 그 출력이 0에 가까워지자(또는 1에 가까워지자) 그 미분은 0에 다가간다. 그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다. 이것이 기울기 소실(gradient vanishing) 이라 알려진 문제이다.

 

이번에는 가중치의 표준편차를 0.01로 바꿔 같은 실험을 반복해보자.

w = np.randm.randn(node_num, node_num) * 0.01

 

- 이번에는 0.5 부근에 집중되었다. 기울기 소실 문제는 일어나지 않았지만, 활성화 값들이 치우쳤다는 것은 표현력 관점에서 큰 문제가 있는 것이다. 즉, 다수의 뉴런이 거의 같은 값을 출력하고 있기 때문에 뉴런을 여러 개 둔 의미가 없어진다는 뜻이다. 그래서 활성화값들이 치우치면 표현력을 제한한다는 관점에서 문제가 발생한다.

 

Xavier 초깃값

- 현재 Xavier 초깃값은 일반적인 딥러닝 프레임워크들이 표준적으로 이용하고 있다. 예를 들어 카페(Caffe) 프레임워크는 가중치 초깃값을 설정할 때 인수로 xavier를 지정할 수 있다. 이 논문은 각 층의 활성화값들을 광범위하게 분포시킬 목적으로 가중치의 적절한 분포를 찾고자 했다. 그리고 앞 계층의 노드가 n개라면 표준편차가 1/루트 n 인 분포를 사용하면 된다는 결론을 이끌었다.

node_num = 100 # 앞 층의 노드 수
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)

- Xavier 초깃값을 사용한 결과를 보면, 층이 깊어지면서 형태가 다소 일그러지지만, 앞에서 본 방식보다는 확실히 넓게 분포됨을 알 수 있다. 각 층에 흐르는 데이터는 적당히 퍼져 있으므로, 시그모이드 함수의 표현력도 제한받지 않고 학습이 효율적으로 이뤄질 것으로 기대된다.

 

 

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

Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과이다. sigmoid 함수와 tanh함수는 좌우 대칭이라 중앙 부근이 선형인 함수로 불 수 있다. 그래서 Xavier 초깃값이 적당하다. 반면 ReLU를 이용할 때는, 이에 특화된 초깃값을 이용하라고 권장한다. 이 특화된 초깃값을 찾아낸 카이밍 히(Kaiming He)의 이름을 따 He 초깃값이라고 한다.

 

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

표준편차가 0.01인 정규본포를 가중치 초깃값으로 사용한 경우
Xavier 초깃값을 사용한 경우
He 초깃값을 사용한 경우

- 결과를 보면 std = 0.01 일 때의 각 층의 활성화 값들은 아주 작은 값들이다. 신경망에 아주 작은 데이터가 흐른다는 것은 역전파 때 가중치의 기울기 역시 작아진다는 뜻이다. 이는 중요한 문제이며, 실제로도 학습이 거의 이뤄지지 않을 것이다.

- 이어서 Xavier 초깃값 결과를 보면 이쪽은 층이 깊어지면서 치우침이 조금씩 커진다. 실제로 층이 깊어지면 활성화값들의 치우침도 커지고, 학습할 때 '기울기 소실'문제를 일으킨다.

- 마지막으로 He 초깃값은 모든 층에서 균일하게 분포되었다. 층이 깊어져도 분포가 균일하게 유지되기에 역전파 때도 적잘한 값이 나올 것으로 기대할 수 있다.

 

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

 

 

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

- 이 실험은 층별 뉴런 수가 100개인 5층 신경망에서 활성화 함수로 ReLU를 사용하였다. 위의 그림에서 보듯 std = 0.01 일 때는 학습이 전혀 이뤄지지 않았다. 앞서 활성화값의 분포에서 본 것처럼 순전파 때 너무 작은 값(0 근처로 밀접한 데이터)이 흐르기 때문이다. 그로 인해 역전파 때의 기울기도 작아져 가중치가 거의 갱신되지 않는다.  반대로 Xavier와 He 초깃값의 경우는 학습이 순조롭게 이뤄지고 있다. 다만 학습 진도는 He 초깃값 쪽이 더 빠르다.

 

지금까지 살펴보았듯 가중치의 초깃값은 신경망 학습에 아주 중요한 포인트이다. 가중치의 초깃값에 따라 신경망 학습의 성패가 갈리는 경우가 많지만, 이를 간과하기 쉬우므로 신경망 학습시 초깃값을 중요하게 생각해야한다.