목차
👀 코드 보기 , 🤷♀️
이 두개의 아이콘을 누르시면 코드, 개념 부가 설명을 보실 수 있습니다:)
CS231N: Lecture 7강의를 빠짐 없이 정리하였고, 어려운 개념에 대한 보충 설명까지 알기 쉽게 추가해 두었습니다.
Overview
1. Fancier optimization
2. Regularization
- 네트워크의 Train/Test Error 간의 격차를 줄이고자 사용하는 추가적인 기법
- Neural Network에서 실제로 사람들이 사용하고 있는 Regularization 전략에 대해서 다뤄보도록 하겠음
3. Transfer Learning
- 원하는 양 보다 더 적은 데이터만을 가지고 있을때 사용할 수 있는 방법
Fancier Optimization
지난 강의를 돌이켜보면 Neural network에서 가장 중요한 것은 바로 최적화 문제 였다는 것을 알 수 있다.
Nerwork의 가중치에 대해서 손실 함수를 정의해 놓으면 이 손심 함수는 그 가중치가 얼마나 좋은지 나쁜지를 알려준다.
그리고 우리는 손심 함수가 가중치에 대한 “산(landscape)”이라고 상상해 볼 수 있을 것이다.
SGD의 문제
[예제]
- X/Y축: 두 개의 가중치를 의미
- 각 색: Loss의 값
- 목표: 가장 붉은색인 지점을 찾는 것(가장 낮은 Loss를 가진 가중치를 찾는 것)
[Solution: Stochastic Gradient Descent(SGD)]
- 지금까지 배운 것 중 위의 예제를 푸는 간단한 최적화 알고리즘
- 코드는 아래 참조
👀 SGD 코드 & 코드 해석 보기
- 미니 배치 안의 데이터에서 Loss를 계산
- Gradient 의 반대 방향 을 이용해서 파라미터 벡터를 업데이트
반대 방향인 이유: 손실 함수는 내려가는 방향 이어야하기 때문
이 단계를 계속 반복하면 결국 붉은색 지역으로 수렴할 것이고 Loss가 낮을 것이다.
[SGD의 문제]
1. 속도 저하 문제
1. 속도 저하 문제
- 우리의 손실함수가 아래와 같이 생겼고 여기에 똑같이 \(W_1\)과 \(W_2\)가 있다고 가정해보자
- 둘중 어떤 하나는 업데이트를 해도 손실 함수가 아주 느리게 변한다.(수평 축의 가중치는 변해도 Loss가 아주 천천히 줄어든다)
- Loss는 수직 방향의 가중치 변화에 훨씬 더 민감하게 반응
- 현재 지점에서 Loss는 bad condition number를 지니고 있음
- 이 지점의 Hessian maxrix의 최대/최소 singular values 값의 비율이 매우 안좋음
- 이 문제는 고차원 공간에서 훨씬 더 빈번하게 발생
- 예시는 2차원 밖에 안되지만 실제로는 가중치가 수천 수억개(수억개의 방향으로 움직임)
➡ 수억개의 방향중에 불균형한 방향이 존재한다면 SGD는 잘 동작하지 않을 것
2. local minima
2. local minima
- X축은 어떤 하나의 가중치를 나타내고 Y축은 Loss를 나타낸다고 하면 휘어진 손실함수는 중간에 “valley”가 하나 있다.
- 이 valley에서 가중치가 0이 되어버려서 학습을 중단해버린다.(이 valley를 목적지라고 착각하는 것이다)
- locally falt하다.
3. saddle points
3. saddle points
- local minima는 아니지만, 한쪽 방향으로는 증가하고 있고 다른 한쪽 방향으로는 감소하고 있는 지역
- 이런 곳에서도 gradient는 0이 되어 학습 중단
- 이 지점 근처도 기울기가 매우 작기 떄문에 비슷한 문제를 일으킴
- local minima보다 훨씬 더 많은 문제를 일으킴
4. 미니배치 학습의 부정확성
4. 미니배치 학습의 부정확성
- 손실함수를 계산할 때는 엄청 엄청 많은 Traing set 각각의 loss를 전부 계산해야하므로 Loss를 계산할 때 마다 매번 전부를 계산하는 것은 어렵다.
- 그래서 실제로는 미니배치의 데이터들만 가지고 실제 Loss를 추정하기만 한다.(매번 정확한 gradient를 얻을 수가 없다)
gradient의 부정확한 추정값(noisy estimate) 만을 구할 뿐이다. - 아래 그림은 이 문제를 조금 과장해서 그린 것인데 각 지점의 gradient에 random uniform noise를 추가하고 SGD를 수행하게 만든 것이다.(gradient에 noise가 들어가면 어떻게 되는지 알 수 있다)
- 손실함수 공간을 이런식으로 비틀거리면서 돌아다니게 되면 minima까지 도달하는데 시간이 더 오래 걸린다.
📜 학생 질문: SGD를 쓰지 않고 그냥 GD를 쓰면 이런 문제가 전부 해결되는지
답변 전, GD란? 더 알아보러 가기
[GD(gradient descent)와 SGD(stochastic gradient descent)의 차이]
두가지 모두 parameter set을 error function이 가장 작아지도록 수정하는 것
- GD(gradient descent): 전체 샘플들을 반복적으로 업데이트(수정)한다.
- SGD(stochastic gradient descent): 훈련 셋에서 하나의 샘플만을 반복적으로 업데이트 한다.
[답변]
자 이전의 taco shell에서의 문제를 다시한번 살펴보면 full batch gradient descent에서도 같은 문제가 발생합니다
Noise의 문제도 한번 볼까요. Noise는 미니배치이기 때문에서만이 아니라 네트워크의 explicit stochasticity 로도 발생합니다.
이는 나중에 더 살펴 볼 것이지만 이는 여전히 문제가 됩니다.
Saddle points 또한 full batch GD에서 문제가 됩니다. 전체 데이터를 사용한다고 해도 여전히 나타날 수 있겠죠
기본적으로 full batch gradient descent를 사용한다 하더라도 이런 문제들이 해결되지는 않습니다.
SGD + Momentum
바로 SGD에 momentum term을 추가하는 아주 간단한 방법으로 SGD를 최적화 하여 위의 문제를 해결 할 수 있다.
[classic 버전의 SGD (왼쪽])
- 오로지 gradient 방향으로만 움직임
[SGD + momentum (오른쪽)]
- gradient 를 계산할 때 velocity를 이용(현재 미니배치의 gradient 방향만 고려하는 것이 아니라 velocity를 같이 고려하는 것)
- 하이퍼 파라미터 rho가 추가
- momemtum의 비율을 나타냄
- velocity의 영향력을 rho의 비율로 맞춰줌
- 보통 0.9와 같은 높은 값으로 맞춰줌
- velocity에 일정 비율 rho를 곱해주고 현재 gradient를 더함
- 이를 통해서 우리는 이제 gradient vector 그대로의 방향이 아닌 velocity vector의 방향으로 나아가게 됨
[작동 방식]
비유해보면 물리적으로 공이 굴러내려오는 것을 상상해 볼 수 있다.
이 공은 떨어지면 속도가 점점 빨라진다.
이 공은 local minima에 도달해도 여전히 velocity(속도)를 가지고 있기 때문에 gradient = 0 이라도 움직일 수 있다.
때문에 local minima, saddle points를 극복할 수 있게 되고 계속해서 내려갈 수 있다.
업데이트가 잘 안되는 경우(poor conditioning) 를 다시 한번 살펴보겠다.
아래와 같이 지그재그로 움직이는 상황이라면 momentum이 이 변동을 서로 상쇄시켜 버린다.
이를 통해서 loss에 만감한 수직 방향의 변동은 줄여주고 수평방향의 움직임은 점차 가속화 될 것이다.
momentum을 추가하게 되면 high condition number problem을 해결하는 데 도움이 되는 것이다.
아래 그림에선 검은색이 일반 SGD이고, 파란색이 Momentum SGD이다.
Momentum을 추가해서 velocity가 생기면 결국 noise가 평균화되버림.
[그림으로 이해]
- 빨간 점: 현재 지점
- Red Vector: 현재 지점에서의 gradient의 방향을 나타냄
- Green vector: Velocity vector
- 실제 업데이트는(autual step) 이 둘의 가중평균으로 구할 수 있다.
- 이는 gradient의 noise를 극복할 수 있게 해준다.
Nesterov momentum
= Nesterov accelerated
Momentum의 변형
계산하는 순서를 조금 바꾼 것임
[기본 SGD momentum]
- “현재 지점” 에서의 gradient를 계산한 뒤에 velocity와 섞어준다.
[Nesterov momentum]
- 빨간 점에서 시작해서 우선은 Velocity방향으로 움직임
- 그리고 그 지점에서의 gradient를 계산(다시 원점으로 돌아가서 둘을 합치는 것)
- 완벽한 설명은 아니지만 두 정보를 약간 더 섞어준다고 생각해 볼 수 있을 것임
- velocity의 방향이 잘못되었을 경우에 현재 gradient의 방향을 좀 더 활용할 수 있도록 해줌
[성능]
- Nesterov는 Convex optimization 문제에서는 뛰어난 성능을 보임.
- 하지만 Neural network와 같은 non-convex problem에서는 성능이 보장되지는 않음
[수식]
- velocity를 업데이트하기 위해서 이전의 velocity와 (x + pv)에서의 gradient를 계산함.
- 그리고 step update는 앞서 계산한 velocity를 이용해서 구해줌.
- 그리고 더 최근의 gradients에 가중치가 더 크게 부여된다.
- 매 스텝마다 이전 velocity에 rho(0.9 or 0.99) 를 곱하고 현재 gradient를 더해준다.
➡ 이를 moving average라고 볼 수 있다.(exponentially weighted moving average) - 그리고 시간이 지날수록 이전의 gradient들은 exponentially하게 감소한다.
📜 수식 더 자세히 이해하기
[기존 수식]
기존에는 Loss와 Gradient를 같은 점(\(x_t\))에서 구했었다.
[Nesterov 수식]
Nesterov는 이 규칙을 조금 비틀어 버렸다.
이는 상당히 성가시지만 다행이도 쉽게 해결할 수 있는 변형 공식이 있다.
변수들을 적절히 잘 바꿔주면 Nesterov를 조금 다르게 표현할 수 있으며 Loss와 Gradient를 같은 점에서 계산할 수 있게 된다. 그리고 수정된 수식(아래 박스)을 통해서 우리는 Nesterov를 새롭게 이해해보자
첫 번째 수식
- 기존의 momentum과 동일
- 기존과 동일하게 velocity 와 계산한 gradient를 일정 비율로 섞어주는 역할을 함
맨 밑의 수식
- 우선 현재 점과 velocity를 더해줌(여기까지는 기존과 동일)
- 그리고 여기에 “현재 velocity - 이전 velocity” 를 계산해서 일정 비율(rho)을 곱하고 더해줌.
➡ Nesterov momentum는 현재/이전의 velocity간의 에러보정(error-correcting term)이 추가된 것임
📜 학생질문: velocity의 초기값을 구하는 좋은 방법이 있는지
velocity의 초기값은 항상 0이다.
이는 하이퍼파라미터가 아닙니다. 그저 0으로 둔다.
velocity은 이전 gradients의 weighted sum이다.
[학습경항성 비교]
- 기본 SGD(검정색): 매우 느리게 학습되고 있음
- momentum(파란색): minima를 그냥 지나쳐 버리는 경향이 있지만(이전의 velocity의 영향을 받기 때문) 스스로 경로를 수정하고는 결국 minima에 수렴
- Nesterov(초록색): 일반 momentum에 비해서 overshooting이 덜하다.( Nesterov에서 추가된 수식 때문)
AdaGrad
AdaGrad는 훈련도중 계산되는 gradients를 활용하는 방법
Adagrad는 velocity term 대신에 grad squared term을 이용
[수식]
학습 도중에 계산되는 gradient에 제곱을 해서 계속 더해줌
Update를 할때 Update term을 앞서 계산한 gradient 제곱 항으로 나눠줌
Condition number인 경우 빨간색 박스 안의 값은 점점 작아지게된다.
📜 빨간색 박스 안의 값이 점점 작아지는 이유
2차원 좌표가 있다고 해 봅시다. 그 중 한 차원은 항상 gradient가 높은 차원입니다. 그리고 다른 하나는 항상 작은 gradient를 가집니다.
Small dimension에서는 gradient의 제곱 값 합이 작습니다. 이 작은 값이 나눠지므로 가속도가 붙게 됩니다.
Large dimension에서는 gradient가 큰 값 이므로 큰 값이 나눠지게 되겠죠. 그러므로 속도가 점점 줄어듭니다.
[❌ 문제]
학습이 계속 진행되면 값이 점점 작아진다.
update 동안 gradient의 제곱이 계속해서 더해지면, 값(estimate)은 서서히(monotonically) 증가하게 된다.
➡ 이는 Step size를 점점 더 작은 값이 되게 한다.
손실함수가 convex(볼록)한 경우에 점점 작아지는 것은 정말 좋은 특징이 될 수 있다. (convex case에서는 minimum에 근접하면 서서히 속도를 줄여서 수렴할 수 있기 때문)
하지만 non-convex case에서는 문제가 될 수 있다 (가령 saddle point에 걸려버렸을 때 AdaGrad는 멈춰버릴 수 있다.)
[⭕ 해결]
AdaGrad의 변형인 RMSProp으로 해결
RMSProp
앞서 언급한 문제를 개선시킨 방법입
[수식]
- RMSProp에서는 AdaGrad의 gradient 제곱 항을 그대로 사용
- 하지만 이 값들을 그저 누적만 시키는 것이 아니라 기존의 누적 값에 decay_rate를 곱해줌(gradients의 제곱을 계속해서 누적해 나감) ➡ 점점 속도가 줄어드는 문제를 해결
- RMSProp에서는 gradient 제곱 항에 쓰는 decay rate는 보통 0.9 또는 0.99정도를 자주 사용함
- 그리고 ‘현재 gradient의 제곱’은 \((1 - decay rate)\) 를 곱해줘서 더해줌
- RMSProp은 gradient 제곱을 계속 나눠준다는 점에서 AdaGrad와 유사함
- 이를 통해 step의 속도를 가속/감속 시킬 수 있음
[학습경항성 비교]
- 기본 SGD(검정색): 매우 느리게 학습되고 있음
- momentum(파란색): overshoots한 뒤에 다시 minima로 돌아옴
- RMSProp(빨간색): 각 차원마다의 상황에 맞도록 적절하게 궤적(trajectory)을 수정시킴
- Adagrad(초록색): RMSProp에 가려서 잘 보이지 않겠지만 학습하다가 죽어버렸다.
[평가]
실제로 AdaGrad는 잘 쓰이지는 않음
그리고 위의 케이스는 convex case이지만 learning rates가 서로 상이하여 Adagrad에게 불리함
아마도 AdaGrad의 Learning rate를 늘리게 되면 RMSProp과 비슷한 동작을 할 것임
➡ 때문에 여러 알고리즘 간에 ‘같은 Learning rates’ 를 가지고 AdaGrad 를 visualization하는 것은 공정하지 못함
➡ visualization을 하고자 한다면 알고리즘 별로 learning rates를 조정하는 것이 좋음
Adam
momentum 계열 vs Ada계열을 조합
- momentum 계열: velocity를 이용해서 step을 조절
- Ada계열: gradients의 제곱을 나눠주는 방식으로 step을 조절(목적지에 갈수록 이동량을 줄임)
그럼 아담과 유사한 알고리즘부터 출발하여 완성형 아담을 만들어보겠다.
[유사 Adam]
- Adam은 first moment와 second moment을 이용해서 이전의 정보(estimate)를 유지시킴.
- 빨간색 박스 안
first_moment
: gradient의 가중 합 (velocity를 담당) - 파란색 박스 안
second_moment
: AdaGrad이나 RMSProp처럼 gradients의 제곱을 이용하는 방법 (sqrt(second moment)를 나눠주는데 second moment는 gradient의 제곱 항임) - Adam ≈ RMSProp + momentum (momentum + second squared gradients)
[❌ 문제]
- 초기에
second_moment
를 0으로 초기화한다.
이렇게 되면,second_moment
를 1회 Update 하고 난 후엔beta2
는decay_rate
로 .9또는 .99로 1에 가까운 값이 된다.(모멘텀의 velocity는 거의 .9에 가까운 큰 값으로 설정하니까) - 그렇기 때문에 1회 업데이트 이후에도
second_moment
는 여전히 0에 가깝다.
➡ 이는 update step에서second_moment
로 나누게 되는데 나눠주는 값이 크기 때문에 초기 step이 엄청나게 커지게 된다.
✨ 중요한 것은 이 커진 step이 실제로 손실함수가 가파르기 때문(geometry)이 아니라는 것이다. (second moment을 0으로 초기화 시켰기 때문에 발생하는 ‘인공적인’ 현상임)
[⭕ 해결: Adam 완성]
- Adam은 초기 Step이 엄청 커져 버릴 수 있고 이로 인해 잘못될 수도 있다고 했다.
- Adam을 이를 해결하기 위해 보정하는 항을 추가한다.(bias correction term)
- first/second moments를 Update하고 난 후 현재 Step에 맞는 적절한 unbiased term 을 계산해줌
- 실제 Adam은 first/second moment만 계산하는 것이 아니라 이렇게 unbiased term을 넣어줘야 함.
[하이퍼파라미터 설정]
- beta_1 = 0.9, beta_2 = 0.999로 설정
- Learning rate를 E-3나 E-4 정도로 설정
➡ 거의 모든 아키텍쳐에서 잘 동작한다.
[평가]
- Adam은 엄청 좋다. 다양한 문제들에도 정말 잘 동작한다.
- 일반적으로 Adam으로 시작해 보는 것은 정말 좋다.
[학습경항성 비교]
- 기본 SGD(검정색): 매우 느리게 학습되고 있음
- momentum(파란색): overshoots한 뒤에 다시 minima로 돌아옴
- RMSProp(빨간색): 각 차원마다의 상황에 맞도록 적절하게 궤적(trajectory)을 수정시킴
- Adagrad(초록색): RMSProp에 가려서 잘 보이지 않겠지만 학습하다가 죽어버렸다.
- Adam(보라색): momentum과 RMSProp을 합쳐놓은듯한 모습(momentum처럼 overshoots하긴 하지만 momentum만큼 엄청 심하지는 않음)
📜 학생 질문: Adam이 해결하지 못하는 것은 무엇인지
Neural networks는 여전이 엄청 크고 학습은 오래걸립니다.
Adam을 쓰더라도 여전히 문제점들이 있습니다.
가령 손실함수가 타원형일 경우를 생각해 봅시다
Adam을 이용하면 각 차원마다 적절하게 속도를 높히고 줄이면서 ‘독립적으로’ step을 조절할 것입니다
하지만 이 타원이 축 방향으로 정렬되어 있지 않고 기울어져 있다고 생각해 봅시다.
이 경우에도 Adam은 차원에 해당하는 축 만을 조절할 수 있습니다.
이는 차원을 회전시킨 다음에 수평/수직 축으로만 늘렸다 줄였다 하는 것입니다. 회전을 시킬수는 없죠
이러한 회전된 타원(poor conditioning) 문제는 Adam을 비롯한 다른 여러 알고리즘들도 다룰 수 없는 문제입니다.
learning rates dacay
[❌ 문제]
지금까지의 모든 Opimization 알고리즘은 learning rate 이라는 하이퍼파라미터를 가지고 있었다.
learning rate를 잘 고르는 것은 상당히 까다로움.
- 노란색: Learning rate가 지나치게 높아 학습을 못함
- 파란색: Learning rate가 너무 낮아 수렴하는데 너무 오래걸림.
[⭕ 해결: learning rates dacay]
- 각각의 learning rates의 특성을 적절히 이용하는 것
- 처음에는 learning rates를 높게 설정한 다음에 학습이 진행될수록 learning rates를 점점 낮추는 것
- step decay: 100,000 iter에서 learning rates를 낮추고 학습시킴
- exponential decay: 학습과정 동안에 꾸준히 learning rate를 낮춤
[step decay learning rate 양상]
- Loss가 계속 내려가다가 어느순간 평평해 지는 듯 하더니 다시 또 내려가는것을 반복한다.
- 평평해지다가 갑자기 내려가는 구간은 Learning rate를 낮추는 구간이다.
- learning rate를 낮추는 시기: 수렴을 잘 하고 있는 상황에서 gradient가 점점 작아질 때
➡ learning rate가 너무 높아서 더 깊게 들어가지 못할때 (bouncing around too much)
➡ 이 상황에서 learning rate를 낮추게 되면 속도가 줄어들 것이고 지속해서 Loss를 내려갈 수 있을 것이다.
[추가정보]
- learning rate decay는 Adam 보다는 SGD Momentum을 사용할 때 자주사용
- learning rate decay는 부차적인 (second-order) 하이퍼파라미터임
- 일반적으로 learning-rate decay를 학습 초기부터 고려하지는 않음
- 보통 학습 초기에는 learning rate decay가 없다고 생각하고 learning rate를 잘 선택하는 것이 중요
➡ learning rate와 dacay 등을 cross-validate 하려고 한다면 문제가 너무 복잡해지기 때문이다.
[learning rate decay를 설정 순서]
1) 우선 decay 없이 학습을 시켜봄
2) Loss curve를 잘 살피고 있다가 decay가 필요한 곳이 어디인지 고려해 보는 것이 좋음
quasi-Newton methods
[1차 테일러 근사 (first-order Taylor apporximation)]
지금까지 배운 Optimization 알고리즘들을 모두 1차미분을 활용한(first-order) 방법이었음
1차원의 손실함수에서 현재 우리의 위치가 빨간색 점이라고 가정해보자.
1) 이 점에서 gradient 를 계산한다.
2) 이 gradient 정보를 이용해서 우리는 손실함수를 선형함수로 근사시킨다.
3) 우리는 이 1차 근사함수를 실제 손실함수라고 가정하고 Step을 내려간다.
[❌ 문제점]
하지만 이 근사함수로는 멀리갈 수 없다.
현재 우리가 사용하는 정보는 1차 미분값일 뿐이다.
[2차 테일러 근사 (second-order approximation)]
1차 근사의 문제점을 보안하기 위해 2차 근사 정보를 추가적으로 이용할 수 있다.
이 함수는 2차함수의 모양이다.
2차 근사를 이용하면 minima에 더 잘 근접할 수 있다.
[다차원으로 확장: ‘Newton step’]
2차 근사를 다차원으로 확장시킨 것을 ‘Newton step’ 이라고 한다.
1) Hessian matrix를 계산 (2차 미분값들로 된 행렬이다)
2) Hessian matrix의 역행렬을 이용하게 되면 실제 손실함수의 2차 근사를 이용해 minima로 곧장 이동할 수 있다.
[특이점]
혹시 이 알고리즘이 다른 Optimization 알고리즘에 비해 특이한점은 Learning rate가 없다는 것이다.
지금 우리는 2차근사 함수를 만들었고 이 2차근사 함수의 minima로 이동하는 것이다.
적어도 “기본적인 Newton’s method” 에서는 learning rate는 불필요하다.(매 step마다 항상 minma를 향해 이동라기 때문이다)
하지만 실제로는 learning rate가 필요하다.(2차 근사도 사실상 완벽하지 않기 때문)
minima로 이동하는게 아니라 ‘minima의 방향’으로 이동하기 때문에 learning rate가 필요하지만 본버전에는 learning rate가 없다.
[❌ 문제점]
‘Newton step’ Deep learning에서는 사용 불가능하다
왜냐하면 Hessian matrix는 N x N 행렬이다(N은 Network의 파라미터 수)
N이 1억이면 1억의 제곱만큼 존재할 것이다.
➡ 이를 메모리에 저장할 방법은 없으며, 또한 역행렬계산도 불가능
[quasi-Newton methods]
그래서 위의 문제점 때문에 실제로는 ‘quasi-Newton methods’ 를 이용한다.
Full Hessian을 그대로 사용하기 보다 근사시킨다.(Low-rank approximations 하는 방법)
[L-BFGS]
L-BFGS도 second-order optimizer이다.
이 방법도 Hassian을 근사시켜서 사용하는 방법이다.
하지만 사실상 DNN에서는 잘 사용하지 않는다. ➡ L-BFGS에서 2차근사가 stochastic case에서 잘 동작하지는 않기 때문
non-convex problems에도 적합하지 않음 (더 깊게는 들어가지 않음)
실제로는 Adam을 제일 많이 씀
하지만 full batch update가 가능하고 stochasticity(확률)이 적은 경우라면 L-BFGS가 좋은 선택이 될 수 있음
L-BFGS가 Neural network를 학습시키는데 그렇게 많이 사용되지는 않지만 앞으로 보게될 Sytle tranfer와 같은 알고리즘에 종종 사용 가능(Sytle tranfer같은 stochasticity와 파라미터가 적은 경우에서 Optimization을 해야할 경우)
Model Ensembles
[Beyond Training Error]
- 지금까지 이야기했던 모든 것들을 전부 training error를 줄이기 위한 방법들이었다.
- optimization 알고리즘들은 training error를 줄이고 손실함수를 최소화시키기 위한 역할을 수행한다.
- 하지만 사실 우리는 Training error에 크게 신경쓰지 않는다.
➡ “한번도 보지 못한 데이터”에 대한 성능이 더 중요
➡ 우리가 원하는 것은 train/test error의 격차를 줄이는 것이다.
[Model Ensembles]
하나의 모델만을 학습시켜 사용하지 않고 여러 모델을 학습시켜 결합하는 방식으로 문제를 처리하는 방법
손실함수 최적화를 이미 모두 끝마친 상황에서 ‘한번도 보지 못한 데이터’ 에서의 성능을 올리는 법
Machine learning 분야에서 종종 사용하는 기법
모델 하나로 원하는 성능을 낼 수 없을 때 앙상블 학습을 사용하면 효과적이며, 개별로 학습한 여러 모델을 조합하여 일반화(generalization) 성능을 향상
[방법]
1) 모델을 하나만 학습시키지 말고 N개의 모델을 독립적으로 학습시킨다.
2) 결과는 N개 모델 결과의 평균을 이용한다.
모델의 수가 늘어날수록 overfitting 줄어들고 성능이 조금씩 향상된다.(보통 2%정도 증가한다)
➡ 엄청나게 큰 변화는 아니지만 상당히 일관적으로 늘어난다.
[sanpshots & Ensembles]
- 모델을 독립적으로 학습시키는 것이 아니라 학습 도중 중간 모델들을 저장(sanpshots)하고 앙상블로 사용
- Test time에는 여러 snapshots에서 나온 예측값들의 평균을 내서 사용
[Cyclic learning rate schedules]
- 최근 ICLR에 좀 더 향상된 앙상블 알고리즘이 발표되었다.
- 이 방법은 아주 독특한 Learning rate 스케줄을 이용한다.
- Learning rate를 엄청 낮췄다가 다시 엄청 높혔다가를 반복한다.
- 이 논문의 아이디어는 이런 방식으로 손실함수에 다양한 지역에 수렴할 수 있도록 만들어준다.
- 이런 앙상블 기법으로 모델을 한번만 Train시켜도 좋은 성능을 얻을 수 있다.
📜 학생 질문: Loss 차이가 크면 한쪽이 overfiting 일 수 있으니 별로 안좋고, 또 차이가 작아도 안좋지 않으므로 좋은 앙상블 결과를 위해서라면 모델 간의 최적의 갭을 찾는 것이 중요하지 않나?
사실 갭이 중요한 것이 아닙니다.
중요한 것은 validation set의 성능을 최대화시키는 것입니다.
하지만 우선 갭을 신경쓰지 않고 모델을 조금 더 Overfitting 시킬 수 있다면 아마 더 좋은 성능을 낼 수 있을 것입니다.
Validation set 성능과 이 갭 사이에는 묘한 관계가 있긴 하지만 오로지 Validation set 성능만 신경쓰면 됩니다.
📜 학생 질문: 앙상블 모델마다 하이퍼파라미터를 동일하게 줘야 하는지
좋은 질문이군요. 그렇지 않을 수도 있습니다.
다양한 “모델 사이즈”, “learning rate”, 그리고 “다양한 regularization 기법” 등을 앙상블 할 수 있습니다.
실제로 그렇게 사용하기도 합니다.
[Polyak averaging]
- 이 방법은 학습중인 네트워크의 smooth ensemble 효과를 얻을 수 있다.
- checkpoints에서의 파라미터를 그대로 쓰지 않고 smoothly decaying average를 사용하는 방법
- 때때로 조금의 성능향상을 보일 수 있음
- 이 또한 시도해볼만한 방법이긴 하지만 실제로 자주 사용하지는 않음
regularization
regularization: 단일모델 성능을 향상 시키는 방법
앙상블이 아닌 단일 모델의 성능을 향상시키는 것이 중요.(단일 모델 성능 향상이 우리 목표)
앙상블을 하려 한다면 test time에 10개의 모델을 돌려야 할 수도 있다. ➡ 그렇게 좋은 방법은 아님
[regularization 방법]
우리가 모델에 어떤 것을 추가할 텐데 모델이 training data에 fit하는 것을 막아줄 것임.
그리고 한번도 보지 못한 데이터에서의 성능을 향상시키는 방법임.
[전에 배웠던 regularization 기법]
- Loss에 추가적인 항을 삽입하는 방법: L2 regularizatio
- ❌ 앞서 강의에서도 말씀드렸듯이 L2 regularization은 Neural networks에는 잘 어울리지 않음
dropout
Neural network에서 가장 많이 사용하는 regularization
[작동 방법]
forward pass 과정에서 임의로 일부 뉴런을 0으로 만듦
forward pass 할때마다 0이 되는 뉴런이 바뀜
Dropout은 한 레이어씩 진행하게 됨
- 과정
1) 한 레이어의 출력을 전부 구함
2) 임의로 일부를 0으로 만듦
3) 다음 레이어로 넘어감
[시각화]
- 왼쪽: dropout이 없음
- 오른쪽: dropout이 적용(뉴런의 일부만 사용)
forward pass iteration 마다 그 모양은 계속 바뀜
📜 학생 질문: 지금 무엇을 0으로 놓는 것인지
activations을 0으로 설정하는 것
각 레이어에서 next activ= prev activ * weight 죠
현재 activations의 일부를 0으로 만들면 다음 레이어의 일부는 0과 곱해질 것입니다.
📜 학생 질문: 어떤 종류의 레이어에서 이를 사용하는지
activations을 0으로 설정하는 것
Dropout은 fc layer에서 흔히 사용합니다. 하지만 conv layers에서도 종종 볼 수 있습니다.
Conv net의 경우에서는 전체 feature map에서 dropout을 시행합니다.
conv layer의 경우에 여러 channels이 있기 때문에 일부 channel 자체를 dropout 시킬 수도 있겠습니다.
[구현]
실제로 Dropout 구현은 아주 쉽다.(두 줄이면 충분)
아래 예시는 3-layer neural network에 여기에 Dropout을 추가한 것
👀코드 보기
임의로 0으로 설정하는 부분만 추가하면 됨 구현하기 아주 쉽습니다.
[사용하는 이유]
대략적으로 말씀드리면, 특징들 간의 상호작용(co-adaptation)을 방지한다고 볼 수 있다.
1. 다양한 특징 골고루 이용
1. 다양한 특징 골고루 이용
- 우선 우리에게 고양이를 분류하는 어떤 네트워크가 있다고 해보자.
어떤 뉴런은 눈에대해, 어떤 뉴런은 꼬리에대해, 또 어떤 뉴런은 고양이의 털에 대해 학습하는 것을 상상해 볼 수 있다.
그리고 이 이미지가 고양이인지 아닌지를 이 정보들을 모두 취합해서 결정을 내리는 것이다. - Dropout을 적용하게 되면 네트워크가 어떤 일부 features에만 의존하지 못하게 해준다.
➡ 대신에 모델이 “고양이다” 라고 예측할 때 다양한 features를 골고루 이용할 수 있도록 한다.
➡ 따라서 Dropout이 Overfitting을 어느정도 막아준다고 할 수 있다.
2. 단일 모델로 앙상블 효과
2. 단일 모델로 앙상블 효과
- Dropout을 적용한 네트워크를 살펴보면 뉴런의 일부만 사용하는 서브네트워크이고, Dropout으로 만들 수 있는 서브네트워크의 경우의 수가 정말 다양하다는 것을 알 수 있다.
- 따라서 Dropout은 서로 파라미터를 공유하는 서브네트워크 앙상블을 동시에 학습시키는 것이라고 생각할 수 있음
- ❌ 하지만 뉴런의 수에 따라서 앙상블 가능한 서브네트워크의 수가 기하급수적으로 증가하기 때문에 가능한 모든 서브네트워크를 사용하는것은 사실상 불가능
➡ Dropout은 아주 거대한 앙상블 모델을 동시에 학습 시키는 것이라고 볼 수 있다.
[Test time에 미치는 영향]
Dropout이 Test time에 미치는 영향을 알아보자.
Dropout을 사용하게되면 기본적으로 Neural network의 동작 자체가 변하게 된다.
기존의 Neural network는 가중치 w와 입력 x에 대한 함수(f) 였다.
하지만 Dropout을 사용하면 Network에 z(“random dropout mask”)라는 입력이 추가된다.
❌ test time에 임의의 값을 부여하는 것은 좋지 않다.
- 가령 여러분이 Facebook에서 일하고 있고 사람들이 업로드하는 이미지를 분류하려 한다고 해보자.
오늘은 고양이라고 분류했던 이미지를 내일이 되서는 다른 이미지라고 분류한다고 하면 그닥 좋지 않은 상황이겠다.
따라서 네트워크가 이미 학습된 네트워크의 Test time에는 이러한 임의성(stochasticity)은 적절하지 않다
⭕ 그래서 대신 그 임의성(randomness)을 average out 시킨다.
- 이는 적분을 통해 randomness를 marginalize out시키는 것이다.
- 하지만 실제로는 이 적분을 다루기는 상당히 까다롭다.
- 이 문제를 해결할 수 있는 간단한 방법 중 하나는 샘플링을 통해서 적분을 근사시키는 것임
z를 여러번 샘플링해서 test time에 이를 average out 시키는 것 - ❌ 하지만 이 방법도 Test time에서의 randomness을 만들어 내기 때문에 좋지 않은 방법이다.
➡ ⭕ locally cheap한 방법으로 적분식 근사화
📜 ⭕ 적분식 근사화 자세히 보기
dropout의 경우에는 일종의 locally cheap한 방법을 이용해서 이 적분식을 근사화시켜 위의 문제를 해결할 수 있다.
가령 여기 하나의 뉴런이 있다.
- 출력: a
- 입력: x, y
- 가중치: \(w_1\), \(w_2\)
Test time에서,
- a= \(w_1x + w_2y\)
자 그럼 이 네트워크를 dropout(p = 0.5) 를 적용해서 train 시킨다고 생각해보자
train time에서의 기댓값은 아래와 같은 경우로 (analytically) 계산해 볼 수 있다.
위의 경우 dropout mask에는 4가지 경우의 수가 존재한다.(x,y가 각각 가려진다, 안가려진다 두가지 경우의 수가 있으니까)
이제 그 값들을 4개의 마스크에 대해 평균화시켜준다.
Train time에서 a의 기댓값은 \(1/2(w_1x + w_2y)\) 이다.
이 부분에서 test/train time 간의 기댓값이 서로 상이하다.
train time의 기댓값은 test time의 절반밖에 안된다.
📜 ⭕ 더 좋은 방법: dropout probability
Test time에서 stochasticity를 사용하지 않고 할 수 있는 값 싼(cheap) 방법 중 하나는 대신에 dropout probability를 네트워크의 출력에 곱하는 것이다.
자 이제 둘의 기댓값이 같아졌다.
이 방법은 이전의 복잡한 적분식을 아주 저렴하게(cheap) local approximation시킬 수 있는 방법이다.
실제로 많은 사람들이 Dropout을 사용할 때 이 방법을 많이 사용한다.
Dropout을 사용하게 되면 네트워크 출력에 dropout probability를 곱해줘야 한다.
📜 요약
Forward pass: 일부 노드를 무작위로 0으로 만들어주는데는 2줄이면 충분
Test time: Test time에서는 그저 값 하나(probability)만 곱해주면 됨
📜 효율 높이기: inverted dropout
그리고 Dropout을 사용할 때 한가지 트릭을 생각해 볼 수 있다.
Dropout을 역으로 계산하는 것이다.
Test time에는 곱하기 연산이 하나 추가되는 것은 상당히 신경쓰이는 일입니다. Test time에서는 계산효율이 중요하다.
Test time에는 기존의 연산을 그대로 사용하고 대신 Train time 에서 p를 나눠준다.
➡ 보통 train은 GPU로 하기 때문
Train time에서는 곱하기 몇 번 추가되는 것에 별로 신경쓰지 않지만 Test time에서는 가능한 효율적으로 동작하길 원한다.
📜 학생 질문: dropout을 사용하게 되면 Train time에서 gradient에는 어떤 일이 일어나는지
Dropout이 0으로 만들지 않은 노드에서만 Backprop이 발생하게 됩니다.
때문에 dropout을 사용하게 되면 전체 학습시간이 늘어납니다. 각 스텝마다 업데이드되는 파라미터의 수가 줄어들기 때문이죠
다시 말해 Dropout을 사용하게 되면 전체 학습시간은 늘어나지만 모델이 수렴한 후에는 더 좋은 일반화 능력을 얻을 수 있습니다.
사실 Dropout은 여기 보이는 일반적인 regularization 전략을 구체화시킨 하나의 예시에 불과합니다.
[효과]
- Train time에는 네트워크에 무작위성(randomness)을 추가해 training data에 너무 fit하지 않게함.
- 네트워크를 마구잡이로 흩뜨려 놓으므로써 training data에 fit하는 것을 방해하는 것임.
- Test time에서는 randomness을 평균화 시켜서 generalization 효과를 주는 것임.
- Dropout이 Regularization에 가장 대표적인 예이긴 하지만 Batch normalization 또한 이와 비슷한 동작을 할 수 있음.
- Train time의 BN를 상기해보면 mini batch로 하나의 데이터가 샘플링 될 때 매번 서로 다른 데이터들과 만나게 됨
- Train time에서는 각 데이터에 대해서 이 데이터를 얼마나 어떻게 정규화시킬 것인지에 대한 stochasticity이 존재했음
- 하지만 test time에서는 정규화를 mini batch 단위가 아닌 global 단위로 수행함으로써 stochasticity를 평균화 시킴
➡ 이런 특성 때문에 BN 은 Dropout과 유사한 Regularization 효과를 얻을 수 있었음
➡ Train time에는 stochasticity(noise)가 추가되지만 Test time에서는 전부 평균화 되기 때문
➡ 실제로 BN을 사용할 때는 Dropout을 사용하지 않음 BN에도 충분히 regularization 효과가 있기 때문임.
- Dropout은 우리가 자유롭게 조절할 수 있는 파라미터 p(BN에는 없음) 가 있기 때문에 효과적이다.
data augmentationn
이러한 Regularization 패러다임에 부합하는 또 한가지 전략
기본 버전의 학습과정에서는 데이터가 있고 레이블이 있고 이를 통해 매 스텝 CNN을 업데이트했음
하지만 그 대신 train time에 이미지를 무작위로 변환시켜 볼 수 있음. (레이블은 그대로 놔둔 채)
즉 학습 모델을 건드리는 것이 아니라 학습시킬 이미지를 무작위로 변환시켜 학습 가능한 데이터를 늘리는(augmentationn) 것이다.
이제 우리는 원본 이미지를 사용하는 것이 아니라 무작위로 변환시킨 이미지로 학습시키게 되는 것이다.
[예시]
- horizontal flips: 이미지가 반전되도 고양이는 여전히 고양이다.
- crop: 이미지를 임의의 다양한 사이즈로 자름
- color jittering: PCA의 방향을 고려하여 color offset을 조절하는 방법, 학습 시 이미지의 contrast 또는 brightness를 바꿔줍니다.
DropConnect
Dropout과 유사한 방법
[방법]
DropConnect은 activation이 아닌 weight matrix를 임의적으로 0으로 만들어주는 방법
Dropout과 동작도 아주 비슷
fractional max pooling
사람들이 자주 쓰지는 않지만 아주 좋은 아이디어라고 생각
보통 2x2 maxpooling 연산은 고정된 2x2 지역에서 수행함.
하지만 fractional max pooling에서는 그렇게 하지 않고 pooling연산을 수행 할 지역이 임의로 선정됨
아래 예시를 보면 Train time에 샘플링될 수 있는 임의의 pooling region가 있다.
그리고 test time에 stochasticity를 average out 시키려면,
pooling regions를 고정시켜 버리거나
여러개의 pooling regions을 만들고 averaging over시킨다.
stochastic depth
Regularization pattern이라는 패러다임에서 봤을때 아주 놀랄만한 논문
왼쪽의 아주 깊은 네트워크가 있다고 할 때,
train time에 네크워크의 레이어를 randomly drop함.
➡ Train time에는 layer 중 일부를 제거해 버리고 일부만 사용해서 학습함.
test time에는 전체 네트워크를 다 사용함.
regularization 효과는 dropout과 같은 다른 방법들과 유사함.
아주 놀랍고 아주 최신의 연구이며(cutting-edge) 실제로 잘 사용하진 않지만 아주 좋은 아이디어임.
📜 학생 질문: 보통 하나 이상의 regularization 방법을 사용하는지
일반적으로는 batch normalization를 많이 사용합니다. 대부분의 네트워크에서 보통 잘 동작하기 때문이죠
아주 깊은 네트워크에서도 수렴을 잘 하도록 도와줍니다.
대게는 BN만으로 충분합니다만 overfitting이 발생한다 싶으면 Dropout과 같은 다양한 방법을 추가해 볼 수 있습니다.
보통은 이를 가지고 blind cross-validation 를 수행하지는 않습니다.
대신에 네트워크에 overfit의 조짐이 보일때 하나씩 추가시켜 보는 것입니다.
transfer learning
모델을 빠르게 학습시킬 수 있는 방법이다.
overfitting이 일어날 수 있는 상황중 하나는 바로 충분한 데이터가 없을 때이다.
우리는 엄청 크고 파워풀한 모델을 원할지 모르겠지만 그 모델은 아주 작은 데이터셋을 지나치게 overfit할 수 있다.
Regularization이 이를 해결할 수 있는 전략 중 하나지만 Transfer learning 이라는 방법도 있다.
- Transfer learning는 사전에 학습시킨 모델을 가져다가 쓰는 것이다.
- 이 방법은 사용하면 아주 작은 데이터셋일지라도 아주 작 동작하는 모델을 만들 수 있다.
- Transfer learning은 “CNN 학습에는 엄청많은 데이터가 필요함” 이라는 미신을 무너뜨려버린다.
- transfer learning이 아주 보편적이다. ➡ 대부분은 ImageNet pretrained-model을 사용하고 현재 본인의 task에 맞도록 fine tune
더 알아보기
[Transfer Learning with CNNs]
우선 여기 CNN모델이 있다
- 1) Train on Imagenet
- 이 CNN을 가지고 우선은 ImageNet과 같은 아주 큰 데이터셋(Imagenet)으로 학습을 한번 시킨다.
- 이정도 데이터셋이면 전체 네트워크를 학습시키기에 충분한 양이다.
- 2) Small Dataset (C classes)
- Imagenet에서 학습된 features를 우리가 가진 작은 데이터셋에 적용
이제는 1000개의 ImageNet 카테고리를 분류하는 것이 아니라 10종의 강아지를 분류하는 문제로 바뀐 것이다.(데이터는 엄청 적음)
이 작은 데이터셋은 C개의 클래스만 가지고있다고 가정해보자.(10개) - 일반적인 절차는, 우선 가장 마지막의 FC Layer는 최종 feature와 class scores간의 연결인데 이를 초기화시킨다.
- 2-1) 기존에 ImageNet을 학습시킬 때는 4,096 x 1,000 차원의 행렬
➡ 우리의 새로운 문제를 풀기 위해서 4,096 x 10(C)으로 바꿔줌 - 2-2) 방금 정의한 가중치 행렬은 초기화시킴
- 2-3) 나머지 이전의 모든 레이어들의 가중치는 freeze시킴(그렇게 되면 linear classifier를 학습시키는 것과 같음)
마지막 레이어만 가지고 우리 데이터를 학습시키는 것임
- Imagenet에서 학습된 features를 우리가 가진 작은 데이터셋에 적용
- 3) Bigger dataset
- 만일 데이터가 조금 더 있다면 전체 네트워크를 fine-tuning 할 수 있다.
- 최종 레이어들을 학습시키고 나면, 네트워크의 일부만이 아닌 네트워크 전체의 학습을 고려해 볼 수도 있을 것이다.
- 데이터가 더 많이 있다면 네트워크의 더 많은 부분을 업데이트 시킬 수 있을지도 모른다.
- 이 때는 보통 기존의 Learning rate보다는 낮춰서 학습시킴
➡ 기존의 가중치들이 이미 ImageNet으로 잘 학습되어 있고 이 가중치들이 대게는 아주 잘 동작하기 때문
➡ 우리가 가진 데이터셋에서의 성능을 높히기 위해서라면 그 가중치들을 아주 조금씩만 수정하면 됨
[transfer learning ckeak list]
따라서 transfer learning을 수행 함에 있어서 이렇게 생긴 2 x 2의 격자 시나리오를 예상해 볼 수 있다.
- 아주 적은 양의 데이터셋이 있는 경우 (very little data)
- 이전에 학습된 데이터셋과 현재 데이터셋이 유사(very similar dataset): 기존 모델의 마지막 레이어만 학습시킴
- 이전에 학습된 데이터셋과 현재 데이터셋이 다름(very different dataset): 어쩌면 네트워크의 더 많은 부분은 다시 초기화시켜야 하거나 하는 더 창의적이고 경험적인 부분이 필요
X-rays나 CAT scans와 같은 의료영상들은 대게 ImageNet의 데이터와는 많이 다름(이 경우에는 좀 더 창의적인 방법이 필요)
- 아주 많은 양의 데이터셋이 있는 경우 (quite a lot of data)
- 이전에 학습된 데이터셋과 현재 데이터셋이 유사(very similar dataset): 모델 전체를 fine tuning
- 이전에 학습된 데이터셋과 현재 데이터셋이 다름(very different dataset): 이 경우에는 더 많은 레이어를 fine-tune