編者按:Google產(chǎn)品分析Zlatan Kremonic分享了參加Kaggle競賽的經(jīng)驗。
問題
Kaggle房價競賽要求參賽者預(yù)測2006年至2010年美國愛荷華州埃姆斯市的房價。數(shù)據(jù)集中包含79個變量,包括許多房屋屬性。你可以在Kaggle網(wǎng)站上了解更多細(xì)節(jié):https://www.kaggle.com/c/house-prices-advanced-regression-techniques
方法
由于我們的目標(biāo)變量是連續(xù)值(售價),因此這是一個典型的回歸問題,讓人聯(lián)想起波斯頓房價數(shù)據(jù)集。評估標(biāo)準(zhǔn)為預(yù)測和實際售價的接近程度(預(yù)測值的對數(shù)與觀測到的售價的對數(shù)的均方根誤差)。
數(shù)據(jù)集中包括大量變量,其中許多是類別變量,因此特征選取是這一問題的關(guān)鍵部分。特征選取的兩種常用方法:
直接使用scikit-learn中的SelectKBest方法。
LASSO回歸。
我在分析中嘗試了這兩種方法,發(fā)現(xiàn)LASSO回歸的結(jié)果要好一些。
另外,我們將使用XGBoost,并在結(jié)果中融合LASSO的輸出,以提升模型的精確度。我們的最終結(jié)果不錯,位于排行榜的前10%(撰寫本文時)。
探索性數(shù)據(jù)分析
因為變量很多,為了節(jié)約篇幅,我不會詳細(xì)演示所有探索性數(shù)據(jù)分析(我在文末列出了GitHub倉庫的鏈接,如果你對探索性數(shù)據(jù)分析的細(xì)節(jié)感興趣,可以查看其中的EDA.ipynb)。相反,我將直接給出我的主要觀察,這些觀察給特征工程提供了信息。
我們有大量的類別屬性,需要進(jìn)行獨熱編碼。
一些數(shù)值列有null值,需要填充。
許多數(shù)值列的分布比較扭曲,需要處理。
如前所述,為了節(jié)約篇幅,這里僅僅給出導(dǎo)入庫、加載數(shù)據(jù)的代碼,不包括探索性數(shù)據(jù)分析部分的代碼。
import os
import pandas as pd
import numpy as np
from scipy.stats import skew
from sklearn.model_selection importGridSearchCV
from sklearn.linear_model importLasso
from sklearn.metrics import mean_squared_error
from xgboost.sklearn importXGBClassifier
import xgboost as xgb
import matplotlib.pyplot as plt
%matplotlib inline
train = pd.read_csv(os.path.join('data', 'train.csv'))
test = pd.read_csv(os.path.join('data', 'test.csv'))
y = train.iloc[:, -1]
train = train.iloc[:, 1:-1]
test = test.iloc[:, 1:]
submission = test.iloc[:, 0]
特征工程
首先,我們將MSSubClass變量(表示建筑分類編碼)從數(shù)值轉(zhuǎn)為字符串,因為這些編碼只是無序的類別。
def mssubclass(train, test, cols=['MSSubClass']):
for i in (train, test):
for z in cols:
i[z] = i[z].apply(lambda x: str(x))
return train, test
接著,我們將對所有數(shù)值特征取對數(shù),包括因變量。由于數(shù)值特征包含很多零值,我們使用log1p,在取對數(shù)前先加一。
def log(train, test, y):
numeric_feats = train.dtypes[train.dtypes != "object"].index
for i in (train, test):
i[numeric_feats] = np.log1p(i[numeric_feats])
y = np.log1p(y)
return train, test, y
我們將用每列的均值填充null值:
def impute_mean(train, test):
for i in (train, test):
for s in [k for k in i.dtypes[i.dtypes != "object"].index if sum(pd.isnull(i[k])>0)]:
i[s] = i[s].fillna(i[s].mean())
return train, test
獨熱編碼時,同樣需要填充null值:
def dummies(train, test):
columns = [i for i in train.columns if type(train[i].iloc[1]) == str or type(train[i].iloc[1]) == float]
for column in columns:
train[column].fillna('NULL', inplace = True)
good_cols = [column+'_'+i for i in train[column].unique()[1:] if i in test[column].unique()]
train = pd.concat((train, pd.get_dummies(train[column], prefix = column)[good_cols]), axis = 1)
test = pd.concat((test, pd.get_dummies(test[column], prefix = column)[good_cols]), axis = 1)
del train[column]
del test[column]
return train, test
整個特征工程流程:
train, test = mssubclass(train, test)
train, test, y = log(train, test, y)
train, test = lotfrontage(train, test)
train, test = garageyrblt(train, test)
train, test = impute_mean(train, test)
train, test = dummies(train, test)
LASSO回歸
LASSO回歸同時起到了正則化和特征選取的作用,可以改善模型的預(yù)測效果。就我們的情況而言,LASSO回歸是完美的算法,因為它有助于降低特征數(shù)并緩解過擬合。
LASSO回歸中需要調(diào)節(jié)的超參數(shù)主要是正則化因子alpha。我們使用GridSearchCV(網(wǎng)格搜索交叉驗證)尋找alpha的最優(yōu)值。
alpha_ridge = [1e-5, 1e-4, 1e-3, 1e-2, 1, 5, 10, 20]
coeffs = {}
for alpha in alpha_ridge:
r = Lasso(alpha=alpha, normalize=True, max_iter=1000000)
r = r.fit(train, y)
grid_search = GridSearchCV(Lasso(alpha=alpha, normalize=True), scoring='neg_mean_squared_error',
param_grid={'alpha': alpha_ridge}, cv=10, n_jobs=-1)
grid_search.fit(train, y)
最終我們得到alpha的最佳值0.0001。為了更直觀地理解alpha的影響,我們可以畫出所有alpha值的均方根誤差:
alpha = alpha_ridge
rmse = list(np.sqrt(-grid_search.cv_results_['mean_test_score']))
plt.figure(figsize=(6,5))
lasso_cv = pd.Series(rmse, index = alpha)
lasso_cv.plot(title = "Validation - LASSO", logx=True)
plt.xlabel("alpha")
plt.ylabel("rmse")
現(xiàn)在用模型擬合訓(xùn)練數(shù)據(jù):
lasso = Lasso(alpha=.0001, normalize=True, max_iter=1e6)
lasso = lasso.fit(train, y)
我們的模型有多少列?
coef = pd.Series(lasso.coef_, index = train.columns)
print("Lasso選中了" + str(sum(coef != 0)) + "個變量,并移除了其他" + str(sum(coef == 0)) + "個變量")
Lasso選中了103個變量,并移除了其他142個變量
此外,我們可以看到,根據(jù)我們的模型,房齡、面積、房屋狀況是最重要的變量。這很符合直覺——在創(chuàng)建模型時檢查模型是否符合常理總是不錯的。
imp_coef = pd.concat([coef.sort_values().head(10),
coef.sort_values().tail(10)])
plt.rcParams['figure.figsize'] = (5.0, 5.0)
imp_coef.plot(kind = "barh")
plt.title("Coefficients in the Lasso Model")
用LASSO模型預(yù)測測試數(shù)據(jù),我們得到的均方根誤差為0.1209,這已經(jīng)足以在排行榜上取得前25%的名次了。
XGBoost模型
由于XGBoost在數(shù)據(jù)科學(xué)競賽中的強力表現(xiàn),從2016年起,這一算法變得家喻戶曉了。這一算法的挑戰(zhàn)之一是處理大數(shù)據(jù)集時,調(diào)整超參數(shù)耗時很久。然而,因為我們的數(shù)據(jù)集包含不到1500項觀測,所以我覺得這是一個嘗試XGBoost的好機會。為了節(jié)約篇幅,我這里不會披露超參數(shù)調(diào)整的細(xì)節(jié)。我主要使用的方法是每次交叉驗證一到兩個參數(shù),以免給我的機器太大的負(fù)擔(dān),同時在調(diào)整會話的間隔重新計算n_estimators的最優(yōu)值。
下面是我實現(xiàn)的最終模型。它的得分是0.12278,事實上這比LASSO模型要差。
regr = xgb.XGBRegressor(
colsample_bytree=0.3,
gamma=0.0,
learning_rate=0.01,
max_depth=4,
min_child_weight=1.5,
n_estimators=1668,
reg_alpha=1,
reg_lambda=0.6,
subsample=0.2,
seed=42,
silent=1)
regr.fit(train, y)
y_pred_xgb = regr.predict(test)
融合模型結(jié)果
最后我們需要組合兩個模型的結(jié)果。我對兩個模型的預(yù)測取了加權(quán)平均。最終的得分是0.11765,明顯比兩個模型單獨預(yù)測的結(jié)果要好。這確認(rèn)了集成學(xué)習(xí)的首要原則,假定誤差率互不相關(guān),集成的誤差率低于單個模型。
predictions = np.expm1(.6*lasso_pred + .4*y_pred_xgb)
之前在特征工程時使用了log1p,所以現(xiàn)在用expm1還原原數(shù)值。注意這里給LASSO更大的權(quán)重(0.6),并不是因為在測試數(shù)據(jù)上LASSO的表現(xiàn)優(yōu)于XGBoost,而是因為在訓(xùn)練數(shù)據(jù)上LASSO的表現(xiàn)優(yōu)于XGBoost(因為建模的時候不能“偷看”測試數(shù)據(jù))。
結(jié)語
這項競賽是一個練習(xí)標(biāo)準(zhǔn)回歸技術(shù)的好機會。我只進(jìn)行了最少的特征工程就取得了前10%的排名。
除了上面的模型,我也嘗試了SelectKBest(搭配Pipeline和網(wǎng)格搜索),將列數(shù)縮減至138,并得到了0.13215的分?jǐn)?shù)。然而,將其與其他模型融合時,效果不佳。后來我又試了隨機森林回歸,得分是0.14377,這不算差,但要在我們的集成中加入這個模型,這個分?jǐn)?shù)顯然還不夠高。
-
Google
+關(guān)注
關(guān)注
5文章
1754瀏覽量
57380 -
數(shù)據(jù)集
+關(guān)注
關(guān)注
4文章
1200瀏覽量
24621
原文標(biāo)題:LASSO回歸與XGBoost:融合模型預(yù)測房價
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論