kaggle
house prices advanced regression techniques
hayleyhell
2022. 11. 18. 19:20
Kaggle과 Colab 연동¶
In [1]:
!pip install kaggle
from google.colab import files
files.upload()
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ Requirement already satisfied: kaggle in /usr/local/lib/python3.7/dist-packages (1.5.12) Requirement already satisfied: certifi in /usr/local/lib/python3.7/dist-packages (from kaggle) (2022.9.24) Requirement already satisfied: six>=1.10 in /usr/local/lib/python3.7/dist-packages (from kaggle) (1.15.0) Requirement already satisfied: python-slugify in /usr/local/lib/python3.7/dist-packages (from kaggle) (6.1.2) Requirement already satisfied: urllib3 in /usr/local/lib/python3.7/dist-packages (from kaggle) (1.24.3) Requirement already satisfied: python-dateutil in /usr/local/lib/python3.7/dist-packages (from kaggle) (2.8.2) Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from kaggle) (2.23.0) Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from kaggle) (4.64.1) Requirement already satisfied: text-unidecode>=1.3 in /usr/local/lib/python3.7/dist-packages (from python-slugify->kaggle) (1.3) Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->kaggle) (3.0.4) Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->kaggle) (2.10)
Saving kaggle.json to kaggle.json
Out[1]:
{'kaggle.json': b'{"username":"minaahayley","key":"3af569f6a8c028e6233a6d29ce2439d3"}'}
In [2]:
ls -1ha kaggle.json
kaggle.json
In [3]:
!mkdir -p ~/.kaggle #create folder name Kaggle
!cp kaggle.json ~/.kaggle #copy kaggle.jason into folder Kaggle
!chmod 600 ~/.kaggle/kaggle.json #ignore Permission Warning
#다운로드가 제대로 되었는지 확인한다.
#ls 명령어는 특정 경로에 어떤 파일이 있는지 확인해 보는 명령어다.
%ls ~/.kaggle
kaggle.json
In [4]:
#Copy API command 후 데이터셋 다운로드하기
!kaggle competitions download -c house-prices-advanced-regression-techniques
#파일 압축 풀기
!unzip house-prices-advanced-regression-techniques.zip
Downloading house-prices-advanced-regression-techniques.zip to /content 0% 0.00/199k [00:00<?, ?B/s] 100% 199k/199k [00:00<00:00, 50.0MB/s] Archive: house-prices-advanced-regression-techniques.zip inflating: data_description.txt inflating: sample_submission.csv inflating: test.csv inflating: train.csv
In [5]:
!mkdir -p ~/.kaggle/competitions/house-prices-advanced-regression-techniques #create folder name Kaggle
!cp sample_submission.csv ~/.kaggle/competitions/house-prices-advanced-regression-techniques #copy into folder Kaggle
!cp test.csv ~/.kaggle/competitions/house-prices-advanced-regression-techniques
!cp train.csv ~/.kaggle/competitions/house-prices-advanced-regression-techniques
#다운로드가 제대로 되었는지 확인한다.
#ls 명령어는 특정 경로에 어떤 파일이 있는지 확인해 보는 명령어다.
%ls ~/.kaggle/competitions/house-prices-advanced-regression-techniques
sample_submission.csv test.csv train.csv
Feature Engineering¶
In [6]:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore', category=RuntimeWarning)
In [24]:
submission = pd.read_csv('~/.kaggle/competitions/house-prices-advanced-regression-techniques/sample_submission.csv')
test = pd.read_csv('~/.kaggle/competitions/house-prices-advanced-regression-techniques/test.csv')
train = pd.read_csv('~/.kaggle/competitions/house-prices-advanced-regression-techniques/train.csv')
In [8]:
submission.sample()
Out[8]:
Id | SalePrice | |
---|---|---|
846 | 2307 | 161474.534864 |
In [25]:
# train 원본 데이터 저장
train_org = train.copy()
In [26]:
# Null이 너무 많은 컬럼과 불필요한 컬럼 삭제
train.drop(['Id', 'Alley', 'FireplaceQu', 'PoolQC', 'Fence', 'MiscFeature'], axis=1, inplace=True)
In [27]:
# 숫자형 Null 컬럼은 평균값으로 대체
train.fillna(train.mean(), inplace=True)
/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:2: FutureWarning: Dropping of nuisance columns in DataFrame reductions (with 'numeric_only=None') is deprecated; in a future version this will raise TypeError. Select only valid columns before calling the reduction.
In [28]:
# Null 값이 있는 피처명과 타입 추출
train.isnull().sum()[train.isnull().sum()>0].index
Out[28]:
Index(['MasVnrType', 'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2', 'Electrical', 'GarageType', 'GarageFinish', 'GarageQual', 'GarageCond'], dtype='object')
데이터 분포도¶
- 피처 데이터 세트도 지나치게 왜곡된 피처가 존재할 경우 회귀 예측 성능을 저하시킬 수 있다.
- skew() 함수를 이용하여 모든 숫자형 피처의 왜곡 정도를 확인하자.
- 1이상인 경우 왜곡 정도 높음
- 왜곡 정도가 높은 피처를 로그 변환 후, 원핫인코딩을 해야 함
In [29]:
# y 값의 분포
train['SalePrice'].hist()
Out[29]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde5c1eb310>
- 정규 분포 형태로 변환하기 위해 로그 변환을 해야 한다.
In [30]:
# 로그 변환
train['SalePrice'] = np.log1p(train['SalePrice'])
train['SalePrice'].hist()
Out[30]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde5c214b90>
In [31]:
from scipy.stats import skew
# 숫자형 피처의 컬럼 index 추출
feature_index = train.dtypes[train.dtypes != 'object'].index
skew_features = train[feature_index].apply(lambda x : skew(x))
# skew(왜곡) 정도가 1이상인 컬럼만 추출
skew_features_top = skew_features[skew_features > 1].sort_values(ascending=False)
skew_features_top
Out[31]:
MiscVal 24.451640 PoolArea 14.813135 LotArea 12.195142 3SsnPorch 10.293752 LowQualFinSF 9.002080 KitchenAbvGr 4.483784 BsmtFinSF2 4.250888 ScreenPorch 4.117977 BsmtHalfBath 4.099186 EnclosedPorch 3.086696 MasVnrArea 2.673661 LotFrontage 2.382499 OpenPorchSF 2.361912 BsmtFinSF1 1.683771 WoodDeckSF 1.539792 TotalBsmtSF 1.522688 MSSubClass 1.406210 1stFlrSF 1.375342 GrLivArea 1.365156 dtype: float64
In [32]:
# 왜곡 정도가 높은 피처를 로그 변환
train[skew_features_top.index] = np.log1p(train[skew_features_top.index])
원 핫 인코딩¶
- 선형 회귀는 카테고리형 데이터가 있을 경우 이를 레이블 인코딩을 통한 숫자형 변환보다는 원 핫 인코딩으로 변환해줘야 한다.
- 회귀 트리의 경우 인코딩 방식에 크게 영향을 받지는 않는다.
In [41]:
# 문자형 피처는 원 핫 인코딩으로 변환
train = pd.get_dummies(train)
train.shape
Out[41]:
(1460, 271)
In [42]:
# 원 핫 인코딩 후 Null 값 없음
train.isnull().sum()[train.isnull().sum()>0].index
Out[42]:
Index([], dtype='object')
이상치 데이터 처리¶
- 특히 회귀 계수가 높은 피처, 즉 예측에 영향을 많이 미치는 중요 피처의 이상치 데이터의 처리가 중요하다.
In [43]:
# 회귀 계수 시각화
def visualize_coefficient(models):
fig, axs = plt.subplots(figsize=(24,10), nrows=1, ncols=3)
fig.tight_layout()
for i, model in enumerate(models):
# 상위 10개, 하위 10개 회귀 계수를 구하고, 이를 판다스 concat으로 결합
coef = pd.Series(model.coef_, index=X.columns)
coef_high = coef.sort_values(ascending=False).head(10)
coef_low = coef.sort_values(ascending=False).tail(10)
coef_concat = pd.concat([coef_high, coef_low])
sns.barplot(x=coef_concat.values, y=coef_concat.index, ax=axs[i])
In [44]:
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X = train.drop('SalePrice', axis=1)
y = np.log1p(train['SalePrice'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=156)
# LinearRegression, Ridge, Lasso 학습, 예측, 평가
lr_reg = LinearRegression()
ridge_reg = Ridge()
lasso_reg = Lasso()
lr_reg.fit(X_train, y_train)
ridge_reg.fit(X_train, y_train)
lasso_reg.fit(X_train, y_train)
models = [lr_reg, ridge_reg, lasso_reg]
visualize_coefficient(models)
- 세 개 모델 모두에서 가장 큰 회귀 계수를 가지는 GrLivArea(주거 공간 크기)의 데이터 분포를 살펴보자
In [45]:
plt.scatter(x=train_org['GrLivArea'], y=train_org['SalePrice'])
Out[45]:
<matplotlib.collections.PathCollection at 0x7fde571b4250>
- 이상치 데이터 조건 : GrLivArea > 4000, SalePrice > 500000
In [46]:
# GrLivArea, SalePrice 모두 로그 변환됐으므로 이를 반영한 조건 생성
outlier_index = train[(train['GrLivArea'] > np.log1p(4000)) & (train['SalePrice'] > np.log1p(50000))].index
# 이상치 데이터 삭제
train.drop(outlier_index, axis=0, inplace=True)
선형 회귀¶
모델 학습/예측/평가¶
In [48]:
# 로그 변환 된 rmse를 측정하는 함수 생성
def get_rmse(model):
pred = model.predict(X_test)
mse = mean_squared_error(y_test, pred)
rmse = np.sqrt(mse)
print(model.__class__.__name__, '로그 변환된 rmse:', np.round(rmse,3))
return rmse
In [49]:
# 여러 모델의 rmse 값을 반환
def get_rmses(models):
rmses = []
for model in models:
rmse = get_rmse(model)
rmses.append(rmse)
return rmses
In [50]:
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X = train.drop('SalePrice', axis=1)
y = train['SalePrice']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=156)
# LinearRegression, Ridge, Lasso 학습, 예측, 평가
lr_reg = LinearRegression()
ridge_reg = Ridge()
lasso_reg = Lasso()
lr_reg.fit(X_train, y_train)
ridge_reg.fit(X_train, y_train)
lasso_reg.fit(X_train, y_train)
models = [lr_reg, ridge_reg, lasso_reg]
get_rmses(models)
LinearRegression 로그 변환된 rmse: 0.132 Ridge 로그 변환된 rmse: 0.125 Lasso 로그 변환된 rmse: 0.258
Out[50]:
[0.13195207393230912, 0.12459727425511735, 0.2575777799483299]
하이퍼 파라미터 튜닝¶
In [51]:
from sklearn.model_selection import GridSearchCV
def best_params(model, params):
grid_model = GridSearchCV(model, param_grid=params, scoring='neg_mean_squared_error', cv=5)
grid_model.fit(X, y)
rmse = np.sqrt(-1 * grid_model.best_score_)
print('{0} 5 CV 시 최적 평균 rmse: {1}, 최적 평균 alpha: {2}'.format(model.__class__.__name__, np.round(rmse, 4), grid_model.best_params_))
ridge_params = {'alpha' : [0.05, 0.1, 1, 5, 8, 10, 12, 15, 20]}
lasso_params = {'alpha' : [0.001, 0.005, 0.008, 0.05, 0.03, 0.1, 0.5, 1, 5, 10]}
best_params(ridge_reg, ridge_params)
best_params(lasso_reg, lasso_params)
Ridge 5 CV 시 최적 평균 rmse: 0.1125, 최적 평균 alpha: {'alpha': 8} Lasso 5 CV 시 최적 평균 rmse: 0.1121, 최적 평균 alpha: {'alpha': 0.001}
In [52]:
# 앞의 최적화 alpha 값으로 학습, 예측, 평가
lr_reg = LinearRegression()
ridge_reg = Ridge(alpha=8)
lasso_reg = Lasso(alpha=0.001)
lr_reg.fit(X_train, y_train)
ridge_reg.fit(X_train, y_train)
lasso_reg.fit(X_train, y_train)
models = [lr_reg, ridge_reg, lasso_reg]
get_rmses(models)
LinearRegression 로그 변환된 rmse: 0.132 Ridge 로그 변환된 rmse: 0.117 Lasso 로그 변환된 rmse: 0.116
Out[52]:
[0.13195207393230912, 0.11736730851679039, 0.1158658441187506]
- alpha 값 최적화 후 테스트 데이터셋의 예측 성능이 더 좋아졌다.
In [53]:
# 피처 중요도 시각화
models = [lr_reg, ridge_reg, lasso_reg]
visualize_coefficient(models)
회귀 트리¶
In [55]:
from sklearn.ensemble import GradientBoostingRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
gbm_reg = GradientBoostingRegressor(n_estimators=1000)
xgb_reg = XGBRegressor(n_estimators=1000, learning_rate=0.05, colsample_bytree=0.5, subsample=0.8)
lgbm_reg = LGBMRegressor(n_estimators=1000, learning_rate=0.05, num_leaves=4,
subsample=0.6, colsample_bytree=0.4, reg_lambda=10, n_jobs=-1)
gbm_reg.fit(X_train, y_train)
xgb_reg.fit(X_train, y_train)
lgbm_reg.fit(X_train, y_train)
models = [gbm_reg, xgb_reg, lgbm_reg]
get_rmses(models)
[07:42:56] WARNING: /workspace/src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror. GradientBoostingRegressor 로그 변환된 rmse: 0.124 XGBRegressor 로그 변환된 rmse: 0.116 LGBMRegressor 로그 변환된 rmse: 0.118
Out[55]:
[0.12434554514384757, 0.11569128150068062, 0.11806601740834839]
혼합 모델¶
In [56]:
# 개별 모델의 학습
ridge_reg = Ridge(alpha=8)
lasso_reg = Lasso(alpha=0.001)
ridge_reg.fit(X_train, y_train)
lasso_reg.fit(X_train, y_train)
# 개별 모델 예측
ridge_pred = ridge_reg.predict(X_test)
lasso_pred = lasso_reg.predict(X_test)
# 개별 모델 예측값 혼합으로 최종 예측값 도출
pred = 0.4 * ridge_pred + 0.6 * lasso_pred
# 최종 혼합 모델의 rmse 값 출력
mse = mean_squared_error(y_test, pred)
rmse = np.sqrt(mse)
print('로그 변환된 rmse:', np.round(rmse,3))
로그 변환된 rmse: 0.115
- 최종 혼합 모델의 rmse가 개별 모델보다 성능보다 약간 개선되었다.
- 0.4나 0.6을 정하는 기준은 없다. 성능이 조금 좋은 쪽에 가중치를 약간 더 둔다.
In [57]:
# 개별 모델의 학습
xgb_reg = XGBRegressor(n_estimators=1000, learning_rate=0.05, colsample_bytree=0.5, subsample=0.8)
lgbm_reg = LGBMRegressor(n_estimators=1000, learning_rate=0.05, num_leaves=4,
subsample=0.6, colsample_bytree=0.4, reg_lambda=10, n_jobs=-1)
xgb_reg.fit(X_train, y_train)
lgbm_reg.fit(X_train, y_train)
# 개별 모델 예측
xgb_pred = xgb_reg.predict(X_test)
lgbm_pred = lgbm_reg.predict(X_test)
# 개별 모델 예측값 혼합으로 최종 예측값 도출
pred = 0.5 * xgb_pred + 0.5 * lgbm_pred
# 최종 혼합 모델의 rmse 값 출력
mse = mean_squared_error(y_test, pred)
rmse = np.sqrt(mse)
print('로그 변환된 rmse:', np.round(rmse,3))
[07:44:06] WARNING: /workspace/src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror. 로그 변환된 rmse: 0.114
- XGBoost와 LightGBM의 혼합 모델의 rmse가 개별 모델의 rmse보다 조금 향상되었다.