经过前面几章内容的学习,我们已经了解了机器学习中的多种分类和回归模型。现在有一个问题,这么多模型究竟哪一个最好呢?以分类任务为例,当拿到一个实际的数据集时,如果是你,你会选择哪种模型进行建模呢?最笨的方法就是挨个都试一下,这样做有没有道理呢?还别说,在实际的情况中真的可能会都去试一下。但是另外一种更常见的做法就是采用集成学习的思想来进行建模,也就是本章中将要介绍的内容。

整个第9章的学习路线如图9-1所示,首先我们将介绍集成学习的基本思想和并以随机森林为例来介绍scikit-learn建模过程,然后介绍集成学习中应用最为广泛的随机森林算法及其对应的特征重要评估方法,最后再来介绍其它几种常见的集成学习算法。

图 9-1 集成学习算法学习路线图

9.1 集成学习算法#

9.1.1 集成学习思想#

假如现在选择A、B、C这3个模型进行建模,最后得到结果是: A的分类准确率为0.93,B的分类准确率为0.95,C的分类准确率为0.88。那最终应该选择哪一个模型呢?是模型B吗?

假设现在一共有100个样本,其标签为二分类(正、负两类),3个模型的部分分类结果如表9-1所示。

表 9-1 不同模型分类结果对比表

在表9-1中的5个样本,模型A和模型C均能分类正确,而模型B不能分类正确,但如果此时将这3个模型一起用于分类任务的预测,并且对于每个样本的最终输出结果采用基于投票的规则在3个模型的输出结果中进行选择。例如表9-1中的第1个样本,模型A和模型C均判定为“负类”只有模型B判定为“正类”,则最后的输出便为“负类”。那么此时,我们就可以得到一个分类准确率为1的“混合”模型。

注意: 在其余的95个样本中,假设根据投票规则均能分类正确。

9.1.2 集成学习种类#

在机器学习中,基于这种组合思想来提高模型精度的方法被称为集成学习(Ensemble Learning)。俗话说“三个臭皮匠,赛过诸葛亮”,这句话就完美阐述了集成学习的潜在思想——通过将多个模型结合在一起来提高整体的泛化能力[1]。常见的集成模型主要包括以下3种:

1. Bagging集成学习

Bagging的核心思想为并行地训练一系列各自相互独立的同类模型,然后将各个模型的输出结果按照某种策略进行组合并输出得到最终结果。例如在分类中可采用投票策略,而在回归中可采用平均策略,在9.2节中将要介绍到了随机森林便是这一集成学习的典型应用。通常来讲,模型越容易过拟合,则越适用于Bagging集成学习方法。

2. Boosting集成学习

Boosting的核心思想为先串行地训练一系列前后依赖的同类模型,即后一个模型用来对前一个模型的输出结果进行修正,最后通过某种策略将所有的模型组合起来并输出最终的结果,在9.4节、9.5节和9.6节中将会详细介绍几种常见Boosting算法的原理及实现过程。通常来讲,模型越容易欠拟合,则越适用于Boosting集成学习方法。

3. Stacking集成学习

Stacking的核心思想为并行地训练一系列各自独立的不同类模型,然后将各个模型的输出结果作为输入来训练一个新模型(例如: 逻辑回归),并通过这个新模型来输出最终预测的结果[2]。通常来讲,Stacking集成学习也适用于欠拟合的机器学习模型。

下面,我们来大致介绍一下各类集成模型中常见的算法和使用示例。

9.1.3 Bagging集成学习#

Bagging全称为Bootstrap Aggregation,而这两个单词也分别代表了Bagging在执行过程中的两个步骤:①Bootstrap Samples; ②Aggregate Outputs。总结起来就是Bagging首先从原始数据中随机抽取多组包含若干数量样本的子训练集,并且对于各子训练集来讲再随机抽取其中若干特征维度作为模型输入;然后分别以不同的子训练集来训练并得到不同的基模型,同时将各个基模型的预测结果进行聚合得到最终的输出,即

$$ y=\frac{1}{M}\sum\limits_{m=1}^{M}{{{f}_{m}}}(x)\tag{9-1} $$

