Ⅰ. activation functions

FC, CNN 등등의 Layer는, 데이터 입력이 들어오면 가중치와 곱하는 연산을 마친 뒤, 활성함수(비선형 연산)를 거치게 된다. 지금부터는 활성 함수의 종류와 장단점에 대하여 소개한다.

1. sigmoid

\[\sigma(x) = \frac{1}{1+e^{-x}}\]

각 입력을 받아서 그 입력을 0~1 사이의 값이 되도록 해준다. 입력값이 크면 출력은 1에 가까울 것이고, 그렇지 않으면 0에 가까울 것이다. 뉴런의 firing rate를 saturation 시키는 것으로 해석시킬 수 있어서 인기가 많았다고 한다(= 0~1 범위의 값을 가질 수 있어서 인기가 많았다).

1-1) 문제점

1-1-1) Gradient Vanishing 문제

sigmoid의 양 끝값은 saturation 되어있는 것을 확인할 수 있는데, 이러한 sigmoid의 특성이 gradient를 없애는 문제가 발생한다. sigmoid를 activation function으로 사용했을 때, backprop 과정에서 일어날 수 있는 상황을 생각해보자.

우선, sigmoid gate의 gradient는 $\frac{\partial L}{\partial x} = \frac{\partial L}{\partial \sigma} \frac{\partial \sigma}{\partial x}$이며, 이 gradient는 앞단으로 계속 전파된다. 이 때, 만약 x가 -10이라면, $\frac{\partial \sigma}{\partial x}=0$(x=-10에서 sigmoid가 flat하기 때문에)이므로 gradient가 0이 된다. 따라서 이 부분에서 gradient가 죽어버리게 되고 밑으로 0이 계속 전달되게 된다. 반대로, x가 10인 경우에도 동일하게 sigmoid가 flat하므로 gradient가 다 죽게된다. 따라서 sigmoid가 flat하지 않은 x=0 근처에서만 잘 동작하고 나머지 구간에서는 이러한 기울기 소실문제가 발생한다.

1-1-2) No zero-centered

sigmoid의 output은 0~1사이의 값으로, 항상 양수이며 zero-centered하지 않다.

또한, sigmoid는 $\sum_{i}w_i x_i + b$ 를 input으로 받으므로, 이를 수식으로 $ f( \sum_{i}w_i x_i + b) $ 와 같이 표현할 수 있다. 여기서, W에 대한 $gradient = \frac{\partial L}{\partial \sigma} \frac{\partial \sigma}{\partial w}$ 인데, $\frac{\partial f}{\partial w}$는 w가 모두 소거되므로 x에 관한 식임을 알 수 있다. 여기서, sigmoid는 항상 양수이기 때문에 x(local gradient)도 항상 양수이다. 따라서 sigmoid의 gradient는 x값에 상관없이 항상 upstream gradient의 부호와 동일하다는 것을 알 수 있다. 즉, W가 모두 같은 방향(파라미터 업데이트 시, 다 같이 증가 or 다 같이 감소)으로 움직이게 될 것이다. 이렇게 되면 다음과 같은 문제가 발생할 수 있다.

그래프와 같이 4분면 중 gradient가 이동할 수 있는 방향은 2개 사분면밖에 없다(부호가 동일한 사분면은 1,3 사분면). 만약 이론적으로 가장 최적의 W 업데이트가 저 파란색 화살표이고, 빨간 화살표의 시작부분부터 가중치를 업데이트한다고 하면, gradient를 여러번 업데이트 해야하는 문제가 생길 수 있다. 파란색 방향으로 gradient가 내려갈 수 없으므로 gradient 가동범위 내에서 여러번 업데이트 해야하는데, 이는 매우 비효율적이다. 이러한 이유로, output이 zero-mean인 function을 원하는 것이다.

1-1-3) exp() 계선비용 문제

exp() 함수는 다른 함수에 비해 계산 비용이 크다. 그리 큰 문제는 아니지만, 굳이 문제로 뽑자면 포함될 수 있다.


