딥러닝 (Deep Learning)/딥러닝 기초

신경망에서 미분이 필요한 이유 2 : gradient descent (경사하강법), learning rate(학습률)

DS지니 2021. 4. 26. 02:13
728x90
반응형
[케라스 창시자에게 배우는 딥러닝] 참고

 

📢 목차

 

1. 그래디언트(gradient)

2. 확률적 경사 하강법 (Stochastic gradient descent)

3. 최적화 방법(optimization method) (=옵티마이저)

 


 

1. 그래디언트(gradient)

 

 

gradient는 수학적으로 텐서 연산의 변화율이다.

이는 다차원 입력, 즉 텐서를 입력으로 받는 함수에 변화율 개념을 확장시킨 것이다.

 

y_pred = dot(W, x)
loss_value = loss(y_pred, y)

위의 식은 신경망의 입력데이터(x)를 가중치(행렬W)와 곱하고 예측값(y_pred)을 얻고, 예측값과 실제값(타깃y)의 손실함수(loss)를 나타낸 식이다.

 

W를 사용해 타깃의 예측값을 계산하고, 손실(예측값과 타깃값 사이의 오차)을 계산할 수 있다.

입력 데이터 x와 y가 고정되어 있다면 이 함수는 W를 손실 값에 매핑하는 함수로 볼 수 있다.

loss_value = f(W)

W의 현재 값을 W0라고 하고, 함수 f의 변화율 함수를 f' 라고 할때,

포인트 W0에서 f의 변화율은 W와 같은 크기의 텐서인 f'(W0)이다.

이 텐서의 각 원소 f'(W0)[i, j]는 W0[i, j]를 변경했을 때 loss_value가 바뀌는 방향과 크기를 나타낸다.

 

다시 말해 텐서 f'(W0)는 W0에서 함수 f(W)=loss_value의 그래디언트(경사도,변화도) 이다.

 

이전 포스트에서 함수 f(x)의 변화율은 곡선의 기울기라고 언급했는데,

즉, f'(W0)는 W0에서 f(W)의 기울기를 나타내는 텐서로 해석할 수 있다.

 

그렇기 때문에 함수 f(x)에 대해서는 변화율의 반대 방향으로 x를 조금 움직이면 f(x)의 값을 감소시킬 수 있다.

*변화율(기울기의) 반대 방향 = 기울기가 (+) 이면 음의 방향, (-) 이면 양의 방향

 

gradient

 

동일한 방식을 적용하면 함수 f(W)의 입장에서는 그래디언트의 반대 방향으로 W를 움직이면 f(W)의 값을 줄일 수 있다.

 

* 오차가 최소가 되는 지점

= 기울기가 0이 되는 방향

= 가중치에서 기울기를 뺐을 때 가중치의 변화가 전혀 없는 상태

 

W1 = W0 - f'(W0) * step (step = 스케일을 조정하기 위한 작은 값, 딥러닝에서 learning rate)

이 말은 기울기가 작아지는 곡면의 낮은위치로 이동된다는 의미이다.

f'(W0)은 W0에 아주 가까이 있을 때 기울기를 근사한 것이므로 W0에서 너무 크게 벗어나지 않기 위해 스케일링 비율 step이 필요하다.

 

 

2. 확률적 경사 하강법 (Stochastic gradient descent)

 

미분 가능한 함수가 주어지면 이론적으로 이 함수의 최댓값을 해석적으로 구할 수 있다.

함수의 최솟값은 변화율이 0인 지점이다. (위 그래프에서는 m인 지점이다.)

따라서 우리가 할 일은 변화율이 0이 되는 지점을 모두 찾고 이 중에서 어떤 포인트의 함수 값이 가장 작은지 확인하는 것이다.

 

신경망에 적용하면 가장 작은 손실 함수의 값을 만드는 가중치의 조합을 해석적으로 찾는 것을 의미한다.

이는 식 f'(W) = 0 을 풀면 해결된다. 이 식은 N개의 변수로 이루어진 다항식이고, N은 네트워크의 가중치 개수이다.

N=2나 3인 식을 푸는 것은 가능하지만 실제 신경망에서 파라미터의 개수가 수천 개보다 적은 경우는 거의 없고 종종 수천만 개가 되기 때문에 해석적으로 해결하는 것이 어렵다.

 

그 대신 훈련 반복 루프(training loop)를 사용 할 수 있다. 

 

🔄 훈련 반복 루프(training loop)
1. 훈련 샘플 x와 이에 상응하는 타깃 y의 배치를 추출한다.
2. x를 사용해 네트워크를 실행하고(forward pass단계), 예측 y_pred를 구한다.
3. y_pred와 y의 차이를 측정해 이 배치에 대한 네트워크 손실을 계산한다.
4. 배치에 대한 손실이 조금 감소되도록 네트워크의 모든 가중치를 업데이트한다.

-----------------------------------------------------------------------------

<미니 배치 확률적 경사 하강법>

