본문 바로가기

Deep Learning(강의 및 책)/CS231

Lecture 7. Training Neural Networks, Part 2

728x90

이번에는 저번 강의에 이어서 Neural Network를 train 할 때 사용되는 내용들에 대한 내용입니다.

이번 강의는 Fancier optimization, Regularization, Transfer Learning에 대해서 말합니다.

 

network에서 loss function을 정의하면 weight가 얼마나 좋은지 나쁜지를 알려줍니다. 위 코드는 SGD(Stochastic Gradient Descent)를 구현한 코드입니다. mini batch안의 데이터에서 Loss를 계산합니다. 그다음 gradient의 반대 방향으로 parameter vector를 업데이트합니다. 반대방향인 이유는 loss function이 내려가는 방향으로 가야 하기 때문입니다. 이러한 과정을 반복하다 보면 결국 loss가 가장 낮은 부분으로 parameter들은 update 될 것입니다.

 

SGD는 문제가 있습니다. loss function이 위와 같이 생겼다고 보겠습니다. 이 경우 수직 방향의 가중치 변화에 훨씬 더 민감하게 반응하기 때문에 loss가 아주 천천히 줄어듭니다. loss에 영향을 덜 주는 수평방향 차원의 가중치는 업데이트가 매우 느리게 진행되기 때문에 loss가 아주 천천히 줄어들고 저렇게 지그재그로 진행될 것입니다.

 

또 다른 문제가 있습니다. 만약 loss가 local minima 또는 saddle point를 갖는다면 학습은 진행되지 않을 것입니다. 먼저 처음 그래프가 local minima에 빠진 모습입니다. 저 위치의 기울기는 0이기 때문에 더이상 loss가 줄어드는 방향으로 update 되지 않습니다. 두 번째 그래프는 saddle point(안장점)에 빠진 모습입니다. saddle point 근처에서도 gradient는 0이 아니지만 매우 작은 값을 갖기 때문에 saddle point 근처에서도 학습은 매우 느리게 진행됩니다. large neural network에서 saddle point 문제가 local minima 문제보다  자주 일어납니다.

 

이러한 문제를 해결하기 위해 SGD에 momentum을 더하는 방식이 등장합니다.  gradient를 게산할 때 velocity도 이용합니다. gradient의 방향뿐만 아니라 velocity도 같이 고려해 값을 변경해나갑니다. 위 코드에서 rho는 hyperparameter로 주로 0.9 또는 0.99와 같이 거의 1에 가까운 값을 갖습니다. 

 

위 그림을 보면 SGD에 momentum을 추가해 local minima와 saddle point에 빠져도 학습이 진행되는 모습을 볼 수 있습니다. 전에 내려오던 velocity가 남아있기 때문에 gradient가 0이 되더라도 학습이 진행됩니다.

 

이번에는 nestrov momentum(nesterov accelerated gradient)에 대해서 얘기합니다. 왼쪽 그림은 일반적으로 actual step을 구하는 모습입니다. 현재 지점(빨간 점)에서 gradient를 계산한 뒤 velocity와 더합니다. nesterov momentum은 계산 순서를 조금 변경합니다. 먼저 velocity 방향으로 이동을 한 후 그 지점에서 gradient를 구합니다. 원래 위치하던 점에서 velocity와 구한 gradient를 더하는 방식입니다. 이 방식은 Convex problem에서는 좋은 성능을 보이지만 non-convex에서는 성능이 보장되지 않습니다.

 

velocity를 update하기 위해서 이전의 velocity와 x + ρv에서의 gradient를 계산합니다. 그리고 step update는 계산한 velocity를 더합니다. 변수들을 적절히 잘 바꿔주면 위와 같이 다르게 표현 가능하며 loss와 gradient를 같은 점에서 계산할 수 있게 됩니다.

 

이 그래프를 보면 momentum들은 velocity가 존재하기 때문에 minima를 지나치는 경향이 있습니다.

 

이번에는 AdaGrad라는 방식입니다. train 도중에 계산되는 gradient를 활용하는 방법입니다. gradient 제곱에 root를 씌운 값으로 나눠주는 모습입니다.

 

만약 loss가 위와 같이 생겼다고 하겠습니다. 2차원 지표가 있고 한 차원은 항상 gradient가 높은 차원이고 나머지 하나는 항상 작은 gradient를 갖는 모습입니다. 작은 값을 갖는 부분은 gradient의 제곱 값 역시 작습니다. dx는 작은 값으로 나눠지기 때문에 계속 가속이 붙게 됩니다. 큰 값을 갖는 부분은 gradient의 제곱 역시 큰 값을 갖기 때문에 dx는 큰 값으로 나눠지게 되고 속도는 점점 줄어들게 됩니다.

또한 AdaGrad는 step이 진행될수록 값 자체가 점점 작아집니다. update동안 gradient의 제곱이 계속해서 더해지고 이로 인해 갈수록 더 큰 값으로 dx를 나누게 되고 x는 더 작아집니다. non-convex case인 경우 saddle point에 걸렸을 때 더 이상 학습이 되지 않을 수 있습니다.

 