2. tanh

sigmoid와 유사하지만 output의 범위를 -1~1로 늘렸다. 범위를 -1까지 확장함으로써 output이 zero-centered하게 되었다.

2-1) 문제점

여전히 양 끝 값에서 평평해지는 구간 존재한다. 따라서 여전히 Gradient Vanishing 문제가 발생한다.


3. ReLU

\[f(x) = max(0,x)\]

ReLU는 딥러닝 분야에서 상당히 자주 쓰이는 activation function으로, 입력값이 음수면 0을 출력, 양수면 입력값 그대로를 출력한다.

3-1) 장점

  1. Gradient Vanishing 문제가 없다.

    ReLU의 가장 큰 장점은, 양의 값에서 완만해지는 부분이 없기 때문에 saturation 되지 않는다는 것이다. 따라서 Gradient Vanishing 문제가 없다.

  2. 지수항이 없고, 단순 max연산만 하기 떄문에 계산이 매우 빠르다.

    그 속도는 sigmoid나 tanh보다 6배정도 더 빠르다고 한다.

  3. 생물학적 타당성또한 relu가 sigmoid보다 더 크다.

    실제 뉴런의 입/출력값은 sigmoid보다 ReLU스럽다고 한다.

3-2) 문제점

3-2-1) zero-centered가 아님

ReLU도 마찬가지로 output이 항상 양수이기 때문에 zero-centered하지 않다. ReLU가 가진 이슈이기도 하다.

3-2-2) 음의 경우 saturation

x가 음수이면 gradient는 0이 된다. 따라서 ReLU는 기본적으로 gradient의 절반을 죽여버리는 셈이다.

3-3-3) Dead ReLU

  1. w의 시작점이 data cloud(train data)에서 너무 멀리 떨어져있는 경우

    어떤 데이터 입력에 대해서도 activate되지 않고, backprop이 일어나지 않는다. (activate되지도, update되지도 않음)

  2. learning late가 너무 큰 경우

    가중치가 날뛰어서 ReLU가 데이터의 mainfold를 벗어나게 된다. (처음에는 학습이 잘 되다가 갑자기 죽어버림)

실제로 학습을 다 시켜놓은 네트워크를 보면 10~20% 가량은 dead relu가 되어있다. relu를 사용하고 있다면 대부분의 네트워크가 이 문제를 겪는다고 할 수 있다. 10%~20%가량이면 네트워크 학습에 크게 지장이 없긴 하지만, dead relu의 비율이 그 이상으로 올라가지 않도록 주의할 필요는 있을 것 같다.

이를 해결하기 위해, relu 초기화 시 positive biases를 추가해주는 경우가 있다. 이렇게 되면, gradient update 시에 active relu가 될 가능성을 높여줄 수 있다고 하지만, 실제로는 도움이 안된다는 의견도 있다. 대부분은 zero-biases로 세팅한다.


4. Leaky ReLU

\[f(x) = max(0.01x, x)\]

negative regime에도 기울기를 살짝 줘서, ReLU의 Gradient Vanishing 문제를 해결하였다. sigmoid, tanh보다 계산도 빠르며, dead relu현상도 없다고 한다.


5. parametric rectifier(PReLU)

\[f(x) = max(\alpha x, x)\]

negative regime에서 기울기가 존재함은 leaky relu와 비슷하지만, prelu는 기울기를 $\alpha$라는 파라미터로 정할 수 있게 하였다. 여기서 $\alpha$는 backprop으로 학습시킬 수 있으며, 활성함수가 좀 더 유연해지게 된다.


6. ELU

\[f(x) = \begin{cases} x \quad \quad \quad \quad \quad \quad \quad if \; x>0 \\ \alpha ( exp(x) - 1 ) \quad \; \ if \; x \le 0 \end{cases}\]

ReLU의 장점을 가져오며 zero-mean에 가까운 출력을 보인다. saturation을 갖긴 하지만, 이러한 saturation과 deactivation이 모델을 좀 더 noise에 강해지게 한다고 주장한다. leaky relu처럼 zero-mean의 출력을 내지만 saturation의 관점에서 relu와 닮아있는 모습이다. 따라서 ReLU와 leaky relu의 중간정도라고 보면 된다.


