看到网上有很多关于Blending和Stacking有什么区别的讨论,但感觉并没有说到点子上,最近在国外看了一篇比较好的博客,简洁明了地贴出了代码和讨论,所以我在这里总结一下。
直接说,其实Blending和Stacking基本上是一样的,除了有一点不同,Blending它在训练基础模型base model
的时候,并没有使用kfold
方法(Stacking使用了Kfold),而是拿了一部分数据,比如说20%的数据,这部分数据不加入基础模型的训练,而是在基础模型都训练好了以后,去预测这部分没有参与训练的数据得到预测概率,然后以各个模型的预测概率作为最终模型的特征。
相关代码如下:
from sklearn.model_selection import train_test_split
class BlendingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
def __init__(self, base_models, meta_model, holdout_pct=0.2, use_features_in_secondary=False):
self.base_models = base_models
self.meta_model = meta_model
self.holdout_pct = holdout_pct
self.use_features_in_secondary = use_features_in_secondary
def fit(self, X, y):
self.base_models_ = [clone(x) for x in self.base_models]
self.meta_model_ = clone(self.meta_model)
X_train, X_holdout, y_train, y_holdout = train_test_split(X, y, test_size=self.holdout_pct)
holdout_predictions = np.zeros((X_holdout.shape[0], len(self.base_models)))
for i, model in enumerate(self.base_models_):
model.fit(X_train, y_train)
y_pred = model.predict(X_holdout)
holdout_predictions[:, i] = y_pred
if self.use_features_in_secondary:
self.meta_model_.fit(np.hstack((X_holdout, holdout_predictions)), y_holdout)
else:
self.meta_model_.fit(holdout_predictions, y_holdout)
return self
def predict(self, X):
meta_features = np.column_stack([
model.predict(X) for model in self.base_models_
])
if self.use_features_in_secondary:
return self.meta_model_.predict(np.hstack((X, meta_features)))
else:
return self.meta_model_.predict(meta_features)
Blending
的好处就是训练时间缩短,这个比较好理解,毕竟拿了一部分数据出来做holdout,在前面训练基模型的时候就只用了较少的数据,在后面训练meta模型的时候holdout数据量又不大,自然总体上时间要加快。但坏处也比较明显,主要体现在holdout数据量少这个问题上。一个是前面在训练基模型的时候用的数据量比stacking少,第二个是在训练meta模型的时候holdout数据量少,可能会造成meta模型对于holdout数据的过拟合,第三个是因为holdout数据和训练数据不一样,自然会比使用kfold的stacking方式要精度更低。
而stacking
相当于是按照n折对数据迭代划分,每次划分都有n-1份数据作为训练集,1份数据作为预测后的结果,更新到特征当中。经过n次后,那么就会产生最终的预测分数,当然有很多数据都是重复使用的,这样的好处就是数据量充足,能够充分利用数据,精度也会更高,坏处就是可能会造成信息泄露的问题,因为在kfold当中除了第一轮以外都是拿着用来训练好的模型去预测之前用来训练这个模型的数据,会有这个风险。
第一轮迭代
第二轮迭代
代码如下:
class StackingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
def __init__(self, base_models, meta_model, n_folds=5, use_features_in_secondary=False):
self.base_models = base_models
self.meta_model = meta_model
self.n_folds = n_folds
self.use_features_in_secondary = use_features_in_secondary
def fit(self, X, y):
"""Fit all the models on the given dataset"""
self.base_models_ = [list() for x in self.base_models]
self.meta_model_ = clone(self.meta_model)
kfold = KFold(n_splits=self.n_folds, shuffle=True, random_state=42)
# Train cloned base models and create out-of-fold predictions
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):
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
if self.use_features_in_secondary:
self.meta_model_.fit(np.hstack((X, out_of_fold_predictions)), y)
else:
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_ ])
if self.use_features_in_secondary:
return self.meta_model_.predict(np.hstack((X, meta_features)))
else:
return self.meta_model_.predict(meta_features)