其中,$M$表示基模型的数量,$f_m(x)$表示不同的基模型。

同时,由于Bagging的策略是取所有基模型的“平均”值作为最终模型的输出结果,所以Bagging集成方法能够很好地降低模型高方差(过拟合)的情况,因此通常来讲,在使用Bagging集成方法的时候,可以尽量使每个基模型都出现过拟合的现象。下面介绍在sklearn中如何使用Bagging集成学习方法。

1. Bagging on KNN

在sklearn中,可以通过语句from sklearn.ensemble import BaggingClassifier导入Bagging集成学习方法中的分类模型。下面先来介绍BaggingClassifier类中常见的重要参数及其含义,示例代码如下:

1 def __init__(self, base_estimator=None, n_estimators=10,
2     max_samples=1.0, max_features=1.0, bootstrap=True, 
3     bootstrap_features=False, n_jobs=None): 

在上述代码中,第1行base_estimator表示所使用的基模型,例如可以是K近邻或者逻辑回归等; n_estimators表示需要同时训练多少个基模型。第2行 max_samples表示每个子训练集中最大的样本数量,其可以是整数也可以是0~1的浮点数(此时表示在总样本数中的占比); max_features表示子训练集中特征维度的数量,由于采用的是随机抽样所以不同的子训练集特征维度可能不一样; bootstrap=True表示在同一子训练集中同一样本可以重复抽样出现。第3行 bootstrap_features=False表示在同一子训练集中同一特征维度不能重复出现,如果设置为True,则在极端情况下所有的特征维度可能都一样; n_jobs表示同时要使用多个CPU核并行进行计算。

下面以KNN作为基模型通过sklearn中的BaggingClassifier类进行Bagging集成学习建模,完整代码可参见AllBooKCode/Chapter09/C01_ensemble_bagging_knn.py 文件,示例代码如下:

1 if __name__ == '__main__':
2     x_train, x_test, y_train, y_test = load_data()
3     bagging = BaggingClassifier(KNeighborsClassifier(n_neighbors=3),
4             n_estimators=5, max_samples=0.8, max_features=3,
5             bootstrap=True, bootstrap_features=False)
6     bagging.fit(x_train, y_train)
7     print(bagging.estimators_features_)
8     print(bagging.score(x_test, y_test))

在上述代码中,第3行表示使用了KNN作为基模型,并且将K值设置为3。第4行分别表示一共采用了5个KNN分类器,每个分类器在进行训练时使用原始训练集80%的样本进行训练,并且每个子训练集仅使用其中3个特征维度(一共有4个)。第5行分别表示在每个子训练集中所使用到的样本是可以重复的,但是使用到的特征维度是不能重复的。

上述代码在运行结束以后,便可以看到类似如下结果:

1 [array([1,3,0]),array([0,1,2]),array([3,1,2]),array([0,1,2]),array([1,3,0])]
2 0.9777

在上述结果中,第1行的5个数组分别表示上面的5个KNN基分类器在训练过程中所使用到的特征维度,例如[1, 3, 0]表示该模型在训练时使用的是第1、第3和第0个特征维度,其他同理。

2. Bagging on Decision Tree

正如上面所介绍的,在通过Bagging方法进行集成学习时其基模型可以是其他任意模型,所以自然而然也可以是决策树。同时,由于对决策树使用Bagging集成方法是一个较为热门的研究方向,因此它还有另外一个响亮的名字——随机森林(Random Forests)。根据Bagging的思想来看,随机森林这个名字也很贴切,一系列的树模型就变成了森林。在sklearn中,如果通过类BaggingClassifier实现Bagging集成学习,当参数base_estimator=None时,默认会采用决策树作为基模型。由于这部分的内容较多并且应用也比较广泛,所以详细内容将会单独放在9.2节中进行介绍。

9.1.4 Boosting集成学习#

