지금까지...

현재까지는 python으로 M.L.N.N.(feed-forward N.N. = fully-connected N.N.)모델을 구현할 때, 성능을 높이는 방법으로는

1) activation func-> ReLU를 쓰자.
2) weight 초기화
3) optimizer를 바꾸기

activation func으로는 ReLU를 쓰면 된다고, 했다. 그외 성능을 올리고 싶으면 다른 것을 쓰면 된다.

이번시간에는 weight initialization에 대해서 공부해보자.



Weight initialzation

weight 초기화는 14~15년도 사이에 발생한 최근 트렌드라고할 수 있다.
그전 까지는 weight초기화로 성능 개선할 수 있다는 인식이 약했다. 처음 초기화만 하면 gradient를 태워서 업데이트 하면 되기 때문이다.
하지만, 최근에 weight를 어떻게 초기화 하냐에 따라서 성능이 달라지는 것을 발견하고 관심을 가지기 시작했다.


weight는 Layer의 타입, neuron의 개수, activation func의 종류 등 여러 configuration에 대해 연관이 있다.  그러기 때문에 공통format을 만든 뒤, 이것을 수정하는 방식으로 사용하면 매우 효율적일 것이다.
예를 들어, activation func을 sigmoid를 썼을 때, tanh를 썼을 때, ReLU를 썻을 때 마다 공통format을 기준으로 미묘하게 수정할 것이다.


그 전에 M.L.N.N.(feed-forward N.N. = fully-connected N.N.)에서 weight 초기화에 따라 모델의 성능을 한번 살펴보자.
weight initialization에 따른 error(낮으면 수렴)를 각 Activation functon 별로 나타낸 것이다.

image

먼저,

  1. Sigmoid 사용시, Layer가 depth4 -> depth5로 1개의 Layer가 추가되는 순간 에러가 굉장이 높아지면서 수렴하지 않는다
  2. Sigmoid를 -> tanh로 바꾸니까 성능이 좋아진다.
  3. tanh Ntanh를 weight initialization을 잘한 Normalized initialization이다.
    같은 activation func이라도, weight초기화만 잘해줬더니, error가 더 낮은 값으로 수렴한다.
    일반적으로는 Normalized initialization을 Xavior initialization으로한다.

결론적으로, 똑같은 딥러닝모델에서 weight를 잘 초기화하냐 하지않느냐에 따라서 성능이 바뀌는 것을 알 수 있다.


여태껏 해줬던 weight초기화 방식을 먼저 살펴보자.
image

-1.0 ~ +1.0에서 uniform분포(주사위 던지기)로 랜덤 초기화 했다.
하지만 이러한 방식은, simple한 data에서나 통하지, M.L.N.N.시 사용하면 수렴이 잘 안된다. lr를 조절하더라도 결국 error가 수렴하지 않는다. Layer를 1개만 썼을 때는, 수렴했지만, Layer를 늘려가면 갈수록 수렴하지 않는다.
이러한 방식을 Small Random Number 초기화 라고 하는  데, 모델이 수렴하지 않는 이유 중 weight 초기값을 잘못설정하는 것도 적지 않는 이유가 되는 것이다.


Small Random Number 초기화( -1 에서 +1 까지 uniform분포로 weight초기화) 방법이 왜 잘못되었는지 알아보자.

image
먼저 Hidden Layer가 2개이며, 각 neuron의 개수가 100개인 M.L.N.N. 모델에서,
Sigmoid를 activation func으로 사용하고 있다고 가정해보자.
첫 Hidden Layer의 neuron값들은 sigmoid를 거쳐서 나온 0~1의 범위를 가지는 100개의 neuron일 것이다.
두번째 weight를 -1 ~ 1 사이값으로 초기화 했다고 가정해보자.

input과 weight2의 dot product결과인 z의 범위는, input 0 ~ 1사이에서 모두 1이 나오고 weight는 모두 -1이 나오면
최소값은 -100 이다. 반대로 weight가 모두 1이 나오면 최대값은 100이다.

여기서 activation(a)값을 시각화 해서보면, 모두 saturation현상이 발생하게 되어, 결국에는 w업데이트가 멈출 것이다.

즉, weight를 - 1 ~ +1로 초기화 하게 되면 (잘못초기화),
activation func으로 넘어가는 input값이, saturation되는 값으로 넘어오게 된 순간 train이 안되게
된다.
Sigmoid 나 tanh를 사용할 경우, M.L.N.N.은 99%확률로 saturation 된다고 한다.


이 saturation을 막는 방법은 여러가지가 있다.
activation function을 ReLU로 바꾸어도 되고, weight를 다시 초기화해도 된다.
이번시간에 다룰 주제는, Weight initializaiton을 통해 saturation을 막도록 잘 초기화 해보는 것이다.


