728x90
반응형

가중치의 초깃값

  • 신경망 학습에서 특히 중요한 것이 가중치의 초깃값

 

초깃값 0으로 할시

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

 

가중치 감소(weight decay)

  • 가중치 매개변수의 값이 작아지도록 학습하는 방법
  • 가중치 값을 작게 해서 오버피팅이 일어나지 않게 하는 방법

가중치 초깃값 0 설정

  • 매우 나쁜 아이디어
  • 가중치 초깃값 0으로 할시 학습이 올바르게 이루어지지 않는다.
  • 가중치를 균일한 값으로 설정해서는 안된다.
    • 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.
    • 순전파 때 입력층 가중치 0이기 때문에 두 번째 층의 뉴런에 모두 같은 값이 전달된다. 모든 뉴런에 같은 값이 입력된다는 것은 역전파 때 두 번째 층의 가중치가 모두 똑같이 갱신된다는 말이 된다.

 

은닉층의 활성화값 분포

  • 은닉층의 활성화값(활성화 함수의 출력 데이터)*의 분포를 관찰하면 중요한 정보를 얻을 수 있다.
# coding: utf-8
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()

[그림1] 가중치를 표준편차가 1인 정규분포로 초기화시 각 층 활성화 값'

 

  • 시그모이드 함수는 출력이 0에 가까워지거나 또는 1에 가까워지면 그 미분은 0에 다가간다. 그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다. 
    • 이것이 기울기 소실(gradient vanishing)이라 알려진 문제다.

가중치 초기값 설정 부분 변경

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

[그림2] 가중치 표준편차가 0.01인 정규분포로 초기화시 각 층의 활성화값 분포

  • 0.5 부근에 집중 
  • 0,1 치우치진 않았으므로 기울기 소실 문제는 일어나지 않았지만, 활성화값들이 치우쳤다는 것은 표현력 관점에서 큰 문제가 있는 것이다.
  • 다수의 뉴런이 거의 같은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 없어진다는 뜻이다.
    • 뉴런 100개가 거의 같은 값을 출력 시 뉴런 1개짜리와 별반 다를게 없다는 의미 

 

사비에르 글로로트(Xavier Glorot)와 오슈아 벤지오(Yoshua Bengio)의 논문에서 권장하는 가중치 초기값 - Xavier 초기값 사용

  • Xavier 초기값 사용시 앞 층에 노드가 많을수록 대상 노드의 초기값으로 설정하는 가중치가 좁게 퍼진다.
 w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)

[그림3] Xavier 초기값 이용시 각 층의 활성화값 분포

 

 

ReLU 사용시 가중치 초기값

  • Xavier 초기값은 활성화 함수가 선형인 것을 전제로 이끈 결과다.
  • sigmoid 함수와 tanh함수는 좌우 대칭이라 중앙 부근이 선형인 함수라고 볼 수 있다.
  • ReLU이용시 ReLU에 특화된 초깃값 이용하라고 권장
    • 카이밍 히(Kaiming He)의 이름을 따 He 초깃값 
    • H2 초깃값은 앞 계층의 노드가 n개일 때, 표준편차가 루트2/n인 정규분포 사용

[그림4] 왼쪽부터 가중치 초깃값 0.01, 사비에르, 히 초깃값 결과

  • std=0.01은 매우 작은 값이다. 신경망에 아주 작은 데이터가 흐른다는 것은 역전파 때 가중치의 기울기 역시 작아진다는 뜻이다.
  • Xavier 초깃값 결과는 층이 깊어지면서 치우침이 조금씩 커진다. - '기울기 소실'문제 일으킴
  • He 초깃값은 모든 층에서 균일하게 분포, 분포가 균일하므로 역전파때도 적절한 값이 나올 것으로 기대할 수 있다.

 

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

# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from mnist import load_mnist
from util import smooth_curve
from multi_layer_net import MultiLayerNet
from 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=[100, 100, 100, 100],
                                  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'}
x = 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(0, 2.5)
plt.legend()
plt.show()

[그림5] MNSIT 데이터셋으로  '가중치 초깃값'

 

출처 : 밑바닥부터 시작하는 딥러닝

https://www.hanbit.co.kr/store/books/look.php?p_code=B8475831198

 

밑바닥부터 시작하는 딥러닝

직접 구현하고 움직여보며 익히는 가장 쉬운 딥러닝 입문서

www.hanbit.co.kr

 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기