Stacking是用新的模型(次学习器)去学习怎么组合那些基学习器,它的思想源自于Stacked Generalization(http://www.machine-learning.martinsewell.com/ensembles/stacking/Wolpert1992.pdf)这篇论文。如果把Bagging看作是多个基分类器的线性组合,那么Stacking就是多个基分类器的非线性组合。Stacking可以很灵活,它可以将学习器一层一层地堆砌起来,形成一个网状的结构,如下图:
![30414d8c81e9793ad75c150d6eca2d92.png](https://i-blog.csdnimg.cn/blog_migrate/97deade34c6695af2483a41004adfd9d.jpeg)
举个更直观的例子,还是那两道加法题:
![75294360cc0b6c004e599cd67b29f81d.png](https://i-blog.csdnimg.cn/blog_migrate/b8d13969d320de9ee10603e7dec984c9.jpeg)
这里A和B可以看作是基学习器,C、D、E都是次学习器。
· Stage1: A和B各自写出了答案。
· Stage2: C和D偷看了A和B的答案,C认为A和B一样聪明,D认为A比B聪明一点。他们各自结合了A和B的答案后,给出了自己的答案。
Stage3: E偷看了C和D的答案,E认为D比C聪明,随后E也给出自己的答案作为最终答案。
在实现Stacking时,要注意的一点是,避免标签泄漏(Label Leak)。在训练次学习器时,需要上一层学习器对Train Data的测试结果作为特征。如果我们在Train Data上训练,然后在Train Data上预测,就会造成Label Leak。为了避免Label Leak,需要对每个学习器使用K-fold,将K个模型对Valid Set的预测结果拼起来,作为下一层学习器的输入。如下图:
![a3bfc02c8cda4ce74534c422eef00c2d.png](https://i-blog.csdnimg.cn/blog_migrate/cdd9e1ff89980d93a8c61850010ed93c.jpeg)
由图可知,我们还需要对Test Data做预测。这里有两种选择,可以将K个模型对Test Data的预测结果求平均,也可以用所有的Train Data重新训练一个新模型来预测Test Data。所以在实现过程中,我们最好把每个学习器对Train Data和对Test Data的测试结果都保存下来,方便训练和预测。
对于Stacking还要注意一点,固定K-fold可以尽量避免Valid Set过拟合,也就是全局共用一份K-fold,如果是团队合作,组员之间也是共用一份K-fold。如果想具体了解为什么需要固定K-fold,
举个例子,假设训练数据一共有x1, x2, x3, x4, x5, x6这6个,并且使用3-fold,在Stage1的时候使用两个Model。
1. 使用固定的k-fold的情况如下图:
![4a789efbd3b02a60671953ec7cac152f.png](https://i-blog.csdnimg.cn/blog_migrate/92655ff1947e3852bb8056975fd213d2.jpeg)
先看Stage1:可以看到m1-m6和n1-n2是相同的k-fold预测出来的结果,m1,m2由x3,x4,x5,x6预测所得,所以m1,m2包含了x3,x4,x5,x6的信息,以此类推。
Stage2:假设我要用如图中所示的Train Set来预测Valid Set,那么Train Set包含了x1-x6的信息,而Valid Set包含了x3-x6的信息。
看到这里你会疑惑,这有什么用呢?别急,再来看看Stage1的两个Model各自用不同的k-fold:
![c8e26a82e2b525df87d06f69b04692a9.png](https://i-blog.csdnimg.cn/blog_migrate/c15f695084a07e0308d860ef72447dcb.jpeg)
请仔细观察Model2的k-fold,现在n1,n6包含了x2-x5的信息,以此类推。
关键在于Stage2,现在Train Set也是包含了x1-x6的信息,而Valid Set也包含了x1-x6的信息,这就是不固定kfold与固定kfold的区别。尽管从固定kfold的图看来,它也有可能出现一定程度的过拟合,但不固定kfold它对Valid Set的过拟合情况会更加严重,所以按照Nutastray说的,通过kfold可以避免人为造成的过拟合。
可能你会问,那如果我们同一层的Stage固定k-fold,而不同层之间不固定,会发生什么?答案是,情况也会比固定k-fold要糟糕,具体的话可以按照上图画一下。所以最终给出的建议是,做Stacking最好还是固定k-fold,如果是团队合作完成项目,那就组员之间共享一份k-fold。
下面给出我的代码:bb=data_train.iloc[:, 4:6749]
#dd=data_train.iloc[:, 3:4]
cc = bb.apply(lambda x: x.fillna(x.mean()), axis=0)
x_data = preprocessing.minmax_scale(cc.iloc[:, :].values, feature_range=(-1,1))
cc['tag'] = data_train.iloc[:, 3:4]
test = dfp.iloc[:, 2:6747].apply(lambda x: x.fillna(x.mean()), axis=0)
#test_data = preprocessing.minmax_scale(test.iloc[:, :].values, feature_range=(-1,1))
Xtest = list(test.columns.values)[4:6745]
x_data_output = dfp.iloc[:, 0:1].values
#print(data_train.iloc[:, 3:4])
predictors = list(cc.columns.values)[4:6745]
alg1 = lgb.LGBMClassifier(boosting_type='gbdt', num_leaves=42, max_depth=-1, learning_rate=0.054, n_estimators=490,
subsample_for_bin=200, objective='binary', class_weight=None, min_split_gain=0.0,
min_child_weight=1, min_child_samples=21, subsample=0.72, subsample_freq=1,
colsample_bytree=0.63, reg_alpha=6.18, reg_lambda=2.718, random_state=142857, n_jobs=-1,
silent=True, importance_type='split')
alg2 = XGBClassifier(n_estimators=60,max_depth=9,min_child_weight=2,gamma=0.9,subsample=0.8,learning_rate=0.02,
colsample_bytree=0.8,objective='binary:logistic',nthread=-1,scale_pos_weight=1)
alg3 = GradientBoostingClassifier(learning_rate=0.01, n_estimators=600, max_depth=7, min_samples_leaf=60,
min_samples_split=1200, max_features=9, subsample=0.7, random_state=10)
lr = LogisticRegression()
pipe1 = make_pipeline(ColumnSelector(cols=predictors[4:2000]), lr)
pipe2 = make_pipeline(ColumnSelector(cols=predictors[2000:4000]), alg2)
pipe3 = make_pipeline(ColumnSelector(cols=predictors[4000:5000]), alg3)
sclf = StackingClassifier(classifiers=[lr, pipe2, pipe3], meta_classifier=alg1)
# Compute the accuracy score for all the cross validation folds. (much simpler than what we did before!)
# kf=cross_validation.KFold(data_train.shape[0],n_folds=10,random_state=1)
kf = model_selection.KFold(n_splits=120, shuffle=False, random_state=1)
scores = model_selection.cross_val_score(sclf, cc[predictors], cc['tag'], cv=kf)
print("scores.mean=