Scikit-learn: 회귀를 이용한 주택 가격 예상

회귀

한번 돌아(일주하여) 원래로 돌아오는 것. 회귀의 개념을 처음 통계에 도입한 사람은 영국의 유전학자 갤톤(Galton. F.)이다. 그는 부모와 자식간의 신장을 조사하여, 일반적으로 장신인 부모의 아이는 장신이지만, 그 평균신장은 부모만큼 크지 않다는 것을 밝혀냈다. 다시 말하면 아이의 신장은 항상 일반적인 평균으로 되돌아가는 경향이 있다고 하여, 이것을 평균의 회귀현상이라 하였다. <fn>http://terms.naver.com/entry.nhn?docId=369855&cid=42413&categoryId=42413</fn>

회귀의 개념은 꽤나 재미있습니다. 항상 평균으로 되돌아가는 경향이라는 말은 많은 점을 시사합니다. 이 포스트에서 우리는 '오차'를 작게 하는 방법에 대해 이야기할 것입니다. 오차는 실제값과 예측값의 차이입니다. 오차를 작게 한다는 말은 예측값을 실제값에 근접하도록 하겠다는 뜻입니다.

고전적이지만 꽤나 유용한 방법은 일반최소제곱(Ordinary least squares, OLS)입니다. 각 항들의 오차에 대하여 제곱의 합을 구한 후 이를 가장 작게 하는 것이 좋은 예측 모델을 만드는 방법입니다.

회귀를 이용한 주택 가격 예상

주택 가격 데이터를 가져와 회귀 모델을 만들어보도록 하겠습니다.

 01) 데이터 가져오기

Boston 지역의 주택 가격 데이터셋을 이용하겠습니다.

import numpy as np

from sklearn.datasets import load_boston
boston = load_boston()

먼저 가져온 데이터를 pyplot 위에 산점도로 나타내보겠습니다.

import matplotlib.pyplot as plt
# x: average number of rooms per dwelling(방 개수), y: 주택 가격
plt.scatter(boston.data[:, 5], boston.target, color='r', s=10)

자세한 데이터 정보는 DESCR를 출력하여 알아볼 수 있습니다.

print(boston.DESCR)

* 출력값

더 보기 ↓

Boston House Prices dataset
===========================

Notes
------
Data Set Characteristics:

:Number of Instances: 506

:Number of Attributes: 13 numeric/categorical predictive

:Median Value (attribute 14) is usually the target

:Attribute Information (in order):
- CRIM per capita crime rate by town
- ZN proportion of residential land zoned for lots over 25,000 sq.ft.
- INDUS proportion of non-retail business acres per town
- CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
- NOX nitric oxides concentration (parts per 10 million)
- RM average number of rooms per dwelling
- AGE proportion of owner-occupied units built prior to 1940
- DIS weighted distances to five Boston employment centres
- RAD index of accessibility to radial highways
- TAX full-value property-tax rate per $10,000
- PTRATIO pupil-teacher ratio by town
- B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
- LSTAT % lower status of the population
- MEDV Median value of owner-occupied homes in $1000's

:Missing Attribute Values: None

:Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
http://archive.ics.uci.edu/ml/datasets/Housing

 

This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978. Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980. N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.

**References**

- Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
- Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.
- many more! (see http://archive.ics.uci.edu/ml/datasets/Housing)

닫기

 02) 1차원 회귀

먼저 하나의 속성만을 가지고 주택 가격을 예측해보도록 하겠습니다. 위 산점도에서 이용한 방 개수를 나타내는 속성을 통해 1차원 회귀 모형을 만들어보겠습니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression()

x = boston.data[:, 5]
# LinearRegression의 함수에 인자로 전달하기 위해서는
# 각 원소의 값이 담긴 2차원 배열을 (n, 1)의 형태로 만들어야 합니다.
# atleast_2d 함수는 x 데이터를 2차원 배열을 (1, n) 형태로 만들어줍니다.
x_1 = np.atleast_2d(x)
# transpose 함수는 (1, n)을 (n, 1)로 바꿔줍니다.
x_2 = np.transpose(x_1)

y = boston.target

# fit 함수로 간단하게 회귀 모형을 만들 수 있습니다.
lr.fit(x_2,y)

1차원 회귀 모형이 잘 만들어졌는데 테스트 해보기 위해 예측값을 설정하고 그래프에 표시해 봅시다.

y_predicted = lr.predict(x_2)
plt.plot(x_2, y_predicted)

 03) 적합도 측정

위와 같이 만든 회귀 모형이 얼마나 유용할지 알아보고자 합니다. 다시 말해 예측값이 실제값과 얼마나 가까운지 보고자 하는데, 이는 각 데이터 포인트에 대하여 예측값과 실제값의 차이의 제곱의 평균, 다른 말로 평균 제곱 오차(Mean squared error, MSE)를 구하여 알 수 있습니다. 일종의 분산과 대응하는 개념입니다. 분산은 평균과 실제값의 차이의 제곱의 평균입니다.

from sklearn.metrics import mean_squared_error
mse = mean_squared_error(y, lr.predict(x_2))

우리가 정규분포에서 분산 대신 표준편차를 주로 활용하듯 평균 제곱 오차보다는 제곱근을 취한 평균 제곱근 오차(Root mean squared error, RMSE)를 더 많이 활용합니다.

rmse = np.sqrt(mse)

