Boosting的意思就是提升,这是一种通过训练弱学习模型的“肌肉”将其提升为强学习模型的算法。要想在机器学习竞赛中追求卓越,Boosting是一种必需的存在。这是一个属于“高手”的技术,我们当然也应该掌握。

Boosting:把模型训练得更强

Boosting的基本思路是逐步优化模型。这与Bagging不同。Bagging是独立地生成很多不同的模型并对预测结果进行集成。Boosting则是持续地通过新模型来优化同一个基模型,每一个新的弱模型加入进来的时候,就在原有模型的基础上整合新模型,从而形成新的基模型。而对新的基模型的训练,将一直聚集于之前模型的误差点,也就是原模型预测出错的样本(而不是像Bagging那样随机选择样本),目标是不断减小模型的预测误差。

下面的Boosting示意图展示了这样的过程:一个拟合效果很弱的模型(左上图的水平红线),通过梯度提升,逐步形成了较接近理想拟合曲线的模型(右下图的红线)。

机器学习模型Boosting示意图(请见339页彩色版插图)

梯度这个词我们已经很熟悉了。在线性回归、逻辑回归和神经网络中,梯度下降是机器得以自我优化的本源。机器学习的模型内部参数在梯度下降的过程中逐渐自我更新,直到达到最优解。

而Boosting这个模型逐渐优化,自我更新的过程特别类似于梯度下降,它是把梯度下降的思路从更新模型内部参数扩展到更新模型本身。因此,可以说Boosting就是模型通过梯度下降自我优化的过程。

像上图所示的弱分类器,经过Boosting,逐渐接近原始函数图像的态势的过程,同学们有没有感觉这是和Bagging相反的思路。刚才的Bagging非常精准地拟合每一个数据点(如很深的决策树)并逐渐找到更粗放的算法(如随机森林)以削弱对数据的过拟合,目的是降低方差。而现在的Boosting,则是把一个拟合很差的模型逐渐提升得比较好,目的是降低偏差

Boosting是如何实现自我优化的呢?有以下两个关键步骤。

(1)数据集的拆分过程—Boosting和Bagging的思路不同。Bagging是随机抽取,而Boosting是在每一轮中有针对性的改变训练数据。具体方法包括:增大在前一轮被弱分类器分错的样本的权重或被选取的概率,或者减小前一轮被弱分类器分对的样本的权重或被选取的概率。通过这样的方法确保被误分类的样本在后续训练中受到更多的关注。

(2)集成弱模型的方法—也有多种选择。可通过加法模型将弱分类器进行线性组合,比如Ada Boost的加权多数表决,即增大错误率较小的分类器的权重,同时减小错误率较大的分类器的权重。而梯度提升决策树不是直接组合弱模型,而是通过类似梯度下降的方式逐步减小损失,将每一步生成的模型叠加得到最终模型。

实战中的Boosting算法,有Ada Boost、梯度提升决策树(GBDT),以及XGBoost等。这些算法都包含了Boosting提升的思想。也就是说,每一个新模型的生成都是建立在上一个模型的基础之上,具体细节则各有不同。

9.3.1 Ada Boost算法

Ada Boost算法的特点是对不同的样本赋予不同的权重。

“咖哥,等一会儿。”小冰说,“这个Ada什么……好像有印象似的。”

咖哥说:“前面讲梯度下降优化器的时候提到过Ada Grad,就是给不同的模型内部参数分配不同的学习速率。Ada,就是adaptive,翻译过来也就是自适应。”

Ada Boost是给不同的样本分配不同的权重,被分错的样本的权重在Boosting过程中会增大,新模型会因此更加关注这些被分错的样本,反之,样本的权重会减小。然后,将修改过权重的新数据集输入下层模型进行训练,最后将每次得到的基模型组合起来,也根据其分类错误率对模型赋予权重,集成为最终的模型。

下面应用Ada Boost算法,来重新解决银行客户流失问题:

from sklearn.ensemble import Ada Boost Classifier # 导入Ada Boost模型

dt = Decision Tree Classifier() # 选择决策树分类器作为Ada Boost的基准算法

ada = Ada Boost Classifier(dt) # Ada Boost模型

# 使用网格搜索优化参数

ada_param_grid = {"base_estimator__criterion" : ["gini", "entropy"],

"base_estimator__splitter" :  ["best", "random"],

"base_estimator__random_state" :  [7, 9, 10, 12, 15],

"algorithm" : ["SAMME", "SAMME.R"],

"n_estimators" :[1, 2, 5, 10],

"learning_rate": [0.0001, 0.001, 0.01, 0.1, 0.2, 0.3, 1.5]}

ada_gs = Grid Search CV(adadt, param_grid = ada_param_grid,

scoring="f1", n_jobs= 10, verbose = 1)

ada_gs.fit(X_train, y_train)# 拟合模型

ada_gs = ada_gs.best_estimator_ # 最佳模型

y_pred = ada_gs.predict(X_test)# 进行预测

print("Ada Boost测试准确率: {:.2f}%".format(ada_gs.score(X_test, y_test)*100))

print("Ada Boost测试F1分数: {:.2f}%".format(f1_score(y_test, y_pred)*100))

我们仍然选择决策树分类器作为Ada Boost的基准算法。从结果上来看,这个问题应用Ada Boost算法求解,效果并不是很好:

Ada Boost测试准确率: 79.45%

Ada Boost测试F1分数: 51.82%

9.3.2 梯度提升算法

