集成学习之stacking
前面介绍了关于回归和分类的算法,同时也讨论了一些如何将这些方法集成为强可学习算法的集成学习方式,分别是 Bagging 和 Boosting 。
本文我们继续讨论集成学习方法的最后一类 —— Stacking ,它不需要花费过多时间的调参就可以得到一个效果不错的算法,同时也比前两种方法容易理解的多, 其实际应用的部分要多过理论理解。
1 Blending集成学习算法
Stacking 可以理解为一个两层的集成,第一层含有多个基础分类器,把预测的结果(元特征)提供给第二层, 而第二层的分类器通常是逻辑回归,他把一层分类器的结果当做特征做拟合输出预测结果。
在介绍 Stacking 之前,我们先来对简化版的 Stacking 进行讨论,也叫做 Blending ,接着再对 Stacking 进行更深入的讨论。
1.1 Blending的基本思路
Blending 的基本思想是通过对不同模型的预测结果进行分析处理,从而得到更好的结果。
下面我们来详细讨论下具体的算法过程:
-
将数据划分为训练集、验证集和测试集,其中练集是为了训练模型,测试集是为了调整模型(调参),测试集则是为了检验模型的优度。 -
创建第一层的多个模型,这些模型可以使同质的也可以是异质的。 -
使用训练集拟合步骤
2
2
2 中的多个模型,然后用训练好的模型预测验证集和测试集得到相应的预测值; -
创建第二层的模型,使用验证集的预测值作为训练集训练第二层的模型; -
使用第二层训练好的模型对第二层测试集的预测值进行预测,该结果即为整个测试集的结果。
整体流程如下图所示:
1.2 Blending的详细说明
对于以上步骤,下面进行详细的说明:
-
在第
2
2
2 到第
3
3
3 步中,我们使用训练集创建了
K
K
K 个模型,如 SVM 、random forests 、XGBoost 等,作为第一层的模型。 训练好模型后将 验证集 输入模型进行预测,得到
K
K
K 组不同的输出,记作
A
1
,
.
.
.
,
A
K
A_1,...,A_K
A1?,...,AK? ,然后将 测试集 输入
K
K
K 个模型也得到
K
K
K 组输出,我们记作
B
1
,
.
.
.
,
B
K
B_1,...,B_K
B1?,...,BK? 。 -
在第
4
4
4 步中,我们使用
K
K
K 组验证集的结果
A
1
,
.
.
.
,
A
K
A_1,...,A_K
A1?,...,AK? 作为第二层模型(回归器或分类器)的特征,验证集的样本标签为因变量,训练第二层模型,得到相应的输出。 -
在第
5
5
5 步中,将测试集结果
B
1
,
.
.
.
,
B
K
B_1,...,B_K
B1?,...,BK? 放入第二层已训练好的分类器,得到测试集的预测结果。
Blending 一个最重要的优点就是实现简单粗暴,没有太多的理论的分析。
但是这个方法的缺点也是显然的,它只使用了一部分数据集作为留出集进行验证,也就是只能用上数据中的一部分,实际上这对数据来说是很奢侈浪费的。
1.3 Blending的具体案例
首先人工创建数据集:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
import seaborn as sns
from sklearn import datasets
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
data, target = make_blobs(n_samples=10000, centers=2, random_state=1, cluster_std=1.0)
X_train1, X_test, y_train1, y_test = train_test_split(data, target, test_size=0.2, random_state=1)
X_train, X_val, y_train, y_val = train_test_split(X_train1, y_train1, test_size=0.3, random_state=1)
print("The shape of training X:", X_train.shape)
print("The shape of training y:", y_train.shape)
print("The shape of test X:", X_test.shape)
print("The shape of test y:", y_test.shape)
print("The shape of validation X:", X_val.shape)
print("The shape of validation y:", y_val.shape)
各数据集规模如下所示:
The shape of training X: (5600, 2)
The shape of training y: (5600,)
The shape of test X: (2000, 2)
The shape of test y: (2000,)
The shape of validation X: (2400, 2)
The shape of validation y: (2400,)
接下来对两层的分类器进行设置,第一层采用支持向量机、随机森林和最近邻,第二层使用线性回归:
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
clfs = [SVC(probability=True), RandomForestClassifier(n_estimators=5, n_jobs=-1), KNeighborsClassifier()]
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
本案例为二分类,取分类器预测为
1
1
1 类的概率为输出结果:
val_features = np.zeros((X_val.shape[0], len(clfs)))
test_features = np.zeros((X_test.shape[0], len(clfs)))
for i, clf in enumerate(clfs):
clf.fit(X_train, y_train)
val_feature = clf.predict_proba(X_val)[:, 1]
test_feature = clf.predict_proba(X_test)[:, 1]
val_features[:, i] = val_feature
test_features[:, i] = test_feature
最后将第一层的验证集的结果作为特征输入第二层,对第二层分类器进行训练,并最终输出集成算法在测试集上的精度:
lr.fit(val_features, y_val)
from sklearn.model_selection import cross_val_score
cross_val_score(lr, test_features, y_test, cv=5)
Blending 通过
5
5
5 折交叉验证下在测试集上的性能表现如下所示:
array([1., 1., 1., 1., 1.])
2 Stacking集成学习算法
基于前面对 Blending 的讨论,可以知道 Blending 的主要缺陷在于,其在集成的过程中只会用到验证集的数据,这对数据实际上是一个很大的浪费。
通过改进 Blending 的这个缺陷,我们就可以得到表现更好的 Stacking 方法。
2.1 Stacking的基本思路
Blending 是使用分割的方式,产生一组训练集和一组验证集,因此在拟合模型时只能够利用固定的验证集数据来训练第二层模型。
如果将第二轮训练类比于对测试误差的估计,这让我们可以很自然地联想到交叉验证的方式。
类似于
K
K
K 折交叉验证,我们以此进行建模(如下图):
-
首先将所有数据集生成测试集和训练集(假如训练集为
10000
10000
10000 个,测试集为
2500
2500
2500 个),使用训练集中的
8000
8000
8000 条作为训练集,剩余
2000
2000
2000 条作为验证集,进行
5
5
5 折交叉验证。 -
每次验证相当于使用了蓝色的
8000
8000
8000 条数据训练出一个模型,使用模型对验证集进行验证得到
2000
2000
2000 条数据,并对测试集进行预测,得到
2500
2500
2500 条数据,这样经过
5
5
5 次交叉检验,可以得到中间的橙色的
5
?
2000
5*2000
5?2000 条验证集的结果(相当于每条数据的预测结果),
5
?
2500
5* 2500
5?2500 条测试集的预测结果。 -
接下来会将验证集的
5
?
2000
5*2000
5?2000 条预测结果拼接成
10000
10000
10000 行长的矩阵,标记为
A
1
A_1
A1? ,而对于
5
?
2500
5*2500
5?2500 行的测试集的预测结果进行加权平均,得到一个
2500
2500
2500 列的矩阵,标记为
B
1
B_1
B1? 。 -
上面得到一个基模型在数据集上的预测结果
A
1
A_1
A1? 、
B
1
B_1
B1? ,这样当我们对
3
3
3 个基模型进行集成的话,相于得到了
A
1
A_1
A1? 、
A
2
A_2
A2? 、
A
3
A_3
A3? 、
B
1
B_1
B1? 、
B
2
B_2
B2? 、
B
3
B_3
B3? 六个矩阵。 -
之后我们会将
A
1
A_1
A1? 、
A
2
A_2
A2? 、
A
3
A_3
A3? 并列在一起成
10000
10000
10000 行
3
3
3 列的矩阵作为训练数据,
B
1
B_1
B1? 、
B
2
B_2
B2? 、
B
3
B_3
B3? 合并在一起成
2500
2500
2500 行
3
3
3 列的矩阵作为测试数据,让下层学习器基于这样的数据进行再训练。 -
再训练是基于每个基础模型的预测结果作为特征(三个特征),次学习器会学习训练如果往这样的基学习的预测结果上赋予权重
w
w
w ,来使得最后的预测最为准确。
3.2 Stacking的具体案例
下面以 iris 数据集为例,通过 mlxtend 实现 Stacking 算法。
为了方便后续的可视化处理,这里仅使用花萼宽度(厘米)和花瓣长度(厘米)这两个特征的数据,第一层使用最近邻(
k
=
1
k=1
k=1 )、随机森林和朴素贝叶斯分类器,第二层使用逻辑回归:
from sklearn import datasets
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
iris = datasets.load_iris()
X, y = iris.data[:, 1:3], iris.target
RANDOM_SEED = 42
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=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))
第一层各模型和 Stacking 模型,在测试集上三折交叉验证的性能表现如下所示:
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()
|