[SSUDA] 주택 가격 예측
https://www.kaggle.com/c/house-prices-advanced-regression-techniques
캐글에 있는 주택 가격 예측 데이터 분석입니다.
부스팅 모델들이 튜닝하는데 시간이 걸리기 때문에 좀 더 간단한 선형 회귀 모델을 사용하겠습니다.
분류 관련 공부를 조금 해본 경험으로, 회귀에 기본인 선형 회귀모델을 이번 데이터를 이용해 공부해보겠습니다.
이번 분석에 핵심 포인트는 숫자 변수 대부분이 치우쳐 있으므로 숫자 변수를 log_transform하는 것입니다.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
from scipy.stats import skew
from scipy.stats.stats import pearsonr
%config InlineBackend.figure_format = 'retina' #set 'png' here when working on notebook
%matplotlib inline
from google.colab import drive
drive.mount('/content/drive')
train = pd.read_csv("/content/drive/MyDrive/house/train.csv")
test = pd.read_csv("/content/drive/MyDrive/house/test.csv")
train.head()
train.info()
all_data = pd.concat((train.loc[:,'MSSubClass':'SaleCondition'],
test.loc[:,'MSSubClass':'SaleCondition']))
id(고유번호)와 설명변수를 뺀 나머지 변수들을 전처리를 위해 all_data 변수로 합쳐주었습니다.
이 코드의 데이터 전처리는 화려하지 않습니다. 기본에 충실합니다.
다음 3가지로 요약할 수 있습니다.
- 로그(기능 + 1)를 사용하여 오른쪽으로 꼬리가 긴 그래프를 변환합니다. 그러면 어느정도 정규화됩니다.
- 범주형 형상에 대한 더미 변수 생성
- 숫자 결측값(NaN)을 각 열의 평균으로 바꾸기
설명변수를 로그변환 해보기
matplotlib.rcParams['figure.figsize'] = (12.0, 6.0)
prices = pd.DataFrame({"price":train["SalePrice"], "log(price + 1)":np.log1p(train["SalePrice"])})
prices.hist()
로그변환 전 우측 꼬리가 두터운 느낌이였는데 잘 정규화 된 모습입니다.
all_data.dtypes
train["SalePrice"] = np.log1p(train["SalePrice"])
numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index
all_data.dtypes => 데이터 타입 나열. 여기서 인덱스는 변수이름이기 때문에 이런 방식으로 쉽게 추출.
skewed_feats = train[numeric_feats].apply(lambda x: skew(x.dropna()))
skewed_feats = skewed_feats[skewed_feats > 0.75]
skewed_feats = skewed_feats.index
all_data[skewed_feats] = np.log1p(all_data[skewed_feats])
shew = 왜도 값을 나타네는 함수. 왜도란 그래프가 비 대칭적인 모양인 것
shew값이 큰 양수값이면 오른쪽으로 긴 꼬리를 가지는 분포를 가집니다.
그러므로 shew값을 기준으로 로그변환을 할 변수를 찾을 수 있습니다.
참고로 apply 함수는 파이썬 데이터 프레임에 적용하는 함수인데, 원하는 함수를 적용하고 싶을때 사용합니다.
이때 apply 기본인자는 axis = 0이므로 열을 기준으로 함수를 적용합니다.
all_data = pd.get_dummies(all_data)
all_data.head(5)
get_dummies 함수로 모든 object형 값이 원핫인코딩 됐습니다.
저번에 프로젝트 할 때 변수를 하나하나 입력했던 것이 생각나는데 더 편한 방식을 알게 되었습니다.
all_data = all_data.fillna(all_data.mean())
결측값이 있을때 각 열의 평균값으로 대체하는 일반적인 방식입니다.
윗 코드와 마찬가지로 저번 프로젝트에서 열마다 함수를 돌려 사용했는데 더 편한 방식을 알게 됐습니다.
X_train = all_data[:train.shape[0]]
X_test = all_data[train.shape[0]:]
y = train.SalePrice
저번 프로젝트에서 트레인, 테스트 데이터에 각각 전처리를 적용했습니다.
하지만 이 방법처럼 all_data로 묶고 한번에 전처리 하는 방식이 깔끔한 것 같습니다.
선형 회귀 모델 적합을 하겠습니다.
이때 라쏘, 릿지 방법을 모두 사용해서 최적의 rmse 값을 찾겠습니다.
from sklearn.linear_model import Ridge, RidgeCV, ElasticNet, LassoCV, LassoLarsCV, Lasso
from sklearn.model_selection import cross_val_score
def rmse_cv(model):
rmse= np.sqrt(-cross_val_score(model, X_train, y, scoring="neg_mean_squared_error", cv = 5))
return(rmse)
cross_val_score 함수는 교차 검증 후 정확도를 리스트로 보여줍니다.
여기서 cv = 5 이기 때문에 5-fold로 교차검증 하게 됩니다.
model_ridge = Ridge()
릿지 모델의 주요 파라미터는 알파입니다.
알파값이 높아지면 규제가 심해지고 과적합을 방지해줍니다.
다만 너무 많이 높아지면 과소적합이 되기 때문에 적절한 값을 찾아야합니다.
alphas = [0.05, 0.1, 0.3, 1, 3, 5, 10, 15, 30, 50, 75]
cv_ridge = [rmse_cv(Ridge(alpha = alpha)).mean()
for alpha in alphas]
다양한 알파값을 릿지 함수에 적용시켰습니다.
여기서 [값 for alpha in alphas] 는 for루프를 리스트 내에서 돌리는 것 입니다.
cv_ridge = pd.Series(cv_ridge, index = alphas)
cv_ridge.plot(title = "Validation - Just Do It")
plt.xlabel("alpha")
plt.ylabel("rmse")
시리즈에 plot를 하면 그래프가 생깁니다.
이때 x축은 인덱스, y축은 본 값이 들어갑니다.
알파값이 10일때 rmse값이 최소로, 알파는 10을 쓰는 것이 좋겠습니다.
보통 규제하는 변수와 예측도를 측정하는 값간에 그래프는 U자형태가 잘 나옵니다.
그 이유는 규제가 약할때와 쌜 때 각각 과소적합, 과적합이 일어나 예측도를 측정하는 값이 커지기 때문입니다.
cv_ridge.min()
최적의 rmse값은 0.1273입니다.
이번엔 라쏘 모델입니다.
라쏘 모델은 릿지 모델과 다르게 영향력이 작은 변수의 계수를 0으로 만듭니다.
변수 선택 과정까지 한번에 할 수 있다는 것이 장점입니다.
model_lasso = LassoCV(alphas = [1, 0.1, 0.001, 0.0005]).fit(X_train, y)
LassoCV 함수로 여러가지 알파값을 동시에 검정할 수 있습니다.
model_lasso.alpha_
rmse_cv(model_lasso).mean()
라쏘 모델이 rmse 값이 훨씬 낮아서 좋습니다.
라쏘 모델을 사용하겠습니다.
coef = pd.Series(model_lasso.coef_, index = X_train.columns)
회귀 모델.coef_ => 계수를 컬럼순으로 보여줍니다.
print("Lasso picked " + str(sum(coef != 0)) + " variables and eliminated the other " + str(sum(coef == 0)) + " variables")
110개 변수는 선택되었고 178개 변수는 계수가 0, 즉 선택하지 않은 변수들입니다.
coef
imp_coef = pd.concat([coef.sort_values().head(10),
coef.sort_values().tail(10)])
matplotlib.rcParams['figure.figsize'] = (8.0, 10.0)
imp_coef.plot(kind = "barh")
plt.title("Coefficients in the Lasso Model")
sort_values() 함수는 범주형 변수의 히스토그램을 아는데 유용한 함수입니다.
여기선 정렬기능으로 사용했는데, 정렬기능으로도 충분히 우수한 것을 보여줬습니다.
정렬된 값 상위 10개, 하위 10개를 시각화했는데, 이 변수들이 핵심 변수입니다.
왜냐하면 계수의 절대값이 큰 값이기 때문입니다.
양의 값으로 가장 큰 GrLivArea변수는 면적으로 주택가격에 당연히 큰 영향을 끼칩니다.
matplotlib.rcParams['figure.figsize'] = (6.0, 6.0)
preds = pd.DataFrame({"preds":model_lasso.predict(X_train), "true":y})
preds["residuals"] = preds["true"] - preds["preds"]
preds.plot(x = "preds", y = "residuals",kind = "scatter")
잔차 그림도 큰 이상이 없습니다.
model_lasso = Lasso(alpha = 0.0005).fit(X_train, y)
pred = model_lasso.predict(X_test)
pred2 = np.exp(pred) - 1
X_test['SalePrice'] = pred2
X_test['Id'] = test['Id']
final = X_test[['Id','SalePrice']]
final.to_csv('/content/drive/MyDrive/houselasso2.csv',encoding='UTF-8', index=False)
알파값 0.0005인 라쏘 모델로 모델을 적합시키고 그 모델로 예측 파일을 만들었습니다.