7. Maxout Neuron

\[max(w_1^Tx+b1, w_2^Tx+b2)\]

입력을 받아들이는 특정한 기본형식을 미리 정의하지 않는 대신에, w1와 x내적한 값 + b1, w2와 x내적한 값 + b2의 최대값을 input으로 사용한다. relu와 leakey relu의 일반화 된 형태라고 할 수 있다. maxout은 이 두개의 선형 함수를 취하기 때문에, saturation되지 않으며 gradient가 죽지 않는다고 한다. 그러나 w1, w2를 지니고 있어야 하기 때문에 뉴런당 파라미터 수가 두배가 된다.


8. 정리

실제로 가장 많이 쓰이고, 대부분의 상황에서 잘 동작하는건 ReLU이다. 하지만 Dead ReLU의 비율을 줄이기 위해서는 Learning Rate를 아주 조심스럽게 결정해야 한다. 이외의 LU계열 activation fucntion들은 아직 실험단계이므로, 참고만 하도록 하자. 또한, sigmoid나 tanh같은 경우는 구식이므로 웬만하면 LU계열 function을 사용하는 것을 추천한다.



Ⅱ. data preprocessing

데이터 전처리는 대표적으로 zero-centered scaling, Normalize등이 있다. 이미지는 이미 각 차원간에 스케일이 어느정도 맞춰져있기 때문에 zero-centered scaling정도만 해주며, 스케일이 다양한 ML 문제에서는 normalization까지 해준다. zero-centered scaling은 모든 특징들이 동일한 범위에 있도록 데이터를 정규화해서 그 특징들이 결과에 동등하게 기여할 수 있도록하는 것이다. training data에서 평균 이미지 채널 계산하고, 이것을 각 이미지 채널에서 빼는 방식이다. 해당 내용은 위에서 소개했으므로 여기까지만 설명하겠다.

1. Sigmoid zero-centered scaling?

그렇다면 sigmoid의 zero-mean문제를 전처리로 해결 가능할까?

첫 번째 레이어에 대해서는 부분적으로 해결할 수 있다. 그러나, “전처리”라는 것은 input 이미지에 대해서만 영향을 미칠 수 있기 때문에, 그 다음 layer부터는 동일한 문제를 겪게 될 것이다. 더 깊은 네트워크일 수록 이 문제는 더 심해진다.



Ⅲ. weight initialization

가중치를 적절한 값으로 초기화하는 것은 중요하다. 만약 모든 파라미터를 0으로 세팅(모든 가중치가 0)한다면, 모든 뉴런이 전부 똑같은 일을 하게될 것이다. 모든 뉴런은 모두 다 같은 연산을 하게되므로, 출력도 모두 같을 것이고 gradient도 같을 것이다. 따라서 모든 가중치가 똑같은 값으로 업데이트 되며, 결국 모든 뉴런이 모두 똑같이 생기게 될 것이다. 이렇게 되면, Symmetry breaking이 일어날 수 없기 때문에 모델이 적절한 추론을 하지 못하게 된다.

따라서 가중치를 임의의 작은 값으로 초기화 해야한다. 그렇다면, 우리는 다음과 같은 아이디어를 떠올릴 수 있을 것이다.

1. Small random numbers

\[W = 0.1 \times np.random.randn(D, H)\]

초기 W를 standard gaussian에서 랜덤 샘플링 하고 좀 더 작은 값을 위해 0.01을 곱해, 표준 편차를 0.01로 만들어주는 방법이다.

이 방법은 작은 네트워크에서는 symmetry breaking에 충분하지만, 깊은 네트워크에서는 잘 작동하지 않을수도 있다.

1-1) 문제점

아래 그래프는 이 가중치로 forward pass 했을 때, 각 레이어별 activations의 평균과 표준편차를 나타낸 것이다. 이 때, activation function으로는 tanh를 사용하였다.

