Search
Duplicate

Optimization (최적화)

loss의 최솟값을 찾아나가는 일련의 과정이다.
Backpropagation 과정 중에 가중치를 업데이트하면서 진행되는데, 이 때 한 스텝마다 이동하는 발자국의 크기(보폭)이 학습률(learning rate)로 정의되고, 앞으로 이동할 방향은 현 지점의 기울기(gradient)를 통해 정의된다.
매 스텝마다 forward pass의 끝단에서 계산되는 손실 함수값은 지금까지 업데이트된 가중치들이 얼마나 잘 설정되어 있는지를 나타내는 지표와도 같다.

SGD(Stochastic Gradient Descent)

# Vanilla update x += - learning_rate * dx #dx: x에서의 loss에 대한 기울기
Python
복사
loss의 값이 최소가 되는 방향으로 가중치를 업데이트하는 방법으로 계속 반복해서 optimization을 진행한다.

문제점

slow

빨간점에서 최적점을 찾아서 이동한다고 생각하면, 수평 방향으로는 기울기 변화가 작기 때문에 매우 느리게 이동하고, 수직 방향으로는 기울기 변화가 크기 때문에 속도가 빠르다. 그래서 Loss는 수직 방향의 가중치 변화에 훨씬 더 민감하게 반응하게 된다. loss가 수직방향의 벡터와 수평 방향 벡터의 합집합 방향으로 이동하기 때문에 지그재그로 이동한다. 이러한 문제는 high demension일수록 더욱 문제가 심각해진다.

local minima & saddle point

(x축은 가중치, y축은 loss)
local minima
극대값들 사이의 극소값에 안착하는 경우 더 이상 가중치가 업데이트 되지 않고 멈춘다. 기울기가 0인 지점을 loss의 최소값으로 인식해 해당 지점(local minima)을 최적점으로 판단하여 더 이상 업데이트가 이뤄지지 않는 것이다. 보통 2차원에서 빈번하게 발생한다.
saddle point
기울기가 완벽하게 0은 아니지만 그 주변이 굉장히 완만해서 업데이트가 느리게 진행되면서 발생하는 문제이다. 보통 고차원 그래프에서 빈번하게 발생한다.

noisy

Mini-Batch Gradient Descent의 경우 미니배치마다 loss를 계산하여 최적화를 진행하는데 이는 매우 비효율적이다. 따라서 많은 noise가 발생하게 된다.

Momentum

vx = 0 while True: dx = compute_gradient(x) vx = rho * vx + dx x -= learning_rate * vx
Python
복사
SGD에 Momentum term 추가한 것이다. 즉, 가중치 x를 업데이트할때 속도 vx의 개념을 추가한 것이다.
v는 가속도를 의미하며, rho는 보통 0.9, 0.99를 이용하여 가속도에 약간의 마찰값을 넣어주는 파라미터다. v는 이전 gradients의 가중합이라고 봐도 무방하다. gradient 를 계산할 때 기울기방향으로 이동하는 것이 아닌 속도 방향으로 이동한다.

의의

local minima & saddle point 한계 극복

공이 언덕을 내려온다고 가정을 해보자. 그 공은 기울기가 0인 지점에서도 이때까지 내려오던 속도에 의해 그 골짜기를 지나 계속 굴러갈 것이다. 즉, 기울기가 0인 지점에서도 업데이트가 되며 이 지점까지 내려오는 속도의 개념을 추가한 것이다. 그렇기 때문에 local minima & saddle point의 한계를 극복할 수 있다.

noise 평균화

Momentum을 추가해서 velocity가 생기면 가속도가 생기며, momentum이 수평방향으로는 가속도를 유지하게 되어 SGD에서 처럼 지그재그로 움직이지 않도록 step을 갖는다. step이 커짐에 따라 noise도 평균화된다.

Nestero Momentum