梯度提升(Granding Boosting)算法是梯度下降和Boosting这两种算法结合的产物。因为常见的梯度提升都是基于决策树的,有时就直接叫作GBDT,即梯度提升决策树(Granding Boosting Decision Tree)。

不同于Ada Boost只是对样本进行加权,GBDT算法中还会定义一个损失函数,并对损失和机器学习模型所形成的函数进行求导,每次生成的模型都是沿着前面模型的负梯度方向(一阶导数)进行优化,直到发现全局最优解。也就是说,GBDT的每一次迭代中,新的树所学习的内容是之前所有树的结论和损失,对其拟合得到一个当前的树,这棵新的树就相当于是之前每一棵树效果的累加。

梯度提升算法,对于回归问题,目前被认为是最优算法之一。

下面用梯度提升算法来解决银行客户流失问题:

from sklearn.ensemble import Gradient Boosting Classifier # 导入梯度提升模型

gb = Gradient Boosting Classifier() # 梯度提升模型

# 使用网格搜索优化参数

gb_param_grid = {'loss' : ["deviance"],

'n_estimators' : [100, 200, 300],

'learning_rate': [0.1, 0.05, 0.01],

'max_depth': [4, 8],

'min_samples_leaf': [100, 150],

'max_features': [0.3, 0.1]}

gb_gs = Grid Search CV(gb, param_grid = gb_param_grid,

scoring="f1", n_jobs= 10, verbose = 1)

gb_gs.fit(X_train, y_train) # 拟合模型

gb_gs = gb_gs.best_estimator_ # 最佳模型

y_pred = gb_gs.predict(X_test) # 进行预测

print("梯度提升测试准确率: {:.2f}%".format(gb_gs.score(X_test, y_test)*100))

print("梯度提升测试F1分数: {:.2f}%".format(f1_score(y_test, y_pred)*100))

输出结果显示,梯度提升算法的效果果然很好,F1分数达到60%以上:

GBDT测试准确率: 86.50%

GBDT测试F1分数: 60.18%

9.3.3 XGBoost算法

极端梯度提升(e Xtreme Gradient Boosting,XGBoost,有时候也直接叫作XGB)和GBDT类似,也会定义一个损失函数。不同于GBDT只用到一阶导数信息,XGBoost会利用泰勒展开式把损失函数展开到二阶后求导,利用了二阶导数信息,这样在训练集上的收敛会更快。

下面用XGBoost来解决银行客户流失问题:

from xgboost import XGBClassifier # 导入XGB模型

xgb = XGBClassifier() # XGB模型

# 使用网格搜索优化参数

xgb_param_grid = {'min_child_weight': [1, 5, 10],

'gamma': [0.5, 1, 1.5, 2, 5],

'subsample': [0.6, 0.8, 1.0],

'colsample_bytree': [0.6, 0.8, 1.0],

'max_depth': [3, 4, 5]}

xgb_gs = Grid Search CV(xgb, param_grid = xgb_param_grid,

scoring="f1", n_jobs= 10, verbose = 1)

xgb_gs.fit(X_train, y_train) # 拟合模型

xgb_gs = xgb_gs.best_estimator_ # 最佳模型

y_pred = xgb_gs.predict(X_test) # 进行预测

print("XGB测试准确率: {:.2f}%".format(xgb_gs.score(X_test, y_test)*100))

print("XGB测试F1分数: {:.2f}%".format(f1_score(y_test, y_pred)*100))

输出结果显示,F1分数也相当不错:

XGB测试准确率: 86.25%

XGB测试F1分数: 59.62%

[ 55 210]]

对于很多浅层的回归、分类问题,上面的这些Boosting算法目前都是很热门、很常用的。整体而言,Boosting算法都是生成一棵树后根据反馈,才开始生成另一棵树。

9.3.4 Bagging算法与Boosting算法的不同之处

咖哥说:“下面请各位同学从各个角度说一说Bagging算法与Boosting算法的不同之处,这样有助于加深对这两种主要集成学习算法的理解。”

小冰先举手发言:“样本的选择不同,Bagging中从原始数据集所抽选出的各轮训练集之间是独立的;而Boosting中每一轮的训练集不变,只是样例在分类器中的权重发生变化,且权重会根据上一轮的分类结果调整。”

同学甲回忆了一下,也发言:“样例的权重不同,Bagging中每个样例的权重相等;而Boosting中的根据错误率不断调整样例的权重,错误率越大则权重越大。”

同学乙受到同学甲的启发,说道:“模型的权重不同,Bagging中所有预测模型的权重相等,而Boosting的Ada Boost算法中每个模型都有相应的权重,对于误差小的模型权重更大。”

同学丙经过深思,说:“Bagging是削弱过于精准的基模型,避免过拟合;Boosting是提升比较弱的基模型,可提高精度。”

还剩下一个同学未发言,大家都把头转过去看他。他愁眉苦脸,努力思考了一下,说:“我感觉模型生成过程中,Bagging中的各个模型是同时(并行)生成的 ,而Boosting中的各个模型只能顺序生成,因为后一个模型的参数需要根据前一个模型的结果进行调整。”

咖哥总结:“大家讲得都不错,尤其是同学丙,它的答案抓住了两者的本质。最后记住Bagging是降低方差,利用基模型的独立性;而Boosting是降低偏差,基于同一个基模型,通过增加被错分的样本的权重和梯度下降来提升模型性能。”

下面休息10分钟吧,之后继续介绍另外一些集成学习算法。