본문 바로가기
ML | DL | Big data/ML

XGBoost (4) - 머신러닝으로 부동산 가격 예측 실습하기 / Tutorial

by 썽하 2020. 7. 14.

지루한 이론과 설치가 끝나고 드디어 실습이다.

이번 글에서는 sklearn에서 제공하는 보스턴 주택 가격 데이터를 이용해 xgboost 모델을 학습시키고 예측하기까지 해 볼 예정이다.

 

개발 환경

설치가 완료된 이상 대부분 개발환경의 제약은 받지 않으나 내가 개발한 환경은 다음과 같다.

  • AWS Linux
  • python 3.6.10

준비하기

  • 이전 글에서 설치한 xgboost용 python을 활성화해준다.
source <설치경로>/bin/activate

  • sklearn, pandas, matplotlib, graphviz를 설치하지 않은 경우 설치해준다.

 

pip install sklearn pandas matplotlib graphviz

앞에 sudo를 붙이거나 가상 환경을 활성화시키지 않은 상태로 설치하는 실수는 하지 말자.

sklearn 설치 예 : 노란색과 비슷한 출력문이 나온다면 설치 성공

실습하기

Full 코드를 한줄한줄 실행해보며 이해해보는 것을 추천한다.

데이터 준비하기

우리가 사용할 데이터는 sklearn에서 제공해주는 보스턴의 주택 가격 데이터이다.

이 데이터를 통해 주택 가격을 예측해보는 xgboost 모델을 만들어보자.

from sklearn.datasets import load_boston
boston = load_boston()

위 코드를 입력하면 저장된 서버에서 데이터를 읽어와 boston이라는 변수에 저장해준다.

 

데이터 탐색하기

가져온 데이터가 어떻게 생겼는지 확인해보자.

읽어온 데이터는 dictionary(딕셔너리) 형태이기 때문에 keys() 메서드로 키를 확인할 수 있다.

print(boston.keys())
>>> dict_keys(['data', 'target', 'feature_names', 'DESCR'])

boston.data.shape이라는 값을 통해 데이터 셋의 사이즈를 확인해볼 수 있다.

print(boston.data.shape)
>>> (506, 13)

(506, 13)을 리턴하는 것을 확인할 수 있는데,

506개의 데이터(주택수)와 13개의 칼럼(특성)으로 이루어졌다는 것을 의미한다.

 

feature_names를 통해 피쳐의 이름을 가져올 수 있다.

print(boston.feature_names)
>>> ['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT']

이름 가지고는 잘 모르겠으니 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)

Attribute Information 아래에 각 칼럼명에 대한 설명이 나와 있다. 자세한 내용은 직접 번역해서 읽어보길..

 

데이터 변환하기

학습을 시키기 위해서는 내가 필요한 데이터 형태로 변환을 해주어야 한다.

XGBoost에서는 DMatric형태로 데이터를 입력할 수 있는데, 이는 메모리에 효율적이고 학습에 빠르게 구현되어있다고 한다. 예전에 강의 들을 때 triplet 표현식으로 구현되어있다고 들었었는데, 공홈에서는 그런 내용이 안 보인다.

 

우선 Pandas dataframe 형태로 변환해보자.

import pandas as pd

data = pd.DataFrame(boston.data)
data.columns = boston.feature_names

그리고 출력

data.head()
>>> (다음 표)

아까 보았던 칼럼명들과 그에 해당하는 값이 있다.

그런데 정작 예측할 가격이 없다. 이는 boston.target에 있으니, 이 값을 한 개의 칼럼으로 추가해주자.

data['PRICE'] = boston.target

info() 메서드를 통해서 데이터 프레임의 각 값이 어떤 타입을 사용하고 있는지 확인할 수 있다.

data.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 14 columns):
CRIM       506 non-null float64
ZN         506 non-null float64
INDUS      506 non-null float64
CHAS       506 non-null float64
NOX        506 non-null float64
RM         506 non-null float64
AGE        506 non-null float64
DIS        506 non-null float64
RAD        506 non-null float64
TAX        506 non-null float64
PTRATIO    506 non-null float64
B          506 non-null float64
LSTAT      506 non-null float64
PRICE      506 non-null float64
dtypes: float64(14)
memory usage: 55.4 KB

우리가 예측할 가격을 포함해서 모든 칼럼이 float64로 되어있다.

 

describe() 메서드를 통해 값들의 대략적인 통계를 보자.

data.describe()
>>> 다음표

 

 

XGBoost에 들어갈 인풋 데이터를 준비할 때 주의할 점은 categorical value를 onehot 인코딩을 통해서 직접 변환해주어야 하는데, 이 예제에서는 해당사항이 없다.

추가적인 정보로 xgboost는 null(NA) 값을 내부적으로 처리해주는 로직이 있어 별도로 처리하지 않아도 된다.(권장되는지는 모르겠다.)

 

이제 DMatrix형태로 변환해보자.