그렇다면, weight를 초기화하여, activation function에 input되는 z( feature dot product weight)가 saturation 범위에 안걸리도록 하려면 어떻게 해야할까?
sigmoid, tanh를 activation func으로 계속 쓴다고 가정하였을 때,
가장 중요한 요소z의 범위( -1 * 100   |  +1 * 100 )에서 100을 의미하는 input되는 뉴런(feature)의 개수이다.

만약 neuron의 개수가 100에서 1000개로 늘었다고 가정해보자.
weight2와 dot product 계산시 z의 최소값, 최대값도 10배 늘게 된다. 그만큼 weight범위를 1/10로 줄여야 z의 범위가 유지될 것이다..
이러한 z의 범위를 결정하는데 있어서, input의 neuron의 개수를 고려해서 weight의 초기화 범위를 수정해야한다.
각 모델의 neuron개수가 정해져있다면, weight초기화 범위만 그 neuron수에 맞게 조절하면 될 것이다.

즉, activation func(a)로 들어가는 z의 범위를 -1 과 1사이로 만들어주어 Sigmoid와 tanh의 input범위를 좁히면,
saturation범위에 안걸리게 된다.
이 때, weight 초기화시 범위를 줄이는 방법으로 z의 범위를 줄이는데, 그 기준이 ( 1/ input neuron의 개수 )가 되는 것이다.

input의 neuron의 개수에 따라 weight초기화 범위를 결정 -> z범위 바뀜 -> 시각화시 a로 들어가는 input범위 바뀜 -> saturation 안걸림


각 레이어마다 이러한 현상이 일어날 것이다.

그럼 input의 neuron 개수 뿐만 아니라 output의 neuron개수도 중요해진다.

이러한 두 개념을 전문 용어로 fan infan out이라고 한다.


Fan in (input neuron의 개수) 와 Fan out(output neuron의 개수)

fan in : input Layer의 neuron 개수
fan out : ouput Layer의 neuron 개수

지금까지는 weight초기화시 dot product 계산이 들어가는 forward propagation만 다루었기 때문에, fan in만 고려했지만,
backward propagation에서도 똑같은 현상이 일어나기 때문에 fan out의 개념도 중요하다.
output neuron의 개수(fan out)이 늘어나면 똑같이 back propagation도 늘어나기 때문에, weight 초기화 범위도 그만큼 줄여줘야한다.
이후에 다루도록 하자.


그러면, sigmoid, tanh를 activation func으로 사용하는 모델사용시 weight 초기화를
기존에 사용했던 -1 ~ +1 에다가 fan in(input neuron의 개수)를 나눈  -1 / fan in  ~  +1 / fan in 으로 uniform 초기화 해주면 될까?

사실 그렇지 않다. 경험적으로 fan in에다가 루트를 씌워준 값을 나누어주면 더 성능이 좋다고 한다.

image


그러면 이렇게 초기화한 weight는 M.L.N.N. (Sigmoid)에서 잘 작동할까? 벤치마킹한 결과를 보자.
image

x축은 epoch이며, y축은 Activation func 의 output값(Activation value)이다.
sigmoid의 경우, 시각화를 생각해서, output값이 0 or 1에 가깝다면 saturation에 걸려 안좋은 상황이다.

[] [] [] [] []   --  hidden Layer가 총 3개 + output Layer 1개에 대해서
a(activation func씌운 output value)를 계속 비교해본 것이다.

빨간색 Layer(Layer1)은 activation output이  평균이 0.5(실선)에 가깝게 나타난다. input(z)는 0에 가깝웠다는 말이 된다.
이 때, 세로바는 a의 표준편차 인데, 0.5에서 왔다갔다한다.
- 우리가 원하는 그림이다. z가 0에서 멀여져 a가 0 or 1으로 갈수록 접선의 기울기가 0이 되는 saturation이 안나와야한다.

Layer가 늘어남에 따라, 초록색Layer -> 파란색Layer -> 검은색 Layer로 갈 수록, 0.5에 가깝던 평균/표준편차가 흔들기리 시작한다.

빨->초->파 순으로 보자. Hideen Layer를 쌓으면서, 평균이 점점 높아진다. 하나 더 쌓으면 평균이 1에 가까워 질 것이다.

output Layer까지 포함하여, 총 3+1 Layer에서 하나의 hidden Layer를 더 쌓게 되면, activation function의 output이 평균 1이 되고, 그곳은 saturation을 일으키게 되는 곳이므로, w의 업데이트가 없어질 것이다.



우리는 Layer가 늘어나더라도, activation value가 saturation되지 않는 곳(sigmoid의 경우, 0.5)에서 평균과 표준편차가 일정하기를 원한다.

