ML/이름 뭐하지

Optimization : Gradient Descent, Mini Batch

goosesong 2023. 8. 17. 21:09

이전까지

x, y 데이터를 통해 Wx+b라는 score function을 공부했다.

이후 loss function을 통해 W값이 얼마나 잘못되었는지 수치적으로 나타내는 방법(SVM, Softmax)을 배웠다. 이때 regularization loss(w만의 함수)를 통해 full loss를 구할 수 있었다. 

 

 

Optimization은 loss를 줄여나가는 과정이다. 

최적화 과정에서 사용할 수 있는 전략이 여러 개 있으니 차차 알아보자.

 

Strategy1 : So bad solution Random search

절대로 사용하지 말아야 할 전략, ramdom search에 대해 알아보자. 

bestloss = float("int")
for num in xrange(1000):
    W = np.random.randn(10, 3073)*0.0001
    loss = L(X_train, Y_train, W)
    if loss < bestloss:
    	bestloss = loss
        bestW = W
    print 'in attempt %d the loss was %f, best %f' %(num, loss, bestloss)

for문을 천 번 반복하는데, 매번 W를 랜덤 하게 설정한다. 그리고 이 가중치 W에 대한 loss를 계산한다. 위 계산을 통해 가장 loss가 낮게 나온 W값을 저장한다.(Wbest)

scores = Wbest.dot(Xte_cols)
Yte_predict = np.argmax(scores, axis = 0)
np.mean(Yte_predict == Yte)

저장한 W와 test data(Xte_cols)를 내적 하여 score를 구한다. 그 후 score값이 최대인 class를 저장하여 실제 정답 데이터와 비교한다.(정확도 계산)

이 행동을 하산으로 비교해 보면, 산속에서 텔레포트하며 최적의 장소를 찾는 것과 같다. 참으로 비효율적인 방법이 아닐 수 없다.

 

 

Strategy2 : Follow the slope ( 경사 )

Optimization의 과정은 산 위에서 눈을 가린 채 더듬어가며 경사를 비교해 하강하는 것과 같다. (한 발자국은 h와 같다.)

1차원의 경우 그냥 미분하면 된다. (다차원인 경우 gradient(기울기)는 편미분 값을 같은 벡터 형태로 나타난다.)

 

이를 numerical gradient, 수치적으로 경사를 구한다고 한다.

 

0.0001을 모든 차원에 대해 더함 ( -1.11, 0.78 ..)
기울기가 음수 == 내려가는 방향이다

def eval_numerical_gradient(f, x):
	fx = f(x)
    grad = np.zeros(x.shape)
    h = 0.00001
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readsrite']) // 객체 생성
    while not it.finished:
    	ix = it.multi_index
        old_value = x[ix]
        x[ix] = old_value + h
        fxh = f(x)
        x[ix] = old_value
        
        grad[ix] = (fxh - fx) / h
        it.iternext()
    return grad

 

현재 W에 대한 loss를 계산한다. 이후 아주 조금 h만큼 움직 인후 다시 loss를 계산한다. W에 대해서만 구했던 loss와 h만큼 움직인 후 loss의 차이를 구해 gradient를 구할 수 있다. (W의 모든 차원에 대해 구한다.)

그러나 이런 수치적인 방법은 근사치를 통한 방법이라 정확한 값이 아닐뿐더러 너무 느려서 사용하지 않는다.

지금 사용하는 방법은 해석적인(Analytically) 방법이다.

loss function을 w에 대하여 미분하는 것이다.

 

지금까지 경사를 따라가며 gradient를 구하는 방법에 대해 알아보았다.  수치적으로 gradient를 계산하는 건 매우 느리나 구현하긴 쉽다. 반대로 해석적으로 gradient를 계산하는 것은 정확하고 빠르나 에러가 일어날 가능성이 있다.

 

Gradient Check

현실에서는 항상 해석적 그레디언트(analytic gradietn)를 사용하지만 수치적으로 gradient를 구해서 실제로 값이 증가하거나 감소하는지 확인한다. 이를 gradient check라고 한다.

 

Gradient Descent ( 경사 하강법 )

weight의 gradient가 증가하는 방향의 반대로 움직이며 최솟값을 찾아나간다. 

while True:
	weight_grads = evaluate_gradient(loss_fun, data, weigths) // gradient가 증가하는 방향, 가중치
    weights += - step_size * weights_grad // step_size만큼 반대방향으로 조정

이 코드는 가장 일반적인 gradient descent 코드로 vanilla Gradient Descent라고도 부른다

 

한 점으로 수렴하는 사진

 

사진의 빨간 부분이 가장 높은 부분이며 짙은 파랑 색일수록 낮은 부분이다. 

 

 

Mini Batch

앞에서 보았던 gradient descent는 training set 전체를 활용하는 full batch gradient descent였다.

# Vanilla Minibatch Gradient Descent

while True:
    data_batch = sample_training_data(data, 256) 
    weights_grad = evaluate_gradient(loss_fun, data_batch, weigths)
    weights += - step_size * weights_grad # 파라미터 업데이트

현실에서는 training set을 쪼갠 다음 일부를 사용하여 파라미터를 업데이트한다. 그리고 또 다른 training set의 일부로 파라미터를 업데이트해 나가는 Mini Batch 방법을 사용한다. 이때 training set의 일부 단위를 Mini Batch이다. 

자원 문제와 시간문제 그리고 데이터의 반영성을 고려하여 데이터를 쪼개지 않고 한 번에 다루는 full batch 방법보단 batch 방법을 선호한다. 

Mini Batch를 사용하는 대표적인 예로 stochastic Gradeitn Descentd가 있으며 일반적으로 32/64/128 크기를 사용한다.  본인의 GPU 환경에 맞춰 정하면 된다. 

이 사진은 Mini Batch를 사용하여 loss가 시간에 따라 감소하는 모습을 그래프로 나타낸 것이다. Mini batch 특성상 잠깐씩 노이즈가 상승했다 감소하는 모습을 보인다. 

 

또한, 적절한 learninig rate를 사용해야 발산하지 않고, 적당한 속도로 loss가 감소할 수 있다. 

파라미터를 업데이트하는 방법도 여러 가지가 있다. SGD, Momentum, NAG, Adagrad, Adadelta, Rmsprop 등이 있다