【集成学习】:Stacking原理以及Python代码实现

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

【集成学习】:Stacking原理以及Python代码实现
Stacking集成学习在各类机器学习竞赛当中得到了⼴泛的应⽤,尤其是在结构化的机器学习竞赛当中表现⾮常好。

今天我们就来介绍下stacking这个在机器学习模型融合当中的⼤杀器的原理。

并在博⽂的后⾯附有相关代码实现。

总体来说,stacking集成算法主要是⼀种基于“标签”的学习,有以下的特点:
⽤法:模型利⽤交叉验证,对训练集进⾏预测,从⽽实现⼆次学习
优点:可以结合不同的模型
缺点:增加了时间开销,容易造成过拟合
关键点:模型如何进⾏交叉训练?
下⾯我们来看看stacking的具体原理是如何进⾏实现的,stacking⼜是如何实现交叉验证和训练的。

⼀.使⽤单个机器学习模型进⾏stacking
第⼀种stacking的⽅法,为了⽅便⼤家理解,因此只使⽤⼀个机器学习模型进⾏stacking。

stacking的⽅式如下图所⽰:
我们可以边看图边理解staking过程。

我们先看training data,也就是训练数据(训练集)。

我们使⽤同⼀个机器学习model对训练数据进⾏了5折交叉验证(当然在实际进⾏stacking的时候,你也可以使⽤k折交叉验证,k等于多少都可以,看你⾃⼰,⼀般k=5或者k=10,k=5就是五折交叉验证),也就是每次训练的时候,将训练数据拆分成5份。

其中四份⽤于训练我们的模型,另外剩下的⼀份将训练好的模型,在这部分上对数据进⾏预测predict出相应的label。

由于使⽤了5折交叉验证,这样不断训练,⼀共经过5次交叉验证,我们就产⽣了模型对当前训练数据进⾏预测的⼀列label。

⽽这些label也可以当作我们的新的训练特征,将预测出来的label作为⼀列新的特征插⼊到我们的训练数据当中。

插⼊完成后,将我们新的tranning set再次进⾏训练,得到我们新,也是最后的model,也就是图中的model6。

这样就完成了第⼀次stacking,对于testing set⽽⾔,我们使⽤model 1/2/3/4/5分别对图中下⾯所对应的testing data进⾏预测,然后得到testing set的新的⼀列feature,或者称之为label。

这样,我们就完成了⼀次stacking的过程。

然⽽,我们不仅可以stacking仅仅⼀次,我们还可以对当前训练出来的模型再次进⾏stacking,也就是重复刚才的过程,这样我们的训练集⼜会多出⼀个新的⼀列,每stacking⼀次,数据就会多出⼀列。

但是如果仅仅⽤⼀个模型进⾏stakcing,效果往往没有那么好,因为这样我们的数据和模型并不具备多样性,也就⽆法从数学上保证我们数据的“独⽴性”,树模型⽐如bagging能够达到很好的效果,包括我们的stacking,模型的多样性越⼤,差异越⼤,集成之后往往会得到更好的收益,这个在数学上有严格的证明的,可以通过binomial distribution的⽅法来证明,⼤家了解即可。

因此这⾥介绍,如何使⽤多个机器学习模型进⾏stacking,这种⽅法在机器学习竞赛当中也更加常见。

⼆.使⽤多个机器模型进⾏stacking
使⽤多个机器学习模型进⾏stacking其实就是每使⽤⼀个模型进⾏训练,就多出⼀个训练集的feature,也就是将训练集多出⼀列。

如下图所⽰:
在这张图当中,我们使⽤了xgboost,lightgbm,random forest分别对同⼀个数据集做了训练,xgboost训练完后,trainning set多出⼀列,lightgbm训练完成后,⼜多出⼀列random forest训练完成后,⼜多出⼀列。

因此我们的traning set⼀共多出了三列。