sigmoid를 activation func으로 쓰는 모델의 성능을 개선하기 위해 fan in을 루트를 씌워서 나누어, weight를 초기화하는 방식의 문제점은 Layer를 쌓을 수록 잘안되는 느낌이 들면서, Hidden Layer가 3개까진 수렴하나, 4개부터는 train이 안되는 문제점을 발견한 것이다.  그리고 그 이유를 activation value가 일정하지 않기 때문에 발생하는 문제라고 생각했다.

Layer를 늘리더라도, activation value가 평균과 표준편차가 일정하게 유지되도록 weight를 초기화하면 Layer를 무한개 쌓을 수 있다는 이론적 결론을 내릴 수 있다.

종합적으로, input( 이전Layer의 activation value)와 
Layer를 늘린 다음층 Layer의 activation value의 평균과 표준편차가 일정해야한다.


아래 그림은, weight initialization을 이상적으로 하여, activation output(value)의 평균(0.5로)과 표준편차가, Layer를 늘리더라도, 항상 이전Layer의 activation의 평균과 표준편차가 일정하게 나온 그래프이다.
image


어떻게 acitvation value의 평균과 표준편차가 Layer를 쌓더라도 일정하게 할 수 있을까?

현재까지는,
weight -1 ~ +1  --> z의 범위가 a의 saturation범위로 걸림 ->
fan in + 루트 개념을 도입해서 weight 초기화 -> Layer를 늘렸더니 a가 saturation범위로 가까워짐 ->
어떠한 weight 초기화 방법으로 인해 -> Layer를 늘리더라도 이전 Layer의 activation value와 평균과 표준편차가 일정하게 유지 되어야한다.


먼저, 가장 이상적인 경우를 구해보자.
평균과 표준편차가 일치해야한다. 증명에 있어서는 평균과 분산으로 weight초기화 format을 만들어보자.

image

무한이 많은 Layer가 있다고 가정하며, 그 중 중간의 Hidden Layer 2개만 떼서 생각해보자.
image

목적은, input X와 output Y (둘다 z에 activation func을 씌운 activation value)의 평균과 분산이 일치하도록 weight 초기화하여, Layer가 늘어나더라도 saturation에 안빠지고 model이 잘 train시키도록 만드는 것이다.
format을 만들기 위해서 몇가지를 가정하는데, X와 Y는 평균0으로서 서로 일치하게 만들어놓는다.
목표는 X와 Y의 분산이 일치하도록 W를 초기화 하는 것이다.
1) W의 평균은 0으로 가정( 초기화시 지정할 수 있다)
2) X와 Y의 평균은 0이라고 가정할 것( 둘다 hidden Layer의 뉴런으로서, activation values다. sigmoid, tanh는 activation value의 평균 0임)
3) 가장 단순한 linear activation function( y =x )를 사용할 것(나중에 튜닝)
  - 그 결과 Y(a) = activation function ( z = W*X)에서 --> Y = W*X 가 된다.


이제 W의 평균은 이미 0으로 가정했지만, 분산V(W)를 뭐가나오도록 초기화할지만 고민하면 된다.
image


image

여기까지의 결과로서, weight를 잘 초기화하기 위해서는,

1. weight의 범위는 [ -1/ 루트(fan in) ~ +1.0 / 루트(fan in) ]
2. weight의 분산은 [ 1 / ( fan in) ] 이 되어야한다.


그러나, 여기까지는 forward propagation만 고려한 weight 초기화 방법이다.
backward propagation도 고려해야한다. 비슷한 공식이 쓰이나, 설명은 생략한다.
결과적으로 W의 분산은 [ 1 / ( fan out ) ]이 되어야한다.

forward propagation과 backward propagtion을 모두 고려했을 때,
이상적인 weight initialization을 위한, weight의 분산은 아래와 같다.
image

input neuron의 개수와 output neuron의 개수가 동일하다면, 그냥 1/nW초기화시 분산으로 쓰면된다.
다르면, 각각의 합을 더한 것을 2에다가 나누는 조화평균을 쓴다.


결과적으로, 초기화하려는 특정구간의 W에 대해서
앞의 Layer의 neuron 개수뒤 Layer의 neuron개수를 더한 것을 2에 나누면 된다.

이러한 weight초기화 방법을 논문에서는 Normalized initialization, 실무상에서는 Xavior initialization 이라고 한다.


Gaussian 과 Uniform - 실제적으로 쓰이는 Xavior initialization시키는 weight초기화 방식2가지


