Loading [MathJax]/jax/output/HTML-CSS/jax.js

데이터 정규화 및 웨이트 초기화

  • weight의 초깃값이 신경망 모델의 학습에 영향을 미치는데, 초깃값을 어떻게 설정해야 좋을까?
  • weight에 관해 생각해보기 위해 입력 데이터를 '깔끔하게' 정리를 해보자.
  • 다양한 입력 데이터가 일정한 범위에 들어가도록 전처리를 해보자. 가장 좋은 방법은 이 범위를 0 1 사이의 값을 갖도록 하는 것이다.
  • 예를 들어 MNIST 데이터의 경우 데이터가 0에서 255까지의 gray scale 값을 갖기 때문에 다음과 같이 코드를 작성하면 입력 데이터의 값이 모두 0부터 1 사이의 값을 갖게 된다.
X = X / 255


  • 위의 코드를 다른 모든 데이터에도 적용이 가능하도록 일반화하면 다음과 같다.
X = X / X.max()


  • 위의 과정처럼 데이터가 일정한 범위 안에 들어가게 해서 그 다음 처리를 원할하게 이루어지도록 하는 것을 정규화(normalization)이라고 한다.
  • 데이터의 분포를 생각하면 데이터의 평균이 0이 되도록 정규화하는 것이 좋다. 이 과정을 코드로 바꾸면 다음과 같다.
X = X / X.max()
X = X - X.mean(axis=1).reshape(len(X), 1)


  • 데이터를 정규화했을 때 데이터의 분포가 한쪽으로 치우지지 않는다면 weight의 성분은 양수값과 음수값을 모두 갖게 될 것이다. 이 때 weight의 값이 한쪽으로 치우지지 않는다면 양수와 음수가 거의 반반으로 비슷하게 분포할 것이다. 따라서 weight의 성분을 모두 0으로 초기화하는 것을 가장 먼저 생각해볼 수 있다.
    • weight의 성분을 모두 같도록 초기화하면 오차역전파식을 사용했을 때 경사값도 같아져버리므로 weight 값이 제대로 갱신되지 못하기 때문에 0에 가까운 난수로 초기화를 해야한다.
    • 지금까지 weight의 값을 정규분포를 사용해 초기화한 이유는 평균이 μ=0에 가까운 난수를 얻기 위함이다. 또한 표준편차 σ가 작을수록 weight의 성분들은 0에 가까워져 평균도 0에 가깝기 때문에 좋다. 따라서 이를 위하여 np.random.normal(scale=0.01, size=shape) 함수를 사용하는 것이다.
  • 단순히 표준편차 σ를 작게 한다고 좋아지는 것은 아니다. 초깃값이 너무 작으면 weight가 계수로 곱해지는 경사값도 너무 작아져 학습이 제대로 진행되지 않는 문제가 발생한다.
    • ReLU는 x=0 부근에서도 경사가 소실되지 않는 성질을 지난 활성화 함수이기 때문에 표준편차를 σ=0.01과 같이 작은 값으로 잡아야 학습이 잘 진행되는 경향이 있다.
  • 문제 해결을 위해 표준편차가 σ=1.0인 표준정규분포를 전제로 여기에 적절한 계수를 곱해서 좋은 초깃값을 생성하는 방법을 사용하는데, a*np.random.normal(scale=0.01, size=shape)에서 a에 어떤 값을 줘야 하는 것인가이다.
    • 주의할 점은 입력 데이터의 차원 수가 높을 수록 (표준편차의 값이 1.0이기 때문에) 생성되는 weight의 성분 값들이 서로 흩어지기 쉬워진다는 것이다. 따라서 weight를 초기화할 때 이 흩어짐을 억제할 수 있는 방법이 필요하다.


웨이트 성분값들의 흩어짐을 막기 위한 방법

  • 입력 데이터 xn차원이고, weight가 W일 때, 활성화되기 전의 값을 p라고 하면 p의 각 성분 pj는 식 (12.1)과 같이 나타낼 수 있다.

(12.1)pj=i=1nwjixi

  • 이 때 E[]가 기대값(평균)이라 하고, Var[]를 분산이라고 하면 식 (12.2)가 성립한다.

(12.2)Var[X]=E[X2](E[X])2

  • pj의 분산은 다음과 같다.

(12.3)Var[pj]=Var[i=1nwjixi](12.4)=i=1nVar[wjixi](12.5)=i=1n{E[(wjixi)2](E[wjixi])2}((12.2))(12.6)=i=1n{E[wji2xi2](E[wji]E[xi])2}(12.7)=i=1n{E[wji2]E[xi2](E[wji])2(E[xi])2}

  • 이제 E[wji2]E[xi2]를 따로 계산하면 다음과 같다.