activation 값은 x와 W를 내적한 값에 tanh를 취한 것이다. 따라서, activation의 평균은 tanh 특성상 항상 0 근처에 있는 것을 확인할 수 있다(tanh의 zero-centered한 특성 반영). 그러나, 표준편차는 가파르게 줄어서 4번째 레이어부터는 거의 0에 수렴하는 것을 볼 수 있다.

아래 그래프는 위에서 얻은 mean, std를 바탕으로 분포로 표현한 것이다.

첫번재 레이어에서는 gaussian스럽게 생긴 좋은 분포를 형성하는 것을 확인할 수 있으나, w를 곱하면 곱할수록 출력값이 급격히 줄어드는 것을 확인할 수 있다. 이는 w가 너무 작은 값들이기 때문에 Wx가 결국 0이 되어 모든 활성함수 결과가 0이 되었기 때문이다. 이는 우리가 원하는 결과가 아니다.

backward pass의 경우도 살펴보자. Chain rule에 의해서 gradient는 upstream gradient $\times$ local gradient이다. $\frac {\partial Wx}{\partial W} = x$이므로 $gradient = upstream \; gradient \times x$ 가 된다. 여기서, $x$는 매우 작기 떄문에 gradient도 매우 작을 것이고, 결국 upstream gradient는 0에 수렴하여 업데이트가 잘 일어나지 않게 된다.

cf) forward pass의 형태를 살펴보고 이런 경우 gradient가 어떤 식으로 전파되는지 짐작할 수 있어야 한다


2. Big random numbers

만약 다음과 같이 가중치를 좀 더 큰 값으로 초기화 하는 경우는 어떨까?

\[W = 1.0 \times np.random.randn(D, H)\]

이런 경우, activation function(tanh)의 특성에 따라 값들이 saturation 될 것이다. tanh의 양 끝의 output은 기울기가 완만하므로 그 때의 gradient는 0이 된다. 따라서 가중치가 업데이트되지 않기 때문에, 출력은 항상 -1이거나 +1이 된다.

적절한 가중치를 얻는 것은 너무 어렵다. 너무 작으면 사라져버리고, 너무 크면 saturation되어 버린다. 적절한 가중치를 얻을 수 있는 좋은 방법은 없을까?


3. Xavier Initialization(Glorot, 2010)

초기 가중치 설정 과정에서 선택할 수 있는 가장 최선의 방법이다. Standard Gaussian에서 임의로 추출한 값을 “입력의 수”로 스케일링 해줌으로써, 입/출력의 분산을 맞춰준다.

\[W = \frac {np.random.randn(D, H)}{\sqrt{D}}\]

위 수식을 통해 직관적으로 이해할 수 있는 것은, 입력의 수가 작으면 그만큼 작은 값으로 나누어 좀 더 큰 값을 얻는다는 것이다. 즉, 입력의 수가 작은 경우 큰 가중치를 곱하고, 반대로 입력의 수가 큰 경우 작은 가중치를 곱해 입/출력의 분산을 맞춰준다. 각 레이어의 입력이 unit gaussian(approximately하다)이길 원한다면 이런류의 초기화 기법을 이용해 볼 수 있다.

모든 Layer의 분포가 gaussian을 따르는 것을 확인할 수 있다.

3-1) 문제

그러나, Xavier Initialization은 ReLU를 쓰면 잘 동작하지 않는다. ReLU는 출력의 절반을 죽이고 그 절반은 매번 0이되기 때문에, 출력의 분산을 반토막내버린다. 따라서 이전과 같은 가정을 하면 ReLU에서 값이 너무 작어져 잘 작동하지 않는다.

분포 그래프를 보면, 뒷단으로 갈수록 분포가 줄어들기 시작하여 점점 더 많은 값들이 0이 되고, 결국에는 비활성 되는 모습이다.

3-2) 해결법 : MSRA Initialization (Kaiming He, 2015)

\[W = \frac {np.random.randn(D, H)}{\sqrt{D/2}}\]

