Deep Learning

[Deep Learning] 가중치의 초깃값

heedy 2022. 11. 13. 15:34
728x90

가중치의 초깃값

신경망 학습에서 특히 중요한 것이 가중치의 초깃값임. 가중치의 초깃값을 무엇으로 설정하느냐가 신경망 학습의 성패가 가르는 일이 실제로도 자주 있음.


초깃값을 0으로 하면?

  • 오버피팅을 억제해 범용 성능을 높이는 테크닉 : 가중치 감소 기법
  • 가중치 감소는 가중치 매개변수의 값이 작아지도록 학습하는 방법. 가중치 값을 작게 하여 오버피팅이 일어나지 않게 함.
  • 가중치의 초깃값을 최대한 작게 만들면 가중치를 작게 만들 수 있음.
  • 그러나 초깃값을 0으로 하면 학습이 올바르게 이뤄지지 않음.
  • 초깃값이 0이 되면 오차 역전파법에서 모든 가중치의 값이 똑같이 갱신됨.
 

은닉층의 활성화값 분포

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

# 은닉층 활성화 값의 변화 실험
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')
  plt.hist(a.flatten(), 30, range = (0,1))
plt.show()

각 층의 활성화 값들이 0과 1에 치우쳐 분포되어 있음. 여기에서 사용한 시그모이드 함수는 그 출력이 0에 가까워지면 그 미분은 0에 가까워짐. 그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라짐.

이것이 기울기 소실 문제임.

# 은닉층 활성화 값의 변화 실험2
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) * 0.01    # 초깃값 설정 부분 변경
  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')
  plt.hist(a.flatten(), 30, range = (0,1))
plt.show()

초깃값을 0.01로 할 경우 0.5 부근에 집중됨. 기울기 소실 문제는 일어나지 않지만 화설화 값들이 치우쳤다는 것은 표현력 관점에서는 다수의 뉴런이 거의 같은 값을 출력하고 있다는 문제가 생김.


Xavier 초깃값

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

Xavier 초깃값

# 은닉층 활성화 값의 변화 실험3 Xavier 초깃값 사용
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) / np.sqrt(node_num)    # 초깃값 설정 부분 변경
  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')
  plt.hist(a.flatten(), 30, range = (0,1))
plt.show()

층이 깊어질수록 형태가 다소 일그러지지만 앞의 방식들보다 넓게 분포됨.


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

Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과임. sigmoid 함수와 tanh 함수는 좌우 대칭이라 중앙 부근이 선형인 함수로 볼 수 있으므로 Xavier 초깃값이 적절함.


반면 ReLU를 이용할 때는 ReLU이 특화된 초깃값을 이요하라고 권장함. 이 초깃값은 He 초깃값이라고 함.

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


마지막으로 He 초깃값은 모든 층에서 균일하게 분포되었습니다. 층이 깊어져도 분포가 균일하게 유지되기에 역전파 때도 적절한 값이 나옴.

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

뉴런수가 100개인 5층 신경망에서 활성화 함수로 ReLU 사용

import os
import sys

sys.path.append(os.pardir)
import numpy as np
import matplotlib.pyplot as plt
from mnist import load_mnist
from common_util import smooth_curve
from common_multi_layer_net import MultiLayerNet
from common_optimizer import SGD


(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000



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] = []



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



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

- 참고: 밑바닥부터 시작하는 딥러닝1
- notobook ipynb file: https://github.com/heejvely/Deep_learning/blob/main/%EB%B0%91%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0_%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94_%EB%94%A5%EB%9F%AC%EB%8B%9D_1/%ED%95%99%EC%8A%B5%20%EA%B4%80%EB%A0%A8%20%EA%B8%B0%EC%88%A0%EB%93%A4.ipynb

 

GitHub - heejvely/Deep_learning: deep learning 기초 공부

deep learning 기초 공부. Contribute to heejvely/Deep_learning development by creating an account on GitHub.

github.com

 

728x90