이번 장에서는 gradient descent 방법에 대해 직관적이고, 쉽게 이해해보고자 한다.
우선 gradient descent 라는 뜻을 생각해보자. 직역을 하면 경사하강법인데, 경사를 내려간다 라는 의미로 받아들이면 된다.
예를 들어 우리가 산 어딘가에 낙하했다면, 우리가 집을 찾아가기 위해 산을 내려가는 과정을 생각하면 된다.
수학적인 관점에서 보면은 gradient descent 는 최소가 되는 지점을 찾는다라고 생각해볼 수 있다. gradient 라는 정보를 가지고 어떤 시도를 해보면서 최소가 되는 지점을 찾아가는 과정이다.
gradient 는 y=ax+b 라는 1차식에서 a 에 해당하는 기울기라고 보면 된다. 우리는 이 a 라는 gradient 정보를 가지고 최소값을 찾는 것이다.
어떤 최소값을 왜 찾아야 하냐?
어떤 최소값을 찾아야 하냐면 다름이 아닌 에러다. 정확히 말하면 에러를 최소화시키게 해주는 파라미터를 찾는 것이다. 그리고 이 과정이 머신러닝에서 학습(learning)을 하는 과정이다. 주어진 데이터를 잘 설명해줄 수 있는, 그래서 예측도 잘 할 수 있는 모델을 만들고자 하는게 머신러닝에서의 목표다. 이를 위해 에러가 가장 적은 모델로 만들고자 하는 것이다.
이해를 돕기 위해 선형회귀분석의 예를 들어 본다. 선형회귀분석을 짧게 요약하면 다음과 같다.
1. X, y 값이 우리에게 주어져 있다. 키(X)와 몸무게(y) 같은 데이터 말이다.
2. X 값에 따른 y 값을 나타내는 하나의 식(모델)을 만들게 된다.
3. 보통 최소제곱법을 사용하여 X 값에 회귀 계수 $\beta$ 를 곱하고 b 를 더하는 모양의 선형회귀식을 만든다.
4. 새로운 데이터가 들어오면 만든 식에 대입해서 새로운 데이터에 대한 예측 값을 계산한다.
위의 과정에서 최소제곱법을 하는 과정이 곧 error 가 최소가 되게 하는 파라미터 $\beta$, b 를 구하는 것이다.
그렇다면 바로 회귀분석에서 gradient 정보를 활용해서 어떻게 $\beta$ 와 b 를 구해나가는지를 살펴보자. (편미분을 잠깐 하는 장면이 나오는데, 편미분을 모르면 '아 저렇게 전개가 되는구나' 하고 넘어가겠지만, 그 부분은 gradient 정보를 구하는 핵심 방법이니 모르는 부분으로 명확히 짚어 두고 있어야 한다.)
toy dataset 을 가지고 gradient decent 방법을 사용해 회귀분석을 해보도록 하자.
dataset 으로는 UCL 에서 제공하는 Seoul Bike Sharing Demand Data Set 을 사용하였다. 모든 변수들을 다 고려해보고 싶지만, 지금은 회귀분석이 아닌 gradient decent 방법으로 parameter 를 업데이트 하는 과정에 초점을 맞추고 있기 때문에 독립변수는 하나만 뽑아서 사용하였다.
독립변수 X 는 '기온' 이다. 그리고 종속변수 y = 자전거 '대여량' 이다. 아래와 같이 기온과 자전거 대여량 사이의 관계를 볼 수 있다.
데이터를 이렇게 보고나니까 선형적인 관계를 볼 수 없었다.. 두 변수 사이의 상관계수는 0.53 정도 나왔는데, 뭐 아무튼 이것으로 계속 진행하였다. 데이터가 너무 많아서 랜덤으로 600개만 뽑아서 사용하였다.
육안으로는 선형의 느낌이 없지만 그래도 둘 사이가 어느정도 선형관계가 있다고 보자.
이제 parameter 를 추정하는 과정에 들어가보자.
gradient descent 방법은 최소값을 찾는 것이라 했다. 바로 error 가 최소가 되게 해주는 X 값에 붙는 w 와 b 를 찾는 것이다.
자, 우리는 데이터 분포를 하나의 직선으로 표현을 하고 싶은 것이다. 저 데이터 분포를 가지고 하나의 직선방정식을 만들게 되면, 새로운 데이터 X' 가 들어오면 그 입력값을 대입만 하여 예측을 할 수 있게 되기 때문이다.
목표: gradient descent 방식으로 error 가 최소가 되게 하는 현재 데이터로 부터 선형회귀식 $y = wx + b$ 를 생성.
$y = wx + b$ 는 우리가 알고 있는 그 1차 방정식의 기본 형태가 맞다. 다만 기울기로 알고 있는 $x$ 의 계수인 $a$ 를 $w$ 로 바꾼 것 뿐이다.
현재 우리는 $w$, $b$ 를 모르고 있다. 이제 이 두 parameter 를 찾기 위해 gradient descent 방법을 사용하자.
parameter 에 대해서 아예 모르기 때문에 기본적으로 랜덤한 값을 준다. $w$ 도 랜덤, $b$ 도 랜덤하게 말이다.
$ \hat{y} = w * x + b$ 로 표현 하였다. $w$ 를 임의로 그냥 1 로 두고, $b$ 는 임의로 그냥 1로 두었다. 그렇게 되면 $ \hat{y} = 1 * x + 1$ 로 표현 할 수 있다.
여기서 $ \hat{y}$ 는 예측한 결과 값이고, 위의 임의로 만든 선형식으로는 당연히 큰 error 를 낼 수 밖에 없다. 여기서 error 라고 하면 $error = \hat{y} - y $ 즉, '에러 = 예측값 - 실제값' 으로 나타낼 수 있다.
위의 error 를 최소화할 수 있도록 $w$ 와 $b$ 를 업데이트 해야한다.
이 때 등장하는 것이 목적함수(objective function), 편미분(partial derivative), 학습률(learning rate) 이다.
목적함수라 하면 말 그대로 뭔가를 얻기 위해 처리를 할 대상이라 생각하면 되고, 우리는 error 를 최소화 하고자 하는 것이 목적이니까 목적함수는 error 로 둔다.
objective function 은 $\frac{1}{N}\sum_{i=1}^{n}(\hat{y} - y)^{2}$ 로 표현한다. 이 말은 오차 제곱의 평균을 의미한다.
이제 저 목적함수를 가지고 $w$ 와 $b$ 를 구하기 위해서 gradient 정보를 추출해내야 한다. gradient 정보라 함은 곧 기울기를 의미하고, 이는 미분했을 때 0이 나오게 하는 식을 풀면 되는 문제로 받아들일 수 있다.
1) $\frac{1}{N}\sum_{i=1}^{n}(\hat{y} - y)^{2}$ 는 $\frac{1}{N}\sum_{i=1}^{n}(w*x + b - y)^{2}$ 로 바꾸자.
2) $w$ 에 대해서 편미분($\frac{\partial }{\partial w} \frac{1}{N}\sum_{i=1}^{n}(w*x + b - y)^{2}$) 한 값을 구한다. 제곱이었던 2가 앞으로 나오고, $w$ 앞의 계수 $x$가 그대로 바깥으로 나온다.
$\frac{2}{N}\sum_{i=1}^{n}(w*x + b - y)*x$ 와 같이 전개가 된다.
2-1) 다시 (2) 에서 얻은 식을 $\frac{2}{N}\sum_{i=1}^{n}(\hat{y} - y)*x$ 로 바꾸어 준다. 어차피 $w*x + b$ 는 $\hat{y}$ 와 같으니까.
2-2) 목적함수를 알았고, 편미분도 했고, 이제 학습률($\alpha$) 만 남게 되었다. 이 $\alpha$ 는 사용자가 직접 정해주어야 하는 hyper parameter 이다. 그리고 이 학습률을 2-1) 에서 구한 식에 곱해주는데, 이 때 생각해보면 hyper parameter 로 상수인 학습률을 곱하니 2-1) 에서 상수인 2 는 의미가 없는 값이 되므로 그냥 없는 셈친다.
그럼 최종적으로 $\frac{1}{N}\sum_{i=1}^{n}(\hat{y} - y)*x * \alpha$ 와 같은 식을 얻게 된다. 여기서 우리가 모르는 값이 있는가? 없다. $\hat{y}$ 는 우리가 초기에 임의로 만든 $w$와 $b$로 구성된 선형식으로 부터 나온 것이고(계속 업데이트될것임.), 이외에 나머지는 다 알고 있기 때문에 하나의 scalar 값으로 output 이 나오게 된다.
3) $b$ 의 경우도 $w$ 에 대해 편미분 했던 것과 똑같은 방법으로 진행하게 되면, $\frac{\partial }{\partial b} \frac{1}{N}\sum_{i=1}^{n}(w*x + b - y)^{2}$ 로 세팅한 후, 편미분하면 $\frac{2}{N}\sum_{i=1}^{n}(w*x + b - y)$ 처럼 된다.
그리고 다시 모양만 바꾸어 주고, 학습률 $\alpha$ 를 곱해주면, $\frac{1}{N}\sum_{i=1}^{n}(\hat{y} - y) * \alpha$ 로 계산된다. $w$ 에 대해 편미분 할 때와 비교해서 $x$ 를 곱해주는 부분만 사라지게 되는 것을 알 수 있게 된다.
그렇다면 이제 2)와 3) 과정을 통해 얻는 각각의 값을 $w$ 와 $b$ 각각 빼줌으로써 새로운 $w$ 와 $b$로 업데이트 하게 되는 것이다.
그리고 이어서 처음에 우리가 임의로 만들었던 $\hat{y} = w*x + b$ 에다가 업데이트한 $w$ 와 $b$ 를 넣고 $\hat{y}$ 를 뽑아내면 된다.
그리고 이 과정을 계속 반복하면 된다. 사용자가 정한 특정 iteration 에 다다르거나, 혹은 특정 error 보다 낮아지면 반복을 멈추게 하여 최종적으로 error 를 최소화하는 $\beta$ 와 $b$ 를 갖춘 선형식을 얻게 되는 것이다.
위의 그래프는 초기값 $w$, $b$ 가 임의로 지정된 것으로 했기에 당연히 예측 값 자체가 오차가 많이 난다.
100번 반복하며 학습한 결과 위와 같은 예측 값을 갖는다. 물론 여기서 음수가 나오기 때문에 저런 것들은 0으로 처리해야하는 등의 작업이 필요하긴 한데, 아무튼 여기서는 학습을 통해서 저렇게 오차가 최대한 적게 하는 $w$와 $b$ 를 계속 업데이트 해가면서 찾는 것이다.
또 한가지 중요한 사실을 빼먹었는데, error 가 직접적으로 update 할 때 반영이 된다. 즉 error 가 크면 곧 update 하는 값도 커지게 된다. 이 의미는 error 가 클수록 그만큼 크게 움직이겠다는 것이다. 그렇기 때문에 초깃값이 처음에 랜덤이여도 금새 어느정도 자리를 잡아갈 수 있게 된다.
learning rate 에 대한 설정은 역시 실험을 통해서 적절한 값을 찾아 주어야 한다. 또한 초깃값 역시 최적의 실험 환경을 위해서 설정하는 방법도 찾아보진 않았지만 이에 대한 연구가 있는 것으로 알고 있다.
파이썬 코드.
데이터는 아래의 ref) 사이트에서 받을 수 있다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
# 데이터 불러오기.
bike = pd.read_csv(r'C:\Users\user\Downloads\SeoulBikeData.csv', encoding='cp949')
X = np.array(bike.iloc[:,3]).reshape((-1,1))
y = np.array(bike.iloc[:,1]).reshape((-1,1))
# 랜덤으로 600개만 가져오기.
idx = [random.randint(0, len(X)) for i in range(600)]
X = X[idx]
y = y[idx]
# 초기에 w 와 b 를 설정.
# learning rate 를 0.005 로 설정.
w = 1
b = 1
alpha = 0.005
# 예측값과 실제값을 보기 위한 plotting function 을 define.
def plot_(y, y_hat):
real=plt.scatter(X, y, label='y', s=5, color='red')
pred=plt.scatter(X, y_hat, label='y_hat', s=5, color='blue')
plt.xlabel('temperature')
plt.ylabel('demand bike')
plt.legend((real, pred), ('real','predict'))
plt.show()
# iteration 100 번을 하며 학습을 진행함.
for i in range(100):
y_hat = w * X + b
error= np.abs(y_hat - y).mean()
w_update = alpha * ((y_hat - y) * X).mean()
b_update = alpha * (y_hat - y).mean()
w = w - w_update
b = b - b_update
if i%10 == 0:
plot_(y, y_hat)
ref) archive.ics.uci.edu/ml/datasets/Seoul+Bike+Sharing+Demand
'머신러닝' 카테고리의 다른 글
Momentum과 Adagrad를 쉽게 이해해보자! (0) | 2020.12.16 |
---|---|
Stochastic Gradient Descent 를 쉽게 이해해보자. (0) | 2020.12.16 |
Ensemble Methods (0) | 2020.11.29 |
Expectation Maximization (1) | 2020.11.24 |
Dimensionality Reduction (0) | 2020.11.16 |
댓글