Boosting同Bagging一样,都用于提高模型的泛化能力。不同的是Boosting方法是通过串行地训练一系列模型来达到这一目的。在Boosting集成学习中,每个基模型都会对前一个基模型的输出结果进行改善。如果前一个基模型对某些样本进行了错误分类,则后一个基模型就会针对这些错误的结果进行修正。这样在经过一系列串行基模型的拟合后,最终就会得到一个更加准确的结果,因此, Boosting集成学习方法经常被用于改善模型高偏差的情况(欠拟合现象)。

在基于Boosting策略的集成学习中应用最为广泛的主要有自适应提升算法(Adaptive Boost, Adaboost)和梯度提升算法(Gradient Boost, GB)这两种。AdaBoost算法的核心思想是它会为每个样本赋予一个权重值,在上一个模型中被错分的样本在下一个模型中将具有更高的权重,以此来尽可能保证每个样本都能够被划分正确,最后再将每个模型的预测结果集成起来作为最终的预测输出。GB算法的核心思想在于它会先通过设定一个目标函数,然后再通过梯度下降算法来对预测结果进行优化。

同时,值得一提的是不管是自适应提升算法还是梯度提升算法,两者本质上都可以看做是一种模型的训练策略,因此,理论上可以将这两种策略运用到现在已有的任何机器学习模型中。例如对于自适应提升算法来说,在sklearn中可以通过语句from sklearn.ensemble import AdaboostClassifier来导入对应的分类模型,并且可以通过AdaboostClassifier中的base_estimator来指定所使用的基模型,如果设置为None,则默认使用决策树。关于这两种策略的详细介绍将会单独放到9.4节和9.6节中专门进行介绍。

9.1.5 Stacking集成学习#

不同于Bagging和Boosting这两种集成学习方法,Stacking集成学习方法首先通过训练得到多个基于不同算法的基模型,然后再通过训练一个新模型来对其它模型的输出结果进行组合。例如选择以逻辑回归和KNN作为基模型,以决策树作为组合模型。Stacking集成方法的做法为首先将训练得到前两个基模型,然后以基模型的输出作为决策树的输入训练模型,最后以决策树的输出作为真正的预测结果。

在sklearn中,可以通过语句from sklearn.ensemble import StackingClassifier导入StackingClassifier集成学习方法中的分类模型。下面先介绍一下StackingClassifier类中常见的重要参数及其含义,代码如下:

def __init__(self, estimators, final_estimator=None, passthrough=False)

在上述代码中,estimators表示所使用的基模型;final_estimator表示最后使用的组合模型; 当passthrough=False时,表示在训练最后的组合模型时只将各个基模型的输出作为输入; 当passthrough=True时,表示同时将原始样本也作为输入。

下面以逻辑回归、K近邻作为基模型,以决策树作为组合模型,并通过sklearn中的StackingClassifier类进行Stacking集成学习建模,完整示例代码可参见 AllBooKCode/Chapter09/C02_ensemble_stacking.py 文件,示例代码如下:

1 if __name__ == '__main__':
2     x_train, x_test, y_train, y_test = load_data()
3     estimators = [('logist', LogisticRegression(max_iter=500)),
4                   ('knn', KNeighborsClassifier(n_neighbors=3))]
5     stacking = StackingClassifier(estimators=estimators,
6                     final_estimator=DecisionTreeClassifier())
7     stacking.fit(x_train, y_train)
8     acc = stacking.score(x_test, y_test)
9     print("模型在测试集上的准确率为:", acc)

在上述代码中,第3~4行分别用来定义两个基模型,并进行相应的初始化。第5~6行用来定义Stacking分类器,并将组合模型指定为决策树。 至此,对于sklearn中Stacking集成学习的示例用法就介绍完了。同时,上述训练过程的网格搜索示例用法可以参见AllBooKCode/Chapter09/C03_ensemble_stacking_gridsearch.py 文件。

9.1.6 小结#

在本节中,我们首先介绍了机器学习中集成学习的基本思想,接着介绍了3种常见集成学习方法Bagging、Boosting和Stacking的基本思想,最后分别就这3种集成学习方法各自在sklearn中的示例用法进行了详细介绍。在接下来的几节内容中,我们将分别就这几个模型进行一个详细地介绍。