이번에는 AdaGrad를 변형시킨 RMSProp가 등장합니다. AdaGrad는 제곱을 그대로 사용하지만 RMSProp는 약간 다릅니다. 기존 누적 값에 decay_rate를 곱합니다. 그리고 gradient의 제곱에 1-decay_rate를 곱합니다. 이러한 방식으로 속도가 계속 줄어드는 문제를 해결할 수 있습니다.

 

Adam이라는 optimzer도 등장합니다. first_moment는 gradient의 가중 합입니다. second_moment는 AdaGrad나 RMSProp 처럼 gradient의 제곱을 이용하는 방법입니다. Adam으로 update를 진행하게 되면 first moment는 velocity를 담당하게 됩니다.  그리고 sqrt(second_moment)로 나눠주는데 이러한 모습인 Adam은 RMSProp와 momentum을 합친 것처럼 보입니다. 그런데 문제도 있습니다.

첫 step에서는 어떻게 될까?? 초기에 second moment를 0으로 초기화합니다. second moment를 1회 update하고 난 후를 생각해보겠습니다. beta2는 decay_rate로 0.9 또는 0.99로 1에 가까운 값입니다. 그렇기 때문에 1회 update 한 이후에도 second moment는 여전히 0에 가까운 값을 갖습니다. update step에서 second moment로 나누게 되는데 나눠주는 값이 작기 때문에 초기 step의 결과는 매우 커집니다. 이는 gradient 자체가 크다는 것이 아니라 second moment를 0으로 초기화해서 생긴 인공적인 현상입니다.

이러한 문제를 해결하기 위해서 Adam은 bias항을 추가합니다. first / second moment를 update한 뒤에 현재 step에 맞는 적절한 unbiased term을 계산해줍니다. 주로 beta1은 0.9로 사용되고 beta2는 0.999, learning rate는 1e-3 또는 5e-4 정도 사용됩니다.

 

위 모습을 보면 Adam은 momentum처럼 overshoot 하지만 momentum처럼 심하지는 않습니다. 그리고 RMSProp 처럼 각 차원의 상황을 따로 고려해서 step을 이동합니다.

 

이번에는 model 앙상블에 대한 얘기입니다. 지금까지 했던 모든 optimizer 알고리즘은 train error를 줄이고 loss function을 최소화시키기 위한 역할을 수행합니다. 사실상 우리가 원하는 것은 train/test error의 격차를 줄이는 것입니다. 우리가 loss function의 최적화를 모두 마친 상황에서 한 번도 보지 못한 데이터에서의 성능을 올리기 위해서 앙상블이 등장합니다. model을 하나만 학습시키지 말고 10개의 모델을 독립적으로 학습시키고 결과는 10개 모델 결과의 평균을 이용합니다. model의 수가 늘수록 overfitting은 줄어들고 성능이 조금씩 향상됩니다.

 

앙상블의 또 다른 방법입니다. 모델들을 독립적으로 학습하는 것이 아닌 하나의 모델을 학습하는데 도중 중간 모델들을 저장하고 이를 앙상블로 사용하는 방법입니다. test time에는 여러 저장된 모델들에서 나온 예측값들의 평균을 내서 사용하면 됩니다.

 

이는 learning rate를 크게 했다가 작게 했다를 반복하는 방법입니다. 이러면 다양한 지역에서 수렴할 수 있도록 도와줍니다. 

이러한 여러 model들을 이용하는 앙상블에는 문제가 있습니다. test time에 존재하는 모델들을 전부 계산해야 하는 문제가 존재합니다. train time이 긴 것은 괜찮지만 test time이 긴 경우는 좋지 않습니다. 그래서 등장하는 개념이 regularization입니다.

 

이전에 본 regularization은 loss에 추가적인 항을 삽입하는 방법이었습니다. 이번에는 dropout이라는 방법에 대해서 소개합니다. 이는 forward 과정에서 임의로 일부 뉴런을 0으로 만듭니다. forward때마다 0이 되는 뉴런은 변합니다. 이러한 dropout은 주로 FC layer에서 사용됩니다. conv layer에서도 종종 등장하곤 하는데 conv layer의 경우 feature map에서 dropout을 시행하게 됩니다. conv layer의 경우 여러 channel이 있기 때문에 일부 channel 자체를 dropout 하는 경우도 있습니다.

 

코드로 구현하면 위와 같이 간단히 구현할 수 있습니다. P는 dropout하는 비율입니다.

 

dropout을 이용해 특징들 간의 상호작용을 방지하는 모습을 볼 수 있습니다. 예를 들어 고양이인지 아닌지를 분류하는 network가 있다고 할 때 어떤 뉴런은 눈에 대해, 어떤 뉴런은 꼬리에 대해, 또 어떤 뉴런은 털에 대해서 학습을 할 것입니다. 그리고 이 이미지가 고양이인지 아닌지를 이 정보들을 모두 합쳐 결정 내립니다. dropout을 적용하게 되면 network가 어떤 일부 feature에만 의존하지 못하게 합니다. 모델이 고양이다라고 예측할 때 다양한 feature를 골고루 이용할 수 있도록 해줍니다. 그래서 dropout은 overfitting을 어느 정도 막아준다고 할 수 있습니다.

 

