Batch Normalization 은 레이어의 출력 activation을 강제로 unit gaussian(0과 1 사이에 존재)으로 만들어 준다. 즉 각 층에서 나온 배치(작은 부분) 단위의 activation을 현재 배치에서 계산한 평균과 분산을 이용해 정규화함으로써 unit gaussian(평균이 0, 분산이 1인분포)으로 만든다. 그 이유는 배치간 분포의 차이가 있으면 학습이 잘 되지 않기 때문이다.
가중치를 잘 초기화시키는 것 대신 학습할 때마다 각 레이어에 Batch Normalization을 하여 모든 레이어가 unit gaussian이 되도록 하여 학습하는 동안 모든 레이어의 입력이 unit gaussian이 되도록 한다(gaussian distribution).
(그러나 실제로 입력이 gaussian을 따르지는 않는다. 입력 데이터가 gaussian과 비슷하다(approximately)고 가정하고 BM이 잘 동작하기를 기대한다.)
배치 당 N개로 구성된 D차원인 학습 데이터 X가 있다고 하자. 각 차원별(각 특징별)로 실제 평균과 분산을 독립적으로 구한 후 한 배치 내에서 계산한 값을 정규화하여 unit guassian 범위를 가진 activation을 만든다. 그렇게 만든 함수 는 미분 가능하므로 backpropagation을 할 수 있다.
Batch Normalization은 Fully Connected Layers/Convolutional Layers와 비선형 함수인 활성화 함수 사이에 존재한다. 깊은 네트워크의 경우(레이어가 많을 경우) 각 레이어의 가중치 W가 지속적으로 곱해져서 Bad scaling effect가 발생하는데, 정규화가 이를 상쇄시켜 준다. Batch Normalization은 스케일만 살작 조정하는 것이기 때문에 FC나 Conv에 모두 적용될 수 있다.
Convolutional Layers에서는 activation map의 같은 채널 요소를 같이 정규화한다. 즉 activation map(채널)마다 평균과 분산을 하나씩 구한다. 그 이유는 CNN이 데이터의 "공간적 구조"를 잘 유지하기를 원하기 때문이다. (FC는 각 차원별로 독립적으로 정규화한다.)
그런데 여기서 한 가지 문제가 있다. FC layer를 거칠 때마다 정규화해주어야 하는가? 즉 tanh의 입력이 꼭 unit gaussian이어야 하는가? 정규화가 하는 일은 입력이 tanh의 linear한 영역에만 존재하도록 하는 것이다. 그러면 포화가 전혀 일어나지 않기 때문이다. 그러나 saturation이 전혀 일어나지 않는 것보다 "얼마나" 일어날지 조정하는 것이 더 좋은 방법이다.
BN에서는 입력값(FC/Conv의 출력값)에 (1) 정규화 연산(평균을 빼서 zero-centered로 만들고 분산으로 나누어 scaling함)을 해주고 (2) 다시 scaling/shifting 연산을 해 준다. 이를 통해 unit gaussian으로 정규화된 값을 스케일링(γ)과 이동(β)시켜 원상복구시킨다. 여기서 γ는 분산, β은 평균이다. 네트워크가 데이터를 tanh에 얼마나 saturation 시킬지 학습하기 때문에 유연성을 얻을 수 있다.
정리하자면 입력이 있을 때 모든 mini-batch에서의 평균과 분산을 각각 계산한다. 계산한 평균과 분산으로 정규화를 해 주고, scaling과 shifting을 해준다.
BN의 장점 은 다음과 같다. (1) gradient의 흐름을 원활하게 해 주어 학습이 더 잘되게(robust) 해준다. (2) learning rates를 증가시킬 수 있고 (3) 가중치 초기화에 강하게 의지하지 않아도 된다. 그래서 BN을 쓰면 학습이 더 쉬워진다. (4) BN은 regularization의 역할도 한다. 각 레이어의 출력은 batch 안에 존재하는 모든 데이터들에 영향을 받기 때문에 이 레이어의 출력은 배치 내의 모든 데이터가 하나로 묶인다. (데이터의 평균, 분산을 사용하기 때문) 그러므로 레이어의 출력은 조금씩 바뀌게 되고 이는 regularization effect이다.
Q. 왜 γ와 β를 학습시켜서 다시 identity function(Y = X)가 되게 하는가?
유연성 때문이다. BN은 레이어의 입력이 unit gaussian이 되도록 강제한다. tanh의 예시에서도 보았듯이 입력데이터가 tanh로 인해 얼마나 saturation 될지를 조절하고 싶을 수도 있다. gaussian normalization이 필요하면 그렇게 해주고, 아니라면 unit gaussian을 scaling 하고 shifting할 수 있다. γ와 β라는 파라미터는 유연성을 제공한다.
Q. 배치 사이즈가 작은 경우 어떻게 해야 하는가?
대부분의 CNN에서 BN은 아주 잘 동작한다. 배치 사이즈가 작으면 정확도가 떨어지지만 효과는 비슷하다. 배치 사이즈가 작은 경우 더 많은 샘플들을 이용해서 평균과 분산을 구하도록 다시 디자인할 수도 있다. 그러나 이런 경우는 흔하지 않다.
Q. 입력을 gaussian 이라고 강제해 버리면 기존의 구조(structure)를 잃지 않는가?
모든 입력들(features) 가우시안 분포로 만든다고 해도 어떠한 구조도 잃지 않는다. 정규화는 "연산이 잘 수행되도록" 데이터를 조금만 이동시키고 스케일링 하는 것이다. (선형변환)
Q. shift와 scale요소를 추가시키고 학습을 시키면 identity mapping이 되서 BM이 사라지는 것 아닌가?
네트워크가 BM이 쓸모없다고 판단해서 identity mapping으로 만든다면(극단적인 경우) 그렇겠지만 실제로 그런 일은 일어나지 않는다. shift와 scale이 일정량 변하나 identity mapping이 될 정도는 아니므로 batch normalization의 효과를 얻을 수 있다.
BN이 Test time에서 어떻게 동작하는가? BN에서 평균과 분산은 학습데이터에서 구한 것이므로 Test time에 추가적인 계산은 하지 않는다. 즉 Training time에 평균과 분산을 계산하고 Test time에 사용한다.
지금까지는 네트워크 설계를 배웠다. 이제부터는 좋은 학습 결과를 얻기 위해 학습과정을 어떻게 모니터링하고 하이퍼파라미터를 조절할 것인지를 배울 것이다.
우선 가장 첫 단계는 데이터 전처리이다. 데이터 전처리에는 zero-mean을 사용한다. (입력값에서 평균을 뺌)
2번째 단계에서는 아키텍쳐(CNN, LSTM 등)를 선택한다. 우선 하나의 Hidden Layer와 50개의 뉴런을 가진 모델로 설정하자. (어떤 모델을 선택해도 상관없다.)
네트워크를 초기화한 후, Forward pass하여 Loss를 구한다. 여기서 Regularization은 0으로 준다. 네트워크가 잘 동작한다면 그 결과값인 Loss는 그럴듯해야 한다. 만약 Softmax classifier를 사용할 때 가중치가 작은 값이면 Loss는 이 되어야 한다. 10개의 클래스라면 Loss는 이 될 것이다. 코드의 결과가 약 2.3이 나왔으므로 Loss가 잘 동작한다는 것을 알 수 있다. 이 방법은 언제든 사용할 수 있는 유용한 방법이다. (Good sanity check)
초기 Loss가 정상이라는 것을 확인했다면 regularization(1e3)을 추가한다. 그 결과로 Loss가 증가한다. 손실함수에 regularization term이 추가되기 때문(일반화되기 때문)이다. 이 또한 유용한 sanity check이다.
학습을 진행할 준비가 끝났으면 이제 학습을 시작한다. 우선 데이터의 일부만 학습시킨다. 데이터가 적으면 당연히 Overfit되고 Loss가 많이 줄어들 것이다. 이 때는 regularization를 사용하지 않고 Loss가 내려가는지만 확인한다.
(위의 코드는 CIFAR-10CDPTJ 20개의 데이터를 가지고 와서 regularization을 0으로 주고 softmax를 사용하여 loss를 찾고 SGD를 사용하여 가중치 최적화를 시킨 것이다.)
Epoch마다 Loss가 0을 향해 잘 내려가는지 확인한다. Loss가 내려감과 동시에 Train Accuracy는 점점 증가하는 것을 볼 수 있다. 데이터가 작은 경우라면 모델이 완벽하게 데이터를 overfit할 수 있어야 한다.
전체 데이터 셋에 regularization을 약간 주면서 적절한 learning rate를 찾아보자. Learning rate는 가장 중요한 하이퍼파라미터 중 하나이며, 가장 먼저 정해야만 하는 하이퍼파라미터이다.
처음에는 learning rate를 1e-3으로 정해 본다. loss가 변하지 않는 것을 알 수 있다. Loss가 잘 줄어들지 않는 가장 큰 요인은 Learning rate가 지나치게 작기 때문이다. Learning rate가 지나치게 작으면 gradient 업데이트가 충분히 일어나지 않고, cost/loss가 변하지 않는다. 그러나 training/ validation accuracy는 20%까지 급상승한다.
그 이유는 확률 값들이 멀리 퍼져있기 때문에 loss는 잘 변하지 않지만 "학습"을 하고 있기 때문에 이 확률이 조금씩 "옮은" 방향으로 바뀌고 있기 때문이다. 따라서 가중치는 서서히 변하지만 Accuracy는 가장 큰 값만 취하기 때문에 Accuracy는 갑자기 뛸 수 있다.
이제 learning rate를 더 큰 값인 1e-6로 바꿔본다.
cost/loss가 NaNs이다. NaNs의 뜻은 cost가 발산(exploded)하였다는 것이다. learning rate가 지나치게 높기 때문에 learning rate를 낮춰야 한다.
learning rate를 3e-3로 바꿔 주어도 여전히 발산한다. 보통 learning rate는 [1e-3, 1e-5] 사이의 값을 사용한다. 이 범위 사이의 값을 이용해서 cross-validation을 한다. 즉 [1e-3, 1e-5] 사이의 값들을 이용해서 learning rate가 지나치게 작은지 아니면 큰지를 결정 할 수 있다.
하이퍼파라미터를 최적화시키고 그 중 가장 좋은 것을 선택하려면 어떻게 해야 할까? 한가지 전략은 바로 cross-validation 이다. Cross-validation은 Training set으로 학습시키고 Validation set으로 평가하는 방법이다. Cross-validation을 사용하여 위에서 정한 hyperparameter(learning rate)가 잘 동작하는지를 확인한다.
우선 coarse stage 에서는 넓은 범위에서 값을 고른다. Epoch 몇 번 만으로도 현재 값이 잘 동작하는지 알 수 있다. Nan이 나오거나 Loss가 줄지 않거나 하는 것을 보면서 적절히 잘 조절할 수 있다. coarse stage가 끝나면 어느 범위에서 잘 동작하는지를 대충 알게 된다.
두 번째 fine stage에서는 좀 더 좁은 범위를 설정하고 학습을 길게 시키면서 최적의 값을 찾는다.
5 epochs을 돌며 coarse search를 하는 과정을 보자. 네트워크 구조는 앞서 만든 것과 유사하다. 확인해야 할 것은 validation accuracy이다. 빨간색으로 표시된 것이 높은 val_acc이다. 빨간 색으로 표시해 둔 지역이 바로 fine-stage를 시작할 만한 범위가 될 것이다.
하이퍼 파라미터 최적화 시에 파라미터를 Log space로 바꾸는 것이 좋다. 즉 파라미터 값을 샘플링할때 로 샘플링하지 말고 (-3, -6)처럼 10의 차수 값만 샘플링하는 것이 좋다. Learning rate는 gradient와 곱해지기 때문에 learning rate의 선택 범위를 log scale을 사용하는 편이 좋다.
learning rate와 regularization의 범위를 조정한다. reg의 경우는 , lr은 로 좁혀준다. 다시 학습을 하면 53%의 val_acc가 보인다. 50개의 은닉층을 가진 2-layer Neural net에서 좋은 결과이다.
그러나 가장 좋은 Accuracy (53.1%)인 부분을 보면 문제가 있다. 결과를 잘 보면 good learning rates는 전부 10E-4 사이에 존재한다. Learning rate의 최적 값들이 좁혀 설정한 범위의 경계부분(9.471549e-04)에 집중되어 있다. learning rate의 최적값이 범위의 경계 부분에 있다면 최적의 Learning rate를 효율적으로 탐색할 수 없다. 예를 들어 최적의 값이 1E-5나 1E-6 근처에 존재할 수도 있다. 탐색 범위를 조금 이동시켜 더 좋은 범위를 찾을 수 있다. 따라서 최적의 값이 정한 범위의 중앙에 위치하도록 범위를 잘 설정 해 주는 것이 중요하다.
좋은 하이퍼파라미터를 찾는 다른 방법은 grid search를 이용하는 것이다. grid search는 하이퍼 파라미터를 고정된 값의 간격으로 샘플링하는 것이다. 하지만 실제로는 random search를 하는 것이 결과가 더 좋다. Grid search 는 시간이 오래 걸리고 다시 실행해도 결과가 비슷한다.
모델이 x축에 더 민감하게 반응을 한다고 할 때 (노란 < 초록), Random search는 x축에서 더 많은 샘플링이 가능하므로 위에 그려놓은 초록색 함수를 더 잘 찾을 수 있다. 그러나 Grid Layout에서는 (x축에서) 세 번의 샘플링만 가능하므로(x축만 고려하면 위에 있는 3개의 점은 하나로 인식되기 때문) 좋은 구간을 제대로 찾을 수 없다.
다시 말하면 Random search를 사용하면 important variable에서 더 다양한 값을 샘플링할 수 있으므로 더 좋은 결과가 나온다. 또한 시간을 단축할 수 있어서 자주 사용할 수 있다.
최적화할 수 있는 하이퍼파라미터에는 Learning rate, decay schedule, update type,
regularization, network architecture 등이 있다. 그 밖에도 히든 유닛과 depth의 수 등을 최적화시킬 수 있다. 일부는 이번 시간에 배운 내용이고 나머지는 다음 강의에서 다룰 것이다.
이 과정은 마치 턴테이블의 knobs를 조절하는 과정과 같다. neural networks practitioner가 여러분이고, 손실함수의 출력이 음악이라고 하면 적절한 출력(loss)을 내기 위해서 모든 것들을
적절히 조절하는 과정이 하이퍼파라미터 최적화이다.
하이퍼파라미터 최적화와 Cross-validation을 많이 해야 한다. cross validation으로 많은 하이퍼파라미터를 직접 돌려보고 모니터해서 어떤 값이 좋고 나쁜지를 확인해야 한다.
loss curve를 보면서 좋은 것을 찾아 시도해보는 일을 계속 반복해야 한다. loss curve를 모니터링할 때 learning rate이 중요하다. loss curve를 보고 어떤 learning rate가 좋고 나쁜지를 알 수 있다. Loss가 발산하면 learning rate가 높은 것이고 너무 평평(linear)하면 너무 낮은 것이다. 가파르게 내려가다가 정체기가 생기면 learning rate가 여전히 너무 높다는 의미이다. learning step이 너무 크게 점프해서 적절한 local optimum에 도달하지 못했기 때문이다.
최적의 learning rate는 오른쪽 그래프처럼 (파란색 선) 기울기가 가파르면서 지속적으로 잘 내려간다. 그 의미는 현재 learning rate를 유지해도 좋다는 뜻이다.
위의 그래프와 같이 loss가 평평하다가 갑자기 가파르게 내려간다면 이는 초기화의 문제일 수 있다. gradient의 backprop이 초기에는 잘 되지 않다가 학습이 진행되면서 회복이 되는 경우이다.
accuracy 수치는 loss와 달리 직관적으로 학습이 제대로 이루어졌는지 알 수 있다. (예: 70%의 정확도) accuracy 그래프에서 train_acc와 val_acc가 큰 차이를 보인다면 학습이 overfit되었다는 뜻이다. regularization를 크게 해야 할 수도 있다. 반면, gap이 거의 없다면 아직 overfit하지 않은 것이고 capacity을 높힐 수 있는 충분한 여유가 있다는 뜻이다.
또한 가중치의 크기 대 가중치 업데이트의 비율()을 지켜볼 필요가 있다. 우선 파라미터의 norm을 구해서 가중치의 규모를 계산한다. 업데이트 사이즈도 norm을 통해 구할 수 있고 업데이트 사이즈를 통해 크게 업데이트되는지를 알 수 있다. 가중치의 크기 대 가중치 업데이트의 비율은 대략 0.001 정도 되면 된다. 이 방법은 업데이트가 너무 크거나 작은지 알 수 있고, 디버깅할 때 유용하다.
6강의 내용: 활성화 함수와 데이터 전처리, 가중치 초기화, Batch Norm, 학습 과정 준비하기,
하이퍼 파라미터 최적화