뉴런 중 절반이 없어진다는 사실을 고려, Xavier의 수식에 추가적으로 2를 더 나눠줌으로서 해당 문제를 해결하였다. ReLU에 최적인 weight 초기화 방법이라고 할 수 있다.

위에서도 잠시 언급하였지만, 적절한 가중치를 얻는 것은 너무 어렵다. 너무 작으면 사라져버리고, 너무 크면 saturation되어 버린다. 그렇다면, weight initialization에 의존하지 않고 Gradient Vanishing 문제를 막을 수 있는 방법은 없을까?


4. Batch Normalization (Ioffe and Szegedy, 2015)

앞서 살펴보았던 방법들은, Gradient Vanishing 문제를 해결하기 위해 activation function을 조작한다거나 가중치 조작을 신중하게 하는 방법 등 간접적인 방법을 사용하였다. 그러나 Batch Normalization은 학습하는 과정에 직접적으로 개입하여 정규화를 시켜준다.

Ioffe와 Szegedy는 위에서 발생했던 문제점이, 각 layer를 지나면서 입력값의 분포가 달라지기 때문에 발생하는 것이라고 생각하였다. 따라서 layer를 지날때마다 분포를 맞춰주자는 것이 Batch Normalization의 기본 idea이다. 각 Layer마다 mini batch를 뽑아서 이 batch에 대한 표본 평균/분산으로 정규화 시켜주며, 해당 정규화 식은 여전히 미분 가능한 함수이기 때문에 forward, backward pass가 가능하다.

일반적으로 FC(or Conv)와 Activation Function 사이에 위치한다. Layer의 연산 결과를 정규화 시킨 뒤, activation function으로 넘겨주는 것이다. 그러나 종종 Batch Normalization을 통한 unit gaussian을 Activation Function에 넘겨주는 게 적합하지 않은 상황이 있을 수도 있다. BN은 Normalization이 적합한가?에 대한 판단 조차도 학습에 의해 가능하게 만들었다.

\[\begin{align} y^{(k)} &= \gamma^{(k)}\hat{x}^{(k)}+\beta^{(k)} \\ \gamma &= \sqrt{Var[x^{(k)}]}\\ \beta &= E[x^{(k)}] \end{align}\]

BN은 위에서 설명한 Normalization 단계를 거친 후, 정규화 된 것을 조정할 수 있는 단계를 거치게된다. Normalize된 정도(scale)를 조정할 수 있는 파라미터 $\gamma$ 와 이를 shift시켜주는 파라미터 $\beta$ 를 학습 가능하게 하여, 모델이 activation func에 얼마나 saturation 시킬지를 학습시킨다. 이를 통해 유연성 얻을 수 있으며, saturation 정도를 조정할 수도 있다.

4-1) 요약

4-1-1) BN 과정

  1. 각 Layer input의 mini-bach에서의 평균과 분산을 계산한다.

  2. 평균과 분산으로 normalize 한다.

  3. 추가적인 scaling/shifting factor를 사용한다.

4-1-2) 장점

  • gradient의 흐름을 보다 원활하게 해주어, 학습이 더 잘되게(robust) 해준다.

  • 따라서 BN을 사용하면, learning rates를 더 키울 수도 있고, 다양한 초기화 기법들도 사용해 볼 수 있다.

  • 결과적으로, BN을 쓰면 학습이 더 쉬워진다.

위에서 설명한 장점 외에도, BN은 regularization 기능도 한다. 왜냐하면, 각 레이어의 입력은 해당 배치의 표본 평균으로 normalize되는데, 이 레이어의 출력은 오직 하나의 샘플에 대한 deterministic한 값이 아니게 되기 때문이다. 즉, batch 내의 모든 데이터가 입력으로 한데 모이게 되며, 이는 regularization effect를 준다.