import xgboost as xgb
from sklearn.metrics import mean_squared_error
import pandas as pd
import numpy as np

필요 모듈들을 임포트 시키고

X, y = data.iloc[:,:-1],data.iloc[:,-1]

X와 y로 나눈 뒤

data_dmatrix = xgb.DMatrix(data=X,label=y)

dmatrix로 변환해준다.

 

학습하기

이제 학습을 시킬 차례다. 그런데 위에서 언급한 말을 정정해야 할 것 같다.

XGBoost에서는 DMatric형태로 데이터를 입력할 수 있는데,
XGBoost에서는 DMatric형태나 sklearn에서 지원하는 형태로 데이터를 입력할 수 있다.

정정한 이유는 xgboost가 sklearn estimate wrapper 형태로'도' 지원되기 때문이다.

다만, 이 형태로 학습을 시킬 때의 속도와 메모리 효율성은 DMatrix일 때보다 떨어진다.

 

우리는 간단한 예제 이기 때문에 sklearn wrapper 형태의 api를 사용해볼 예정이다.

 

우선 train, test 셋을 80:20 비율로 나누고,

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)

 

우리가 예측할 범위는 (0~무한대)인 특정 값이므로 regressor가 어울리니 regressor를 생성해준다.

(파라미터에 대한 설명은 이글 참고)

xg_reg = xgb.XGBRegressor(objective ='reg:linear', colsample_bytree = 0.3, learning_rate = 0.1, max_depth = 5, alpha = 10, n_estimators = 10)

그리고 fit() 메서드를 호출해 학습시켜준다.

xg_reg.fit(X_train,y_train)

 

예측하기

학습이 완료되었으면 이제 test 데이터로 예측을 해보자.

precit 메서드에 테스트 데이터를 넣어 호출하면 예측치가 나온다.

preds = xg_reg.predict(X_test)

실제 값과 얼마나 차이가 날까? RMSE로 측정해보자.

rmse = np.sqrt(mean_squared_error(y_test, preds))
print("RMSE: %f" % (rmse))
>>> RMSE: 10.569356

음.. 10.5 정도가 나왔다. RMSE가 낮으면 더 좋은 모델인데, 엄청 좋은 것 같진 않다.

xgb는 cross validation을 기본적으로 제공해주니 더 좋은 모델을 다시 한번 만들어 보자

 

Cross Validation으로 학습하고 예측 결과 비교하기

우선 cv로 학습은 다음과 같이 하면 된다.

(파라미터에 대한 설명은 이글 참고)

params = {"objective":"reg:linear",'colsample_bytree': 0.3,'learning_rate': 0.1,'max_depth': 5, 'alpha': 10}
cv_results = xgb.cv(dtrain=data_dmatrix, params=params, nfold=3,num_boost_round=50,early_stopping_rounds=10,metrics="rmse", as_pandas=True, seed=123)

cv결과는 cv_results에 매 스탭마다 저장되어있다.

확인해보자.

cv_results.head()
>>> (아래 표)

이런 식으로 모두 저장되는데 마지막에 저장된 test-rmse-mean이 우리가 확인해볼 결과이다.

마지막 값을 확인해보자.

print((cv_results["test-rmse-mean"]).tail(1))
>>> 49    3.975679
>>> Name: test-rmse-mean, dtype: float64

약 4.0 이 나왔다! 10.5보다 훨씬 개선된 결과이다!

 

모델 시각화하기

모델 시각화를 하기 위해서는 matplotlib이 설치되어있어야 한다.

tree 시각화는 다음과 같은 코드로 할 수 있다.

import matplotlib.pyplot as plt

xgb.plot_tree(xg_reg,num_trees=0)
plt.rcParams['figure.figsize'] = [40, 10]
plt.show()
>>> (다음 그림)

 

 

Boostring 방식으로 학습했기 때문에, step 마다 하나의 tree가 생성된다.

즉 위와 비슷한 tree가 step 수만큼 더 있는 것이다.

num_trees=0 이 부분을 수정하면 다른 step에 생성된 tree들도 확인해볼 수 있다.

 

다음으로 feature importance 확인하기.

graphviz가 설치되어있어야 한다.

xgb.plot_importance(xg_reg)
plt.rcParams['figure.figsize'] = [10, 10]
plt.show()
>>> (다음 그림)

위로 갈수록 feature의 중요도가 높고 아래로 갈수록 중요도가 낮다.

 

100%는 아니지만 xgb에서 중요한 feature가 다른 모델에서도 중요한 경우가 많다.

xgb는 이렇게 간단하게 시각화하고 결과를 뽑을 수 있기에,

초벌 모델링을 xgb로 하고 중요한 feature를 확인하는데 쓰기도 한다.

 

이상으로 XGBoost tutorial을 진행해보았다.

다음 글에서는 실전용 데이터로 학습과 시행착오들을 어떻게 겪어 나가는지 과정을 포스팅해 볼 예정이다.

 

Reference

댓글