NLP-writing

미적분 - 경사 하강법(gradient descent)

summerorange 2023. 4. 27. 16:37
반응형

호다닥 작성하겠습니다.

경사하강법 gradient descent

경사하강법은 인공지능에서 많이 언급되는 단어입니다. ML, AL 책에서 정말 많이 본 개념이 경사하강법 gradient descent 이었습니다. 영어 단어로 gradient기울기, 경사, 변화나 증감을 의미하고, descent하강, 내려오다, 내려가다, 내리막이 되다 란 의미를 가진 단어입니다. gradient descent는 내려오는 기울기의 변화 란 뜻이 되면서 미분의 개념이 연결됩니다. 미분 공식들은 현재 상태에서 앞으로 어떻게 변화할지를 측정하니까요. 인공신경망에서는 최적의 파라미터 값을 찾을 때 많이 사용됩니다. 

 

편미분 partial derivate

AL에서의 미적분은 일반적인 미적분과는 다르게 다변량 미적분이 필요합니다. 일반적인 미적분은 함수가 f(x) = x^2 + x + 5 와 같은 다항식에서 미분과 적분의 개념을 구해서 순간변화율과 평균변화율 그리고 넓이의 값을 계산하는 것이죵. 그런데 multivariate calculus의 경우에는 변수가 하나만 있는 게 아니라 여러 개 존재합니다. 입력 값이 많다보니 여러개의 변수 값을 가지게 되는데 각 변수마다의 움직임을 측정해야 합니다. 동일하게 접선의 기울기를 구해서 측정하는 미분 개념이 들어가는데 여러 개의 변수를 미분할 때는 편미분(partial derivate)라고 합니다. 예를 들어서

미분 derivate

f(x) =  = (x - 1)^2 를 미분하는 것은 f'(x) = 2x - 2 혹은 dy / dx 라고 표현

편미분 partial derivate

f(x, y) = x^2 -  xy + 2를 편미분하면

∂f / ∂x = 2x - y 

∂f / ∂y = -x 

로 나옵니다.

 

편미분이 중요한 이유

에러의 최소값을 구하거나 레이어를 쌓을 때 backpropagation을 구할 때 필요합니다. 차원이 여러 개 넘어가면 애초에 손계산은 불가능하고 컴퓨터로 덧셈, 뺄셈, 곱셈, 나눗셈을 해야 하는데, 입력 노드 값은 하나인 경우는 잘 없습니다(...있을까??). 수십개의 멀티 입력값이 있고 각 항목 당 가중치와 오차를 계산해야 합니다. 쉽게 이해하면 화살표 하나의 움직임 당 back 방향으로 가중치를 계산하는 게 편미분입니다. 아무리 컴퓨터라도 시간이 걸리기 때문에 backpropagation을 구할 때는 시간이 좀 걸립니다. (특히, cpu) 대신 오차는 줄어듭니다. 한 번 왔다 가는 걸 1 epoch 로 계산합니다. 

 

경사하강법 손계산하기

경사하강법 2차 함수를 통해서 간단하게 손계산을 해보면....

예를 들어, 어떤 오차가 f(x) = (x - 1)^2 의 함수의 형태로 값이 변한다고 가정하고 이 오차를 최소화한다고 한다면...✍️

직관적으로 생각했을 때, 최적값은 x = 1 입니다. 혹은 이차함수 미분했을 때의 값은 f'(x) = 2x -2 = 2(x -1)로 구해서 계산해도 되지만 이 함수는 간단하기 때문에 사람은 눈으로 풀 수 있죠. 하지만,

"이 계산을 컴퓨터에겐 어떻게 시키죠?"

일단 손으로 풀면...

뭐가 최소값인지 알 수 없는 상태라고 치고. 일단 대략 감에 의지해 시작점을 3 정도로 잡고,

3에서 시작해서 조금씩 내려갑니다. f'(x) 값이 0에 가까울 수록 최소값이니까,

일단 f'(x) = 2x -2 에서 0이 나오면 최적의 값입니다. derivate의 목표를 대략 0.000001로 잡고, 이 값이 나오도록 움직입니다.

어느 정도로 움직일 건지를 learning rate; lr이라고 하는데 0.01로 lr을 잡았습니다 (혹은 0.001, 0.0001 이렇죠..)

(이후 x값) = (현재 x값) - (learning rate) * dy/dx

현재 값 3 기준,

으로 계산 하면 3 - (0.01) * 4 = 2.96 입니다.

f'(3) =dy / dx = 6 - 2 = 4 이고

lr 이 0.01 이니 0.01 * 4 = 0.04

3 - 0.04 = 2.96 정도 입니다.

그럼 현재 값 3 - 2.96 = 0.04 정도의 차이가 생겼습니다.

그럼 다음 값은 2.96이고 이걸 다시 넥스트 스텝으로 구해 줍니다.

현재 값 2.96 기준,

2.96 - (0.01)*f'(2.96) = 2.96 - (0.01) * (3.92) = 2.9208 입니다.

(현재 값) - (이후 값) = (learning rate) * dy /dx 

해당 차이는 0.0392 정도입니다

현재 값 2.9208 기준,

2.9208 - (0.01)*f'(2.9208) = 2.9208 - (0.01)*(3.84159) = 2.882384...

이전 값과의 차이는 0.0384159.....

순으로

계속 반복합니다 

이전 값과의 차이를 0이 되도록 줄여나갔을 때, 나오는 x값이 최적값입니다.

이젠 컴퓨터 없인 힘들어서 코드로 실행하면,

# 원본 함수 (x-1)^2
def original_function(x):
  return (x-1)**2

# 미분하기
def derivate_function(x):
  print("derivate : ", 2*(x-1))
  return 2 * (x - 1)

다음과 같이 함수를 정의하고

current_x = 3
learning_rate = 0.01
derivate_goal = 0.000000000000000001 # 최대한 0에 가깝도록 한다면?
current_minus_previous_value = 1 
iteration_count = 0

while current_minus_previous_value > derivate_goal:
  previous_x = current_x
  current_x = current_x - rate * derivate_function(previous_x)
  print("x(n+1) 값 : ", current_x)
  current_minus_previous_value = abs(current_x - previous_x)
  print("x(n+1)-x(n) : ", current_minus_previous_value)
  iteration_count += 1
  print("-"*100)
  
print(current_x)
print(iteration_count)

 로 돌리면 결과는

1이 나오고.

 

거의 0에 가깝게 돌린 횟수는 1,655번 정도 돌렸습니다.

 

결론: 

사람은 눈으로 보면 미분 함수 f'(x) = 2(x-1) 에서 minimum 값이 1이라는 걸 직관적으로 이해하지만,

컴퓨터는 그렇지 못하다. 해당 값이 나오기 위해 천번 이상 돌렸습니다~ 그리고 이런 과정을 짜는 게 AI, ML 분야입니다.

끝.

반응형