(12.8)E[wji2]=Var[wji]+(E[wji])2

(12.9)E[xi2]=Var[xi]+(E[xi])2


  • (12.8)과 식 (12.9)의 곱을 계산하면 식 (12.10)과 같다.

(12.10)E[wji2]E[xi2]=(Var[wji]+(E[wji])2)(Var[xi]+(E[xi])2)(12.11)=Var[wji]Var[xi]+Var[wji](E[xi])2+(E[wji])2Var[xi]+(E[wji])2(E[xi])2


  • (12.11)을 식 (12.9)에 대입하면 식 (12.11)과 식 (12.9)의 마지막 항들이 소거되어 식 (12.12)가 된다.

(12.12)Var[pj]=i=1n{Var[wji]Var[xi]+Var[wji](E[xi])2+(E[wji])2Var[xi]}


  • 이제 입력 데이터가 정규화가 되어 있다면 E[xi]=0이며, 또한 weight가 이상적으로 정규분포를 따른다고 가정하면 E[wji]=0이기 때문에 식 (12.12)는 식 (12.15)가 된다.

(12.13)Var[pj]=i=1n{Var[wji]Var[xi]+Var[wji](E[xi])2+(E[wji])2Var[xi]}(12.14)=i=1nVar[wji]Var[xi](12.15)=nVar[wji]Var[xi]


  • (12.15)에 따라 p의 분산을 x의 분산과 같게 하려면 weight W의 각 성분의 분산은 1n이어야 한다. 즉 Var(wj)=1n이어야 Var(W)=1n이기 때문에 식 (12.15)에 의해 Var[p]=Var[x]이 된다.


  • a가 상수이고 X를 확률변수라고 하면 Var[aX]=a2Var(X)이기 때문에 앞에서 언급한 것처럼 a*np.random.normal(scale=0.01, size=shape)에서 a에 어떤 값을 줘야 하는 것인가 하는 문제로 돌아가면 a는 식 (12.16)이 되어야 한다.

(12.16)a=1n


  • 따라서 a*np.random.normal(scale=0.01, size=shape)는  다음과 같이 구현해야 한다.
np.sqrt(1.0 / n) * np.random.normal(size=shape)


  • (12.15)를 구하기 위해 몇 가지 가정을 한 상태에서 식을 전개하였다. 이 가정을 바꾸면 초기화하는 방법들을 만들어 낼 수 있다.


LeCun 등 1998년 논문에서 제시하는 초기화 방법

  • Yann LeCun, Leon Bottou, Genevieve B. Orr and Klaus-Robert Müller, Efficient BackProp, Neural Networks: Tricks of the Trade, pp. 9-50, Springer, 1998

  • 이 방법은 정규분포 또는 균등분포를 적용해 초기화하며 균등분포를 적용할 경우의 코드는 다음과 같다.
np.random.uniform(low=np.sqrt(1.0 / n),
                  high=np.sqrt(1.0 / n),
                  size=shape)


  • Keras에서는 kernel_initializer='lecun_uniform'이라는 별칭을 사용할 수 있다.


Glorot and Bengio 2010년 논문에서 제시하는 초기화 방법

  • 이 방법은 정규분포나 균등분포를 사용할 경우 각각의 웨이트 초깃값에 관해 연구한 것으로 코드로 나타내면 균등분포일 경우에는 다음과 같다.
np.random.uniform(low=np.sqrt(6.0 / (n_in + n_out)),
                  high=nq.sqrt(6.0 / (n_in + n_out)),
                  size=shape)


  • 정규분포일 경우에는 다음과 같이 구현하는 것으 좋다고 알려져있다.
np.sqrt(3.0 / (n_in + n_out)) * np.random.normal(size=shape)


  • TensorFlow에서 이 초기화 방법을 사용하려면 tf.contrib.layers.xavier_initializer(uniform=True)를 호출하면 된다.
  • Keras에서는 각각 init='glorot_uniform'init='glorot_normal'을 호출하면 된다.


He 등 2015년 논문에서 제시하는 초기화 방법

  • ReLU를 사용할 경우 어떻게 초기화하는지에 관해 설명하는데 다음과 같이 구현하는 좋다고 주장한다.
np.sqrt(2.0 / n) * np.random.normal(size=shape)


  • Keras에서는 init='he_normal' 사용할 수 있다.



출처 : 정석으로 배우는 딥러닝





'신경망(Neural Network) 스터디' 카테고리의 다른 글

14. 학습 조기 종료  (2) 2018.02.01
13. 학습률 설정  (0) 2018.01.29
11. 학습 과정 시각화  (3) 2018.01.19
10. 신경망 모델 구현 방법  (0) 2018.01.19
9. 오버피팅 문제 해결  (0) 2018.01.19

+ Recent posts