[사이킷런 정주행] 1. LinearRegression

선형 회귀

회귀는 연속적인 타깃을 예측하는 알고리즘입니다. 그 중에 선형 회귀Linear Regression가 가장 기본입니다. 선형 회귀는 훈련 데이터에 가장 잘 들어 맞는 선형 방정식

\hat{y}=w_1 \times x_1+w_1 \times x_1+\cdots+w_n \times x_n+b

를 찾는 문제입니다. 여기에서 n은 훈련 데이터에 있는 특성의 수입니다.

편의상 bw_0으로 바꾸어 하나의 벡터 \bold{w}로 나타내겠습니다. \bold{w}에 포함된 w_0에 대응하기 위해 훈련 데이터에 x_0=1을 추가하여 벡터 \bold{x}를 정의합니다. 이제 이 선형 방정식은

\hat{y} =\begin{pmatrix} w_0 & w_1 & \cdots & w_n \end{pmatrix}\cdot\begin{pmatrix} x_0 \\ x_1 \\ \vdots \\ x_n \end{pmatrix} =\bold{w}^T\bold{x}

와 같이 간단히 쓸 수 있습니다. 훈련 샘플이 하나가 아니라 여러개이므로 벡터 \bold{x}를 다음과 같이 행렬로 확장할 수 있습니다. 벡터는 굵은 소문자, 행렬은 굵은 대문자를 사용합니다. 여기에서 m은 훈련 샘플의 수입니다.

\bold{\hat{y}} =\begin{pmatrix}   x_0^1 & x_1^1 & \cdots & x_n^1 \\   x_0^2 & x_1^2 & \cdots & x_n^2 \\   \vdots \\   x_0^m & x_1^m & \cdots & x_n^m \end{pmatrix} \cdot \begin{pmatrix} w_0 \\ w_1 \\ \cdots \\ w_n \end{pmatrix} =\begin{pmatrix}   \bold{x}^1 \\   \bold{x}^2 \\   \vdots \\   \bold{x}^m \end{pmatrix} \cdot \begin{pmatrix} w_0 \\ w_1 \\ \cdots \\ w_n \end{pmatrix} =\bold{X} \bold{w}

얼마나 잘 들어 맞는지를 측정 방법으로는 평균 제곱 오차Mean Square Error, MSE를 사용합니다.

\text{MSE} =\frac{1}{m} (\bold{y}-\bold{\hat{y}})^2 =\frac{1}{m} (\bold{y}-\bold{X}\bold{w})^2

이런 측정 함수를 비용 함수cost function이라고 부릅니다. 선형 회귀의 비용 함수인 평균 제곱 오차를 최소화하는 선형 방정식의 \bold{w}를 찾아야 합니다. 해석적인 방법으로 해를 구할 수 있습니다. 비용 함수를 미분하여 도함수가 0이 되는 점을 찾습니다. 먼저 MSE 비용 함수를 간단한 식으로 표현하겠습니다.

\text{MSE} =\frac{1}{m} (\bold{y}-\bold{X}\bold{w})^2 =\frac{1}{m} (\bold{y}-\bold{X}\bold{w})^T(\bold{y}-\bold{X}\bold{w}) \\ \\ =\frac{1}{m} (\bold{y}^T\bold{y}-\bold{y}^T\bold{X}\bold{w}-\bold{w}^T\bold{X}^T\bold{y}+\bold{w}^T\bold{X}^T\bold{X}\bold{w}) =\frac{1}{m} (\bold{y}^T\bold{y}-2\bold{w}^T\bold{X}^T\bold{y}+\bold{w}^T\bold{X}^T\bold{X}\bold{w})

\frac{1}{m}은 미분 결과에 영향을 미치지 않으므로 제외하고 \bold{w}에 대해 미분합니다.

\frac{\partial}{\partial \bold{w}}\text{MSE} =-2\bold{X}^T\bold{y}+2\bold{X}^T\bold{X}\bold{w}

