Weight Initialization 기법 정리

Weight Initialization의 목적? layer를 거치면서 값들이 exploding 하거나 vanishing 하지 않는 적절한 weight로 초기화.

 

Motivating Example

Activation function이 없는 10개의 Layer를 가진 Network를 가정.

import numpy as np

x = np.random.normal(size=128)
print('input:', x.mean(), x.std())

for i in range(10):
    n_in, n_out = 128, 128
    W = np.random.normal(size=[n_out,n_in])
    x = W.dot(x)
print('output:', x.mean(), x.std())
input: -0.040166840963675954 0.935467846610908
output: -1155167148.6368685 27324378091.68085

최종 activation은 여러 Layer들을 거치면서 값이 exploding하는것을 확인할 수 있다.

initialzation된 weight 값이 큰 경우 exploding 할 수 있고, 반대로 작은 값인 경우 vanishing할 수 있기 때문에 적절한 크기의 weight로 초기화가 필요하다는 것을 알 수 있다.

 

깊은 Layer를 가지는 모델일수록 적절한 크기의 weight로 초기화가 필수적이며 이를 위한 여러 방법들이 제안되어 있다.

공통적인 아이디어는 레이어를 통과하더라도 activation의 통계적 특성 (e.g. variance)이 유지되도록 weight의 분포를 만드는 것이다.

 

LeCun Initialization (1998)

$ W \sim N(0,{1\over {n_{in}}}) $

$ W \sim unif(-\sqrt{3 \over {n_{in}}}, \sqrt{3 \over {n_{in}}}) $

 

아이디어: Layer를 통과하더라도 activation의 variance가 같아지도록 하는 분포로 초기화하자. (i.e. $Var[W\mathbf{x}] = Var[\mathbf{x}]$)

 

W, x의 각 값들이 독립적이고 같은 분포에서 나왔다고 가정하면 $ Var[W\mathbf{x}] = n_{in}Var[W]Var[\mathbf{x}] $이므로 $ Var[W] = {1 \over n_{in}} $이 되어야 한다.

(NOTE: uniform의 경우 $ unif(-a, a) $의 variance는 $ a^2 \over 3 $이므로 $ \sqrt{3} $이 붙는다.)

 

for i in range(10):
    n_in, n_out = 128, 128
    W = np.random.normal(size=[n_out,n_in]) * (1/np.sqrt(n_in))
    x = W.dot(x)
print('output:', x.mean(), x.std())
output: 0.17532933245903154 1.1086845913207162

layer를 거치더라도 발산하지 않고 activation의 variance가 1에 가깝게 유지되는것을 확인할 수 있다.

(가정: LeCun Initialization에서는 activation function을 통과했을 때의 variance 변화는 고려하지 않는다.)

 

Xavier Initialization (2010)

Paper: Understanding the difficulty of training deep feedforward neural networks (AISTATS'10)

$ W \sim N(0,{2\over {n_{in} + n_{out}}}) $

$ W \sim unif(-\sqrt{6 \over {n_{in} + n_{out}}}, \sqrt{6 \over {n_{in} + n_{out}}}) $

 

아이디어: Layer를 통과했을 때 activation 뿐만 아니라 gradient의 variance도 같아지도록 고려하자. (i.e. $Var[\frac{\partial Cost}{\partial \mathbf{x^{\mathcal{l}}}}] = Var[\frac{\partial Cost}{\partial \mathbf{x^{\mathcal{l+1}}}}]$)

 

backprop은 output에서 input 방향으로 진행되므로 gradient의 variance는 fan-out인 n_out에 영향을 받는다.

따라서 $Var[W] = {1\over {n_{in}}}$ 뿐만 아니라 $Var[W] = {1\over {n_{out}}}$ 두가지 조건을 모두 만족시켜야 gradient의 variance도 같아진다.

(activation function의 미분 값은 1이라고 가정.)

하지만 $ n_{in} = n_{out}$인 경우에만 가능하기 때문에 두 값의 절충점인 $ 1\over{n_{in}} $과 $ 1 \over{n_{out}}$의 조화평균 $ 2\over {n_{in} + n_{out}}$인 variance를 가지도록 초기화 한다.

(왜 하필 조화평균일까? 논문에서는 따로 언급하지는 않는다.)

 

He Initialization (2015)

Paper: Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification (ICCV'15)

$ W \sim N(0,{2\over {n_{in}}}) $

$ W \sim unif(-\sqrt{6 \over {n_{in}}}, \sqrt{6 \over {n_{in}}}) $

 

아이디어: LeCun Initialization에서 ReLU activation을 통과했을 때의 variance 변화도 고려하자. (i.e. $Var[ReLU(W\mathbf{x})] = Var[\mathbf{x}]$)

 

PReLU activation function과 함께 제안된 방식이다. 앞에서의 가정을 똑같이 적용하면 $Var[ReLU(W\mathbf{x})] = {1\over2}n_{in}Var[W]Var[\mathbf{x}]$ 이므로 $Var[W] = {2\over{n_{in}}}$이 되어야 한다.

 

  • ReLU를 사용하는 network에 LeCun Initialization 적용
for i in range(10):
    n_in, n_out = 128, 128
    w = np.random.normal(size=[n_out,n_in]) * (1/np.sqrt(n_in))
    x = w.dot(x)
    x = x * (x > 0) # ReLU activation
print('output:', x.mean(), x.std())
output: 0.017780790380734105 0.02754261071341547

ReLU를 사용하는 network에 LeCun Initialization을 적용할 경우 activation이 vanishing된다.

 

  • ReLU를 사용하는 network에 He Initialization 적용
for i in range(10):
    n_in, n_out = 128, 128
    w = np.random.normal(size=[n_out,n_in]) * (np.sqrt(2/n_in))
    x = w.dot(x)
    x = x * (x > 0) # ReLU activation
print('output:', x.mean(), x.std())
output: 0.7760240767327892 1.0219019518538777

He Initialization을 사용할 경우 variance가 1에 가깝게 유지되는 것을 확인할 수 있다.

 

일반화하여 여러 Activation function들에 대해서도 variance 변화를 고려한 scaling factor를 구할 수 있다.