정규분포 상에서 대부분의 데이터가 평균을 기준으로 좌측과 우측으로 표준편차 만큼 떨어진 거리에 있는 것처럼, 다시 말해 표준편차의 두배 정도되는 범위 내에 대부분의 데이터가 위치해 있는 것처럼 우리는 실제값과 예측값의 차이가 아무리 커봤자 대략 평균 제곱근 오차의 두배일 것이라고 이야기할 수 있습니다.

하지만, 사실 이 역시도 직관적인 수치를 내주지는 못합니다. 그래서 우리는 예측 모형을 널 모델과 비교하게 됩니다. 널 모델은 모든 예측값을 주택 가격의 평균이라고 가정한 모델입니다. 즉, 매우 단순한 예측 모형과 우리가 만든 예측 모형을 비교해보자는 것입니다.

\dpi{100} \fn_jvn \small R^2 ~ score = 1 - \frac{\sum(y_i-\hat{y_i}))}{\sum(y-\bar{y_i})} = 1 - \frac{MSE}{VAR(y)}

\dpi{80} \fn_jvn \small \hat{y}는 우리가 구한 회귀 모델의 예측값이고 \dpi{80} \fn_jvn \small \bar{y}는 \dpi{80} \fn_jvn \small y의 평균을 의미합니다. 다시 말해 \dpi{80} \fn_jvn \small \bar{y}는 널 모델의 예측값입니다. 우리의 모형이 완벽했다면 \dpi{80} \fn_jvn \small R^2 ~ score(결정 계수)의 값은 1이 됩니다. MSE가 0이라는 말은 실제값과 예측값 사이의 차이가 없다는 뜻이니깐요. 반대로 MSE가 분산과 비슷하거나 혹은 분산보다 커져버리면 정말 형편없는 예측 모델이라는 것을 뜻합니다.

\dpi{80} \fn_jvn \small R^2 ~ score은 역시나 Scikit-learn에 구현되어 있기 때문에 코드 한줄로 뽑아낼 수 있습니다.

from sklearn.metrics import r2_score
r2 = r2_score(y, lr.predict(x_2))
print(r2)

* 출력값
0.483525455991

아래와 같이도 \dpi{80} \fn_jvn \small R^2 ~ score을 구할 수 있습니다.

r2 = lr.score(x_2, y)

 

 04) 다차원 회귀

다차원 회귀 모형을 구하는 방법은 오히려 더 쉽습니다. x 데이터를 가공하지 않고 그대로 fit() 함수에 넣어주면 됩니다.

x = boston.data
y = boston.target
lr.fit(x,y)

x 변수가 많아지므로 x 축에 예상값, y 축에 실제값을 설정해서 그래프를 그려봅시다.

p = lr.predict(x)
plt.scatter(p, y, s=10)
plt.xlabel("Predicted price")
plt.ylabel("Actual price")

plt.show()

 

 05) 벌점화 회귀/정규화 회귀

끝으로 벌점화 회귀(정규화 회귀)를 Scikit-learn을 이용하여 구현하는 방법에 대해 간단하게 알아보겠습니다. 제대로된 내용 설명은 다른 포스트에서 다루겠습니다.

벌점화란 과적합화된 파라미터 값에 대해 벌점을 줍니다. 이렇게 벌점을 추가하게 되면 비벌점 회귀보다 절대값이 작은 계수가 만들어지게 되는데 이를 통해 Overfitting을 막을 수 있습니다. 벌점을 준다는 것은 편향(bias)을 추가한다는 것과 같은 말입니다. 그렇기 때문에 비벌점 모델보다 훈련 데이터와의 적합도가 떨어지는 것이 사실입니다. 하지만 이는 오히려 전체적인 결과를 더 나은 방향으로 이끕니다. Overfitting 될 경우 자질구레한 노이즈까지 반영될 가능성이 크지만 모델을 살짝 단순하게 만듦으로써 좀 더 일반적인 상황에 맞는 방향으로 모델을 만듭니다.

벌점화 회귀의 종류는 크게 L1 벌점과 L2 벌점으로 나뉩니다. 각각을 라소(Lasso), 리지(Ridge)라고 부릅니다. L1 벌점은 계수의 절대값으로 나타내어지고, L2 벌점은 계수의 제곱의 합으로 나타내어집니다. 일래스틱넷(ElasticNet)은 L1 벌점과 L2 벌점을 동시에 이용하는 모델을 나타냅니다.

\dpi{100} \fn_jvn \small Lasso :~ argmin\begin{Vmatrix} y - X\beta \end{Vmatrix}^2 + \alpha\sum\left | \beta \right |

\dpi{100} \fn_jvn \small Ridge :~ argmin\begin{Vmatrix} y - X\beta \end{Vmatrix}^2 + \alpha\sum\beta^2

Scikit-learn에서 일래스틱넷을 구현하기는 매우 쉽습니다. 이전에 사용했던 Linear Regression을 ElasticNet으로 변경해주기만 하면 됩니다.

from sklearn.linear_model import ElasticNet
en = ElasticNet(alpha=0.5)

ElasticNet의 인자로 전달되는 alpha는 위 식에서의 \dpi{100} \fn_jvn \small \alpha를 의미합니다. \dpi{100} \fn_jvn \small \alpha가 0은 비벌점 회귀를 나타냅니다. \dpi{100} \fn_jvn \small \alpha가 커지면 비벌점 회귀와는 매우 다른 모델이 만들어지겠지요.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다