ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Gradient Descent 에 대한 이해
    ML engineer/DIY Machine Learning 2024. 8. 4. 19:10
    반응형

    배경

    머신러닝에 있어 매우 중요한 알고리즘인 gradient descent에 대해 알아봅시다.
    머신러닝, 신경망 네트워크, 딥러닝에 대해 이야길 듣거나 접했다면, 이런 것들이 대부분 gradient descent를 통해 학습되었을 정도로 중요한 알고리즘/방법론인데요,
    현업에서 종종 "모델이 상관관계를 학습 한다", "이 데이터에 대해 학습시켰다"와 같은 표현을 듣게 되는데요, 이런 표현들이 의미하는 근간에는 결국 gradient descent라는 알고리즘이 사용됩니다.
    알고리즘으로 풀고자 하는 문제는, $f(x) = x^2$ 에 대해 최소값을 찾는 문제라고 칩시다.

    손으로 그려서 정확한 그래프는 아닙니다만
    손으로 그려서 정확한 그래프는 아닙니다만


    즉, gradient descent 은 일종의 최적화 테크닉으로, 어떤 함수의 최솟값을 찾는 알고리즘/방법론입니다. (함수를 반전시키면 물론 최댓값을 구하는데 활용할 수 도 있습니다.)
    위의 함수 $f(x)$ 값을 최소화 하는 $x$값은 당연하게도, $x=0$ 이겠죠?

    알고리즘

    그럼 이걸 iterative 한 알고리즘으로 최솟값을 "근사" 하는 방법을 생각해 봅시다.
    필요한 파라미터는 다음과 같습니다.
    - iterations: 알고리즘 수행 횟수
    - learning rate: 각 iteration마다 얼마나 학습할지 (gradient descent의 정도)
    - initial guess: 초기 값 (0이 아닌 값이어야 합니다)
    gradient_descent(iterations, learning_rate, init)
    위와 같은 함수를 구현한다고 생각하면 되겠죠?
    구현을 하기 위해서, 아래와 같은 테스트 케이스를 두 개 정도 만들어 둡시다.

    Input : < iterations = 0, learning_rate = 0.01, init = 3 > 
    Output : 3
    
    
    Input : < iterations = 10, learning_rate = 0.01, init = 3 >
    Output : 2.451218

    첫 번째 케이스는 base case로 보시면 되고, 반복 횟수가 0이기 때문에, init 값인 3이 그대로 출력된 것입니다.
    두 번째 케이스는 0.01 만큼씩 10번 gradient descent를 수행한 결과, 3에서 2.451218 까지 변화한 모습입니다.
    알고리즘에 대해서 아직 모른다 하더라도, 초기값에서 우리가 찾고자 하는 값인 0에 조금 더 가까워졌다는 걸 알 수 있습니다.
    여기서 조금만 더 생각해 본다면, iteration을 늘려서 점점 더 정답인 0에 가깝게 갈 수 있다는 것과, learning rate을 더 큰 값을 쓰면 더 적은 반복 횟수로 정답인 0에 다가갈 수 있다는 걸 눈치채실 수 있습니다.
    하지만, 여기서 알고 가셔야 할 중요한 점은, gradient descent는 수학적인 정답을 찾는 알고리즘이 아니라, 정답을 향해 다가가면서 "근삿값"을 찾는 방법론이란 것입니다.
    위의 예제는 중학생도 쉽게 알 수 있을 만한 간단한 함수기 때문에 이런 복잡한 근사법을 이용하지 않고도 정답을 찾을 수 있겠지만, 우리가 실제로 머신러닝을 사용하는 대상 함수는 3차원 공간에 그릴 수 조차 없는 매우 복잡한 다차원 함수 (그나마 다차원 함수로 표현이나 가능하면 다행일 정도죠)를 상대로는, 정답을 찾기가 매우 어렵기 때문에 gradient descent와 같은 근사 알고리즘을 이용하는것입니다.
    e.g. ChatGPT, deepfake, midjourney 등의 거대 모델들을 생각해보면, 근사값 조차도 상상이 불가능한 수준으로 복잡한 함수로 표현 되겠죠.


    수학 한스푼

    잠깐 수학 좀 지나가겠습니다.. 간단한 미분이지만, 수학이 불편하신 분들은 대충 숨 참고 다음 섹션으로 스크롤 내리셔도 좋습니다.
    위의 $f(x) = x^2$ 함수에서 우리는 쉽게 1차 도함수를 구할 수 있습니다. $f'(x) = 2x$ 죠, 미분을 함으로써 특정 $x$에서의 접선의 기울기를 찾을 수 있는데요, 이 접선을 따라서 현재 위치 (초기값 3)에서 learning rate 0.01 만큼 음의 방향으로 이동하는것 입니다. (즉, 0을 향해 조금 내려가는것이죠)
    자 미분을 아시는 분들이라면, 이쯤 되면 알고리즘의 이름이 이해가 되실겁니다. Gradient (경사/기울기) Descent (하강), 즉 접선의 기울기를 따라서 하강한다. 간단하죠?
    learning rate라는 명칭도, 이 기울기를 따라서 한번에 얼마만큼 내려 갈지를 판단하는 파라미터라고 보면 됩니다.
    초기값 3으로 부터 한단계 이동하는 과정을 수학적으로 풀어보면 다음과 같습니다.
    $\lambda = $ learning rate
    $x_2 = x_1 - \lambda f'(x_1)$
    $x_2 = 3 - (0.01) * 2(3)$
    $x_2 = 3 - 0.06 = 2.94$


    직관적 이해

    자, 수학 끝났습니다 숨 쉬어도 됩니다.
    이제 이걸 직관적으로 이해해 봅시다.
    앞서 gradient descent가 최소값을 근사하는 알고리즘이라고 했죠? 그럼 최소값을 찾는것이 머신러닝에서 무슨 상관이 있는것이냐 하는 질문이 나올수 있습니다.
    머신러닝에서 어떤 문제를 풀기 위해 문제를 정의 하게 될때, 우리는 하나의 loss function, 혹은 cost function 을 정의합니다. 현란한 용어들 같지만, 저는 이걸 그냥 간단하게 error function (오차 함수) 라고 생각해도 된다고 이야기 합니다.
    즉, 우리가 학습 하려는 모델의 예측값과, 실제 정답 간의 오차를 계산하는 함수인 것이죠.
    그럼 이 "오차" 함수를 최소화 한다면, 모델의 예측값이 정답에 그만큼 가깝다는 뜻이기 때문에 모델을 잘 학습 하려면 이 함수를 최소화 해야겠죠? 따라서, 최소화 알고리즘이 중요한것입니다.

    구현 실습

    그럼 이제 gradient descent를 간단한 구현을 통해 확인 해볼 차례입니다.
    제가 전체 코드를 제공 하겠지만, 꼭 IDE, jupyter, VI 등 켜시고, 직접 구현 해보시기 바랍니다.
    먼저 pseudo code를 생각해보면 다음과 같이 볼 수 있겠죠.

    # 반복 할 횟수 만큼
    for num_iterations:
        # 현재 추정값 x에서의 기울기를 계산하고
        calculate_gradient(current_x)
        # 현재 추정값 x를 delta 만큼 이동하여 업데이트 합니다
        current_x = current_x - delta

    - 실제로 구현할때에는, 도함수를 쉽게 구할 수 없는 경우가 더 많을 텐데요, (지금 이해를 돕기 위해 간단한 $f(x) = x^2$를 사용했지만)
    이 부분은 편리하게 pytorch, tensorflow등 머신러닝 패키지에서 자동으로 계산 해주기 때문에 얼마나 다행인지 모르겠습니다... 라떼는..
    그럼 잠시 아래 코드를 보지 마시고, 위의 두 테스트 케이스를 확인 할 수 있는 아래 함수를 직접 구현해보시기 바랍니다.

    def get_minimizer_x(self, iterations: int, learning_rate: float, init: int) -> float:
        x = init
        for _ in range(iterations):
            gradient = 2 * x
            x = x - learning_rate * gradient
        return round(x, 6)
    
    
    print(get_minimizer_x(10, 0.01, 3))
    # 2.451218

    이와 같은 방법으로 정말 정답인 $x=0$에 다가갈 수 있는지 파라미터를 좀 조정해서 확인해봅시다.

    get_minimizer_x(10, 0.2, 3)
    
    Iteration 0 :  x = 3
    Iteration 1 :  x = 1.7999999999999998
    Iteration 2 :  x = 1.0799999999999998
    Iteration 3 :  x = 0.6479999999999999
    Iteration 4 :  x = 0.3887999999999999
    Iteration 5 :  x = 0.23327999999999993
    Iteration 6 :  x = 0.13996799999999995
    Iteration 7 :  x = 0.08398079999999997
    Iteration 8 :  x = 0.05038847999999998
    Iteration 9 :  x = 0.030233087999999984
    Iteration 10 :  x = 0.018139852799999988

    learning rate를 0.2로 올려주고, 실행해보니 정말 10번만에 0.018로, 0에 꽤 가까워 졌음을 확인 했습니다.


    실제로 머신러닝으로 어떤 문제를 풀다 보면, 모델을 어떻게 만드느냐, 학습을 어떻게 하느냐에 따라서 정말 최소값을 찾더라도, 정답에서 크게 벗어나 있을 수 있고, 애초에 정답을 모르는 문제 환경일 수도 있기 때문에, 단순히 툴을 사용할 줄 아는, 이미 잘 만들어진 모델을 돌려본적만 있는 개발자가 제대로된 머신러닝 엔지니어라고 보기 어려운 이유기도 합니다.
    이런 기본적인 개념들을 근간에 두고, 정답에 가까이 갈 수 있는 모델을 잘 설정하고, 답이 정해져있지 않은 문제 상황에서 답을 근사값으로 설계 할 수 있는 사람이 정말 실력있는 머신러닝 엔지니어가 되겠죠?

    생각 해볼 거리

    Q. learning rate이 너무 작거나 너무 크면 어떻게 될까?
    - 파라미터를 어떻게 조정하느냐에 따라 어떻게 과정이 달라질지 위의 구현을 수정해보면서 테스트 해보시기 바랍니다.
     
     
     

    반응형

    'ML engineer > DIY Machine Learning' 카테고리의 다른 글

    Linear Regression에서 Forward 이해하기  (3) 2024.09.14
    시리즈를 시작하며  (0) 2024.08.04

    댓글

Designed by naubull2.