갈루아의 반서재

결정트리와 타이타닉 가설 설명 (3) - 회귀를 이용한 예측


회귀로 주택 가격 예측


현재까지 예측하고자하는 결과는 이산 집합에 속했다. 하지만 실수 값을 예측해야하는 경우도 있다. 학습하는 방법은 같다. 다만 리스트에서 범주를 선택하는 대신에 분류기는 속성 학습의 조합 각각에 대해 실수를 반환하는 함수처럼 행동한다. 목적범주가 무한수인 분류기로서 회귀 문제를 생각할 수 있다.

많은 문제는 목적으로 선택하는 범주에 따라 분류와 회귀 태스크 둘 다로서 모델화될 수 있다. 예를 들어, 혈당 레벨의 예측은 회귀 태스크 뿐만 아니라 누군가가 당뇨병이 있는지 없는지 예측하는 분류 태스크도 되는 것이다.

그러면 속성의 함수로서 주택 가격을 예측해보자. 보스턴의 주택 가격에 대한 데이터셋으로, 13개의 속성과 하나의 목적 범주(소유자 거주 주택의 중앙값)인 506개 인스턴스를 가진다. 이 데이터의 각 속성값은 실수이다.

※ 관련데이터는 아래 링크 참조

http://archive.ics.uci.edu/ml/datasets/Housing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from sklearn.datasets import load_boston
>>> 
>>> boston = load_boston()
>>> 
>>> print boston.data.shape
>>> print boston.feature_names
>>> print np.max(boston.target), np.min(boston.target),  np.mean(boston.target)
 
(50613)
['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO'
 'B' 'LSTAT']
50.0 5.0 22.5328063241
cs


각 속성의 의미는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
>>> 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 14is 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)
cs


학습데이터를 훈련데이터와 테스트데이터로 구분하고 정규화한다.

1
2
3
4
5
6
7
8
9
>>> from sklearn.cross_validation import train_test_split
>>> X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target, test_size=0.25, random_state=33)
>>> from sklearn.preprocessing import StandardScaler
>>> scalerX = StandardScaler().fit(X_train)
>>> scalery = StandardScaler().fit(y_train)
>>> X_train = scalerX.transform(X_train)
>>> y_train = scalery.transform(y_train)
>>> X_test = scalerX.transform(X_test)
>>> y_test = scalery.transform(y_test)
cs


교차검증을 통해 테스트데이터를 보존하면서 과적합을 피하면서 최적의 모델을 선정하는 방법을 찾는다. 하지만 회귀에는 추가적인 이슈가 있다. 결과 평가에 있어 정확도 기준은 적합하지 못하다. 실수 값을 에측하기 때문에 최종값을 정확하게 예측하는 것은 거의 불가능하기 때문이다.

가장 일반적인 방법은 R2 점수이거나 모델의 결과 설명 변동explained variance의 비율을 측정하는 결정 계수coefficient of determination 이다. scikit-learn 는 회귀 모델의 기본 점수 함수이다. 이 점수는 1이 최대이며 모델이 모든 목적값을 완벽하게 예측함을 의미한다. 이 측정을 사용하면 모델의 훈련과 성능을 평가하는 함수를 만들며, 5-중첩 교차 검증을 사용해 결정계수를 구하도록 한다.

1
2
3
4
5
6
7
>>> from sklearn.cross_validation import *
>>> def train_and_evaluate(clf, X_train, y_train):
>>>     clf.fit(X_train, y_train)
>>>     print "Coefficient of determination on training set:",clf.score(X_train, y_train)
>>>     cv = KFold(X_train.shape[0], 5, shuffle=True, random_state=33)
>>>     scores = cross_val_score(clf, X_train, y_train, cv=cv)
>>>     print "Average coefficient of determination using 5-fold crossvalidation:",np.mean(scores)
cs


첫 번째 모델 : 선형모델


선형모델이 답하는 질문은 학습차원으로 만들어진 14차원 공간의 어떤 초평면이 속성들과 가깝게 위치하고 있는가있다. 이 초평면을 발견한 후 예측은 새로운 점의 초평면위에 투영계산을 줄이고 목적값 좌표를 반환한다.

