티스토리 뷰

가중치 초기화

은닉층의 활성화 분포

은닉층의 활성화값(활성화 함수의 출력 데이터)의 분포를 관찰하면 중요한 정보를 얻을 수 있습니다. 
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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(1000100)  # 1000개의 데이터
node_num = 100  # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5  # 은닉층이 5개
activations = {}  # 이곳에 활성화 결과를 저장
 
= 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(1len(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(), 30range=(0,1))
plt.show()
 
cs

이 코드를 실행해보면 각 층의 활성화 값들이 0과 1에 치우쳐진 것을 볼 수 있습니다. 여기서 사용한 시그모이드 함수는 0에(혹은 1)가까워지자 그 미분은 0에 다가갑니다. 그래서 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라집니다. 이러한 문제를 기울기 소실이라 합니다. 이것은 층이 깊을수록 더 심각해 집니다.


이번에는 가중치를 0.1 대신 0.01로 바꾼 후 실행해보면, 각 층의 활성화값 분포는 0.5부근에 집중됩니다. 물론 0과 1로 치우쳐지진 않았으니 기울기 소실 문제는 일어나지 않겠지만 활성화값들이 치우쳤다는 것은 표현력 관점에서 큰 문제가 있다는 것인거죠.(다수의 뉴런이 거의 같은 값을 출력하고 있으니 은닉층을 여러 개 둔 이유가 없음). 이렇게 활서화 값들이 치우쳐지만 표현력을 제한한다는 점에서 문제가 됩니다. 따라서 우리는 각 층의 활성화값은 적당히 고루 분포되어야 합니다. 층과 층 사이에 적당하게 다양한 데이터가 흐르게 해야 신경망 학습이 효율적으로 이뤄지기 때문입니다. 반대로 치우친 데이터가 흐르면 기울기 소실이나 표현력 제한 문제에 빠져서 학습이 잘 이뤄지지 않습니다.


이번에는 Xavier Glorot와 Yoshua Bengio의 논문에서 권장하는 가중치 초깃값인 Xavier 초깃값을 사용해 보겠습니다. 이것으 일반적인 딥러닝 프레임워크들이 표준적으로 사용하고 있습니다. 이 논문은 각 범위의 활성화값들을 광범위하게 분포시킬 목적으로 가중치의 적절한 분포를 찾기 위해 앞 계층의 노드가 n개라면 표준편차가 1/sqrt(n)인 분포를 사용하면 됩니다. Xavier 초깃값을 사용하면 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 펴집니다. 


코드 실행은 위 코드에서 가중치 초깃값 설정 부분을 다음과 같이 고쳐주면 됩니다.

1
w=np.random.randn(node_num, node_num)/np.sqrt(node_num)
cs
코드를 실행해보면 층이 깊어지면서 형태가 다소 일그러지지만, 앞에서 본 방식보다는 확실히 넓게 분포됨을 볼 수 있습니다. 여기서 오른쪽으로 갈수록 약간씩 일그러지는 것을 개선하고 싶으며 sigmoid대신 tanh함수를 이용하면 됩니다. tanh함수는 (0,0)에서 s자 모양을 보여줍니다.

Xavier초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과입니다.(sigmoid와 tanh는 중아 부근이 대칭이기에 선형으로 간주) ReLU를 사용할 때는 haiming He가 찾아낸 He 초깃값을 사용하라 합니다. He초깃값은 앞 계층의노드가 n개일 때 , 표준편차가 sprt(2/n)인 정규분포를 사용합니다. ReLU는 음의 영역이 0이라서 더 넓게 분포시키기 위해 2배의 계수가 필요하기 때문입니다.

정리하자면 활성화 함수로 ReLU를 사용할 때는 He초깃값을, sigmoid나 tanh등의 s자 모양의 곡선일 때는 Xavier초깃값을 사용하면 됩니다.

MNIST dataset으로 본 가중치 초깃값 비교

MNIST dataset가지고 가중치의 초깃값을주는 방법이 신경망 학습에 얼마나 영향을 주는지 알아보겠습니다.(std=0.01, Xavier 초깃값, He초깃값 비교)
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# coding: utf-8
import os
import sys
 
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.util import smooth_curve
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD
 
 
# 0. MNIST 데이터 읽기==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
 
train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000
 
 
# 1. 실험용 설정==========
weight_init_types = {'std=0.01'0.01'Xavier''sigmoid''He''relu'}
optimizer = SGD(lr=0.01)
 
networks = {}
train_loss = {}
for key, weight_type in weight_init_types.items():
    networks[key] = MultiLayerNet(input_size=784, hidden_size_list=[100100100100],
                                  output_size=10, weight_init_std=weight_type)
    train_loss[key] = []
 
 
# 2. 훈련 시작==========
for i in range(max_iterations):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    for key in weight_init_types.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizer.update(networks[key].params, grads)
    
        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)
    
    if i % 100 == 0:
        print("===========" + "iteration:" + str(i) + "===========")
        for key in weight_init_types.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))
 
 