Ⅳ. Babysitting the Learning Process

  • 1. preprocess the data

    zero-mean을 사용

  • 2. Choose the architecture

    학습하고자 하는 모델을 선택한다.

  • 3. 초기 loss가 정상인지 확인

    forward pass를 하고난 후에 loss가 그럴듯 해야 한다. 가령 우리가 softmax를 사용하고자 한다면, 가중치가 작은 값일 때 Loss는 negative log likelihood가 되어야 한다(loc(C) = -log(1/c)). 가령 10개의 클래스라면 loss는 -log(1/10). 그렇지 않으면 잘못된 모델이다.

  • 4. regularization 추가

    regularization term을 추가해봐서 Loss가 증가하는지 확인해본다. 손실함수에 regularization term이 추가되면, Loss도 증가해야 한다.

  • 5. 우선 학습

    우선, 데이터의 일부만 학습 시켜 본다. 데이터가 적으므로 당연히 overfit이 생길 것이고 loss가 많이 줄어들 것이다. 이 때, regularization term을 사용하지 않고, Loss가 내려가는지를 확인하여 Epoch마다 Loss가 0을 향해 꾸준히 잘 내려가는지 확인한다. 데이터가 작은 경우라면 모델이 완벽하게 데이터를 overfit할 수 있어야 하며, 이를 확인하는 단계이다.

  • 6. 학습

    전체 데이터 셋 사용을 사용하여 학습한다. 초기에는, regularization을 약간만 주면서 적절한 learning rate를 찾는다. 이 때, Loss가 잘 줄어들지 않는다면 Learning rate가 지나치게 작은 것이고, 반대로 Loss(cost)가 NaN(발산)이면, Learning rate가 지나치게 큰 것이다. 보통은 1e-3 ~ 1e-5 사이의 값을 사용한다.



Ⅴ. Hyperparameter Optimization

1. 과정

하이파 파라미터 튜닝은 넓은 범위에서 값을 골라내는 coarse stage 이후, 더 좁은 범위에서 값을 골라내는 fine stage를 거친다.

  • 1. coarse stage

    넓은 범위에서 값을 골라냄. epoch 몇 번 만으로도 현재 값이 잘 동작하는지 알 수 있음. nan이 뜨거나 혹은 loss가 줄지 않거나 하는 것을 보면서, 어느 범위에서 잘 동작하겠다를 대충 알게 됨.

  • 2. fine stage

    좀 더 좁은 범위를 설정하고 학습을 좀 더 길게 시켜보면서 최적의 값을 찾는다. train 동안 cost가 어떻게 변하는지를 살펴봄. cost가 NaN으로 발산하고 있다면, loop를 멈춰버리고 다른 하이퍼 파라미터를 선택


실제로는 grid search 보다는 random search를 하는 것이 더 좋다. 만약 모델이 어떤 특정 파라미터의 변화에 더 민감하게 반응하고 있다고 생각해보면(노랑 < 초록), Grid Search가 더 비효율적인 움직임을 보인다고 할 수 있다. 위 그림에서 grid는 search 기회 3번, random은 search 기회가 9번 주어진다.

3. Visualization

3-1) Learning Rate

Learning rate가 크면, 최적해를 찾지 못하고 건너 뛰는 경우가 생기기 때문에 Loss가 일정 수준 이하로 줄지 않거나, 발산한다. 반대로 Learning rate가 너무 낮으면 Loss가 제대로 줄어들지 않는 모습이다. 따라서 Loss가 부드러운 곡선을 그리는 것이 바람직하다고 할 수 있다. 실제로는 Loss가 아래 파란 그래프처럼 나오는 것이 바람직하다.

3-2) Weight Initialization

또한 아래 그래프처럼 Loss가 줄지 않다가 갑자기 줄어드는 경우가 있을 수 있다.

이런 경우는, gradient의 bakcpropagation이 초기에는 잘 되지 않다가 학습하며 회복되는 것이다. 따라서 Initialization을 다시 해줘야한다.

3-3) Regularization Term

train accuracy와 validation accuracy가 큰 차이를 보인다면 overfitting을 의심해 볼 수 있다. 이런 경우, regularization term을 추가하면 된다.

댓글남기기