그럼 가깝다는 의미는 무엇인가? 보통 측정은 최소 제곱법 least square 이다. 각 인스턴스에서 평면까지의 거리를 계산한 후 제곱하고 모두 합한다. 합이 가장 작은 초평면이 최소 제곱법 추정기가 된다.

데이터가 얼마나 적합화되었는지 알 수 없기에 - 14차원 산포도를 출력하기는 어렵다 - 제곱 손실을 최소화하는 SGDRegressor 라는 선형 모델로 시작해보자.

1
2
3
4
5
6
>>> from sklearn import linear_model
>>> clf_sgd = linear_model.SGDRegressor(loss='squared_loss', penalty=None,  random_state=42)
>>> train_and_evaluate(clf_sgd,X_train,y_train)
 
Coefficient of determination on training set: 0.743617732983
Average coefficient of determination using 5-fold crossvalidation: 0.710809853468
cs

다음과 같이 기법이 계산된 초평면 계수를 출력할 수 있다.

1
2
3
4
5
>>> print clf_sgd.coef_
 
[-0.08527595  0.06706144 -0.05032898  0.10874804 -0.07755151  0.38961893
 -0.02485839 -0.20990016  0.08491659 -0.05495108 -0.19854006  0.06126093
 -0.37817963]
cs

과적합화를 피하기 위해 penalty = None 라는 매개변수를 사용하고 있다. 일부계수가 너무 큰 초평면에 벌칙을 주고, 각 속성은 예측된 값과 유사한 초평면을 찾아 과적합화를 피한다. 이 매개변수는 일반적으로 L2 norm (계수의 제곱합) 또는 L1 norm (계수의 절대값의 합) 이다. L1, L2 벌칙을 사용한다면 어떻게 모델이 작동하는지 살펴보자. 특별히 향상된 결과는 보여주지 못하고 있다.

1
2
3
4
5
6
7
8
9
10
11
>>> from sklearn import linear_model
>>> clf_sgd = linear_model.SGDRegressor(loss='squared_loss', penalty='l1',  random_state=42)
>>> train_and_evaluate(clf_sgd,X_train,y_train)
>>> 
>>> print clf_sgd.coef_
 
Coefficient of determination on training set: 0.74358692291
Average coefficient of determination using 5-fold crossvalidation: 0.710763609874
[-0.08513539  0.06692218 -0.05024796  0.10866782 -0.0773804   0.38962599
 -0.02471903 -0.209519    0.08451328 -0.05474711 -0.1984843   0.0611701
 -0.37819011]
cs


1
2
3
4
5
6
7
8
9
10
11
>>> from sklearn import linear_model
>>> clf_sgd = linear_model.SGDRegressor(loss='squared_loss', penalty='l2',  random_state=42)
>>> train_and_evaluate(clf_sgd,X_train,y_train)
>>> 
>>> print clf_sgd.coef_
 
Coefficient of determination on training set: 0.743616743208
Average coefficient of determination using 5-fold crossvalidation: 0.71081206667
[-0.08527102  0.06705766 -0.05033251  0.10874257 -0.07754044  0.38959758
 -0.02485757 -0.20986818  0.08489774 -0.05494986 -0.19853109  0.06125895
 -0.37815076]
cs


두 번째 모델 : 회귀를 위한 서포터 벡터 머신


SVM의 회귀 버전은 초평면을 찾는 것을 대신해 사용된다.

1
2
3
4
5
6
>>> from sklearn import svm
>>> clf_svr = svm.SVR(kernel='linear')
>>> train_and_evaluate(clf_svr, X_train, y_train)
 
Coefficient of determination on training set: 0.71886923342
Average coefficient of determination using 5-fold crossvalidation: 0.707838419194
cs

특별히 향상되지 않았다. SVM의 중요한 장점은 비선형 함수를 사용할 수 있다는 것이다. 이를테면 데이터의 근사치를 계산하는 다항함수이다.

1
2
3
4
5
>>> clf_svr_poly = svm.SVR(kernel='poly')
>>> train_and_evaluate(clf_svr_poly, X_train, y_train)
 
Coefficient of determination on training set: 0.904109273301
Average coefficient of determination using 5-fold crossvalidation: 0.779288545488
cs

결과는 결정 계수의 관점에서 6 정도 향상되었다. 방사 기저 함수 커널을 사용해 실제로 좀 더 향상시킬 수 있다.