# 3. 그래프 그리기==========
markers = {'std=0.01''o''Xavier''s''He''D'}
= np.arange(max_iterations)
for key in weight_init_types.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(02.5)
plt.legend()
plt.show()
 
cs
코드를 실행해보겠습니다.

std는 학습이 이뤄지지 않습니다. 너무 작은값이 흐리기 때문입니다. 그로 인해 역전파 때의 기울기도 작아져 가중치가 거의 갱신되지 않는거죠. Xavier과 He중에서는 He가 학습 속도가 더 빠른 것을 알 수 있습니다.


이처럼 가중치의 초깃값은 신경망에서 매우 중요한 요소입니다. 


배치 정규화

가중치의 초깃값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습히 원활하게 수행되는 것을 보았습니다. 그럼 이제 배치 정규화를 사용해 각 층의 활성화를 강제로 적당히 퍼뜨리는 방법을 설명해 보겠습니다.

배치 정규화를 쓰는 이유는 다음과 같습니다.


1. 학습 속도 개선

2. 초깃값에 크게 의존하지 않는다.

3. 오버피팅을 억제(드롭아웃 등의 필요성 감소)


배치 정규화의 기본 아이디어는 각 층의 활성화값이 적당히 분포되도록 조정하는 것입니다. 배치 정규화를 삽입하는 위치는 활성화 함수 앞(혹은 뒤)에 넣어주면 됩니다.


아래는 배치 정규화를 수식으로 나타낸 것입니다.

먼저 Input으로 사용된 미니배치의 평균과 분산을 계산을 한다. 그 다음 hidden layer의 활성화값/출력값에 대해서 평균이 0, 분산이 1이 되도록 정규화(Normalization)를 한다(=변환; transform). 그럼으로써 데이터 분포가 덜 치우치게 되고 배치 정규화 단계마다 확대scale와 이동shift 변환(transform)을 수행한다.. 


효과

MNIST dataset을 사용해 배치 정규화 계층을 사용할 때와 사용하지 않을 때의 학습 진도의 차이를 확인해 보겠습니다.


위 그래프를 보면 거의 모든 경우에서 배치 정규화를 사용할 때의 학습 진도가 빠른 것으로 나타납니다. 실제로 배치 정규화를 이용하지 않는 경우엔 초깃값이 잘 분포되어 있지 않으면 학습이 전혀 진행되지 않는 모습도 확인할 수 있습니다.

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

합성곱 신경망(CNN)-전체 구조 훓어보기  (0) 2018.10.09
오버피팅(가중치 감소, 드롭아웃)  (0) 2018.09.30
매개변수 갱신  (0) 2018.09.27
오차역전파(2)  (0) 2018.09.18
오차역전파법(1)  (0) 2018.09.17
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함