주택가격 예측하기
필요한 라이브러리 불러오기¶
import pandas as pd
import numpy as np
import os
import tarfile
from six.moves import urllib
import matplotlib.pyplot as plt
# 항상 동일한 결과를 얻기 위해 random.seed 값을 설정한다
np.random.seed(42)
# 주피터 노트북 안에 그림이 나오도록 설정
%matplotlib inline
0. 분석할 데이터셋¶
아래의 코드는 분석할 데이터셋을 로컬 디스크에 다운로드 합니다.
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml/master/"
HOUSING_PATH = os.path.join("input", "housing")
HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/housing.tgz"
def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
if not os.path.isdir(housing_path):
os.makedirs(housing_path)
tgz_path = os.path.join(housing_path, "housing.tgz")
urllib.request.urlretrieve(housing_url, tgz_path)
housing_tgz = tarfile.open(tgz_path)
housing_tgz.extractall(path=housing_path)
housing_tgz.close()
fetch_housing_data()
housing = pd.read_csv("./input/housing/housing.csv")
housing.tail()
총 20639 개의 개별 데이터가 있는것과 좌표를 뜻하는 longitude, latitude 행이 앞쪽에 있는 것을 알 수 있습니다. 또한, 집에 대한 정보들이 여러 행으로 구분되어 있습니다.
1.2. 기초 통계분석 하기¶
위에서 살펴본 표는 대부분 수치(숫자)형으로 나와있음으로, 간단하게 describe()
함수로 기초 통계분석을 할 수 있습니다.
housing.describe()
기초 통계분석을 통해 평균값(mean), 표준편차(std), 최소값(min), 최대값(max), 각종 백분위수(25%, 50% 75%) 등을 알수 있습니다.
1.3. 히스토그램 그리기¶
간단한 히스토그램을 그려 전체 데이터의 모양을 어떻게 생겼는지 확인합니다.
fig = housing.hist(bins=50, figsize=(20, 15))
1.4. 지리적 데이터 시각화¶
사용한 데이터셋에는 각각의 위도 경도값이 들어있습니다. 이것을 이용해 지리적 정보를 시각화 합니다.
import matplotlib.image as mpimg
california_img = mpimg.imread("./input/housing/california.png")
ax = housing.plot(
kind="scatter",
x="longitude",
y="latitude",
figsize=(10, 7),
s=housing["population"] / 100,
label="population",
c="median_house_value",
cmap=plt.get_cmap(),
colorbar=False,
alpha=0.5,
)
plt.imshow(california_img, extent=[-124.55, -113.80, 32.45, 42.05], alpha=0.6)
prices = housing["median_house_value"]
tick_values = np.linspace(prices.min(), prices.max(), 11)
cbar = plt.colorbar()
cbar.ax.set_yticklabels(["$%dk" % (round(v / 1000)) for v in tick_values], fontsize=14)
plt.show()
1.5. 상관관계 조사¶
숫자형 특성이 11개이므로 만약 모든 상관관계를 조사한다면 총 121개의 그래프가 그려집니다. 그렇게 많은 그래프를 하나로 표현하면 오히려 더 알아보기 힘들기 때문에 여기에서는 앞서 히스토그램에서 상관관계가 높아보이는 특성 몇 개만 추려서 시각화 합니다.
from pandas.tools.plotting import scatter_matrix
corr_matrix = housing.corr()
attributes = [
"median_house_value",
"median_income",
"total_rooms",
"housing_median_age",
]
fig = scatter_matrix(housing[attributes], figsize=(10, 10), alpha=0.2)
결과를 보면 중간 소득(median_income
)이 중간 주택 가격(median_house_value
)과 가장 상관 관계가 있어보입니다.
좀더 크게 산점도를 그려보겠습니다.
housing.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.1)
plt.axis([0, 16, 0, 550000])
import seaborn as sns
corr = housing.corr()
# Set up the matplotlib figure
f, ax = plt.subplots(figsize=(10, 7))
# Draw the heatmap with the mask and correct aspect ratio
with sns.axes_style("white"):
sns.heatmap(corr, vmax=0.3, cmap="YlGnBu", square=True, linewidths=0.3)
위의 결과를 통해 total_rooms, households, total_bedrooms, population간의 높은 상관관계가 있음을 알 수 있습니다. 다음과 같은 특성을 추가합니다.
- rooms_per_household
- bedrooms_per_room
- population_per_household
housing["rooms_per_household"] = housing["total_rooms"] / housing["households"]
housing["bedrooms_per_room"] = housing["total_bedrooms"] / housing["total_rooms"]
housing["population_per_household"] = housing["population"] / housing["households"]
rooms_per_household와 평균 주택 가격의 산점도를 그려봅니다.
housing.plot(kind="scatter", x="rooms_per_household", y="median_house_value", alpha=0.2)
plt.axis([0, 5, 0, 520000])
plt.show()
2.2. one-hot encoding¶
데이터셋의 ocean_proximity
행은 수치형 데이터가 아닙니다. 따라서 기계학습에 사용하기 위해 get_dummies
기능을 사용해 one-hot encoding을 수행합니다.
df = pd.get_dummies(data=housing, columns=["ocean_proximity"])
2.3. 결측치의 처리¶
데이터셋의 결측값을 각각의 행의 평균값으로 치환해주는 작업입니다.
from sklearn.preprocessing import Imputer
imputer = Imputer(strategy="median")
# housing_num = housing.drop('ocean_proximity', axis=1)
# 다른 방법: housing_num = housing.select_dtypes(include=[np.number])
imputer.fit(df)
X = imputer.transform(df)
housing_tr = pd.DataFrame(X, columns=df.columns)
housing_tr.tail()
2.4. 데이터셋 레이블 분리하기¶
기계학습을 위해 데이터셋의 평균주택가격(median_house_value
)행을 분리해 레이블로 사용합니다.
housing_labels = housing_tr["median_house_value"].copy() # 레이블로 사용
housing_tr.drop("median_house_value", axis=1, inplace=True) # 레이블 삭제
2.5. 데이터셋 정규화¶
기계학습의 성능을 높이기 위해 데이터셋을 정규화합니다.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# scaler.fit(housing_tr)
scaled_df = scaler.fit_transform(housing_tr)
scaled_df.shape
총 데이터의 숫자는 20640이고 16개의 특성으로 구성된 배열임을 알 수 있습니다.
2.6. 학습용, 확인용 데이터 나누기¶
무작위 샘플링을 통해 데이터를 학습용과 확인용으로 나눕니다. 학습용을 80% 확인용은 20% 비율로 분리합니다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
scaled_df, housing_labels, test_size=0.2, random_state=42
)
print(X_train.shape, X_test.shape)
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression(n_jobs=-1) # -1 means use all cpu core
lin_reg.fit(X_train, y_train)
성능의 평가를 위해 예측값과 실제값의 RMS를 구합니다.
from sklearn.metrics import mean_squared_error
y_pred = lin_reg.predict(X_test)
rms = np.sqrt(mean_squared_error(y_test, y_pred))
print(rms)
대부분의 주택의 중간가격이 120,000 ~ 265,000$ 인데 오차가 약 70,000$ 인것은 만족스럽지 못합니다. 이제 다른 모델을 사용해 기계학습을 해봅니다.
3.2. 의사결정트리 모델¶
DecisionTreeRegressor
모델을 사용해보고 성능은 동일하게 RMS를 구합니다.
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(n_jobs=-1)
tree_reg.fit(X_train, y_train)
y_pred = tree_reg.predict(X_test)
rms = np.sqrt(mean_squared_error(y_test, y_pred))
print(rms)
오히려 더 나쁜 결과가 나왔습니다. 이것은 의사결정트리 모델이 과접합(overfitting)되었기 때문입니다. 이제 랜덤 포레스트 모델을 사용해봅니다.
3.3 랜덤 포레스트 모델¶
성능의 평가는 RMS를 측정합니다.
from sklearn.ensemble import RandomForestRegressor
forest_reg = RandomForestRegressor(n_jobs=-1)
forest_reg.fit(X_train, y_train)
y_pred = forest_reg.predict(X_test)
rms = np.sqrt(mean_squared_error(y_test, y_pred))
print(rms)
가장 나은 성능을 보여주긴 하지만 그리 만족스럽지는 못합니다. 다른 모델인 서포트 벡터 머신(support vector machine, SVM)도 사용해 보겠습니다.
3.4. 서포트 벡터 머신¶
from sklearn.svm import SVR
svm_reg = SVR(kernel="linear")
svm_reg.fit(X_train, y_train)
y_pred = svm_reg.predict(X_test)
rms = np.sqrt(mean_squared_error(y_test, y_pred))
print(rms)
from sklearn.model_selection import GridSearchCV
param_grid = [
# try 12 (3×4) combinations of hyperparameters
{"n_estimators": [3, 10, 30], "max_features": [2, 4, 6, 8]},
# then try 6 (2×3) combinations with bootstrap set as False
{"bootstrap": [False], "n_estimators": [3, 10], "max_features": [2, 3, 4]},
]
forest_reg = RandomForestRegressor(random_state=42, n_jobs=-1)
# train across 5 folds, that's a total of (12+6)*5=90 rounds of training
grid_search = GridSearchCV(
forest_reg,
param_grid,
cv=5,
scoring="neg_mean_squared_error",
return_train_score=True,
)
grid_search.fit(X_train, y_train)
cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score), params)
결과를 표로 출력합니다.
cv = pd.DataFrame(grid_search.cv_results_)
cv.head()
가장 좋은 매개변수의 값은 아래와 같습니다. 이때의 성능(RMS)값은 49392.4163034 입니다.
grid_search.best_params_
grid_search.best_estimator_
4.3. 성능에 가장 영향주는 특성¶
gridsearch에서 가장 좋은 성능의 모델을 가지고 성능에 가장 큰 영향을 주는 특성을 확인해보겠습니다.
feature_importances = grid_search.best_estimator_.feature_importances_
plt.bar(range(len(feature_importances)), feature_importances)
8번째(0부터 시작하기 때문)의 값이 가장 큰 영향을 주는데, 이것은 평균수입(median_income)에 대한 값입니다.
4.2. RandomizedSearch¶
Gridsearch는 계산시간이 아주 올래걸리기 때문에 최근에는 RandomizedSearch 방법으로 최적화를 많이 진행합니다. 이름에서 알 수 있듯, 사용자가 매개변수의 범위를 지정해주면 무작위로 매개변수를 조합한 성능을 측정합니다.
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
param_distribs = {
"n_estimators": randint(low=1, high=200),
"max_features": randint(low=1, high=8),
}
forest_reg = RandomForestRegressor(random_state=42, n_jobs=-1)
rnd_search = RandomizedSearchCV(
forest_reg,
param_distributions=param_distribs,
n_iter=10,
cv=5,
scoring="neg_mean_squared_error",
random_state=42,
)
rnd_search.fit(X_train, y_train)
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score), params)
가장 좋은 성능의 결과를 확인합니다.
final_model = rnd_search.best_estimator_
y_pred = final_model.predict(X_test)
rms = np.sqrt(mean_squared_error(y_test, y_pred))
print(rms)
5. 마치며¶
기계학습으로 캘리포니아의 주택가격을 예측해보는 것을 살펴보았습니다. 결론적으로 주택가격에 가장 영향을 주는 특성은 평균소득이었으며, 예측 오차는 약 48434$ 입니다.