빨간점에서 velocity 방향으로 출발한 뒤 거기서 gradient를 계산한다. 그리고 다시 원점으로가서 actual step으로 최적화를 진행한다.
이전 스텝의 velocity 방향을 따라 먼저 이동하여 그 자리에서 gradient를 계산하고 다시 본래 자리로 돌아와 actual step을 이동한다. 즉, gradient를 계산하는 지점과 실제 이동하는 지점이 다르다. 이렇게 되면 새롭게 변화되는 식에서 error-correcting term이라는 것이 생기는데 이는 이전 velocity와 현재 velocity의 차이를 반영하여 급격한 슈팅을 방지한다는 측면에서 효과가 있고 더 빠른 학습을 가능하게 한다고 한다. (https://velog.io/@yookyungkho/딥러닝-옵티마이저-정복기부제-CS231n-Lecture7-Review 참고)
Convex optimization에서는 잘 작동하지만, Neural Network와 같은 non-convex에서는 잘 사용하지 않는다.

AdaGrad

grad_sqaured = 0 while True: dx = compute_gradient(x) grad_squared += dx * dx x -= learning_rate * dx/(np.sqart(grad_squared) + 1e-7)
Python
복사
grad_squared를 이용하여 dx의 제곱에 루트를 씌운 값을 x값 업데이트할때 나눠주는 방식이다. 학습 중에 기울기의 제곱값을 grad_squred에 계속 더해서 update step에서 나눠 준다. small gradient인 경우에는 grad_squared가 작은 값으로 나눠주니 속도가 더 잘붙고, large gradient인 경우에는 큰 수로 나누어서 wiggling dimension은 slowdown해서 천천히 내려오게 된다. 즉 파라미터 x들 마다 다른 학습률을 제공한다.
수직축과 수평축의 업데이트 속도를 적절히 맞춘다.

문제점

step size가 오랜시간 지속되는 상황에서는 grad가 매우 작아지게 되어 전체적인 속도가 매우 느려지게 되며 가중치가 0이 되어 학습이 종료될 수 있다.
non-convex에서는 saddle point에 걸린다.
잘 쓰이지 않는다.

RMSProp update

grad_squared = 0 while True: dx = compute_Gradient(x) grad_sqaured = decay_rate * grad_sqaured + (1-decay_rate) * dx * dx x -= learning_rate * dx / (np.sqrt(grad_squared) + 1e-7)
Python
복사
AdaGrad과 마찬가지로 grad_squared를 이용하지만, RMSProp update에서는 훈련하는 동안에 값을 계속해서 축적시킨다는 차이가 있다. 누적된 grad_squared항에 decay_rate를 곱하고, 현재의 dx항에는 (1-decay_rate)를 곱해 grad_squared가 누적되는 속도를 줄여준다. Momentum 방식과 유사하다.
보통 decay rate으로 0.9나 0.99를 사용하며 오버슈팅이 심하지 않은 편이다.

Adam

# almost first_moment = 0 second_moment = 0 while True: dx = compute_gradient(x) first_moment = beta1 * first_moment + (1-beta1)*dx second_moment = beta2 * second_moment + (1-beta2) * dx * dx x -= learning_rate * first_moment / (np.sqrt(second_moment) + 1e-7))
Python
복사
RMSProp + Momentum
First moment와 second moment를 이용해 이전 정보를 유지하고 gradients의 제곱을 이용하는 방법이다. 이때 First moment는 velocity를 담당한다. 그리고 second moment는 AdaGrad이나 RMSProp처럼 gradient 제곱 활용한다. 초기에 second moment를 0으로 초기화한 후 1회 업데이트하고 나서도 beta2 = decay_rate로 0.9또는 0.99로 1에 가까운 값이기 때문에 여전히 0에 가깝다. update step에서 second moment로 나누기 때문에 초기 step이 엄청나게 커져버릴 수 있기 때문에 Adam은 이를 해결하기 위해 현재 스텝에 맞는 bias correction term 추가한다. first/second moments를 Update하고 난 후 현재 Step에 맞는 적절한 unbiased term 을 계산하는 방식이라고 할 수 있다.
회전된 타원(poor conditioning) 문제는 여전히 해결할 수 없다는 한계가 있다.

Ensemble

여러개의 모델을 각각 따로 학습시켜 그 평균값을 이용하는 방식이다. train/test error의 격차를 줄이는 가장 쉬운 방법이다.
모델을 독립적으로 학습시키는 것이 아니라 학습 도중 중간 모델들을 저장(sanpshots)하고 앙상블로 사용할 수 있다. 그리고 Test time에는 여러 snapshots에서 나온 예측값들을 평균을 내서 사용하는데 이런 snapshots은 Training 과정 중간에 저장한다.