이 도함수가 0이 되는 \bold{w}는 다음과 같습니다. 이 식을 정규 방정식Normal Equation이라고 합니다.

\bold{w}=(\bold{X}^T\bold{X})^{-1}\bold{X}^T\bold{y}

샘플 데이터

사이킷런에 포함된 샘플 데이터 중 캘리포니아 주택 가격 데이터셋을 사용하겠습니다.

import sklearn
import numpy as np
import matplotlib.pyplot as plt

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing

fetch_california_housing()함수를 호출하여 사이킷런의 Bunch 클래스 객체를 얻습니다. 캘리포니아 주택가격 데이터셋은 전체 샘플 개수가 20,640개이고 8개의 특성을 가집니다.

housing = fetch_california_housing()
print(housing.data.shape, housing.target.shape)
(20640, 8) (20640,)

train_test_split() 함수를 사용해서 75%는 훈련 세트로 25%는 테스트 세트로 분리합니다. 편의상 그래프로 나타내기 편하도록 하나의 특성만 사용하겠습니다. 사이킷런의 모델은 훈련 데이터가 2차원 배열일 것으로 예상합니다. 따라서 housing.data 에서 하나의 특성만 선택하더라도 2차원 배열이 되도록 넘파이 슬라이싱을 사용했습니다. 여기서는 첫 번째 특성만 사용합니다.

X_train, X_test, y_train, y_test = train_test_split(housing.data[:, 0:1], 
                                                    housing.target, random_state=42)
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
(15480, 1) (5160, 1) (15480,) (5160,)

정규 방정식

앞서 구한 정규 방정식을 이용하여 평균 제곱 오차가 최소가 되는 모델 파라미터 \bold{w}를 구해 보겠습니다.

\bold{w}=(\bold{X}^T\bold{X})^{-1}\bold{X}^T\bold{y}

먼저 훈련 데이터 X_train에 절편에 해당하는 x_0=1을 추가합니다. np.ones()np.hstack() 함수를 사용하면 손쉽게 X_train의 첫 번째 열에 1로 채워진 벡터를 추가할 수 있습니다. 

x0 = np.ones((X_train.shape[0],1))
X = np.hstack((x0, X_train))
print(X.shape)
(15480, 2)

정규 방정식에 나오는 전치 행렬은 넘파이 배열에서 바로 변환이 가능합니다. 역행렬은 넘파이에 있는 선형 대수 모듈 아래 inv() 함수를 사용하여 구할 수 있습니다.

w = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y_train)
print(w)
[0.44967564 0.41788087]

w 배열의 첫 번째 원소가 절편이고 두 번째 원소가 기울기가 됩니다. 1차원 데이터셋이므로 그래프에 출력하여 학습된 방정식을 그려볼 수 있습니다. 먼저 X_trainy_train으로 산점도를 그리고 학습된 방정식을 나타내기 위해 (0, w[0])에서 (10, 10*w[1]+w[0])를 지나는 직선 그래프를 그려 보겠습니다.

plt.scatter(X_train, y_train)
plt.plot([0, 10], [w[0], 10*w[1]+w[0]], 'r')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

LinearRegression

사이킷런의 LinearRegression 클래스는 사이파이SciPylstsq() 함수를 사용하여 선형 회귀 문제를 풉니다. 이 함수는 특잇값 분해(SVD) 방식을 사용하여 유사 역행렬을 계산합니다. 사이킷런의 모델 클래스를 사용할 때는 x_0 값을 고려하지 않아도 됩니다. 다음 코드는 훈련 데이터 X_trainy_train을 사용하여 선형 회귀를 수행한 결과 입니다. 

lr = LinearRegression()
lr.fit(X_train, y_train)
lr.score(X_test, y_test)
0.47083837938023365

사이킷런의 회귀 모델 클래스들은 RegressorMixin 클래스를 상속합니다. 이 클래스는 결정 계수 R^2 점수를 계산하는 score() 메서드를 제공합니다. 결정 계수Coefficient of determination는 -1~1 사이의 값을 가지며 공식은 다음과 같습니다. 여기에서 \bold{\bar{y}}는 타깃 데이터의 평균입니다.