4.1 네트워크의 파라미터에 대한 손실 함수의 그래디언트를 계산한다.(backward pass)
4.2 그래디언트 반대 방향으로 파라미터를 조금 이동시킨다.
(W -= step * gradient 처럼 하면 배치에 대한 손실이 조금 감소할 것이다.)

 

랜덤한 배치 데이터에서 현재 손실 값을 토대로 하여 조금씩 파라미터를 수정한다. 미분 가능한 함수를 가지고 있으므로 그래디언트를 계산하여 단계 4를 효율적으로 구현할 수 있다. 그래디언트의 반대 방향으로 가중치를 업데이트하면 손실이 매번 조금씩 감소할 것이다.

 

단계 4는 가중치를 업데이트하는 여러가지 방법이 있는데 그 중 하나인 Mini-batch SGD(mini-batch stochastic gradient descent)를 설명했다. 확률적(stochastic)이란 단어는 각 배치 데이터가 무작위로 선택된다는 의미이다.

*확률적이란 과학적 표현으로 무작위(random)하다는 뜻이다.

 

SGD

그림에서 볼 수 있듯이 step 값을 적절히 고르는 것이 중요하다. 이 값이 너무 작으면 곡선을 따라 내려가는데 너무 많은 반복이 필요하고 지역 최솟값(local minimum)에 갇힐 수 있다.

반대로 step이 너무 크면 손실 함수 곡선에서 임의의 위치로 이동시킬 수 있다.

 

미니배치가 아닌 그냥 SGD 알고리즘은 반복마다 하나의 샘플과 하나의 타깃을 뽑는 것이다. 모든 데이터를 다 사용해 반복을 실행하기 때문에 더 정확하게 업데이트되지만 더 많은 시간과 비용이 든다. 

그렇기 때문에 적절한 크기의 미니 배치를 사용하는 것이 효율적이다.

 

위의 그래프는 1D 파라미터 공간에서 경사 하강법을 설명하고 있지만 실제로는 매우 고차원 공간에서 경사 하강법을 사용하게 된다. 신경망에 있는 각각의 가중치 값은 이 공간에서 하나의 독립된 차원이고 수만 또는 수백만 개가 될 수도 있다. 

https://commons.wikimedia.org/wiki/File:Gradient_ascent_(surface).png

 

 

신경망이 훈련되는 실제 과정은 시각화하기 어렵다. 입력 특성이 2개인(2D) 손실 함수는 3차원으로 나타낼 수 있지만 특성이 더 늘어나면 그림으로 더이상 표현하지 못한다. 그렇기 때문에 저차원 표현으로 얻은 직관은 실전과 항상 맞지 않는다는 것을 유념해야 한다. *이는 딥러닝 연구 분야에서 오랫동안 여러 이슈를 일으키는 근원이었다.

 

 

3. 최적화 방법(optimization method) (=옵티마이저)

 

업데이트할 다음 가중치를 계산할 때 현재 그래디언트 값 뿐만 아니라 이전에 업데이트된 가중치를 여러가지 다른 방식으로 고려하는 방식도 있다. 예를들어 모멘텀을 사용한 SGD, Adagrad, RMSProp 등이 있다. 

 

이들을 모두 최적화 방법(optimization method) 또는 옵티마이저(optimizer)라고 부른다.

특히 여러 변종(?)들에서 사용하는 모멘텀(momentum)개념은 아주 중요하다. 물리학에서 영감을 얻은 모멘텀은 SGD에 있는 2개의 문제점인 수렴 속도와 지역 최솟값을 해결한다.

 

local minima에 빠지게 되면 그 지점 근처에서는 왼쪽, 오른쪽 어디로 이동해도 손실이 증가한다. 대상 파라미터가 작은 학습률을 가진 SGD로 최적화되었다면 최적화 과정이 전역 최솟값(global minimum)으로 향하지 못하고 이 지역 최솟값에 갇히게 될 것이다.

 

모멘텀은 최적화 과정을 손실 곡선 위로 작은 공을 굴리는 것으로 생각하면 쉽게 이해할 수 있다. 모멘텀이 충분하면 공이 골짜기에 갇히지 않고 전역 최솟값에 도달할 수 있다. 현재 기울기 값(현재 가속도) 뿐만 아니라 (과거의 가속도로 인한) 현재 속도를 함께 고려해 각 단계에서 공을 움직인다.

실전에 적용할 때는 현재 그래디언트 값뿐만 아니라 이전에 업데이트한 파라미터에 기초하여 파라미터 w를 업데이트 한다.

 

# momentum 구현의 예

past_velocity = 0
momentum = 0.1  #모멘텀 상수
while loss > 0.01:   #최적화 반복 루프
    w, loss, gradient = get_current_parameters()
    velocity = momentum * past_velocity - learning_rate * gradient
    w = w + momentum * velocity - learning_rate * gradient
    past_velocity = velocity
    update_parameter(w)

 

728x90
반응형