之前的工作我们完成了数据的读取、初步的分析、缺失值的处理、特征的变换和提取,接下啦我们将使用不同的基本回归模型先对数据集进行训练,然后再集成多种回归模型共同对房价进行综合预测并判断集成法的有效性。
1、建立模型
导入算法包
from sklearn.linear_model import ElasticNet,Lasso#弹性网络和lasso回归
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.kernel_ridge import KernelRidge#核岭回归
from sklearn.pipeline import make_pipeline
from sklearn.base import BaseEstimator, TransformerMixin, RegressorMixin, clone
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from sklearn.metrics import mean_squared_error
import xgboost as xgb
import lightgbm as lgb
#%%
y_train = train.SalePrice.values
train = all_data[:train.shape[0]]
test = all_data[train.shape[0]:]
评价函数
先定义一个评价函数,我们采用5折交叉验证,为与比赛的评价标准一致,我们用RMSE来为每个模型打分。我们使用sklearn中的cross_val_score()函数,但是这个函数没有shuffle属性,我们添加一行代码,以便在交叉验证之前对数据集进行shuffle。shuffle=True表示对数据打乱,重新洗牌,这样就能设置随机种子,但是注意,对于时间序列数据,不能打乱
n_folds = 5
def rmsle_cv(model):
kf = KFold(n_splits=5,shuffle=True,random_state=42).get_n_splits(train.values)
print("kf={}".format(kf))
rmse = np.sqrt(-cross_val_score(model,train.values,y_train,scoring='neg_mean_squared_error',cv=kf))
return rmse
sklearn中的参数scoring下,均方误差作为评判 标准时,却是计算”负均方误差“(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考 虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss)。在sklearn当中,所有 的损失都使用负数表示,因此均方误差也被显示为负数了.
基本回归模型
基本模型
- LASSO回归
lasso回归就是在传统的线性回归目标函数后面加上了一个L1范数,也称为L1正则项,这样就能使得不重要特征的系数压缩至0,从而实现较为准确的参数估计效果,但是该模型可能对异常值非常敏感,由于数据集中依然存在一定的离群点,可能会影响特征的平均值和方差,影响标准化结果。在此种情况下,使用中位数和四分位数间距进行缩放会更有效。所以我们sklearn中的RobustScaler对数据进行标准化处理
RobustScaler
传统的StandScaler是将该特征的每一个样本减去该列的均值,再除以该列的方差。但数据包含许多异常值是,使用均值和方差进行缩放可能并不是一个好的选择,在此种情况下,我们可以使用RobustScaler对中位数和四分位数间距进行缩放会更有效,结果更加具有鲁棒性
IQR:四分位距,第三四分位数(0.75)与第一四分位数(0.25)之间的距离
from sklearn.preprocessing import RobustScaler
lasso = make_pipeline(
RobustScaler(),
Lasso(alpha=0.0005,random_state=1)
)
- 核岭回归
还有一种压缩估计的方法,他通过在目标函数后加上一个L2正则项,同样达到防止过拟合的作用。核岭回归是先对数据作核变换以适应各种非线性情形,然后再对数据作岭回归的一种模型
KRR = KernelRidge(alpha=0.6,kernel='polynomial',degree=2,coef0=2.5)
- ElasticNet Regression(弹性网络)
当多个特征存在相关时,Lasso回归可能只会随机选择其中一个,岭回归则会选择所有的特征。这时很容易的想到如果将这两种正则化的方法结合起来,就能够集合两种方法的优势,这种正则化后的算法就被称为弹性网络回归。
ENet = make_pipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=.9, random_state=3))
- Gradient Boosting Regression(梯度提升树GBRT)
梯度提升回归树(GBRT)算法是以CART回归树为基学习器的集成学习模型,它与上期提及到的随机森林模型有着很大的区别。
GBRT是采用boosing方法,而RF采用的是baggging方法。
RF上期说过,它是从原始训练样本集中有放回地重复随机抽取n个样本生成新样本集合训练多棵决策树,然后分类结果按分类树投票的大多数结果来定的。
GBRT通过迭代多棵回归树生成多个弱模型,然后将每个弱模型的预测结果相加,通过不断减小损失函数的值来调整模型。
与其他集成学习算法相比,梯度提升回归树对参数设置更为敏感,正确设置模型参数会提高GBRT模型的预测精度。
GBoost = GradientBoostingRegressor(n_estimators=3000, learning_rate=0.05,
max_depth=4, max_features='sqrt',
min_samples_leaf=15, min_samples_split=10,
loss='huber', random_state=5) # 设置hue loss使其对异常值具有鲁棒性
- XGBoost(极致梯度回归)
xgb是基于GBRT的一种改进算法,通过引入模型复杂度重新构造目标函数,在迭代每一棵树的过程中都力求最小化,同时最小化了模型的损失函数即损失率和模型的复杂度,从而来提高算法的运算效率
model_xgb = xgb.XGBRegressor(colsample_bytree=0.5, gamma=0.05,
learning_rate=0.05, max_depth=3,
min_child_weight=1.8, n_estimators=2200,
reg_alpha=0.5, reg_lambda=0.8,
subsample=0.5, random_state=7, nthread=-1)
- LightGBM
LightGBM也是基于GBRT算法的一种优化改进, 其改进之一是采用了基于直方图的决策层树算法,对每个特征的取值做分段函数,将所有样本在该特征上的取值划分到某一段中,最终把特征取值离散化;改进之二是采用Leaf-wise的生长策略,使增益高的节点优先生长,从而减少低增益点浪费计算资源的问题,提高计算效率。
model_lgb = lgb.LGBMRegressor(objective='regression', num_leaves=5,
learning_rate=0.05, n_estimators=720,
max_bin=55, bagging_fraction=0.8,
bagging_freq=5, feature_fraction=0.2,
feature_fraction_seed=9, bagging_seed=9,
min_data_in_leaf=6, min_sum_hessian_in_leaf=11, verbose=-1)
基本模型效果评价
我们通过评估交叉验证RMSE的均值和方差来看看这些基础模型的表现
models = {
"Lasso":lasso,
"ElasticNet":ENet,
"Kernel Ridge":KRR,
"Gradient Boosting":GBoost,
"XGBoost":model_xgb,
"LGBoost":model_lgb
}
for model_name,model in models.items():
score = rmsle_cv(model)
print("{}: {:.4f} ({:.4f})\n".format(model_name,score.mean(),score.std()))
kf=5
Lasso: 0.1114 (0.0073)
kf=5
ElasticNet: 0.1115 (0.0074)
kf=5
Kernel Ridge: 0.1175 (0.0081)
kf=5
Gradient Boosting: 0.1168 (0.0081)
kf=5
XGBoost: 0.1172 (0.0060)
kf=5
LGBoost: 0.1161 (0.0069)
可以看出,在基本回归模型中,Lasso回归的RMSE最小,方差不是最小,但总体来说表现是最好的。下面我们采用集成中的Stacking方法对几个基本回归模型进行集成学习。
堆叠方法(Stacking Models)
集成学习往往能进一步提高模型的准确性,Stacking是其中一种效果颇好的方法,简单来说就是学习各个基本模型的预测值来预测最终的结果。
#定义一个新类,声明stacking方法
class StackingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
def __init__(self, base_models, meta_model, n_folds=5):
self.base_models = base_models # 第一层模型
self.meta_model = meta_model # 第二层模型
self.n_folds = n_folds
# 运用克隆的基本模型拟合数据
def fit(self, X, y):
self.base_models_ = [list() for x in self.base_models]
self.meta_model_ = clone(self.meta_model)#sklearn的clone函数属于深层拷贝
kfold = KFold(n_splits=self.n_folds, shuffle=True, random_state=156)
# 训练克隆的第一层模型
out_of_fold_predictions = np.zeros((X.shape[0], len(self.base_models)))#接收每个模型在每轮迭代中的预测结果
for i, model in enumerate(self.base_models):
for train_index, holdout_index in kfold.split(X, y):
#kfold.split将训练集和测试集进行切分,切分出来的索引有n_splits轮,每一轮有两组,一组为测试一组为训练。
instance = clone(model)
self.base_models_[i].append(instance)#上面将每个第一层模型转化为一个列表,这个列表会接收每一轮迭代的新版本
instance.fit(X[train_index], y[train_index])
y_pred = instance.predict(X[holdout_index])
out_of_fold_predictions[holdout_index, i] = y_pred
# 使用交叉验证预测的结果作为新特征,来训练克隆的第二层模型
self.meta_model_.fit(out_of_fold_predictions, y)
return self
# 在测试数据上做所有基础模型的预测,并使用平均预测作为由元模型完成的最终预测的元特征
def predict(self, X):
meta_features = np.column_stack(
[
np.column_stack([model.predict(X) for model in base_models]).mean(axis=1)
for base_models in self.base_models_
]
)
return self.meta_model_.predict(meta_features)
这里我们用ENet、KRR和GBoost作为第一层学习器,用Lasso作为第二层学习器。查看以下Stacking的交叉验证评分:
stacked_averaged_models = StackingAveragedModels(base_models=(ENet, GBoost, KRR), meta_model=lasso)
score = rmsle_cv(stacked_averaged_models)
print("Stacking Averaged models score: {:.4f} ({:.4f})".format(score.mean(), score.std()))
kf=5
Stacking Averaged models score: 0.1083 (0.0074)
建立最终模型
集成StackedRegressor, XGBoost和 LightGBM
我们将XGBoost、LightGBM和StackedRegressor以加权平均的方式融合在一起,建立最终的预测模型
- 首先,定义一个评价函数,表示预测值和实际值之间的均方根误差 RMSE
def rmsle(y, y_pred):
return np.sqrt(mean_squared_error(y, y_pred))
- 其次,用整个训练集训练模型,预测测试集的房价,并给出模型在训练集上的评分
- StackedRegressor:
stacked_averaged_models.fit(train.values, y_train)
stacked_train_pred = stacked_averaged_models.predict(train.values)
stacked_pred = np.expm1(stacked_averaged_models.predict(test.values))
print(rmsle(y_train, stacked_train_pred))
0.07631937112031571
- XGBoost:
model_xgb.fit(train, y_train)
xgb_train_pred = model_xgb.predict(train)
xgb_pred = np.expm1(model_xgb.predict(test))
print(rmsle(y_train, xgb_train_pred))
0.07971210954256541
- LGB:
model_lgb.fit(train, y_train)
lgb_train_pred = model_lgb.predict(train)
lgb_pred = np.expm1(model_lgb.predict(test.values))
print(rmsle(y_train, lgb_train_pred))
0.07158286128042535
之后我们以0.7,、0.15、0.15的权重来加权三个集成模型,最终得到了本次任务的RMSE
print('集成模型的得分:{}'.format(rmsle(y_train, stacked_train_pred*0.70 + xgb_train_pred*0.15 + lgb_train_pred*0.15)))
集成模型的得分:0.07411433251894477