这样我们再⽤多出这三列的数据重新使⽤新的机器学习模型进⾏训练,就可以得到⼀个更加完美的模型啦!这也就是多个不同机器学习模型进⾏stacking的原理。

同时,我们也可以对多个机器学习模型进⾏多次堆叠,也就是进⾏多次stakcing的操作。

三.stacking模型代码实现(基本实现)
由于sklearn并没有直接对Stacking的⽅法,因此我们需要下载mlxtend⼯具包(pip install mlxtend)
# 1. 简单堆叠3折CV分类,默认为3,可以使⽤cv=5改变为5折cv分类
from sklearn import datasets
iris = datasets.load_iris()
X, y = iris.data[:, 1:3], iris.target
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from mlxtend.classifier import StackingCVClassifier
RANDOM_SEED = 42
clf1 = KNeighborsClassifier(n_neighbors=1)
clf2 = RandomForestClassifier(random_state=RANDOM_SEED)
clf3 = GaussianNB()
lr = LogisticRegression()
# Starting from v0.16.0, StackingCVRegressor supports
# `random_state` to get deterministic result.
sclf = StackingCVClassifier(classifiers=[clf1, clf2, clf3], # 第⼀层分类器
meta_classifier=lr, # 第⼆层分类器,并⾮表⽰第⼆次stacking,⽽是通过logistic regression对新的训练特征数据进⾏训练,得到predicted label random_state=RANDOM_SEED)
print('3-fold cross validation:\n')
for clf, label in zip([clf1, clf2, clf3, sclf], ['KNN', 'Random Forest', 'Naive Bayes','StackingClassifier']):
scores = cross_val_score(clf, X, y, cv=3, scoring='accuracy')
print("Accuracy: %0.2f (+/- %0.2f) [%s]" % (scores.mean(), scores.std(), label))
输出:
3-fold cross validation:
Accuracy: 0.91 (+/- 0.01) [KNN]
Accuracy: 0.95 (+/- 0.01) [Random Forest]
Accuracy: 0.91 (+/- 0.02) [Naive Bayes]
Accuracy: 0.93 (+/- 0.02) [StackingClassifier]
画出决策边界:
# 我们画出决策边界
from mlxtend.plotting import plot_decision_regions
import matplotlib.gridspec as gridspec
import itertools
gs = gridspec.GridSpec(2, 2)
fig = plt.figure(figsize=(10,8))
for clf, lab, grd in zip([clf1, clf2, clf3, sclf],
['KNN',
'Random Forest',
'Naive Bayes',
'StackingCVClassifier'],
itertools.product([0, 1], repeat=2)):
clf.fit(X, y)
ax = plt.subplot(gs[grd[0], grd[1]])
fig = plot_decision_regions(X=X, y=y, clf=clf)
plt.title(lab)
plt.show()
如果想要更加清楚地指定,我们的多个机器学习学到的数据输出的概率(假设我们的label为概率),是否会被平均,按照多个模型的平均值来变成我们的训练数据的列(个⼈不太推荐这么⼲,因为⼀旦平均之后,其实就缺失了⼀些模型差异性的信息和特征,这样在某些情况下反⽽得不太好的结果)。

因此我们使⽤以下的代码:
# 使⽤概率作为元特征
clf1 = KNeighborsClassifier(n_neighbors=1)
clf2 = RandomForestClassifier(random_state=1)
clf3 = GaussianNB()
lr = LogisticRegression()
sclf = StackingCVClassifier(classifiers=[clf1, clf2, clf3],
use_probas=True,
average_probas = False,
meta_classifier=lr,
random_state=42)
#average_probas = False 表⽰不对概率进⾏平均
print('3-fold cross validation:\n')
for clf, label in zip([clf1, clf2, clf3, sclf],
['KNN',
'Random Forest',
'Naive Bayes',
'StackingClassifier']):
scores = cross_val_score(clf, X, y,
cv=3, scoring='accuracy')
print("Accuracy: %0.2f (+/- %0.2f) [%s]"
% (scores.mean(), scores.std(), label))
输出:
3-fold cross validation:
Accuracy: 0.91 (+/- 0.01) [KNN]
Accuracy: 0.95 (+/- 0.01) [Random Forest]
Accuracy: 0.91 (+/- 0.02) [Naive Bayes]
Accuracy: 0.95 (+/- 0.02) [StackingClassifier]
四.Stacking配合grid search⽹格搜索实现超参数调整
# 3. 堆叠5折CV分类与⽹格搜索(结合⽹格搜索调参优化)
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from mlxtend.classifier import StackingCVClassifier
# Initializing models
clf1 = KNeighborsClassifier(n_neighbors=1)
clf2 = RandomForestClassifier(random_state=RANDOM_SEED)
clf3 = GaussianNB()
lr = LogisticRegression()
sclf = StackingCVClassifier(classifiers=[clf1, clf2, clf3],
meta_classifier=lr,
random_state=42)
params = {'kneighborsclassifier__n_neighbors': [1, 5],
'randomforestclassifier__n_estimators': [10, 50],
'meta_classifier__C': [0.1, 10.0]}
grid = GridSearchCV(estimator=sclf,
param_grid=params,
cv=5,
refit=True)
grid.fit(X, y)
cv_keys = ('mean_test_score', 'std_test_score', 'params')
for r, _ in enumerate(grid.cv_results_['mean_test_score']):
print("%0.3f +/- %0.2f %r"
% (grid.cv_results_[cv_keys[0]][r],
grid.cv_results_[cv_keys[1]][r] / 2.0,
grid.cv_results_[cv_keys[2]][r]))
print('Best parameters: %s' % grid.best_params_)
print('Accuracy: %.2f' % grid.best_score_)
输出:
0.947 +/- 0.03 {'kneighborsclassifier__n_neighbors': 1, 'meta_classifier__C': 0.1, 'randomforestclassifier__n_estimators': 10} 0.933 +/- 0.02 {'kneighborsclassifier__n_neighbors': 1, 'meta_classifier__C': 0.1, 'randomforestclassifier__n_estimators': 50} 0.940 +/- 0.02 {'kneighborsclassifier__n_neighbors': 1, 'meta_classifier__C': 10.0, 'randomforestclassifier__n_estimators': 10} 0.940 +/- 0.02 {'kneighborsclassifier__n_neighbors': 1, 'meta_classifier__C': 10.0, 'randomforestclassifier__n_estimators': 50} 0.953 +/- 0.02 {'kneighborsclassifier__n_neighbors': 5, 'meta_classifier__C': 0.1, 'randomforestclassifier__n_estimators': 10} 0.953 +/- 0.02 {'kneighborsclassifier__n_neighbors': 5, 'meta_classifier__C': 0.1, 'randomforestclassifier__n_estimators': 50} 0.953 +/- 0.02 {'kneighborsclassifier__n_neighbors': 5, 'meta_classifier__C': 10.0, 'randomforestclassifier__n_estimators': 10} 0.953 +/- 0.02 {'kneighborsclassifier__n_neighbors': 5, 'meta_classifier__C': 10.0, 'randomforestclassifier__n_estimators': 50} Best parameters: {'kneighborsclassifier__n_neighbors': 5, 'meta_classifier__C': 0.1, 'randomforestclassifier__n_estimators': 10} Accuracy: 0.95
得到最后最好的参数如上。

当然如果,你想要更好的搜索参数,可以使⽤贝叶斯搜索参数,或者随机参数搜索法,对参数进⾏搜索。

结语:在这篇博⽂当中,我论述了stacking的原理和python的代码实现,希望⼤家读完后会有收获。

如果觉得看了有收获的同学,请在下⽅点个赞,推荐⼀下,谢谢啦!
参考⽂献:。

相关文档
最新文档