weight시, 위에서 구한 Xavior initialization을 쓰고싶다면
[ 2/ fan in + fan out ] w의 variance + w의 평균은 0으로 초기화하는 방식Gaussian 초기화(평균0, 분산 Xavior지정 w초기화)라고한다.
그리고 w의 범위의 min값, max값을 지정해줘서  균등한 랜덤분포로 초기화하는 방식Uniform 초기화(범위지정 균등 w초기화)라고 한다.
image
uniform 초기화 방식은 min(a) 와 max(b)값을 지정해주면, 그 사이에서 균등하게 랜덤분포를 뽑아내며,
분산은 공식유도상  1/12 * (b-a)^12로 이미 증명되어있따.
image
Uniform의 분산이 위에서 good weight initialization이 되도록하는 Gaussian의 분산 [ 2 / (fan in + fan out) ]과 동일하게 나오도록 하기 위한 범위(a, b)를 구해보자. 결과적으로 범위는 a,b는  +- 루트( 6 / fan in + fan out)이 되어야한다.
그 과정을 증명해보자.
image



결과적으로, weight초기화시 Xavior initialization하는 방법 2가지가 있는데,
1. Gaussian 방식으로 w초기화  :  평균0, 분산 : 2 / (fan in + fan out)
2. Uniform 방식으로 w초기화 : min, max를 +/- : 루트 ( 6 / (fan in + fan out) )

이러한 Xavior initialization 으로 weight를 초기화시키는데,
forward / backward 모두 activation value의 평균과 표준편차를 Layer마다 일정하게 유지시켜,
saturation없이 w를 잘 업데이트 시킬 수 있게 된다.

위는 before아래Xavior initialization로 weight초기화 after이다.
image

image


지금 껏, M.L.N.N.의 forward propagation
어떤 hidden Layer의 activation output의 평균과 표준편차가(Sigmoid or tanh) 모든Layer에서 일정하게 유지되도록 만들어주는 weight초기화 format인 Xavior initialization(분산 :  2/ fan in + fan out    or  루트 6/ fan in+ fan out)을 쓰도록 해서
Layer를 쌓을 때마다 평균, 분산(표준편차)가 변하지 않고 일정하게 만들었다.
(before에서는 평균은 0으로 유지되나, 표준편차(분산)의 폭이 좁아지면서 0이 되면, activation output값이 전부 0이 나와버려, 잘못된 결과가 나온다)

여기서 Layer를 더 쌓을수 있어서 성능이 더 좋아질 것이다.(결정적 장점)

backward propagation은 뒤쪽의 Layer의 w변화량(L을 w로 편미분)을 앞쪽 Layer의 w변화량으로 나눈 w변화량의 비율인데, Xavior intialization을 쓰지 않게 되면, forward의 Layer 1->5 처럼, Layer 5->1로 오면서, 평균은 0으로 유지되지만, 표준편차(분산)이 폭이좁아지면서 0이 되어버리면, activation value(output)이 전부 0이 나와버려서, 잘못된 결과 -> Layer를 줄여야한다.


Xavior initialization으로 weight를 잘 초기화하면, Layer를 더 쌓을 수 있지만,  충분한데도 더 쌓지 말 것.

image


결론

1. 같은 모델이라도 weight 초기화 잘하면 Layer 더 쌓을 수 있어 성능이 좋아진다.
2. weight 초기화 방식은 Gaussian(분산)이든 Uniform(범위)이든 기본적으로 랜덤이지만, fan in과 fan out이 깊게 연관된다.
3. sigmoid나 tanh를 activation func으로 쓸 땐, Xavior(Normaizaed) initialization을 사용하여 weight를 초기화하면,  Layer를 더 쌓을 수있다.
4. 그러나 ReLU는 Xavior를 구할 때 쓰던 가정인, X,Y,W의 평균 = 0 중
   activation output인 X, Y의 평균이 0이 아니기 때문에 튜닝해야한다.



실습

로컬 노트북 파일

복습 노트북의...뭐가 수치..가 이상하게 나왔다. solution을 참고해서 다시 해보자.


Keras에서의 weight 초기화 방식


https://keras.io/initializers/

- random_normal : Gaussian분포로서(평균0, Xavior분산적용) -> 평균과 표준편차를 이용해서 weight 초기화
- random_uniform : min, max지정하여 하는 것. 앞으로 잘 안쓰일 것(Small_random_number)
- Othgonal : 자연어 처리시 쓰이는 것
- lecun_uniform : 그냥 루트 씌운 것
- glorot_normal : Xavior initialization by Gaussian 방식
- glorot_uniform : Xavior initialization by Uniform 방식
- he_normal : 케인 he 가 만든 ReLU의 initialization
- he_uniform: 케인 he 가 만든 ReLU의 initialization

imageimageimageimageimage

+ Recent posts