1
2
3
4
5
>>> clf_svr_rbf = svm.SVR(kernel='rbf')
>>> train_and_evaluate(clf_svr_rbf, X_train, y_train)
 
Coefficient of determination on training set: 0.900132065979
Average coefficient of determination using 5-fold crossvalidation: 0.833662221567
cs

RVF 커널은 일부 문제에 사용되며 매우 효율적임을 보여준다. 실제로, RBF는 scikit-learn의 SVM의 기본기법이다.


세 번째 모델 : 랜덤 포레스트 다시보기


회귀에 적용할 때 트리 성장 과정은 분류를 위해 사용한 것과 같으나, 예측할 때 잎 노드에 도착하면 다수결로 범주를 정하는 대신에 대표적인 실제값을 반환한다. 이를테면, 목적값의 평균 등으로 말이다.

sklearn.ensemble 모듈의 ExtraTreesRegressor 클랙스로 구현된 엑스트라 트리를 사용해보자. 이 기법은 별도의 무작위 레벨을 추가한다. 각 트리가 다른 무작위 속성 부분 집합을 선택할 뿐만 아니라 각 결정에 대해 경계값을 무작위로 선택한다.

1
2
3
4
5
6
>>> from sklearn import ensemble
>>> clf_et=ensemble.ExtraTreesRegressor(n_estimators=10, random_state=42)
>>> train_and_evaluate(clf_et, X_train, y_train)
 
Coefficient of determination on training set: 1.0
Average coefficient of determination using 5-fold crossvalidation: 0.861758978344
cs

※ 참고 http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.ExtraTreesRegressor.html

눈 여겨볼 첫 번째는 과소적합화를 완전히 제거했을 뿐만 아니라 교차검증을 사용해 성능을 대략 3정도 향상시켰다는 것이다. 엑스트라 트리의 흥미로운 특성은 엑스트라 트리가 회귀 태스크에 대한 각 속성의 중요성을 계산한다는 데 있다. 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> from operator import itemgetter
>>> print sorted(zip(clf_et.feature_importances_, boston.feature_names), key=itemgetter(1))
 
[(0.017052578400506287'AGE'), 
(0.015142513715149682'B'), 
(0.023602561777571307'CHAS'), 
(0.025733049004581798'CRIM'), 
(0.039713133345196064'DIS'), 
(0.034405644939308928'INDUS'), 
(0.28421522796368465'LSTAT'), 
(0.031874162235100457'NOX'), 
(0.099511801492762245'PTRATIO'), 
(0.018941821085751577'RAD'), 
(0.35814513144036819'RM'), 
(0.046618521397262996'TAX'), 
(0.0050438532027558842'ZN')]
 
cs


평가


테스터 데이터로 최종 모델의 성능을 평가해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> from sklearn import metrics
>>> def measure_performance(X, y, clf, show_accuracy=True, show_classification_report=True, show_confusion_matrix=True, show_r2_score=False):
>>>     y_pred = clf.predict(X)   
>>> 
>>>     if show_accuracy:
>>>         print "Accuracy:{0:.3f}".format(
>>>            metrics.accuracy_score(y, y_pred)
>>>         ),"\n"
>>>         
>>> 
>>>     if show_classification_report:
>>>         print "Classification report"
>>>         print metrics.classification_report(y, y_pred),"\n"
>>>      
>>>     if show_confusion_matrix:
>>>         print "Confusion matrix"
>>>         print metrics.confusion_matrix(y, y_pred),"\n"
>>> 
>>>     if show_r2_score:
>>>         print "Coefficient of determination:{0:.3f}".format(
>>>            metrics.r2_score(y, y_pred)
>>>         ),"\n"
>>> 
>>> measure_performance(X_test, y_test, clf_et, show_accuracy=False, show_classification_report=False, show_confusion_matrix=False, show_r2_score=True)
 
Coefficient of determination:0.802 
cs


일단 최적 모델을 선택하고 사용 가능한 모든 데이터를 사용한다. 전체 훈련 데이터로 최적의 모델을 훈련할 수 있으나 더 이상 사용 가능한 데이터는 없기 때문에 미래의 데이터에 대해 성능을 측정할 수 없다.