- 공유 링크 만들기
- 이메일
- 기타 앱
경사하강법(Descent Gradient)
최소자승법은 공식화된 계산식에 의해 결정된 회귀계수를 적용합니다. 경사하강법은 수치적으로 모델을 최적화하는 과정에서 회귀계수를 산출합니다.
식 1의 선형회귀모형은 독립변수(x)와 종속변수(y)의 사이의 다음의 선형식을 만족하는 최적의 회귀계수를 결정하는 것입니다.
\begin{align} \tag{식 1}&y=xW\\&W:\text{회귀계수(가중치)}\end{align}
예를 들어 다음의 자료는 sklearn.datasets.make_regression(n_samples, n_features, coef, random_state, …)
함수를 사용하여 생성한 데이터 입니다.
import numpy as np import pandas as pd from sklearn.datasets import make_regression import matplotlib.pyplot as plt import sympy as sp
X,y, cof= make_regression(n_samples=1000, n_features=1, coef=True, random_state=1) cof
array(38.69892343)
X.shape, y.shape
((1000, 1), (1000,))
역으로 위 자료의 선형회귀 모델을 생성한다고 할 경우 y=xw로부터 식 2와 같이 계산되는 mse가 최소가 되는 방식으로 W를 결정해야 합니다.
$$\begin{align}\tag{식 2}\text{mse}&=\frac{\sum^n_{i=1}(\hat{y}-y)^2}{n}\\&=\frac{\sum^n_{i=1}(xW-y)^2}{n} \end{align}$$
W를 임의적으로 적용할 경우 mse의 변화는 그림 1과 같이 2차 곡선의 형태를 나타냅니다.
plt.figure(figsize=(4, 3)) w=np.linspace(-50, 50, 100)#회귀계수의 변화 mse=[np.sum((X*i-y)**2)/len(X) for i in w] plt.plot(w, mse) plt.xlabel("Coef, W") plt.ylabel("MSE") plt.show()
최소자승법의 경우는 mse의 최소값이 되는 지점에서 회귀계수를 결정하지만 경사하강법의 경우는 임의적으로 지정한 회귀계수의 초기값으로부터 이 값의 변화에 의해 도달하는 최소 mse 지점의 계수를 결정하는 방법입니다. 이 방법은 수치해석에 의한 것으로 최소자승법의 여러 전제조건에서 자유로운 분석 방법입니다.
이 방법은 다음의 과정으로 이루어집니다.
- 초기 회귀모형 생성(회귀계수, 편차를 지정합니다.)
- 그 모형에 대한 MSE를 계산합니다. 이것은 모형에 대한 비용함수(cost function) 또는 손실함수(loss function)이라고 합니다.
- 회귀계수에 대해 비용함수 변화율$\left(\frac{d(cost)}{dw}\right)$을 고려하여 새로운 계수를 계산합니다.
- 비용함수가 극소점에 도달할 때까지 또는 변화가 없을 때까지 1 ∼ 3번 과정을 반복합니다. 이 과정을 최적화(optimization)라 합니다.
그림 2에서 나타낸 것과 같이 회귀 계수에 대한 비용 변화율은 미분, $\frac{d(cost)}{dw}$,으로 계산됩니다. 회귀 계수에 대해 이 결과를 다시 고려하는 과정을 반복하여 최적화된 값을 도출합니다.
np.random.seed(1) x=np.linspace(-4, 4, 100) y0=0.5*x w=np.random.rand(1) mse=np.sum((w*x-0.2-y0)**2)/len(x) dmse_x=np.sum(2*(w*x-0.2-y0)*w)/len(x) mset=[float(mse)] wt=[float(w)] for i in range(20): w=w-0.03*np.sum(2*(w*x-0.2-y0)*w)/len(x) wt.append(float(w)) mset.append(float(np.sum((w*x-0.2-y0)**2)/len(x))) plt.figure(figsize=(4,3)) plt.scatter(wt, mset, color="red") plt.plot(wt, mset, label="Cost Function") plt.legend(loc="best") plt.xlabel("Weight(W)", size=12, weight='bold') plt.ylabel("Cost(mse)", size=12, weight='bold') plt.annotate('w', xy=(wt[4]+0.01, mset[4]), xytext=(wt[1]+0.01, mset[1]), arrowprops=dict(facecolor='navy', width=1)) plt.text(0.44, 0.070, r"$\mathbf{w=w-\eta \frac{d(cost)}{dw}}$") plt.show()
위 과정을 경사하강법(Gradient Descent)이라고 하며 그 알고리즘을 수식으로 전개하면 다음과 같습니다(식 3 ∼ 7). 그림 2에서 나타낸 것과 같이 초기의 w, b를 랜덤하게 지정하는 것으로 시작하며 w, b를 감소시키면서 손실(loss)의 변화를 확인합니다. 그러한 변화 동안 최소의 손실이 생성되는 지점에서 w, b를 모수로 하여 모형을 설정합니다.
식 3은 경사하강법의 기본 특성치인 비용함수를 나타낸 것입니다.
\begin{align}\tag{식 3}&\epsilon=\hat{y}-y \\&\begin{aligned}\text{cost}&=\frac{\sum^n_{i=1}(\epsilon)^2}{n}\\&=\frac{\epsilon^T \cdot \epsilon}{n} \end{aligned}\\ &\epsilon: \text{error} \end{align}
가중치의 수정을 위해 비용함수는 식 4와 같이 각 가중치들과 편차에 의해 미분됩니다.
\begin{align}\tag{식 4}\text{cost}&=\frac{\sum^n_{i=1}(\hat{y}-y)^2}{n}\\\frac{d(cost)}{dw}&=\frac{\sum^n_{i=1}(wx-b-y)^2}{n}\\&=\frac{\sum^n_{i=1}2(wx-b-y)x}{n}\\\frac{d(cost)}{db}&=\frac{\sum^n_{i=1}2(wx-b-y)}{n} \end{align}
식 4에서 비용함수의 미분 결과를 행렬연산으로 나타내면 식 5와 같습니다.
\begin{align}\tag{식 5}\frac{d(cost)}{dw}&=\frac{2(wx-b-y)x}{n}\\&=\left(2 \times \begin{bmatrix}\epsilon_1&\epsilon_2&\cdots&\epsilon_n\end{bmatrix} \cdot \begin{bmatrix}x_1\\x_2\\ \vdots\\x_n\end{bmatrix}\right)\frac{1}{n}\\\frac{d(cost)}{db}&=\frac{2(wx-b-y)}{n}\\&=\left(2 \times \begin{bmatrix}\epsilon_1&\epsilon_2&\cdots&\epsilon_n\end{bmatrix} \cdot \begin{bmatrix}1\\1\\\vdots\\1\end{bmatrix}\right)\frac{1}{n} \end{align}
식 5의 두 계산은 식 6과 같이 결합하여 나타낼 수 있습니다.
$$\tag{식 6}d(cost)=\left(2 \times \begin{bmatrix}\epsilon_1&\epsilon_2&\cdots&\epsilon_n\end{bmatrix} \cdot \begin{bmatrix}x_1&1\\x_2&1\\\vdots&\vdots\\x_n&1\end{bmatrix}\right) \frac{1}{n}$$
식 6의 결과에 의한 각 가중치들과 편차의 개선 과정을 최적화(optimization)이라 하며 식 7과 같이 이루어집니다.
\begin{align}\tag{식 7}&w= w- \varUpsilon \frac{d(cost)}{dw}\\ & \varUpsilon : \text{학습률(learning rate)} \end{align}
최적화 과정은 각 가중치의 미분 결과에 의존됩니다. 즉, 그 결과가 크다면 최적화의 단계 간 간격이 증가하여 극소점을 발견할 확률이 매우 낮아집니다. 이를 방지하기 위해 최적화 단계의 간격(최적화 속도)을 조절할 필요가 있습니다. 그 조절인자를 학습률(learning rate, η)라고 합니다. 이러한 최적화 과정을 학습(train)과정이라하며 이 과정을 통해 회귀계수가 결정됩니다. 그러나 학습률은 학습과정의 시작과 함께 지정되는 변수입니다. 학습률과 같이 과정의 시작에 지정되는 변수를 초매개변수(hyper-parameter)라고 합니다.
예 1)
단순한 예로 y=2x를 따르는 자료에 대해 경사하강법을 적용하여 회귀계수 2를 산출해 봅니다.
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
y | 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 |
선형모델에 편차항을 첨가하기 위해 독립변수에 1로 이루어진 새로운 변수를 첨가해야 합니다.
x=np.arange(0, 10) x1=np.c_[np.ones(10), x] x1
array([[1., 0.], [1., 1.], [1., 2.], [1., 3.], [1., 4.], [1., 5.], [1., 6.], [1., 7.], [1., 8.], [1., 9.]])
y=np.array([2*i for i in x]) y
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
임의로 회귀계수와 편차의 초기값을 지정합니다.
np.random.seed(0) param=np.random.rand(2,1) param
array([[0.5488135 ], [0.71518937]])
최초로 생성한 모델에 의해 추정값을 계산합니다.
$$\begin{bmatrix}y_1\\y_2\\\vdots\\ y_n \end{bmatrix}=\begin{bmatrix}1&x_1\\1&x_2\\\vdots\\ 1&x_n \end{bmatrix}\begin{bmatrix}0.5488135\\0.71518937\end{bmatrix}$$
#추정값 pre=np.dot(x1, param) pre
array([[0.5488135 ], [1.26400287], [1.97919224], [2.6943816 ], [3.40957097], [4.12476034], [4.8399497 ], [5.55513907], [6.27032843], [6.9855178 ]])
#오차 error=pre-y.reshape(-1,1) error
array([[ 0.5488135 ], [ -0.73599713], [ -2.02080776], [ -3.3056184 ], [ -4.59042903], [ -5.87523966], [ -7.1600503 ], [ -8.44486093], [ -9.72967157], [-11.0144822 ]])
위에서 산출한 오차를 기반으로 비용 즉, mse를 계산합니다.
cost=np.dot(error.T, error)/len(y) cost
array([[41.00114681]])
$\frac{d(\text{cost})}{dw}$와 $\frac{d(\text{cost})}{db}$ 를 계산합니다.
grad=2*np.dot(error.T,x1)/len(error) grad
array([[-10.46566869, -68.29488458]])
위의 미분된 값과 학습률을 고려하여 회귀계수와 편차를 업데이트 합니다.
param=param-0.01*grad.reshape(-1,1) param
array([[0.65347019], [1.39813821]])
sklearn 패키지의 SGDRegressor()
클래스는 위의 과정을 모두 수행합니다. 이 클래스에 의한 생성된 모델을 .fit(x, y)
메소드로 데이타에 적합시키고 결정계수는 .score(x, y)
메소드로 확인할 수 있습니다. 이 메소드에 전달되는 독립변수(x)는 2차원 numpy 배열객체, 반응변수(y)는 1차원 벡터이어야 합니다. 또한 회귀계수와 편차는 각각 .coef_, .intercept_
속성으로 계산할 수 있습니다.
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
from sklearn.linear_model import SGDRegressor x2=x.reshape(-1,1) reg=SGDRegressor(max_iter=1000, tol=1e-3, learning_rate='constant', eta0=0.01) reg1=reg.fit(x2, y) print(f'scroe:{np.around(reg1.score(x2, y),4)}, coef:{np.around(reg1.coef_,4)}, intercept:{np.around(reg1.intercept_,4)}')
scroe:0.9995, coef:[1.9725], intercept:[0.2326]
예 2)
다음 금융자료로부터 1일전의 설명변수에의한 당일 삼성반도체의 주가를 추정하기 위한 회귀모델을 구축해 봅니다.
설명변수 | 코스피지수(코스피), sk하닉스(하이닉스),원-달러환율(wond) , 미국필라델피아 반도체(sox), 삼성전자(samE)의 시가, 고가, 저가, 종가 |
---|---|
반응변수 | 삼성 반도체(ksemi)의 종가 |
import numpy as np import pandas as pd import yfinance as yf from sklearn.preprocessing import StandardScaler, MinMaxScaler from sklearn.linear_model import SGDRegressor from sklearn.model_selection import train_test_split
다음 코드로 자료를 호출합니다.
st=pd.Timestamp(2020,1, 1) et=pd.Timestamp(2024, 9,29) nme={'코스피':'^KS11', '하이닉스':'000660.KS', "wond":"KRW=X", "sox":"^sox", '삼성전자':'005930.KS'} da=pd.DataFrame() for i,j in nme.items(): d=yf.download(j, start=st, end=et)[["Open","High","Low","Close"]] d.columns=[i+"_"+k for k in ["o","h","l","c"]] da=pd.concat([da, d], axis=1) da=da.ffill().dropna() da.columns
Index(['코스피_o', '코스피_h', '코스피_l', '코스피_c', '하이닉스_o', '하이닉스_h', '하이닉스_l', '하이닉스_c', 'wond_o', 'wond_h', 'wond_l', 'wond_c', 'sox_o', 'sox_h', 'sox_l', 'sox_c', '삼성전자_o', '삼성전자_h', '삼성전자_l', '삼성전자_c'], dtype='object')
자료의 마지막 열을 반응변수로 하기 위해 다음과 같이 설명 변수를 분리합니다. 또한 훈련과 검증 시트로 구분합니다.
ind=da.values[:-1,:-1] de=da.values[1:,-1] final=da.values[-1:, :-1] indScaler=StandardScaler().fit(ind) indNor=indScaler.transform(ind) finalNor=indScaler.transform(final) Xtr, Xte, ytr, yte=train_test_split(indNor, de, test_size=0.3, random_state=3) print(finalNor.round(3))
[[ 0.209 0.186 0.189 0.141 2.121 2.085 2.101 2.008 0.72 0.75 0.702 0.72 2.224 2.166 2.156 2.119 -0.385 -0.373 -0.373]]
모델을 생성합니다. 훈련과 검증 세트에서의 결정계수는 두 그룹간의 차이가 미미한 것으로 과적합이나 과소적합의 위험성이 나타나지 않는다고 할 수 있습니다.
reg=SGDRegressor(l1_ratio=0, max_iter=10000, tol=1e-5, eta0=0.001, random_state=3) reg1=reg.fit(Xtr, ytr) print(f'score_tr:{np.around(reg1.score(Xtr, ytr), 4)}\nscore_te:{np.around(reg1.score(Xte, yte), 4)}')
score_tr:0.9864 score_te:0.9879
이 모델의 메소드 .predict()
로 예측치를 나타냅니다.
pre=reg1.predict(finalNor) pre
array([64840.49471834])
댓글
댓글 쓰기