또 다른 해석이 있는데 dropout을 이용해 단일 모델로 앙상블 효과를 얻을 수 있다고 합니다. 그래프를 보면 뉴런의 일부만 사용하는 sub network모습입니다. dropout으로 만들 수 있는 sub network의 경우의 수는 매우 다양하므로 따라서 dropout은 서로 parameter를 공유하는 sub network 앙상블을 동시에 학습시키는 것이라 볼 수 있습니다.

 

dropout을 적용하지 않은 model은 x와 W의 곱으로 이루어진 y가 나오는데, dropout을 사용하면 z라는 random mask도 input으로 들어갑니다. test의 경우 mask가 random하게 적용되는 것은 좋지 않습니다.

 

test time에서 stochasticity를 사용하지 않고 간단하게 dropout을 적용하려면 dropout probability를 network의 output에 곱하면 됩니다. 이러면 train와 test에서의 기댓값은 같은 모습을 보이게 됩니다.

 

inverted dropout의 경우 train time에서는 p를 나눠주는 연산만 추가됩니다. train의 경우 GPU로 연산을 하기 때문에 나눗셈 하나가 추가되는 것은 큰 문제가 되지 않습니다. 하지만 test의 경우 연산 하나가 추가되는 경우 계산의 복잡도에 따라 연산속도가 달라집니다.

 

regularization은 train time에서 randomness를 추가해 너무 fit하지 않게 만들어 줍니다. dropout은 batch normalization과 유사한 regularization 효과를 줄 수 있어서 BN을 사용한 경우 dropout을 따로 사용하지 않습니다. 그래도 dropout은 p를 마음대로 조절할 수 있기 때문에 둘 다 사용하는 경우도 있습니다.

 

train data를 무작위로 변환시키고 label은 그대로 놔둔 채 CNN의 input으로 넣어줍니다. 이렇게 되면 원본 이미지를 사용하는 것이 아니라 무작위로 변환시킨 이미지로 학습시킨 것이 됩니다. 무작위로 변환시키는 예시로 대칭 이동을 하거나 자르거나 크기를 줄이거나 밝기 등을 조절하는 방법들이 있습니다. 결국 이렇게 이미지를 변환시켜도 그 이미지는 원본처럼 고양이 이미지라는 사실은 달라지지 않습니다.

 

fractional max pooling이라는 방법도 소개되고 있습니다. 보통 2x2 max pooling연산은 고정된 2x2 지역에서 수행합니다. 하지만 fractional max pooling에서는 그렇게 하지 않고 pooling 연산을 수행할 지역이 임의로 선정됩니다. 위 예시를 보면 train time에 sampling 될 수 있는 임의의 pooling region을 볼 수 있습니다. test time에서는 pooling regions을 고정시켜 버리거나 혹은 여러 개의 pooling regions을 만들고 averaging over 시켜도 됩니다.

 

transfer learning은 CNN 학습에 많은 데이터가 필요하다는 말은 틀린 말이라는 것을 보여준다 합니다.

 

CNN을 ImageNet이라는 아주 큰 data로 학습을 진행합니다. 우리가 원하는 것은 ImageNet class가 아닌 강아지를 10종으로 분류하는 것이라고 하겠습니다. ImageNet을 통해 학습된 feature들을 이용해 우리가 가지고 있는 작은 data를 학습시킵니다. 일반적인 network의 마지막 부분에는 FC Layer가 feature와 class score 간의 연결을 하는데 이 부분을 우리가 원하는 형태로 초기화합니다. ImageNet을 학습하면 4096x1000차원의 행렬이지만 우리가 원하는 문제를 풀기 위해 4096x10으로 바꿉니다. 그리고 방금 정의한 가중치 행렬은 초기화시키고 이 전에 존재하던 다른 layer들(ImageNet으로 학습된)은 유지합니다. 마지막 layer만 학습하는 것은 linear classifier를 학습시키는 것과 같습니다. 이렇게 model을 학습시키면 작은 data를 가지고 잘 학습된 model을 만들 수 있습니다.

만약 data가 조금 많다면 전체 network를 fine-tuning할 수 있습니다. 최종 layer들을 학습시킨 후, network 전체의 학습을 할 수 있습니다. 보통 이 경우 learning rate를 줄여서 학습을 진행합니다. 기존 weight들은 어느 정도 잘 학습되어 있어서 weight들을 조금만 변경하면 되기 때문입니다.

 

위 표를 보면 가지고 있는 data와 data가 내가 가지고 있는 data와 얼마나 비슷한지에 따라 model이 학습이 잘 되는지에 대한 내용을 정리해 보여주고 있습니다.