1. 왜 필요한데?
머신 러닝(Machine Learning)에서 우리는 기계가 넣은 데이터를 토대로 우리가 원하는 예측값을 토해내길 바란다.
단순하게 표현하자면 이때의 기계는 함수다.
\( y = f(x) \)
가령 우리가 고양이가 들어있는 사진을 마구 이 함수에 넣으면, 결과로 '고양이'라는 글자가 나오길 원한다는 것이다. 그러기 위해서 우리는 기계를 학습시켜야 한다. 일단 저 $f(x)$를 (사실 다양한 형태가 있을 수 있지만 기본을 위해 최대한 간단하게) $Wx + b$라는 일차함수 형태로 표현해보자.
$ y = f(x) = Wx + b $
고양이 사진을 마구 집어넣는데 그걸 곧이곧대로 '고양이'라고 뱉어주는 W와 b의 값을 우리가 이미 알고 있다면 우리는 머신러닝을 할 필요가 없다. 그러면 고양이 사진을 넣으면 어차피 알아서 그것이 '고양이'라고 예측해 줄테니, 그걸 그 함수를 활용할 일만 남을 뿐이다. 그러나 우리는 W와 b를 모른다는 것이 현재 상황이며 이것을 구해내서 이 함수, 즉 기계를 완성시키는 것이 우리의 목표인 것이다.
수학적인 예시로 다시 들어보자.
$ x = \{1,2,3,4\} 일\ 때,\ y = \{3,5,7,9\}$
이런 x와 y를 만족하는 함수 식은 $y=2x+1$이라는 것을 우리는 쉽게 알 수 있다. 우리는 쉽게 아는데, 기계는 쉽게 모른다! 우리야 대충 눈대중으로, 아니면 W와 b를 미지수로 하는 새로운 이차방정식을 세워서 풀어내겠지만, 기계는 그렇지 못한다. 이런 고상한 기계를 위해 친히 우리가 사용하는 방법이 바로 경사하강법이 되시겠다.
2. 그래서 경사하강법이 무엇인가?
좋은 예시가 있는 것 같아 빌린다.
우리는 산에 있다. 그러나 이 산은 안개가 너무나도 자욱해 당장 한 치 눈 앞도 보이지 않는다. 이때 정상을 향하려면 모든 방향으로 발을 더듬어가며 산의 높이가 높아지는 방향으로 조금씩 나아가면 된다.
경사하강법(Gradient Descent)은 이러한 방법을 통해 점차 경사가 낮아지는 방향으로 나아가는 방법을 말한다. 이차함수를 생각했을 때, 가장 경사가 낮은 곳은 어디인가? 극점이다.
위의 그림을 아까의 예시로 다시 생각해보자. 우리는 산의 정상을 가고 싶다. 그렇다면 우리는 높이가 높아지는 방향으로 조금씩 발을 내딛으면 된다. 점차 경사는 완만해지다가(완만해지지 않더라도 상관은 없다) 결국 극점에 다다르면 기울기(경사)는 0이 될것이다. 그렇다면 그곳이 최대점이겠지! 이런 방식이 경사하강법의 개념이다.
정확히 말하자면 위의 예시는 높은 곳을 향해가는 것이라 경사상승법을 말하는 것이다. 헌데 어차피 이걸 그대로 거꾸로 하면 그게 경사하강법이 된다. 경사하강법의 함수의 극소값의 위치를 구하기 위해 사용하는 방법인 것이다.
조금 더 수식적으로 이해를 해보자.
우리는 경사하강법을 알고 싶은 것이니까, 극소점이 있도록 아래로 볼록한 이차함수를 생각해보자. 위 그림에서 오른쪽 빨간 점 $(x, f(x))$에서의 기울기는 어떻게 될까? 미분이 무엇인지 알고 있다면, 미분한 값은 해당 함수의 접선의 기울기의 함수라는 것을 알고 있을 터, 해당 지점의 기울기는 $f'(x)$가 될 것이다. 해당 지점은 극소점과 비교해서 오른쪽에 있고, 그래서 기울기가 양수이다. 이때 우리는 이 기울기를 어떻게 해야 극소점으로 향할 수 있는가? 그림만 봐도 알겠지만, 우리는 극소점의 오른쪽에 위치해있으니 왼쪽으로 $x$값을 옮길수록 극소점에 가까이 가게 될 것이다(물론 너무 가면 안 된다). 그러면 단순하게 $x$값이 줄어들기만 하면 된다는 것이다. 그래서 나온 발상이 $x$에서 양수인 값 $f'(x)$를 빼자는 것이다. 사실 어떤 값을 빼더라도 양수인 값만 빼면 되긴 한다. 굳이 $f'(x)$를 빼는 것은 다음의 이유에서 그렇다.
방금 전까지 우리는 극소점 기준 오른쪽에서 극소점으로 다가가는 방법을 알아보았다. 그렇다면 왼쪽에서는 어떻게 극소점으로 갈 수 있을 것인가? $x$값이 오른쪽으로 나아가야 극소점으로 가게 될 것이고, 이는 $x$값이 증가해야 함을 뜻한다. 그렇다면 양수인 값을 더하거나, 음수인 값을 빼주면 되겠지? 오잉? 근데 해당 지점 $x$에서의 기울기는 어떻게 되는가? 당연하지만, 음수다! 그러므로 $x- f'(x)$은 조금 더 오른쪽으로 이동하는 결과를 만든다는 것이다. 코드로 표현하자면..
x = x - grad_x
#grad_x는 미분값을 뜻한다.
이것을 계속 반복해주면, x값은 오른쪽이든 왼쪽이든 결국 극소점으로 조금씩 나아가게 된다는 것이다. 이때 이렇게 미분값을 빼주는 것의 장점이 또 하나 더 있다. 미분값은 결국 극점에 다다르면 0이 된다. 그렇다면 극점에 x가 도착하게 된다면 x는 더 이상 갱신되지 않는다는 것이다. 그렇기에 알아서 극점에서 멈추기에 우리는 우리가 정확하게 몇 번 x를 업데이트 해야하는지 알 필요가 없다.
코드로 경사하강법 구현하기
def func(val):
fun = 우리가 아는 수학적 함수
return fun(val)
def difference_quotient(f, x, h=1e-9):
result = (f(x+h) - f(x)) / h #우리가 아는 수학적 함수의 미분
return result
def gradient_descent(func, init_point, lr_rate=1e-2, epsilon=1e-5):
val = init_point
grad = 1
while np.abs(grad) > epsilon:
grad = difference_quotient(func, val)
val -= lr_rate * grad
간단하게 코드로 경사하강법을 나타낸 모습이다. 마지막 함수의 반복문을 조금 설명할 필요가 있겠다. 인자로 들어오는 것들만 봐도 얼추 충분할 것이다.
- func: 수학적 함수. 첫 번째 함수를 뜻한다!
- init_point: 초기값. 결국 val이란 값의 초기값이 되어 극점으로 나아가게 될 값이다.
- lr_rate: learning_rate. 학습률이라고 한다. 이 값을 통해 한 번에 이동할 정도를 조정해준다. 이 값이 너무 크다면 극점으로 가지 않고 극점 사이로 왔다갔다 하면서 발산하게 될 것이고, 너무 작다면 원하는 시간 내에 극점에 도달하지 못할 것이다.
- epsilon: 반복문 탈출조건! 내가 원하는 값에 거의 도달했는지를 가늠하는 변수라고 볼 수 있다. 컴퓨터는 소수가 들어가는 연산을 할 때 정확한 0에 도달하지 못하는 경우가 더러 있다. 미분값이 0.0000000...0001 정도라면 얼추 0이라 칠만 하잖냐! 이때 이 변수를 통해 그 값을 설정해주는 것이다. 보통은 어느 정도의 값에 다다랐는지를 체크하기 보다 아예 학습횟수를 따져서 반복문을 탈출시키는데, 그것은 에폭epochs이라 표현하는 것으로 알고 있다.
이렇게 하면 $x$를 넣었을 때 $f'(x)$로 미세하게 $x$를 조정해가면서 극소점으로 다가가게 될 것이다.
어떻게 활용할 것인가? - 선형 회귀
경사하강법이 무엇인지는 얼추 알겠다. 그런데 이게 머신러닝에서 어떻게 사용된다는 것이냐? $ y = f(x) = Wx + b $에서 W와 b를 찾아낼 수 있다는 것인가?
조금 말로 정리해보자. 우리는 $1,2,3,4$라는 데이터 $x$를 가지고 있고 $3,5,7,9$라는 데이터 $y$를 가지고 있다. 처음에 우리는 임의의 W와 b를 상정해서 $Wx + b$를 구한다. 이것은 예측값이라 하여 통상 $\hat{y}$이라 부른다. 그리고 이 값과 데이터 $y$의 차이를 구한다. 이 차이가 0이라면, 우리는 정확한 W와 b를 가지고 있다고 볼 수 있을 것이다. 왜냐? 실측값과 예측값의 차이가 없다는 뜻이잖냐. 반대로 이 차이가 크다면 우리의 W와 b는 실제와 동떨어진 값을 하고 있다는 뜻일 것이다. 차이는 작을수록 좋겠지? 그래서! 우리는 최대한 $y - \hat{y}$가 작아지게 하는 W와 b을 찾아다니면 되는 것이다!
단순한 모양의 선형회귀
위에서 구태여 언급은 하지 않았지만, 이런 식으로 W와 b를 찾아가는 것을 선형회귀Linear Regression라고 부른다.
우리는 수많은 데이터 x를 넣을 예정이다. 그 모든 데이터를 한번에 활용하는 방법은 다음과 같다.
$\Large \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i}),\ 이때 \hat{y_i} = Wx_i + b$
무슨 뜻인고 하니, 모든 데이터에 대해 차이를 계산한 후에 그것을 다 더해 평균을 내는 방식이다. 그러나 이때 문제가 있는데 $y-\hat{y}$가 반드시 양수라는 법이 없다. 즉 음수와 양수가 어우러져 더해지면 서로의 값이 서로를 상쇄하는 결과가 나오며 우리가 얻고 싶던 실제값과의 간극을 제대로 파악할 수 없게 된다. 이것을 해결하는 방법은 $y-\hat{y}$에 제곱을 해주는 것이다. 흔히 분산(평균을 뺀 후 제곱을 한 평균)을 구할 때 사용하는 방법이다!
$\Large \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2,\ 이때 \hat{y_i} = Wx_i + b$
이렇게 도출된 식은 흔히 손실 함수loss function, 오차error 등의 이름으로 불리는데, 현재 사용하고 있는 이 제곱의 방식의 손실 함수를 평균 제곱 오차Mean Squared Error라고 부른다.
이제 우리는 이 식을 전개하고, 각각 W와 b에 대해서 편미분을 진행한 후에 값을 조정해주면 되는 것이다.
편미분은 미지수가 많을 때 미분을 하는 방법. 다른 미지수를 전부 상수 취급하고 하나의 미지수만을 기준으로 미분한다. 그렇게 만들어지는 미분값들을 모아서 벡터로 만들면 그레디언트 벡터gradient vector라고 부른다.
그레디언트 벡터를 이용하면 어느 차원에 있는 함수이건 상관없이 최소값을 향해 값을 조정해나갈 수 있게 된다.
손으로 계산한 값. 이 정도는 다행이 문과인 내 수준에서도 천천히 해보면 풀 수 있는 수준이었다. 이제 우리는 계속 이 미분값을 토대로 값을 갱신해나가면 원하는 W와 b를 얻을 수 있게 되는 것이다.
이제 코드를 통해 보자. 위 코드는 인자로 넣을 train_x와, 그에 맞는 정답지 train_y를 만드는 코드이다. 중간에 sym.poly를 통해 보이지만 우리가 선형 회귀를 통해 맞추면 되는 수식은 바로 $y=7x + 2$이다.
위에 신나게 써내린 수식을 코드로는 대충 이렇게 표현할 수 있다. 잘 보면 $y-\hat{y}$가 아니라 거꾸로 $\hat{y}-y$를 한 것을 볼 수 있는데 앞에 마이너스 붙이기 귀찮아서 그랬다.. 또 여기에는 2가 곱해져있지 않다. 그 이유는 어차피 lr_rate를 통해 값을 조정할 것이기 때문에 크게 상관없어서 그리 한 것이다.
단순하게 100번 돌리면 b는 제대로 학습되지 않지만, 1000번 쯤 가면 정말 우리가 원하던 $7x+2$에 근접하게 되는 것을 확인할 수 있다.
'인공지능 in 네부캠 AI 4기 > 기초 & 이론' 카테고리의 다른 글
확률과 통계 for AI, Machine Learning(2) (0) | 2022.09.26 |
---|---|
확률과 통계 for AI, Machine Learning(1) (0) | 2022.09.26 |
경사하강법(Gradient Descent) 구현(2) (0) | 2022.09.24 |