[CS231n] Lecture 6: Training Neural Networks I 정리
지난 시간 복습
- 지난 시간에 함수를 Computational graph로 표현하는 방법을 배움
- 또한, Neural Network에 대해서 배웠음
- Linear Layer가 있고, 중간 중간에 비선형 연산자(activation function)을 끼워 넣으면서 여러 층으로 쌓음
- CNN은 Spatial Structure를 사용하기 위해 Convolution Layer를 사용하는 Neural Network의 특수한 형태
- Convolutional 필터가 입력 이미지를 슬라이딩해서 계산한 값들이 모여 출력 Activation map을 만듦
- Convolution Layer는 일반적으로 다수의 필터를 사용
- 각 필터는 서로 다른 Activation map을 생성
- 위 그림에서, 어떤 입력이 있고 Depth가 있음
- 이제 Activation map을 만들어야 하는데, Activation map은 필터의 개수만큼 존재
- 각 activation map은 입력의 공간적인 정보를 보존하고 있음
- 이제 우리가 하고싶은 것은 가중치, 즉 파라미터를 업데이트하고 싶음
- 이전에 배웠던 것 처럼, Optimization을 통해서 Network의 파라미터를 학습할 수 있음
- Loss가 줄어드는 방향으로 이동하고 싶음 (경사 하강법)
- 이는 gradient의 반대 방향으로 이동하는 것과 동일
- 이를 Mini-batch Stochastic Gradient Descent라고 함
- Mini-batch SGD는…
- 우선 데이터의 일부만 가지고 Forward pass를 수행한 후 Loss를 계산
- 이후, Backpropagation을 진행하며 gradient를 계산
- 계산된 gradient를 이용하여 파라미터를 업데이트
Training Neural Network
- 이제부터는 Neural Network의 학습에 대해서 다룰 예정
- 우선, Neural Network 학습을 처음 시작할 때 필요한 기본 설정에 대해 알아볼 것
- Activation function 선택, 데이터 전처리, 가중치 초기화, Regularization, gradient checking 등이 이에 속함
- 또한, Training dynamics에 대해서도 다룰 예정
- 학습이 잘 되고 있는지 확인하는 방법에 대해서 배움
- 어떤 방식으로 파라미터를 업데이트할 것인지 배움
- 가장 적절한 하이퍼파라미터를 찾기 위한 하이퍼파라미터를 Optimization하는 방법도 배움
- Evaluation 과정에서 적용할 수 있는 내용도 다룸
- Lecture 6은 part 1으로, 다음의 내용을 학습
- Activation Function
- Data Preprocessing
- Weight Initialization
- Batch Normalization
- 학습 과정 다루기
- Hyperparameter Optimization
Activation Functions
- 지난 시간까지 배운 내용에 따르면,
- 데이터 입력이 들어오면 가중치와 곱해줌
- 그 다음 Activation Function, 즉 비선형 연산을 거침
- Activation Function의 종류에는
- Sigmoid, tanh, ReLU, Leaky ReLU, Maxout, ELU 등
- 지난 시간까지는 Sigmoid만 학습
- 이번 시간에는 좀 더 다양한 종류의 Activation function과 이들간의 trade-off에 대해서 다뤄볼 예정
- 가장 먼저 Sigmoid에 대해서 살펴보면,
- 각 입력을 받고, 그 입력을 [0, 1] 사이의 값이 되도록 해줌
- 0 근처 구간(regime)을 보면 선형함수 같아 보임 (/ 모양)
- 이는, Sigmoid가 일종의, 뉴런의 firing rate를 saturation 시키는 것으로 해석할 수 있기 때문 (firing rate = 활성률, 반응속도)
- 즉, 어떤 값이 0에서 1사이의 값을 가지면, 이를 firing rate라고 생각할 수 있음
- Sigmoid는 3가지 문제를 갖고 있음
- Sigmoid는 입력 값을 [0, 1] 사이의 값으로 saturation 시키기 때문에, gradient를 죽일 수 있음
- Sigmoid의 출력이 zero centered 하지 않다는 것
- exp()로 인해 계산 비용이 크다는 것
- Sigmoid가 갖는 문제 1. gradient가 죽는다
- 만약, 데이터 x가 존재하고, 그에 따른 sigmoid의 출력이 있다고 하자
- 우선 dσdL가 역전파 될 것이고, dxdσ와 곱해지게 될 것
- 이런 값들이 연쇄적으로 backpropagation 될 것
- 만약, x=−10이라면, gradient는 어떻게 될까?
- Sigmoid에서 음의 큰 값이면, sigmoid가 flat하게 되고, gradient가 0이 될 것
- 즉, 거의 0에 가까운 값이 backpropagation 될 것
- 결국 이 부분에서 gradient가 죽어버리고, 이후로 0이 계속 전달되게 될 것
- x=0이라면 어떻게 될까?
- 이 구간에서는 gradient 0이 아닌 다른 값으로 존재하기 때문에, 잘 동작함
- x=10에서는 어떻게 될까?
- x 값이 큰 양수일 경우에도, sigmoid가 flat하기 때문에, gradient를 다 죽이게 됨
- 결국, gradient가 잘 흐르지 않음
- Sigmoid가 갖는 문제 2. sigmoid의 출력이 zero centered 하지 않음
- 뉴런의 입력이 항상 양수일 때, 어떤 일이 발생하는지 살펴보면,
- 모든 x가 양수일 때, 이 x는 어떤 가중치와 곱해지고 activation function을 통과하게 될 것
- 이때 가중치 W에 대한 gradient를 한번 생각해보자
- 이 Layer에서의 local gradient가 어떻게 될지 한번 생각해보면,
- 우선 d(activationfunction)dL을 계산할 것이고, 이 Loss가 backpropagation 될 것
- 그리고, local gradient가 있을텐데, 기본적으로 그냥 x
- 즉, backpropagation 과정에서 곱해지는 local gradient인 x는 전부 양수이기 때문에, d(activationfunction)dL의 부호가 유지가 될 것
- 결론적으로, gradient가 전부 양수 또는 전부 음수가 될 것
- 다시 얘기하면,
- 위에서 d(activationfunction)dL가 넘어오는데, 이 값이 음수 또는 양수가 될 것
- 이 값은 local gradient와 곱해질 것이고, 이때 local gradient인 dWd(activationfunction)는 그냥 x가 될 것임
- 결국, gradient의 부호는, upstream gradient의 부호와 같음
- 이것이 의미하는 바는 가중치 W가 모두 같은 방향으로만 움직일 것임을 의미
- 파라미터를 업데이트 할 때, 다 같이 증가하거나, 다 같이 감소할 수 밖에 없음
- 이런 gradient 업데이트 방식은 아주 비효율적
- 따라서, 우리는 일반적으로 zero-mean data를 원함
- 입력 x가 양수/음수 모두를 가지고 있으면, 전부 같은 방향으로 움직이는 일은 발생하지 않을 것
- 다음으로 살펴볼 activation function은 tanh
- sigmoid와 유사
- 가장 큰 차이점은 zero-centered라는 것
- 범위가 [-1, 1]
- 이를 통해서 Sigmoid의 두 번째 문제를 해결
- 그러나, saturation 때문에 여전히 gradient가 죽음
- 여전히 gradient가 flat 해지는 구간이 존재
- sigmoid 보다는 조금 낫지만, 그래도 여전히 문제점은 존재
- 다음으로는 ReLU (자주 쓰임)
- 이전 강의에서 Convolution layer 사이 사이에 ReLU가 있었음
- ReLU 함수는 f(x)=max(0,x)
- 이 함수는 element-wise 연산을 수행하며, 입력이 음수이면 값이 0이 됨
- 양수이면, 입력 값 그대로를 출력
- 기존의 sigmoid와 tanh이 갖고 있던 문제점을 어느정도 해결
- ReLU 함수의 특성 상, 적어도 입력의 절반은 saturation 되지 않음 (ReLU의 가장 큰 장점)
- 기존의 Sigmoid는 함수 안에 지수 항이 존재했지만, ReLU는 단순히 max 연산이므로 계산이 매우 빠름
- ImageNet 2012에서 우승한 AlexNet이 처음 ReLU를 사용
- 그러나, ReLU가 갖고 있는 문제점은,
- 더이상 zero-centered가 아니라는 것
- tanh가 이 문제를 해결했는데, ReLU는 다시 이 문제를 가지게 됨
- 또한, ReLU는 양수에서 saturation이 되지 않지만, 음수에서는 saturation이 된다는 것
- 조금 더 자세히 살펴보면,
- x=−10이면, gradient는 0이 될 것
- x=10이면, gradient는 선형 영역(linear regime)에 속함
- x=0이면, gradient는 0
- 즉, ReLU는 gradient의 절반을 죽여버리는 셈
- 따라서, dead ReLU (죽은 ReLU)라는 현상을 겪을 수 있음
- Data cloud (=training data)가 있을 때,
- ReLU는 평면의 절반만 activate 된다는 것을 알 수 있음
- 각 평면(초록, 빨간색 직선)이 각 ReLU를 의미
- ReLU가 data cloud에서 떨어져 있는 경우에 dead ReLU가 발생할 수 있음 (빨간 직선)
- dead ReLU에서는 activate가 일어나지 않고 update 되지 않음
- 반면, active ReLU는 일부는 active되고, 일부는 active되지 않을 것
- 몇 가지 이유로 이런 일이 발생할 수 있음
- 초기화를 잘못한 경우
- 그림에서 dead ReLU 처럼 생긴 경우
- 즉, 가중치 평면이 data cloud에서 멀리 떨어져 있는 경우
- 이런 경우, 어떤 데이터 입력에서도 activate 되는 경우가 존재하지 않을 것이고, backpropagation도 일어나지 않을 것임
- 즉, update 되지도, activate 되지도 않을 것
- 더 흔한 경우는, Learning rate가 지나치게 높은 경우
- 처음에는 적절한 ReLU로 시작할 수 있다고 해도, 만약 update를 지나치게 크게 해서 가중치가 날뛴다면, ReLU가 데이터의 manifold를 벗어남
- 이런 일들은 학습 과정에서 충분히 일어날 수 있음
- 그래서, 처음에 학습이 잘 되다가 갑자기 죽어버리는 경우가 생기는 것
- 실제로 학습을 다 시켜놓은 Network를 살펴보면, 10~20% 가량은 dead ReLU가 되어 있음
- 이것은 문제이긴 하나, ReLU를 사용하고 있는 대부분의 Network는 겪을 수 있는 문제
- 그러나, 이 정도면 Network 학습에 크게 지장이 있진 않음
- 따라서, 실제 ReLU를 초기화할 때, positive bias를 추가해주는 경우가 있음
- update 시에 active ReLU가 될 가능성을 조금이라도 더 높혀주기 위함
- 하지만, 도움이 된다는 의견도 있고, 그렇지 않다는 의견도 있음
- 이 방법을 항상 사용하는 것은 아니고, 대부분 zero-bias로 초기화 함
- ReLU 이후에 조금 수정된 버전이 나왔음 (Leack ReLU)
- ReLU와 유사하지만, negative regime에서 더 이상 0이 아님
- negative에서 기울기를 살짝 주게 되면, 앞서 설명했던 문제를 상당 부분 해결할 수 있음
- Leaky ReLU의 경우,
- negative regime에서도 saturation되지 않음
- 계산이 효율적 (max 연산이기 때문에)
- dead ReLU 문제를 해결
- 다른 예시로는, PReLU가 있음
- PReLU는 negative space에서 기울기가 존재한다는 점이 Leaky ReLU와 유사
- 단, PReLU에서의 기울기는, α라는 파라미터로 결정됨
- α를 정해놓는 것이 아니라, backpropagation으로 학습시키는 파라미터로 만듦
- 다른 예시로는 ELU
- ReLU의 이점을 그대로 가져옴
- 또한, zero-mean output을 출력 (Leaky ReLU, PReLU의 이점)
- 하지만, Leaky ReLU와 비교해보면, ELU는 negative space에서 saturation
- 즉, 음수 영역에서 gradient가 없음
- 그러나, 이런 saturation이 noise에 더 robustness 함
- 즉, deactivation이 모델에 robustness를 향상시킬 수 있다는 주장
- ELU는 ReLU와 Leaky ReLU의 중간 정도
- Maxout Neuron
- 지금까지 본 activation function과는 조금 다르게 생김
- 입력을 받아들이는 기본 형식을 미리 지정하지 않음
- 대신, w1에 x를 내적한 값과 +b1, w2에 x를 내적한 값과 +b2의 최대값을 사용
- Maxout은 이 두 함수 중 최대값을 취함
- Maxout은 ReLU와 Leaky ReLU의 좀 더 일반화된 형태
- Maxout은 이 두개의 선형 함수를 취하기 때문
- 또한, 선형이기 때문에 saturation 되지 않으며, gradient가 죽지 않음
- 문제점은 뉴런 당 파라미터의 수가 2배가 된다는 것
- 이제는 w1과 w2를 지니고 있어야 함
- 일반적으로 ReLU를 많이 사용하며, 잘 동작함
- 다만, ReLU를 사용할 때, Learning rate를 잘 결정해야함
- learning rate에 따른 가중치 업데이트 → dead ReLU 문제 발생
- 또한, Leaky ReLU, Maxout, ELU와 같은 다양한 activation function을 사용할 수 있음
- tanh 또한 사용할 수 있지만, 일반적으로 ReLU or ReLU의 변형이 더 잘 동작함
- sigmoid는 사용하지 않는 것이 좋음 (가장 구식)
- 데이터의 특성에 따른 적절한 activation function을 찾는 것이 좋음
Data Preprocessing
- 이제는 실제 Network를 훈련시켜 볼 예정
- 실제 입력 데이터가 있을 때, 우리는 일반적으로 입력 데이터를 전처리 함
- 가장 대표적인 전처리 과정은 zero-mean으로 만들고, normalize 하는 것
- normalization은 보통 표준편차로 함
- 앞서, 모든 입력이 전부 양수인 상황을 살펴봄 (zero-centered)
- 이때, 모든 뉴런이 양수의 gradient를 얻게 됨
- 따라서 최적화 과정에서, 최적화 방향으로 바로 진행하는 것이 아니라, 지그재그로 움직이면서 최적화를 진행하게 됨
- 전부 양수일 때 뿐만 아니라, 음수일 때, 0일 때 모두 해당
- normalization을 해주는 이유는, 모든 차원이 동일한 범위 안에 있게 해줘서, 동등한 기여를 하도록 하게 함
- 이미지의 경우, 전처리로 zero-centering 정도만 해줌
- normalization을 하지 않는 이유는, 이미지는 이미 각 차원 간 스케일이 어느 정도 맞춰져 있기 때문 (0~255 사이의 pixel로 이뤄져 있기 때문에)
- Machine learning 에서는, PCA나 whitening과 같은 복잡한 전처리 과정도 존재
- 그러나, 이미지에서는 zero-centering 정도만 사용하고, normalization을 비롯한 여러 복잡한 방법들을 잘 사용하지 않음
- 일반적으로 이미지를 다룰 때, 굳이 입력을 더 낮은 차원으로 projection 하지 않음
- 이미지를 다룰 때, 기본적으로 zero-mean으로 전처리를 해줌
- 평균 값은 전체 training data에서 계산
- 보통 입력 이미지의 사이즈를 서로 맞춰주는데, Network에 들어가기 전에 평균 값을 빼줌
- Test time에서, test data에도 training data에 계산한 평균 값으로 빼줌
- 실제로, 일부 Network는 채널 전체의 평균을 구하지 않고, 채널마다 평균을 독립적으로 계산하는 경우도 존재
- 채널 별 평균이 비슷할지, 독립적으로 계산할 지는 판단하기 나름
Weight Initialization
- 다음으로는, Weight를 어떻게 초기화 시켜야 하는지 알아볼 예정
- Two Layer Neural Network 예시를 보면, (우리가 할 일은 가중치 업데이트)
- 맨 처음에 어떤 초기 가중치들이 있음
- 그리고, gradient를 계산해서 가중치를 업데이트 할 것임
- Q1. 만약, 모든 가중치 = 0이면 어떻게 될까?
- 가중치가 0이라서, 모든 뉴런은 모두 다 같은 연산을 수행함
- 출력도 모두 같을 것이고, 결국 gradient도 서로 같음
- 결국, 모든 뉴런이 모두 똑같이 생기게 됨
- 이것이 모든 가중치를 동일하게 초기화 시키면 발생하는 일
- 즉, 초기 가중치를 적당한 값으로 초기화 해줘야 함
- 초기화 문제를 해결하는 첫 번째 방법은, 임의의 작은 값으로 초기화 하는 것
- 이 경우, 초기 W를 표준정규분포(standard gaussian)에서 샘플링
- 좀 더 작은 값을 위해, 스케일링을 진행
- 0.01로 나눠서, 표준 편차를 0.01로 만들어 줌
- 그러나, 이 방법은 깊은 Network에서는 문제가 생길 수 있음
- Layer 당 500개의 neuron이 있고, 이런 layer 10개로 이루어진 Network
- 이때, 가중치를 임의의 작은 값으로 초기화
- 데이터를 랜덤으로 만들어주고, 이 데이터를 forward 시켰을 때, 각 layer 별 activation 수치를 통계화 시켜보면, 다음 이미지와 같음
- activation function은 tanh를 사용
- 각 layer 출력의 평균과 표준 편차를 계산
- 평균은 항상 0 근처에 있음 (tanh 특성 - zero centered)
- 표준 편차를 보면, layer를 지날수록 점차 줄어듦
- 가장 아래의 그래프를 보면, (forward pass)
- 첫 번째 layer에서는 가우시안 분포와 비슷한 형태의 분포를 형성하고 있음
- 그러나, W를 곱할수록 출력 값이 급격하게 줄어듦 (W가 너무 작은 값들이라서)
- 결국 layer를 거치다 보면, 표준 편차가 0이 됨
- 따라서, 모든 activation function의 결과가 0이 됨
- Backward pass로 다시 생각해보면,
- 각 layer의 입력이 엄청 작은 값들이기 때문에, 입력 값이 점점 0에 수렴
- Backpropagation 과정에서, local gradient는 x (W×x)
- 이때 x는 엄청 작은 값이기 때문에 gradient도 작음 (결국 업데이트가 잘 일어나지 않음)
- 가중치를 조금 더 큰 값으로 초기화하는 경우, (편차가 0.01이 아닌, 1)
- 큰 가중치를 통과한 출력을 구하고, 이를 tanh를 거치게 된다면, 값들이 모두 saturation 될 것임 (tanh 특성 - [-1, 1] 사이의 값을 갖는다)
- 즉, 출력이 항상 -1 이거나 1일 것임
- 모든 출력이 saturation 될 것이고, 따라서 gradient는 0이 될 것임
- 널리 알려진 좋은 방법 중 하나는 Xavier Initialization
- Standard Gaussian으로 뽑은 값을 입력의 수로 스케일링 함
- 기본적으로 Xavier Initialization이 하는 일은, 입출력의 분산을 맞춰주는 것
- 식에서 직관적으로 얻을 수 있는 것은, 입력의 수가 작으면 더 작은 값으로 나누고 좀 더 큰 값을 얻음
- 추가적으로 더 큰 가중치가 필요함
- 작은 입력의 수가 가중치와 곱해지기 때문에, 가중치가 더 커야만 출력의 분산 만큼 큰 값을 얻을 수 있음
- 반대로, 입력의 수가 많은 경우에는 더 작은 가중치가 필요
- 그러나, Xavier Initialization은 ReLU를 쓰면 잘 동작하지 않는다는 문제가 존재
- ReLU는 출력의 절반을 죽임 (음수 구간)
- 출력의 절반은 항상 0이기 때문에, 출력의 분산을 반토막 냄
- 이 문제를 해결하기 위한 방법이 He Initialization을 사용하는 방법
- 뉴런들 중 절반이 없어진다는 것을 고려하기 위해서, 기존 Xavier Initialization 식에서 2를 나눠줌
- 실제 입력(fan_in)은 절반만 들어가므로, 반으로 나눠주는 텀을 추가 (분모에서 fan_in/2)
- 결과를 보면, 전체적으로 좋은 분포를 형성하고 있음
Batch Normalization
- Gaussian의 범위로 activation을 유지시키는 또 다른 아이디어
- 우리는 layer의 출력이 unit Gaussian이길 원함
- Batch Normalization은 강제로 unit Gaussian을 따르게 끔 만듦
- 어떤 레이어로부터 나온 Batch 단위 만큼의 activation이 있다고 했을 때,
- 우리는 이 값들이 unit Gaussian 이길 원함
- 모든 데이터들에서 평균을 뺀 후, 분산으로 나눠줌으로서, 평균을 0으로 만들고, 분산을 1로 만듦
- 누구나 이 데이터를 unit Gaussian으로 만들 수 있음
- 현재 Batch에서 계산한 mean과 variance를 이용해서 normalization을 수행
- 가중치를 잘 초기화 시키는 것 대신, 학습할 때 마다 각 layer에 이런 작업을 수행해서 모든 layer가 unit Gaussian이 되도록 해줌
- 이제부터 할 일은, Network의 forward pass 동안에 그렇게 되도록 명시적으로 만들어 주는 것
- 각 뉴런을 평균과 분산으로 normalization 해줌으로서, 이런 일을 함수로서 구현하는 것
- Batch 단위로 한 layer에 입력으로 들어오는 모든 값들을 이용해서, 평균과 분산을 구하고, 이를 이용하여 normalization을 수행
- 이 함수를 보면, 입력 x와 평균, 분산이라는 상수를 가지고 있는 함수이기 때문에, 미분이 가능함 (즉, Backpropagation 이 가능)
- Batch 당 N개의 학습 데이터가 있고, 각 데이터가 D차원이라고 가정하면,
- 각 차원별로 평균과 분산을 구함
- 한 Batch 내 이걸 전부 계산해서 normalize 함
- 이 연산은 FC나 Conv layer 직후에 넣어줌
- 깊은 Network에서 각 layer의 W가 지속적으로 곱해지기 때문에 Bad scaling effect가 발생했지만, normalization은 그 bad effect를 상쇄시킴
- BN은 입력의 스케일만 살짝 조정해주는 역할이기 때문에, FC와 Conv 어디에든 적용할 수 있음
- Conv layer에서 차이점이 있다면, normalization을 차원마다 독립적으로 수행하는 것이 아니라, 같은 activation map의 같은 채널에 있는 요소들은 같이 normalize 함
- Conv layer의 경우, activation map(채널, depth)마다 평균과 분산을 하나만 계산
- 그 후, 현재 Batch에 있는 모든 데이터로 normalize 함
- Batch Normalization이 갖는 한 가지 문제점은,
- FC layer를 거칠 때마다, 매번 normalization을 해주는 것에 대한 의문
- 정말로 tanh의 입력이 unit Gaussian이길 바라는 건지?
- normalization이 하는 일은, 입력이 tanh의 linear한 영역에만 존재하도록 강제하는 것
- 즉, 평균이 0이고, 분산이 1이 되도록 강제로 만들어줌
- 이렇게 하면, saturation이 전혀 일어나지 않음
- 하지만, saturation이 전혀 일어나지 않는 것 보단, 얼마나 saturation이 일어날지를 조절하면 더 좋음
- 위 문제를 해결하기 위해서, 기존의 BN에서 scaling 연산을 추가
- Unit Gaussian으로 nomarlize된 값들을,
- 즉, 이 값들을 학습을 통해 조절함으로서, normalize 정도를 조절할 수 있음
- 이를 이용하면, normalized 된 값들을 다시 원상복구 할 수도 있음
- 이때는, 감마 = 분산, 베타 = 평균 으로 진행
- 입력이 주어졌을 때,
- mini-batch에서의 평균과 분산을 계산
- 이 후, 평균과 분산으로 normalize
- 다시 추가적인 scaling, shifting factor를 사용
- BN은 gradient의 흐름을 보다 원활하게 해주며, 결국 학습이 더 잘되도록 (robust) 해줌
- 또한, learning rate를 더 키울 수도 있고, 다양한 초기화 기법을 사용해볼 수 있음
- BN을 쓰면 학습이 더 쉬워진다 (BN을 써야하는 이유)
- BN은 regularization의 역할도 함
- 각 layer의 출력은 해당 데이터 뿐만 아니라, batch안에 존재하는 모든 데이터들의 영향을 받음 (평균, 분산)
- 각 layer의 입력은 해당 batch의 평균으로 normalize 되기 때문에
- 따라서, layer의 출력은 오직 하나의 샘플에 대해서 정해지는 값이 아님
- 즉, layer의 출력은 조금씩 바뀌게 되고, 이는 regularization 효과를 줌
- test time에서 batch normalization이 어떻게 동작하는지 살펴보면,
- BN에서 구한 평균과 분산은 train data에서 구한 것
- test time에서 추가적인 계산은 하지 않음
- training time에서, running average 같은 방법으로 평균 분산을 계산하고, test time에서 사용
Babysitting the Learning Process
- 지금까지는 네트워크 설계를 배웠고, 이제부터는 학습 과정을 어떻게 모니터링하고 하이퍼파라미터를 조절할 것 인지 배움
- 첫 단계는 데이터 전처리
- 앞서 배운 것 처럼, zero-mean을 사용
- 다음으로 architecture를 선택
- 이번 예시에서는, 하나의 hidden layer와 50개의 뉴런을 가진 모델
- 다음으로 할 일은, Network를 초기화 시키는 것
- 학습을 진행할 준비가 끝났으면, 학습을 진행
- 처음 시작할 때 좋은 방법은, 데이터의 일부만 우선 학습 시켜 보는 것
- 데이터가 적으면 당연히 overfitting이 생길 것 이고, Loss가 많이 줄어들 것임
- 이때는 regularization을 사용하지 않음
- loss가 감소하는지 확인하는 것
- 이제부터는 정말로 학습을 시작할 차례
- 전체 데이터셋을 사용할 것 이고, regularization을 약간만 주면서 적절한 learning rate를 찾아야 함
- learning rate는 가장 중요한 하이퍼파라미터 중 하나이며, 가장 먼저 정해야만 하는 하이퍼파라미터
- learning rate가 지나치게 작으면, loss가 잘 줄어들지 않음
- learning rate가 지나치게 높으면, loss가 발산
- 따라서, 적절한 learning rate를 찾는 것이 중요
- 일반적으로, 1e-3 ~ 1e-5 사이의 값을 사용
- 이 사이의 값을 사용해서 cross-validation을 수행
Hyperparameter Optimization
- 다음으로 하이퍼파라미터는 어떻게 정해줘야 하는지?
- 하이퍼파라미터를 최적화 시키고, 그 중 가장 좋은 것을 선택하려면 어떻게 하는 것이 좋을까?
- 첫 번째 전략은 cross-validation
- cross-validation은 training set으로 학습시키고, validation set으로 평가하는 방법
- coarse stage에서, 넓은 범위의 값을 골라냄
- 이를 통해서, 어느 범위에서 잘 동작할지 대충 알게 됨
- 이후, fine stage에서 좀 더 좁은 범위를 설정하고, 학습을 더 길게 시켜보면서 최적의 값을 찾음
- 위 코드는, 5 epochs 동안 coarse search를 진행하는 예시
- 여기서 확인해야하는 것은 validation accuracy
- 하이퍼파라미터를 찾는 또 다른 방법은, grid search를 이용하는 것
- 하이퍼파라미터를 고정된 값과 간격으로 샘플링하는 것
- 실제로 grid search보단, 이전의 random search가 더 좋음
- 하이퍼파라미터의 종류에는,
- learning rate, decay schedule, update type, regularization, network architecture 등 여러가지가 있음
- 우리는 여러가지 하이퍼파라미터를 직접 돌려보고 모니터링해서, 어떤 값이 좋고 나쁜지를 확인해야함
- 즉, 하이퍼파라미터에 따른 loss curve를 보면서 좋은 것을 찾아서 시도하는 일을 반복해야함
- loss curve를 모니터링 하는데, learning rate가 굉장히 중요함
- 따라서, loss curve를 보고, 어떤 learning rate가 좋은지 알아볼 수 있어야 함
- loss가 발산하면, learning rate가 너무 높은 것이고, linear하면 너무 낮은 것임
- 빨간색 선과 같이, 가파르게 내려가면서, 지속적으로 잘 내려가는 경우의 learning rate가 좋은 것
- 초기에는 평평하다가, 중간에 갑자기 loss가 떨어지는 경우
- 만약, train_acc와 val_acc가 큰 차이를 보인다면,
- 이는 overfitting의 문제일 수 있음
- 따라서, regularization의 강도를 높여야 함
- 마지막으로, 우리는 가중치의 크기 대비 가중치 업데이트의 비율을 지켜볼 필요가 있음
- 이 비율이 대략 0.001 정도 되길 원함
- 이 비율을 통해서, 업데이트가 지나치게 크거나 작은지에 대한 감을 어느정도 가질 수 있음