목차
- Overview
- Activation Function
- Data Preprocessing
- Weight Initialization
- Batch Normalization
- Babysitting the Learning Process
- Hyperparameter Optimization
👀 코드 보기 , 🤷♀️
이 두개의 아이콘을 누르시면 코드, 개념 부가 설명을 보실 수 있습니다:)
CS231N: Lecture 6강의를 빠짐 없이 정리하였고, 어려운 개념에 대한 보충 설명까지 알기 쉽게 추가해 두었습니다.
Overview
1. One time setup
- 우선 NN 학습을 처음 시작할때 필요한 기본 설정에 대해 알아볼 것임
- activation functions(활성함수 선택), preprocessing(데이터 전처리), weight initialization(가중치 초기화), regularization, gradient checking
2. Training dynamics
Training dynamics에 대해서도 다룰 것임
- babysitting the learning process (학습이 잘 되고 있는지 확인하는 법에 대해 배울 것)
- parameter updates (어떤 방식으로 파라미터를 업데이트할 것인지에 대해서도 배울 것)
- hyperparameter optimization (가장 적절한 하이퍼파라미터를 찾기 위해 하이퍼파라미터를 Optimization 하는 방법도 배울 것)
3. Evaluation
model ensembles
- 평가에 대해서도 이야기 할 것
- Model ensemble에 대해서도 배움
Activation Function
자 먼저 지난시간에 봤던 Layer(ex_FC, CNN)를 살펴보면 데이터 입력이 들어오면 가중치와 곱한다.
그 다음 활성함수, 즉 비선형 연산을 거친다.
활성함수의 예시를 보겠다.
이번 시간에는 좀 더 다양한 종류의 활성함수와 그들간의 Trade-off에 대해 다뤄보도록 하겠다.
Sigmoid
[수식 & 그래프]
[특징]
- 하는 일은 각 입력을 받아서 그 입력을 [0, 1] 사이의 값이 되도록 해줌.
- 입력의 값이 크면 Sigmoid의 출력은 1에 가까움 값이 작으면 0에 가까울 것임.
- 0 근처 구간(rigime)을 보면 선형스러움 (/ 모양)
- 역사적으로 아주 유명: Sigmoid가 일종의, 뉴런의 firing rate를 saturation 시키는 것으로 해석할 수 있기 때문임.
- 어떤 값이 0에서 1 사이의 값을 가지면, 이를 firing rate라고 생각할 수 있을 것임
- 나중에 배울 ReLU가 생물학적 타당성이 더 크다는게 밝혀졌지만 Sigmoid 또한 이런 식으로 해석할 수 있다는 점을 알고 넘어가면 된다.
[문제점]
Sigmoid에는 3가지 문제점이 존재한다
1) Saturated neurons “kill” the gradients
- 이는 기울기 소실, 폭발 문제인데 위의 문장만 봐선 이해가 안되니 더 설명을 해보겠다.
- 이 Sigmoid gate를 Computraional graph에서 한번 살펴보겠다.
- 여기 데이터 X가 있고 출력이 있습니다.
- Backprop에서 Gradient는 어떻게될까?
- 우선 dL/dσ 가 있습니다 ➡ dL/dσ가 밑으로 내려감
- 그리고 dX/dσ를 곱하게 될 것임 ➡ 이 값이 local sigmoid function의 gradient가 될 것이다.
- 그리고 이런 값들이 계속 연쇄적으로 Backporp될 것이다.
- ❌) X가 -10이면 gradient는 어떻게 될까?
- gradient는 0이 됨(그래프 맨 왼쪽으로 0)
- Sigmoid에서 음의 큰 값이면 sigmoid가 flat하게 되고 gradient가 0이 됨(그래프를 확인해보면 된다)
- 그리고 이 값이 밑으로 내려가면 거의 0에 가까운 값이 backprob 될 것이다 ➡ 이 부분에서 gradient가 죽어버리게 되고 밑으로 0이 계속 전달되게 된다.
- ⭕) X가 0이면 gradient는 어떻게 될까?
- 이 구간에서는 잘 동작할 것이다(그래프 중간으로 정상작동)
- 아주 그럴싸한 gradient를 얻게 될 것이고 backprop 이 잘 될 것임
- ❌) X가 10이면 gradient는 어떻게 될까?
- X가 값이 큰 양수 일 경우에도 sigmoid가 flat하기 때문에 gradient들 다 죽이게 됨(그래프 맨 오른쪽으로 0)
- 그러면 gradient가 잘 흐르지 않겠다.
2) Sigmoid outputs are not zero-centered
- 두 번째 문제는 바로 sigmoid 의 출력이 zero centered 하지 않아 gradient 업데이트가 효율적이지 않음
- 이게 왜 문제인지 한번 알아보도록 하겠다.
- ❌) 뉴런의 입력(x)이 항상 양수or 음수일 때
- 이 x는 어떤 가중치랑 곱해지고 활성함수를 통과할 것이다.
- 이 Layer에서의 local gradient가 어떻게 될지 한번 생각해 보자.
우선 dL/d(활성함수)를 계산하여 Loss가 내려가고 Local gradient가 있을텐데, 기본적으로 그냥 X이다.
위에서 넘어오는 dL/df(활성함수)의 값이 양수가 되면, local gradient는 이 값이랑 곱해질 것이고 dF(활성함수)/dW는, 그냥 X가 될 것이다.
➡ gradient의 부호는 그저 위에서 내려온 gradient의 부호과 같아질 것이다.
➡ W가 모두 같은 방향으로만 움직일 것임을 의미
➡ 파라미터를 업데이트 할 때 다 같이 증가하거나 다 같이 감소하거나 할 수 밖에 없음
➡ 여기서의 문제는 이런 gradient 업데이트는 아주 비효율적이라는 것
- 예제로 비효율 확인(W가 이차원인 예제)
- W에 대한 두개의 축으로 이루어져 있다.
- 전부 양수 또는 음수로 업데이트된다는 것을 해석해보면, gradient가 이동할 수 있는 방향은 4분면 중 이 두 영역뿐(초록색)이다.
그래서 이론적으로 가장 최적의 W 업데이트가 파란색 화살표이고 우리가 어떤 초기 시작점부터 내려간다고 했을 때, 시작점은 빨간 화살표의 맨 처음 부분이다. - 하지만 gradient는 파란색 방향으로 내려갈 수 없다. ➡ 그 방향으로는 움직일 수 없기 때문이다.
- 그렇기 때문에 우리는 여러번 gradient 업데이트를 수행해 줘야 한다. ➡ 가령 이렇게 빨간 화살표 방향와 같이, gradient가 이동 가능한 방향으로만 이동을 할 수 있게 된다.
- 이것이 바로 우리가 일반적으로 zero-mean data을 원하는 이유이다
- ⭕ 입력 X가 양수/음수를 모두 가지고 있으면 전부 같은 방향으로 움직이는 일은 발생하지는 않을 것이다.
3) exp() is a bit compute expensive
exp()
로 인해 계산비용이 크다는 것임 ➡ 그렇게 큰 문제는 아님- 큰 그림으로 봤을 때 다른 연산들, 가령 내적의 더 계산이 비싸긴 하지만 굳이 문제로 뽑자면 이렇다는 것이다.
tanh
sigmoid 랑 유사하지만 범위가 [-1 , 1]
[수식 & 그래프]
[특징]
- 문제 2) 해결: sigmoid와 가장 큰 차이점이 있다면, 이제 zero-centered 라는 것이다.
- 문제 1) 존재: 하지만 saturation때문에 여전히 Gradient가 죽음(여전히 gradient가 평평해 지는 구간존재)
- tanh는 sigmoid보다는 조금 낫지만 그래도 여전히 문제점 존재.
ReLU
CNN 강의할 때 본 적 있을 것이다.
Conv layer들 사이 사이에 ReLU가 있었다
[수식 & 그래프]
[특징]
- 이 함수는 element-wise 연산을 수행하며 입력이 음수면 값이 0이 된다, 그리고 양수면 입력 값 그대로를 출력한다.
- ReLU는 상당히 자주 쓰임
- ImageNet 2012에서 우승한 AlexNet이 처음 ReLU를 사용하기 시작함
[문제점]
그리고 기존 sigmoid와 tanh한테 있었던 문제점들을 한번 살펴보자면,
1) Saturated neurons “kill” the gradients(반만 해결)
- ⭕ ReLU는 양의 값에서는 saturetion되지 않음. ➡ 적어도 입력스페이스의 절반은 saturation 되지 않을 것임
➡ ReLU의 가장 큰 장점 - ❌ 음의 경우에선 문제 해결 못함
- ❌) X가 -10인 경우
gradient가 0이 될 것이다. - ⭕) X가 10인 경우
선형 영역(linear regime)에 속하므로 문제가 되지 않는다(양의 값이다) - ❌) X가 0인 경우
여기에서는 정의되지 않지만 실제로는 0이다.
- ❌) X가 -10인 경우
- 이는 ReLU는 gradient의 절반을 죽여버리는 셈이다.
➡ 그래서 우리는 dead ReLU 라는 현상을 겪을 수 있다. - dead ReLU 예시
- 자 여기 data cloud(여러분이 가진 traing data라고 생각하면 됨)가 있으면 ReLU에서는 평면의 절반만 activate 된다는 것을 알 수 있다.
각 평면(초록 빨강 직선)이 각 ReLU를 의미한다고 보면 된다. - 그리고 ReLU가 data cloud에서 떨어져 있는 경우에 “dead ReLU” 가 발생할 수 있다.
➡ dead ReLU에서는 activate 가 일어나지 않고 update되지 않는다.
➡ 반면 active ReLU는 일부는 active되고 일부는 active하지 않을 것이다.
- 자 여기 data cloud(여러분이 가진 traing data라고 생각하면 됨)가 있으면 ReLU에서는 평면의 절반만 activate 된다는 것을 알 수 있다.
- dead ReLU 원인
- 초기화를 잘 못한 경우
- 가중치 평면이 data cloud에서 멀리 떨어져 있는 경우
이런 경우 어떤 데이터 입력에서도 activate 되는 경우가 존재하지 않을 것이고 backporp이 일어나지 않을 것이다.
이런 경우 update되지도 activate되지도 않을 것이다. - Leraning rate가 지나치게 높은 경우(더 흔함)
처음에 “적절한 ReLU” 로 시작할 수 있다고 해도 만약 update를 지나치게 크게 해 버려 가중치가 날뛴다면 ReLU 가 데이터의 manifold를 벗어나게 된다. ➡ 그래서 처음에는 학습이 잘 되다가 갑자기 죽어버리는 경우가 생기는 것이다.
- 실제로 학습을 다 시켜놓은 네트워크를 살펴보면 10~20% 가량은 dead ReLU가 되어 있다.
하지만 일부분만 dead ReLU가 발생하는 경우는 네트워크 학습에 크게 지장이 있진 않다.
2) Sigmoid outputs are not zero-centered(해결 못함)
- ReLU는 더이상 zero-centered가 아님 ➡ tanh가 이 문제는 해결했는데 ReLU는 다시 이 문제를 가지고 있게 됨
3) exp() is a bit compute expensive(해결)
- 계산 효율이 아주 뛰어남
- 기존의 sigmoid는 함수 안에 지수 항이 있었지만, ReLU는 단순히 max 연산이므로 계산이 매우 빠르다.
➡ ReLU를 사용하면 실제로 sigmoid나 tanh보다 수렴속도가 훨씬 빠르다 (거의 6배 정도) - 생물학적 타당성도 ReLU가 sigmoid보다 크다.
📜 학생 질문: data cloud에서 ReLU가 죽어버리는지 아닌지를 어떻게 알 수 있나요?
여기 예를 다시 한 번 보겠습니다. 간단한 2차원의 경우입니다.
ReLU의 입력 값은 가령 W1X1 + W2X2 같은 값이 되겠죠
그리고 이 가중치가 초평면(초록 빨강)을 이루게 되겠죠
그리고 여기에서 positive인 절반만 가져갈 것입니다. 나머지 절반은 죽어버리겠죠
그러니 W의 초평면의 위치와 Data의 위치를 고려했을 때 초평면 자체가 Data와 동떨어지는 경우가 발생할 수 있습니다.
학습 과정에서 일부 ReLU는 data cloud와 동떨어져 있을 수가 있습니다.
Leaky ReLU
ReLU를 조금 수정한 버전 중 하나
[수식 & 그래프]
[특징]
- ReLU와 유사하지만 negarive regime에서 더 이상 0이 아님
[문제점]
negative에도 기울기를 살짝 주게 되면 앞서 설명했던 문제를 상당 부분 해결하게 됨
1) Saturated neurons “kill” the gradients(해결)
Leaky ReLU의 경우에는 negative space 에서도 saturation 되지 않음.
dead ReLU 현상도 더 이상 없음
2) Sigmoid outputs are not zero-centered(해결)
3) exp() is a bit compute expensive(해결)
계산이 효율적임
sigmoid 나 tanh보다 수렴을 빨리 할 수 있음(ReLU랑도 비슷하게 생겨서)
PReLU
ReLU를 조금 수정한 버전 중 하나
= parametric rectifier
[수식]
[특징]
- PReLU는 negative space에 기울기가 있다는 점에서 Leaky ReLU와 유사함.
- 다만 여기에서는 기울기가 alpha 라는 파라미터로 결정됨.
- alpha를 딱 정해놓는 것이 아니라 backpro으로 학습시키는 파라미터로 만든 것임.
➡ 활성함수가 조금 더 유연해 질 수 있음
ELU
ReLU를 조금 수정한 버전 중 하나
= Exponential Linear Units
[수식 & 그래프]
[특징]
- ELU는 ReLu의 이점을 그대로 가져옴
- 하지만 ELU는 zero-mean 에 가까운 출력값을 보임 (zero-mean에 가까운 출력은 앞서 leaky ReLU, PReLU 가 가진 이점이었음)
- 하지만 Leaky ReLU와 비교해보면 ELU는 negative에서 “기울기” 를 가지는 것 대신에 또 다시 “saturation” 하게된다.
➡ ELU가 주장하는건 이런 saturation이 좀 더 잡음(noise)에 강인할 수 있다는 것이다.
➡ 이런 deactivation이 좀더 강인함을 줄 수 있다고 주장한다.(ELU의 논문에서는 왜 ELU가 더 강인한지에 대해 타당성을 다양하게 제시하고 있다) - ELU는 ReLU와 Leaky ReLU의 중간 정도라고 보면 된다.
- ELU는 Leaky ReLU처럼 zero-mean의 출력을 내지만, Saturation의 관점에서 ReLU의 특성도 가지고 있다.
Maxout Neuron
이건 지금까지 본 활성함수들과는 좀 다르게 생겼다.
[수식 & 그래프]
[특징]
- 입력을 받아드리는 특정한 기본형식을 미리 정의하지 않는다.
대신 \((w_1에 x를 내적한 값 + b_1)\)과 \((w_2에 x를 내적한 값 + b_2)\)의 최댓값을 사용한다. - ReLU와 leaky ReLU의 좀 더 일반화된 형태이다.
왜냐하면 Maxout은 이 두 개의 선형함수를 취하기 때문이다. - Maxout또한 선형이기 때문에 saturation 되지 않으며 gradient가 죽지 않을 것이다.
[문제점]
- 뉴런당 파라미터의 수가 두배가 된다는 것
➡ 이제는 W1과 W2를 지니고 있어야 한다.
참고
지금까지 다양한 활성함수를 살펴보았다.
-
실제로 가장 많이들 쓰는 것은 ReLU 이다.
➡ 다만 ReLU를 사용하려면 leraning rate를 아주 조심스럽게 결정해야 할 것이다.
learning rate에 관해서는 이번 강의의 후반부에 좀 더 다뤄보도록 하겠다. -
또한 여러분은 Leaky ReLU, Maxout, ELU와 같은 것들도 써볼 수 있겠다.
하지만 이들은 아직 실험단계이긴 하다. -
여러분들의 문제에 맞춰 어떤 활성함수가 잘 동작하는지 확인해 볼 수 있을 것이다.
tanh도 써볼 수 있지만 대게는 ReLU와 ReLU의 변종들이 좀 더 잘 동작한다고 보면 된다. -
Sigmoid는 사용하지 않는 편이 좋습니다. 가장 구식이고 요즘은 LU 패밀리 계열이 더 잘 동작한다.
Data Preprocessing
자 그럼 이제 데이터 전처리에 대해 배워보겠다.
지금까지는 활성 함수에 대해 배워보았다.
이제는 실제로 네트워크를 훈련시켜 보려 한다.
모델을 트레이닝 하기 전 모델을 학습시킬 입력 데이터는 전처리 해준다.
가장 대표적인 전처리 과정은 “zero-mean”으로 만들고 “normalize” 하는 것이다.
normalization은 보통 표준편차로 한다.
📝 클릭하여 그림으로 직관적 이해해보기
이는 다음 강의 복습에 나오는 것인데 필요할 것 같아서 추가해뒀다.
[task]
자 여기에서 우리는 Binary classification 문제를 푼다. 빨간/파란 점들을 나누는 것이다.
[not normalized/centered]
왼쪽의 경우 not normalized/centered 데이터이다.
이 경우에도 물론 classification이 가능하지만 선이 조금만 움직여도 classification이 잘 되지 않는다. 왼쪽의 예시가 의미하는 것은 손실 함수가 아주 약간의 가중치 변화에도 엄청 예민하다는 것입니다. 왼쪽의 경우 동일한 함수를 쓰더라도 학습 시키기 아주 어렵다.
재차 말씀드리지만 Loss가 파라미터에 너무 민감하기 때문이다.
[After normalization]
반면 오른쪽은 데이터의 중심을 원점에 맞추고(zero-center), Unit variance로 만들어 준 경우이다.
오른쪽에서의 선이 조금씩 흔들리는 경우는 보면 손실 함수는 이런 가중치의 변동에 덜 민감함을 알 수 있다.
이 경우가 최적화가 더 쉽다.
학습이 더 잘되는 것이다.
그리고 이는 Linear classification의 경우에만 국한되는 것이 아니다.
Neural network 내부에도 다수의(interleavings) linear classifer가 있다고 생각할 수 있다. 이 경우에도 Neural network의 입력이 zero-centered가 아니고 Unit variance가 아닌 경우라면,
레이어의 Weight matrix가 아주 조금만 변해도 출력은 엄청 심하게 변하게 된다. 이는 학습을 어렵게한다.
이를 통해 왜 nomalization가 중요한지를 좀 더 직관적으로 이해하실 수 있을 것가다.
[전처리를 하는 이유]
앞서 zero-centering에 대해 다룬 적이 있었다.(입력이 전부 positive인 경우)
➡ 그러면 모든 뉴런이 positive인 gradient를 얻게 된다.
➡ 이는 최적하지 못한(suboptimal) 최적화가 된다.
데이터가 전부 positive 일 때 뿐만이 아니라 0 이거나 전부 negative일 경우에도 이런 일은 발생하곤 한다.
그러므로 normalization을 해주는 이유는 모든 차원이 동일한 범위 안에 있게 해줘서 전부 동등한 기여(contribute)를 하게 하는 것이다.
[이미지 전처리]
실제로는 이미지의 경우 전처리로 zero-centering 정도만 해준다.
이미지의 경우는 normalization 하지는 않는다. 왜냐하면 이미지는 이미 각 차원 간에 스케일이 어느정도 맞춰져 있기 때문이다.
따라서 스케일이 다양한 여러 ML 문제와는 달리 이미지에서는 normalization을 엄청 잘 해줄 필요가 없다.
그리고 Machine learning 에서는 PCA나 whitening 같은 더 복잡한 전처리 과정도 있긴 한다만,
이미지에서는 단순히 zero-mean 정도만 사용하고 normalization 그 밖의 여러 복잡한 방법들은 잘 안쓴다.
일반적으로는 이미지를 다룰 때는 굳이 입력을 더 낮은 차원으로 projection 시키지 않는다.
그 대표적인 예로는 CNN이 있는데, CNN에서는 오히려 원본 이미지 자체의 spatial 정보를 이용해서 이미지의 spatial structure를 얻을 수 있도록 한다.
📜 학생 질문: Train Phase에서 전처리를 해줬으면 Test Phase에서도 똑같은 전처리를 해 줘야 하는것 인가?
맞다.
일반적으로 Train에서 계산한 평균을 Test데이터에도 동일하게 적용해 줍니다. Training data에서 구한 평균을 그대로 사용한다.
일반적으로 Train에서 계산한 평균을 Test데이터에도 동일하게 적용해 준다. Training data에서 구한 평균을 그대로 사용것이다
평균 값은 전체 Training data에서 계산
보통 입력이미지의 사이즈를 서로 맞춰주는데 가령 32x32x3 이고 네트워크에 들어가기 전에 평균값을 빼주게 된다.
그리고 Test time의 이미지에도 Training time에 계산한 평균 값으로 빼주게 된다.
실제로 일부 네트워크는 채널 전체의 평균을 구하지 않고 채널마다 평균을 독립적으로 계산하는 경우도 있다.
➡ 여기서 채널별로 평균이 비슷할 것인지 아니면 독립적으로 계산해야 할 것인지는 여러분이 판단하기 나름이다.
➡ AlexNet 이후 VGGNet 같은 네트워크가 이러한 형태인데 추후에 다시 다루도록 하겠다.
📜 학생 질문: 채널의 의미
여기에서 채널은 RGB입니다. 이미지가 32x32x3 이면 Width(32) x Height(32) x RGB(3) 입니다.
VGG식으로 하면 RGB별로 각각 평균을 구하는 것입니다.
📜 학생 질문: 평균을 어떻게 계산하는지
평균은 여러분의 “학습 데이터 전부” 를 가지고 계산
📜 학생 질문: 만약 미니배치 단위로 학습시킨다고 해도 평균 계산은 미니배치 단위로 각각이 아니라 전체로 계산하는지
맞습니다.
우리가 구하는 평균 이라는 것은 트레이닝 데이터의 평균입니다.
배치에서 뽑은 데이터도 사실 전체 데이터에서 나온 것들이죠 결국은 배치 평균이나 전체 평균이나 구해보면 같습니다.(이상적으로)
그러니 처음에 전체 트레이닝 이미지를 이용하여 단 한번만 평균을 계산 해 놓는 편이 낫습니다.
반대로 엄청 큰 데이터의 전체 평균을 구할때도 굳이 모든 데이터를 전부 쓰지 않고 적절하게 샘플링해서 구할수도 있겠죠
📜 학생 질문: 데이터 전처리가 Sigmoid Problem을 해결할 수 있는지
맞습니다.
우리가 구하는 평균 이라는 것은 트레이닝 데이터의 평균입니다.
배치에서 뽑은 데이터도 사실 전체 데이터에서 나온 것들이죠 결국은 배치 평균이나 전체 평균이나 구해보면 같습니다.(이상적으로)
그러니 처음에 전체 트레이닝 이미지를 이용하여 단 한번만 평균을 계산 해 놓는 편이 낫습니다.
반대로 엄청 큰 데이터의 전체 평균을 구할때도 굳이 모든 데이터를 전부 쓰지 않고 적절하게 샘플링해서 구할수도 있겠죠
zero mean의 한계
우리가 수행 할 전처리는 zero mean 입니다. 앞서 sigmoid 에게는 zero-mean이 필요하다고 했다.
데이터 전처리가 sigmoid의 zero-mean 문제를 단지 “첫 번째” 레이어에서만 해결할 수 있을 것이다.
처음에는 입력 이미지가 zero-mean 이므로 해결 할 순 있겠으나 하지만 그 다음 레이어부터는 똑같은 문제가 반복될 것이다.
Deep network라면 점점 더 심해질 것이다.
즉, 가면 갈수록 더 non-zero-mean 을 겪게 될 것이다.
따라서 이미지 전처리가 Sigmoid에서의 문제를 해결하기에는 충분하지 않을 것이이다.
Weight Initialization
이제 weight를 어떻게 초기화시켜야 하는지를 알아보겠다.
“Two Layer Neural Network” 예시를 한번 보자.
우리가 할 일은 가중치 업데이트이다.
맨 처음에 어떤 초기 가중치들이 있고 gradient를 계산해서 가중치를 업데이트할 것이다.
[첫 번째 질문: “모든 가중치 = 0” 이면 어떻게 될까?]
즉, 모든 파라미터를 0으로 세팅하면 어떻게될까라는 질문이다.
📝 첫번째 질문 정답 보기
“Symmetry breaking”이 일어난다.
이것의 의미는,
입력 데이터들이 뉴런의 어떤 영역에 속할 것이고 죽지 않을 수도 있다.
하지만 중요한 것은 전부 똑같은 일을 할 것이라는 것이다.
가중치가 0 이라서 모든 뉴런은 모두 다 같은 연산을 수행한다.
출력도 모두 같을 것이고. 결국 gradient도 서로 같다.
결국 모든 가중치가 똑같은 값으로 업데이트 된다.
결국 모든 뉴런이 모두 똑같이 생기게 된다.
우리가 그다지 원하는 것은 아니다.
그러므로 우리는 뉴런들이 서로 다르게 생기길 원한다.
📜 학생 질문: gradient는 가중치 뿐만 아니라 Loss의 영향도 받으므로 결국 각각의 gradient는 다를 것이지 않나?
각 뉴런이 어떤 클래스에 연결되어 있는지에 따라 뉴런들이 서로 다른 loss를 가질 수는 있습니다.
하지만 네트워크 전체를 보면 많은 뉴런들이 동일한 가중치로 연결되어 있을 것이다.
결국 모든 뉴런이 같은 방식으로 업데이트 될 것이고 이는 문제가 된다.
가중치 초기화: Small random numbers
[방법]
초기화 문제를 해결하는 첫번째 방법은 임의의 작은 값으로 초기화하는 것이다.
이 경우 초기 W를 표준정규분포(standard gaussian)에서 샘플링한다.
그리고 좀 더 작은 값을 위해 스케일링을 해준다. ➡ 0.01을 나눠 표준편차를 1e-2 즉 0.01로 만들어준다.
이런 식으로 모든 가중치를 임의의 값으로 초기화한다.
여러분의 네트워크가 작은 네트워크(symmetry breaking에 뭐 이정도)라면 이 방법으로도 충분하다.
[문제]
하지만 이 방법은 더 깊은 네트워크에서 문제가 생길 수 있다.
이게 어떤 문제가 생기는지 조금 더 깊은 네트워크를 가지고 아래 실험으로 알아보겠다.
- setting
- 여기 레이어당 500개의 뉴런이 있는 10개의 레이어로 이루어진 네트워크가 있다.
- nonlinearities로는 tanh를 사용해보자.
- 그리고 가중치는 “임의의 작은 값” 으로 초기화시킨다.
데이터를 랜덤으로 만들어주고 이 데이터를 forward pass시켜보겠다.
그리고 각 레이어별 activations 수치를 통계화 시켜보겠다.
- 분석
- 여기 수치들의 결과는 각 레이어 출력의 평균과 평균과 표준편차를 계산한 것이다.
- 첫번째 레이어
- (왼쪽 그래프) 평균은 항상 0 근처에 있음.(tanh 특성상)
- X와 W를 내적한 값에 tanh를 취한 후 그 값을 저장하는데, tanh가 zero-centered 이니까 평균이 0에 가까운건 당연하다.
- (오른쪽 그래프, 아래 히스토그램) 표준편차는 아주 가파르게 줄어들어서 0에 수렴
- 첫 번째 레이어에서는 가우시안스럽게 생긴 좋은 분포를 형성하고 있는것을 볼 수 있다.
- 하지만 문제는 우리가 W를 곱하면 곱할수록 W가 너무 작은 값들이라서 출력 값이 급격히 줄어드는 것이다(결국 0이 된다)
➡ 그래서 모든 활성함수 결과가 0이 된다.
- (왼쪽 그래프) 평균은 항상 0 근처에 있음.(tanh 특성상)
- backwards pass로 다시한번 생각 . 방금 전까지 한 것을 forward pass했다고 가정해보고 이제 gradient를 구해보자
각 가중치들에 해당하는 gradient가- 위의 경우는 각 레이어의 “입력” 이 엄청 작은 것이므로 입력 값이 점점 0에 수렴한다.
➡ Backporp을 생각해보면 “upstream gradient”가 점점 전파되는 것이다. - 그럼 현재 가중치를 업데이트하려면 “upstream gradient”에 local gradient를 곱해주면 된다.(앞에서 배웠다)
우선 \(W_X\)를 \(W\)에 대해 미분해보면 local gradient는 입력 \(X\)가 된다.
이렇게 생각해보면 앞서 다뤘던 문제와 유사한 문제가 생긴다는 것을 알 수 있다.
➡ X가 엄청 작은 값이기 때문에 gradient도 작을 것이고 결국 업데이트가 잘 일어나지 않는다.
- 위의 경우는 각 레이어의 “입력” 이 엄청 작은 것이므로 입력 값이 점점 0에 수렴한다.
이런 실험은 gradient의 흐름이 네트워크에 어떤 영향을 미치는지 생각해 볼 수 있는 좋은 예시이다.
이제는 forward pass의 형태를 살펴보고는 이런 경우 gradient가 어떤 식으로 전파되는지 짐작 할 수 있을 것이다.
그리고 다양한 입력 타입에 따라 weight와 gradient가 어떤 영향을 미치는지도 생각해 볼 수 있다. 또한 gradients가 연결되면서(chaining) 어떤 식으로 전파(flowing back)되는지를 한번 생각해본다면 gradient를 backprop하는 과정은 그 반대이다.
upstream gradient에 W의 gradient인 X를 곱한다.
여기서 입력 X은 어떤 내적(dot product)의 결과이다.
또한 backward pass의 과정에서 upstream gradient를 구하는 것은 현재 upstream에 가중치를 곱하는 것이다.
➡ 우리는 W를 계속해서 곱하기 때문에 Backwork pass에서도 Forward 처럼 점점 gradient값이 작아지게 된다.
➡ 따라서 upstream gradients는 0으로 수렴하게 된다.
가중치 초기화: large number
지금까지는 가중치가 엄청 작을 때 발생하는 문제를 살펴보았다.
그렇다면 가중치를 좀 더 큰 값으로 초기화하면 어떨지 생각해보자.
[가중치 편차 = 1]
가중치의 편차를 0.01 이 아니라 1로 하면 어떻게 될까?(가중치가 전보다 크다)
이렇게 큰 가중치를 통과한 출력 \(W_X\)를 구하고 이를 tanh를 거치게 된다면 어떨까?
앞서 tanh의 입력에 따라 어떤 일이 발생하는지 배웠다.
그렇다면 문제가 뭘까?
➡ 값들이 saturation 될 것임
가중치가 큰 값을 가지므로 tanh의 출력은 항상 saturation될 것이다.
➡ 출력이 항상 -1 이거나 +1 일 것이다.
앞서 말했듯 값이 saturation 될 것이고 gradient는 0이 된다.
➡ 가중치 업데이트가 일어나지 않음
[평가]
결국 적절한 가중치를 얻기는 너무 어려움
너무 작으면 사라져버리고 너무 크면 saturation되어 버림
그래서 사람들이 어떻게 하면 가중치 초기화를 잘 할 수 있을까 하고 열심히 고민했음
가중치 초기화: Xavier initialization
널리 알려진 방법 중 하나로 Glorot가 2010에 발표한 논문이다.
자 그럼 맨 위에 W의 공식을 한번 보겠다.
Standard gaussian으로 뽑은 값을 “입력의 수” 로 스케일링해준다.(\(Xavier 초깃값(1/√n)\))
즉, 각 뉴런의 가중치 벡터를 w = np.random.randn(n) / sqrt(n)
로 초기화
[역할]
- 기본적으로 Xavier initialization가 하는 일은 입/출력의 분산을 맞춰주는 것이다.
- 그리고 이 수식을 통해서 직관적으로 이해할 수 있는 것은,
입력의 수가 작으면 더 작은 값으로 나누고 좀 더 큰 값을 얻는다. - 그리고 우리는 더 큰 가중치가 필요하다.
왜냐하면 작은 입력의 수가 가중치와 곱해지기 때문에, 가중치가 더 커야만 출력의 분산 만큼 큰 값을 얻을 수 있기 때문이다. - 반대로 입력의 수가 많은 경우에는 더 작은 가중치가 필요하다.
[사용]
각 레이어의 입력이 Unit gaussian이길 원한다면 이런 류의 초기화 기법을 사용해 볼 수 있다.
각 레이어의 입력을 Unit gaussian스럽게(approximately) 만들어 줄 수 있다.
여기서 가정하는 것은 현재 Linear activation이 있다고 가정하는 것이다.
가령 tanh의 경우 우리가 지금 tanh의 active region안에 있다고 가정하는 것이다.
[❌ 문제]
ReLU를 쓰면 잘 동작하지 않는다.
ReLU를 쓰면 무슨 일이 일어나는지를 보면,
ReLU는 출력의 절반을 죽여 그 절반은 매번 0이 된다. ➡ 그래서 결국 출력의 분산을 반토막 내버린다.
그러므로 이전과 같은 가정을 해버리면 ReLU에서는 잘 작동하지 않는 ➡ 값이 너무 작아지는 것이다.
위에 보이는 것이 그런 현상인데 분포가 또 줄어들기 시작한다.
점점 더 많은 값들이 0이 되고 결국은 비활성(deactivated) 되어버린다.
[⭕ 해결]
이 문제를 해결하기 위한 일부 논문이 있는데 여기에서는 추가적으로 2를 곱해준다. \(√(2/n)\)
이는 뉴런 들 중 절반이 없어진다는 사실을 고려하기 위해서이다.
일제 입력은 반밖에 안들어가므로 반으로 나눠주는 텀을 추가적으로 더해 주는 것이고 실제로 잘 동작한다.
결과를 보면 전체적으로 좋은 분포를 형성하고 있는것을 볼 수 있다.
실제로 이 작은 변화는 트레이닝에 있어서 엄청난 차이를 보인다.
[INSIGHT]
가령 일부 논문을 보면 그런 차이가 트레이닝이 정말 잘 되거나 하나도 안되거나를 결정하는 결과를 보이기도 한다.
적절한 초기화는 여전히 활발이 연구되는 분야이다.
초기화는 우선 Xavier Initialization을 추천하며 그 밖에 다른 방법을 시도해 보는 것도 좋을 것이다.
📜 더 관심있는 분들을 위한 논문 모음
Understanding the difficulty of training deep feedforward neural networks by Glorot and Bengio, 2010
Exact solutions to the nonlinear dynamics of learning in deep linear neural networks by Saxe et al, 2013
Random walk initialization for training very deep feedforward networks by Sussillo and Abbott, 2014
Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification by He et al., 2015
Data-dependent Initializations of Convolutional Neural Networks by Krähenbühl et al., 2015
All you need is a good init, Mishkin and Matas, 2015
Batch Normalization
gaussian의 범위로 activation을 유지시키는 것에 관련한 또 다른 아이디어를 하나 소개하겠다.
우리는 레이어의 출력이 unit gaussian 이길 바란다.
➡ Batch normalization이라 불리는 이 아이디어는 그렇다면 그렇게 만들어보자 하는 것이다.
그냥 강제로 그렇게 만들어보자
[목표]
어떤 레이어로부터 나온 Batch 단위 만큼의 activations 이 있다고 했을때, 우린 이 값들이 Unit gaussian이기를 원한다.
누구나 이 데이터를 Unit Gaussian으로 (empirically) 만들 수 있다.
현재 Batch에서 계산한 mean과 variance를 이용해서 Normalization 해 볼 수 있을 것이다.
가중치를 잘 초기화 시키는 것 대신에 학습 할 때 마다 각 레이어에 이런 일을 해줘서 모든 레이어가 Unit gaussian이 되도록 해준다.
그래서 이제부터 할 일은 네트워크의 forward pass 동안에 그렇게 되도록 “명시적으로” 만들어 주는 것이다.
[동작 방법]
각 뉴런을 Batch단위로 한 레이어에 입력으로 들어오는 모든 값들을 이용해서 평균과 분산을 구한 후,
이 값으로 Normalization 해주므로서 이런 일을 함수로(functionally) 구현한다.
그리고 이를 함수로 나타내면 미분 가능한 함수이다.
➡ 평균과 분산을 “상수” 로 가지고만 있으면 언제든지 미분이 가능하며 따라서 Backprop이 가능하게 된다.
Batch당 N개의 학습 데이터가 있고 각 데이터가 D차원이라고 해보자.
1) 각 차원별로(feature element별로) 평균을 각각 구해줌
2) 한 Batch 내에 이걸 전부 계산해서 Normalize 함
3) 그리고 이 연산은 FC나 Cov Layer 직후에 넣어줌
[역할]
깊은 네트워크에서 각 레이어의 W가 지속적으로 곱해져서 Bad scaling effect가 발생했습니다만,
Normalization은 그 bad effect를 상쇄시켜 버림
➡ BN은 입력의 스케일만 살짝 조정해 주는 역할이기 때문에 FC와 Conv 어디에든 적용가능
[Conv Layer와 차이점]
- Conv Layer
- Normalization을 차원마다 독립적으로 수행
- Conv Layer의 경우 Activation map(채널, Depth) 마다 평균과 분산을 하나만 구합니다.
- BN
- 같은 Activation Map의 같은 채널에 있는 요소들은 같이 Normalize 해줌
- 왜냐하면 Conv 특성상 (convolutional property) 같은 방식으로 normalize 시켜야 하기 때문
[❌ 문제: saturation정도 조절 불가]
FC layer를 거칠 때 마다 매번 normalization를 해주는 것에 대한 의문 존재.
normalization이 하는 일은 입력이 tanh의 linear한 영역에만 존재하도록 강제하는 것이다.
➡ 그렇게 되면 saturation이 전혀 일어나지 않게된다.
그런데 saturation 이 전혀 일어나지 않는것 보다는 “얼마나” saturation이 일어날지를 우리가 조절하면 더 좋다.
[⭕ 해결: scaling]
Batch normalization에서는 normalization연산이 있다.(왼쪽 상단 Narmalize: 수식)
그 이후에 BN에서는 scaling 연산을 추가한다.
이를 통해 Unit gaussian으로 normalize 된 값들을
[scaling효과: 원상복구]
이렇게 하면 normalized 된 값들을 다시 원상복구 할 수 있도록 해준다. 그렇게 하고 싶다면 말이다.
네트워크가 값 들을 원상복구 하고 싶다면,
“감마 = 분산”, “베타 = 평균” 로 하면된다.
BN을 하기 전 값으로 돌아가는 것이다.
네트워크가 데이터를 tanh에 얼마나 saturation 시킬지를 학습하기 때문에 우리는 유연성을 얻을 수 있다.
[요약] Batch normalization를 다시한번 요약해 보자면,
- 1) 입력이 있고 우리는 mini-batch에서의 평균과 분산을 계산한다.
모든 mini-batch 마다 각각 계산해 준다. - 2) 평균과 분산으로 Normalize 한 이후에 다시 추가적인 scaling, shifting factor를 사용
➡ “레이어의 입력”에 평균을 빼고 표준편차로 나눠주면 gaussian이 됨
[효과]
- BN은 gradient의 흐름을 보다 원활하게 해주며 결국 더 학습이 더 잘되게(robust) 해줌
- BN을 쓰면 learning rates를 더 키울 수도 있고 다양한 초기화 기법들도 사용해 볼 수 있음
- 사람들이 BN을 쓰면 학습이 더 쉬워진다고 함.
- 그리고 또 하나 짚고 넘어가야 할 것은 BN이 regularization의 역할도 한다는 것입니다.
각 레이어의 출력은 해당 데이터 하나 뿐만 아니라 batch 안에 존재하는 모든 데이터들에 영향을 받는다.(평균, 분산)
왜냐하면 각 레이어의 입력은 해당 배치의 (표본)평균으로 Normalize 되기 때문이다.
➡ 그렇기 때문에 이 레이어의 출력은 이제 오직 하나의 샘플에 대한 deterministic한 값이 아니게 되고, Batch 내의 모든 데이터가 입력으로 한대 묶인다고 볼 수 있다.
➡ 그러므로 더 이상 레이어의 출력은 deterministic하지 않고 조금씩 바뀌게 되고 이는 regularization effect를 준다.
📜 학생 질문: 우리가 입력을 gaussian 이라고 강제해 버리면 기존의 구조(structure)를 잃는 것 아닌지
우선은 아닙니다. 데이터의 전처리를 할때도 gaussian을 쓴다.
모든 입력들(features) 가우시안 분포로 만든다고 해도 떠한 구조도 잃지 않는다.
데이터에 “연산이 잘 수행되도록” 우리는 그저 데이터를 조금만 움직이고 스케일링 하는 것이다 (선형변환임)
[수식]
이는 다음 강의 복습에 나오는 것인데 필요할 것 같아서 추가해뒀다.
BN에서는 forward pass 시에 미니배치에서의 평균과 표준편차를 계산해서 Normalization을 수행했다.
그리고 레이어의 유연한 표현성(expressivity)을 위해서 scale, shift 파라미터를 추가다.
Babysitting the Learning Process
자 이제 학습과정을 다루는법에 대해 알아보겠다.
지금까지 네트워크 설계를 배웠습니다. 이제는 좋은 결과를 얻기 위해서 학습과정을 어떻게 모니터링하고 하이퍼파라미터를 조절할 것인지를 배워보겠다.
[Step 1: Preprocess the data]
좋은 학습 결과를 얻기 위해서 우선 가장 첫 단계는 데이터 전처리이다.
➡ 앞서 배운것 처럼 zero-mean을 사용
[Step 2: Choose the architecture]
그리고 아키텍쳐를 선택해야한다.
우선 하나의 Hidden Layer와 50개의 뉴런을 가진 모델을 선택했다고 가정해보자.
그 밖에 어떤 모델을 선택해도 상관없다.
이제 해야할 일은 네트워크를 초기화시키는 것이다.
Forward pass를 하고난 후에 Loss가 그럴듯해야한다.
지난 강의에서 배웠듯이 가령 우리가 softmax를 사용하고자 한다면 우리는 가중치가 작은 값일 때 Loss가 대강 어떻게 분포해야 하는지를 이미 알고있다.
[sanity check 1: Loss cheak]
ex) Softmax classifier의 Loss는 negative log likelihood 가 되어야한다.
가령 10개의 클래스라면 Loss는 \(-log(1/10)\) 이 될 것이다.
밑을 보면 약 2.3 정도 되는 것을 확인 가능하다.
이를 통해 Loss가 원하는 대로 동작한다는 것을 알 수 있다.
➡ 이 방법은 언제든 사용할 수 있는 유용한 방법이다 (Good sanity check)
[sanity check 2: add regularization]
자 그럼 초기 Loss가 정상이라는 것을 확인했다면, 지금까지는 여기 regularization을 0으로 설정하였다(위의 파란색 네모) ➡ 앞서 계산한 2.3은 regularization을 사용하지 않은 상태입니다.
여기에 regularization을 추가하면 Loss가 증가한다.
손실함수에 regularization term이 추가되기 때문이다.
이 또한 유용한 sanity check이다.
[Step 3: Before Train]
자 이제 학습을 진행할 준비가 끝났다.
이제 학습을 한번 시작해보자
처음 시작할 때 좋은 방법은 데이터의 일부만 우선 학습시켜 보는 것이다.
데이터가 적으면 당연히 Overfit이 생길 것이고 Loss가 많이 줄어들 것이다.
이때는 regularization를 사용하지 않는다.
그리고 Loss가 내려가는지를 확인하는 것이다.
아래처럼 Epoch마다 Loss가 잘 내려가는지를 보는 것이다.
Loss가 0을 향해 꾸준히 잘 내려가는지를 확인해보자.
➡ (당연히) Loss가 내려감과 동시에 Train Accuracy는 점점 증가한다
➡ 데이터가 작은 경우라면 모델이 완벽하게 데이터를 overfit 할 수 있어야한다.
[Step 4: Train with Learning rate]
자 지금까지의 sanity checks가 잘 끝났다면 이제부터는 정말로 학습을 시작할 차례다.
이제는 전체 데이터 셋을 사용할 것이고 regularization을 약간만 주면서 적절한 learning rate를 찾아야한다.
Learning rate는 가장 먼저 정해야하는 가장 중요한 하이퍼파라미터 중 하나다.
우선 learning rate 몇 가지를 정하고 실험해보자.
[Learning rate= 1e- 6]
그 결과로는 loss가 좀처럼 변하지 않는 것을 알 수 있다
Loss가 잘 줄어들지 않는 가장 큰 요인은 Learning rate가 지나치게 작은 경우이다
Learning rate가 지나치게 작으면 gradient 업데이트가 충분히 일어나지 않게 되고 cost가 변하지않는다.
여기에서 유심히 살펴봐야 할 점은 loss가 잘 변하지 않음에도 training/validation accuracy가 20%까지 급상승한다.
📜 급상승하는 이유 보기
현재 Softmax를 사용하고 있고 loss가 크게 안변했음에도 Accuracy는 올라간 상태이다.
현재 상황은 확률 값들이 아직까지 멀리 퍼져있고 때문에 loss는 여전히 비슷비슷한 것이다.
가중치는 서서히 변하고 있지만 Accuracy는 갑자기 뛰어버릴 수 있다 ➡ Accuracy는 그저 가장 큰 값만 취하기 때문에 Accuracy가 크게 뛸 수 있는 것이다.
[Learning rate= 1e6]
learning rate를 더 큰 값인 1e6로 바꿔보자.
자 결과를 보면 cost가 NaNs임을 알 수 있다.➡ NaNs이라는 것은 cost가 발산(exploded)한 것이다.
주된 이유는 learning rate가 지나치게 높기 때문이다.
[Learning rate= 3e-3]
위의 경우에는 learning rate를 낮춰야만 한다.
그럼 더 낮은 값인 3e-3으로 바꿔보자.
그 결과는 여전히 발산한다.
[적절한 Learning rate]
보통 learning rate는 1e-3 에서 1e-5 사이의 값을 사용한다.
이 범위 사이의 값을 이용해서 cross-validation을 수행해준다
➡ 이 사이의 값들을 이용해서 learning rate가 지나치게 작은지 아니면 큰지를 결정할 수 있을 것이다.
Hyperparameter Optimization
자 그럼 하이퍼파라미터는 어떻게 정해줘야 할까?
또 하이퍼파라미터를 최적화시키고 그중 가장 좋은 것을 선택하려면 어떻게 해야 할까?
cross-validation
취할 수 있는 한가지 전략은 바로 cross-validation이다.
Cross-validation은 Training set으로 학습시키고 Validation set으로 평가하는 방법이다.
이 hyperparameter가 잘 동작하는지를 확인해본다.
[First stage: 넓은 범위 좁히기]
우선 coarse stage 에서는 넓은 범위에서 값을 골라냄
Epoch 몇 번 만으로도 현재 값이 잘 동작하는지 알 수 있다.
Epoch이 많지 않아도 어떤 하이퍼파라미터가 좋은지 아닌지를 알 수 있다.
Nan이 뜨거나 혹은 Loss가 줄지 않거나 하는 것을 보면서 이에 따라 적절히 잘 조절한다.
coarse stage가 끝나면 어느 범위에서 잘 동작하겠다를 대충 알게 될 것이다.
[Second: 학습시간 늘리기]
두 번째 fine stage에서는 좀 더 좁은 범위를 설정하고 학습을 좀 더 길게 시켜보면서 최적의 값을 찾는다.
NaNs 으로 발산하는 징조를 미리 감지할 수도 있다.
Train 동안에 Cost가 어떻게 변하는지를 살펴보는 것이다.
이전의 cost보다 더 커지거나 한다면, 가령 이전보다 cost가 3배 높아졌다거나 하면 잘못 하고 있는 것이다.
Cost가 엄청 크고 빠르게 오르고 있다면 loop를 멈춰버리고 다른 하이퍼파라미터를 선택하면된다.
[예시]
이는 5 epochs을 돌며 coarse search를 하는 과정이다.
네트워크 구조는 앞서 만든 것과 유사하다.
여기에서 확인 해야 할 것은 validation accuracy이다.
높은 val_acc에는 빨간색으로 표시를 해뒀다.
✨ 빨간색으로 표시해 둔 지역이 바로 fine-stage를 시작할 만한 범위가 될 것이다.
한 가지 주목할 점은 하이퍼 파라미터 최적화 시에는 Log scale로 값을 주는 것이 좋다. ➡ 파라미터 값을 샘플링할때 10^-3 ~ 10^-6 을 샘플링하지 말고 10의 차수 값만 샘플링하는 것이 좋다.(-3 ~ -6)
➡ 왜냐하면 learning rate는 gradient와 곱해지기 때문에 learning rate의 선택 범위를 log scale을 사용하는 편이 좋다.
➡ 따라서 차수(orders of magnitude)을 이용하는 편이 좋다.
[범위 재조정]
자 그럼 범위를 다시한번 조절해 보겠다. (오른쪽)
reg는 경우를 보면 범위를 10^-4 에서 10^0 정도로 좁히면 좋을 것 같다. ➡ 나쁘지 않은 방법이다.
이쪽 범위에서 53%의 val_acc를 보이는 것을 알 수 있다. ➡ 학습이 가장 잘 되는 구간이 될 것이다.
[문제]
하지만 여기에는 문제가 하나 있다.
자 우리는 여기에서 가장 좋은 Accuracy를 찾았다. (위 그림에서 화살표)
잘 보면 good learning rates는 전부 10E-4 사이에 존재하고 있다.
Learning rate의 최적 값들이 우리가 다시 좁혀 설정한 범위의 경계부분에 집중되어 있다는 것을 알 수 있다.
➡ 이것은 좋지 않다 (이렇게 되면 최적의 Learning rate를 효율적으로 탐색할 수 없을 수도 있기 때문)
➡ 실제로 최적의 값이 1E-5에 근처에 존재할 수도 있고, 어쩌면 1E-6 쯤에 있을지도 모른다
탐색 범위를 조금 이동시킨다면 더 좋은 범위를 찾을 수 있을지도 모른다.
당연히 여러분은 최적의 값이 내가 정한 범위의 중앙 쯤에 위치하도록 범위를 잘 설정해 주는 것이 중요하다.
[Third: 평가 & 반복]
Cross-validation 정말 많이 해야 할 것입니다.
cross validation으로 엄청나게 많은 하이퍼파라미터를 직접 돌려보고 모니터해서 어떤 값이 좋고 나쁜지를 확인해야 한다.
여기 있는 loss curve를 보면서 좋은 것을 찾아서 시도해보는 일을 계속 반복해야 한다.
loss curve를 모니터링 하는데 있어서 앞서 언급했듯 learning rate이 정말 중요하다.
그리고 loss curve를 보고 어떤 learning rate가 좋고 나쁜지를 알아볼 수 있다.
- Loss가 발산: learning rate가 높음
- Loss가 너무 평평(linear): learning rate가 너무 낮은 것이다.(학습이 거의 안된다는 뜻이니까)
- Loss가 가파르고 지속적으로 내려감(빨간색): 최적의 learning rate ➡ 현재 learning rate를 유지해도 좋음
- Loss가 가파르게 내려가다가 어느순간 정체기: 이 또한 여전히 너무 높다는 의미(learning step이 너무 크게 점프해서 적절한 local optimum 에 도달하지 못하는 경우)
[loss curve]
- loss가 평평하다가 갑자기 가파르게 내려가는 것을 보게 된다면 이는 초기화의 문제일 수 있다.
- gradient의 backpropb이 초기에는 잘 되지 않다가 학습이 진행되면서 회복이 되는 경우임.
[overfit]
- 만일 train_acc와 va_acc가 큰 차이를 보인다면, 이는 overfit일지 모르므로 egularization 의 강도를 높혀야 할 지도 모음
- gap이 없다면 아직 overfit하지 않은 것이고 capacity을 높힐 수 있는 충분한 여유가 있다는 것을 의미한다.
[가중치의 크기 대비 가중치 업데이트의 비율]
- 이를 지켜볼 필요가 있다.
- 우선 파라미터의 norm을 구해서 가중치의 규모를 계산한다.
- 그리고 업데이트 사이즈 또한 norm을 통해 구할 수 있고 이로 얼마나 크게 업데이트 되는지를 알 수 있다.
- 우리는 이 비율이 대략 0.001 정도 되길 원한다.
- 이 값은 변동이 커서 정확하지 않을 수 있지만 업데이트가 지나치게 크거나 작은지에 대한 감을 어느정도 가질 수는 있음.
➡ 너무 업데이트가 지나치거나 아무 업데이트도 없으면 안된다
grid search
하이퍼파라미터를 찾는 또 다른 방법 이는 하이퍼파라미터를 고정된 값과 간격으로 샘플링하는 것이다.
하지만 실제로는 grid search보다는 이전처럼 random search를 하는 것이 더 좋다.
[Random search]
- 오른쪽에 있는 것과 같은 결과를 얻을 수 있을 것
- Random이 더 좋은 이유
- 바로 내 모델이 어떤 특정 파라미터의 변화에 더 민감하게 반응을 하고 있다고 생각해보면 (노란 < 초록)
➡ 이 함수가 더 비효율적인 dimentionality를 보인다고 할 수 있음 (노랑 에는 별로 영향을 안 받음)
➡ Random search는 중요한 파라미터(초록) 에게도 더 많은 샘플링이 가능하므로 위에 그려놓은 초록색 함수를 더 잘 찾을 수 있을 것입니다.
- 바로 내 모델이 어떤 특정 파라미터의 변화에 더 민감하게 반응을 하고 있다고 생각해보면 (노란 < 초록)
[Grid Layout]
- 단지 세 번의 샘플링 밖에 할 수 없으므로 Good region이 어디인지 제대로 찾을 수 없음.
➡ Random search를 사용하면 important variable 에서 더 다양한 값을 샘플링 할 수 있으므로 더 좋음
그 밖의 하이퍼파라미터들
- Learning rate
- decay schedule
- update type
- regularization
- network architecture
- 히든 유닛
- depth etc…
이 모든 하이퍼파라미터를 전부 다 최적화 시킬 수 있다.
일부는 이번 시간에 배운 내용이고 나머지는 다음 강의에서 좀 더 다뤄보도록 하겠다.
📜 학생 질문: 보통 하이퍼파라미터를 몇 개씩 선택하는지
그 선택은 모델에 따라 다릅니다. 선택한 하이퍼파라미터의 수가 많을 수록 기하급수적으로 경우의 수가 늘어납니다.
때문에 한번에 너무 많이 할 수 는 없습니다.
또 이는 여러분이 얼마나 많은 자원을 학습에 사용할 수 있는지도 중요합니다.
이는 사람마다 다르고 실험마다 다릅니다.
저같은 경우 두 세가지 정도를 고르는 편이고 많아도 네 가지 정도만 선택합니다. 그 이상이면 out of control이 되버립니다.
일반적으로는 Learning rate가 가장 중요합니다. Learning rate를 가장 먼저 선택해 놔야만 합니다.
regularization, learning rate decay, model size 같은 것들은 LR보단 덜 중요합니다.
Block Coordinate Descent(BCD) 같은 방법을 쓸 수도 있습니다.
가령, 우선 Learning rate를 정해놓은 다음에 다양한 모델 사이즈를 시도해 보는 것입니다.
이 방법을 쓰면 기하급수적으로 늘어나는 Search space를 조금은 줄일 수 있습니다.
하지만 이 방법은 정확히 어떤 순서로 어떻게 찾아야 할지 정해야 하는 것이 가장 큰 문제입니다.
📜 학생 질문: 하이퍼파리미터 값을 변경할 시에 다른 하이퍼파라미터의 최적 값이 변해버리는 경우가 빈번하냐
그런 일이 가끔 발생하긴 합니다. Learning rates가 이런 문제에 덜 민감함에도 실제로 이런 일이 발생하곤 합니다.
Leraning rates가 좋은 범위 내에 속했으면 하지만 보통은 optimal 보다는 작은 값이고 학습 속도가 길어지곤 합니다.
\ 이런 경우에는, 오늘 배우게 될, 더 좋은(fancier) 최적화 방법을 사용하면 모델이 learning rate에 덜 민감하도록 할 수 있습니다.
📜 학생 질문: Learning rate를 작게 하고 Epoch을 늘리면 어떤 일이 발생하는지
그렇게 되면 엄청 오래 걸리겠죠
Learning rates를 낮추고 오랫동안 학습시키게 되면 이론적으로는 항상 동작하는 것이 맞습니다.
하지만 실제로는 Learning rate가 0.01이냐 0.001이냐는 상당히 중요한 문제가 됩니다.
여러분이 적절한 Leraning rate를 찾으면 6시간, 12시간 또는 하루면 학습을 다 시킬 수 있을 텐데
그런데 여러분이 엄청 조심스러워서 Learning rate를 10배, 100배 줄여 버라면 하루면 끝나는 것이 100일이 걸릴수 있습니다.
그럼 세 달이 걸리게 되는 것입니다. 좋지 않은 경우입니다.
보통 컴퓨터과학 분야를 배울때 이런 constants(10배 100배)를 중요하게 다루지 않는 경향이 있는데
당신은 실제로 훈련하는 것에 대해 생각하고 있습니다. 그 constants은 많은 문제가됩니다.
✨다음 이 수업에 대한 복습의 내용까지 이 포스트에 추가했습니다✨