R^2 = 1 - \sum (\bold{y}-\bold{\hat{y}})^2 / \sum (\bold{y}-\bold{\bar{y}})^2

이 공식을 직접 계산해서 score() 메서드의 결과를 재현해 보죠.

y_pred = lr.predict(X_test)
r2 = 1 - ((y_test - y_pred)**2).sum() / ((y_test - y_test.mean())**2).sum()
print(r2)
0.47083837938023365

LinearRegression 클래스가 구한 모델 파라미터는 가중치와 절편이 coef_intercept_ 인스턴스 변수에 따로 저장되어 있습니다. 하나의 특성만 사용했기 때문에 구해진 가중치 배열의 원소가 하나입니다.

print(lr.coef_, lr.intercept_)
[0.41788087] 0.44967564199686194

이 값은 앞서 정규 방정식으로 계산한 값과 거의 동일합니다. 여기에서도 앞에서와 같이 그래프로 모델의 방정식을 그려보죠.

plt.scatter(X_train, y_train)
plt.plot([0, 10], [lr.intercept_, 10 * lr.coef_ + lr.intercept_], 'r')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

입력 데이터의 최소, 최대값 사이가 0을 포함하지 않을 수 있기 때문에 LinearRegression 클래스는 의미있는 절편을 구하기 위해서 훈련 데이터의 평균을 원점에 맞춥니다. fit() 메서드에 입력된 훈련 데이터는 참조에 의한 전달이 됩니다. 원본 데이터를 변경하지 않으려면 훈련 데이터를 복사해서 사용해야 합니다. LinearRegression 클래스의 copy_X 매개변수에서 이를 조정할 수 있으며 기본값은 True로 훈련 데이터를 복사하여 사용합니다.

fit_intercept 매개변수를 기본값 True에서 False로 바꾸면 절편을 계산하지 않습니다. 데이터셋이 원점에 맞추어져 있을 때 사용할 수 있습니다.

lr_no_intercept = LinearRegression(fit_intercept=False)
lr_no_intercept.fit(X_train, y_train)
print(lr_no_intercept.coef_, lr_no_intercept.intercept_)
[0.51131441] 0.0

절편이 0입니다. 앞에서와 마찬가지로 이 직선을 훈련 데이터의 산점도 위에 그려보죠.

plt.scatter(X_train, y_train)
plt.plot([0, 10], 
         [lr_no_intercept.intercept_, 10 * lr_no_intercept.coef_ + 
          lr_no_intercept.intercept_], 'r')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

normalize 매개변수를 기본값 False에서 True로 바꾸면 각 특성에서 평균을 빼고 L2 노름으로 나누어 정규화합니다. 그 결과 각 특성은 L2 노름이 1이 됩니다. 현재 사이킷런의 구현은 fit_interceptTrue일 때만 이 매개변수가 작동됩니다. 이는 버그이며 사이킷런 0.21 버전에서 수정될 것으로 예상됩니다.

복수개의 타깃을 가진 데이터셋을 훈련할 때는 n_jobs 매개변수를 1 이상으로 지정하여 시스템의 CPU 코어를 최대한 활용할 수 있습니다. n_jobs 매개변수의 기본값은 한 개의 코어만을 사용하며 -1로 지정하면 가용한 모든 코어를 사용합니다.


이 포스트에 사용한 코드는 http://nbviewer.jupyter.org/github/rickiepark/sklearn-tutorial/blob/master/linear_model/1.LinearRegression.ipynb 에서 볼 수 있습니다.

[사이킷런 정주행] 1. LinearRegression”에 대한 1개의 생각

  1. 핑백: [텐서 플로우 블로그 (Tensor ≈ Blog)] [사이킷런 정주행] 1. LinearRegression - DEVBLOG - 개발자 메타블로그

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.