1. GBDT简介

Boosting、Bagging和Stacking是集成学习(Ensemble Learning)的三种主要方法。Boosting是一族可将弱学习器提升为强学习器的算法,不同于Bagging、Stacking方法,Boosting训练过程为串联方式,弱学习器的训练是有顺序的,每个弱学习器都会在前一个学习器的基础上进行学习,最终综合所有学习器的预测值产生最终的预测结果。

梯度提升(Gradient boosting)算法是一种用于回归、分类和排序任务的机器学习技术,属于Boosting算法族的一部分。之前我们介绍过Gradient Boosting算法在迭代的每一步构建一个能够沿着梯度最陡的方向降低损失的学习器来弥补已有模型的不足。经典的AdaBoost算法只能处理采用指数损失函数的二分类学习任务,而梯度提升方法通过设置不同的可微损失函数可以处理各类学习任务(多分类、回归、Ranking等),应用范围大大扩展。梯度提升算法利用损失函数的负梯度作为残差拟合的方式,如果其中的基函数采用决策树的话,就得到了梯度提升决策树 (Gradient Boosting Decision Tree, GBDT)。

基于梯度提升算法的学习器叫做GBM(Gradient Boosting Machine)。理论上,GBM可以选择各种不同的学习算法作为基学习器。现实中,用得最多的基学习器是决策树。

决策树有以下优点:

  • 决策树可以认为是if-then规则的集合,易于理解,可解释性强,预测速度快。
  • 决策树算法相比于其他的算法需要更少的特征工程,比如可以不用做特征标准化。
  • 决策树可以很好的处理字段缺失的数据。
  • 决策树能够自动组合多个特征,也有特征选择的作用。
  • 对异常点鲁棒
  • 可扩展性强,容易并行。

决策树有以下缺点:

  • 缺乏平滑性(回归预测时输出值只能输出有限的若干种数值)。
  • 不适合处理高维稀疏数据。
  • 单独使用决策树算法时容易过拟合。

我们可以通过抑制决策树的复杂性,降低单棵决策树的拟合能力,再通过梯度提升的方法集成多个决策树,最终能够很好的解决过拟合的问题。由此可见,梯度提升方法和决策树学习算法可以互相取长补短,是一对完美的搭档。

2. GBDT回归算法

2.1 GBDT回归算法推导

xgboost回归模型cv python boosting回归算法_损失函数

2.2 GBDT回归算法实例

(1)数据集介绍

训练集如下表所示,一组数据的特征有年龄和体重,身高为标签值,共有4组数据。

xgboost回归模型cv python boosting回归算法_拟合_02

测试数据如下表所示,只有一组数据,年龄为25、体重为65,我们用在训练集训练好的GBDT模型预测该组数据的身高值为多少。

xgboost回归模型cv python boosting回归算法_决策树_03

(2)模型训练阶段

参数设置:

  • 学习率:learning_rate = 0.1
  • 迭代次数:n_trees = 5
  • 树的深度:max_depth = 3

xgboost回归模型cv python boosting回归算法_损失函数_04

现将残差的计算结果列表如下:

xgboost回归模型cv python boosting回归算法_损失函数_05

xgboost回归模型cv python boosting回归算法_拟合_06

xgboost回归模型cv python boosting回归算法_拟合_07

xgboost回归模型cv python boosting回归算法_损失函数_08

xgboost回归模型cv python boosting回归算法_决策树_09

以上划分点的总平方损失最小为0.025有两个划分点:年龄21和体重60,所以随机选一个作为划分点,这里我们选年龄21。现在我们的第一棵树长这个样子:

xgboost回归模型cv python boosting回归算法_损失函数_10

我们设置的参数中树的深度max_depth=3,现在树的深度只有2,需要再进行一次划分,这次划分要对左右两个节点分别进行划分:

对于左节点,只含有0,1两个样本,根据下表结果我们选择年龄7为划分点(也可以选体重30)。

xgboost回归模型cv python boosting回归算法_决策树_11

对于右节点,只含有2,3两个样本,根据下表结果我们选择年龄30为划分点(也可以选体重70)。

xgboost回归模型cv python boosting回归算法_拟合_12

现在我们的第一棵回归树长下面这个样子:

xgboost回归模型cv python boosting回归算法_拟合_13

xgboost回归模型cv python boosting回归算法_损失函数_14

此时的树长这下面这个样子:

xgboost回归模型cv python boosting回归算法_拟合_15

xgboost回归模型cv python boosting回归算法_拟合_16

下面将展示每棵树最终的结构,这些图都是我GitHub上的代码生成的,感兴趣的同学可以去运行一下代码。https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_Regression

第一棵树:

xgboost回归模型cv python boosting回归算法_拟合_17

第二棵树:

xgboost回归模型cv python boosting回归算法_损失函数_18

第三棵树:

xgboost回归模型cv python boosting回归算法_拟合_19

 

第四棵树:

xgboost回归模型cv python boosting回归算法_拟合_20

 

第五棵树:

xgboost回归模型cv python boosting回归算法_损失函数_21

xgboost回归模型cv python boosting回归算法_拟合_22

3. 手撕GBDT回归算法

本篇文章所有数据集和代码均在我的GitHub中,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning

3.1 用Python3实现GBDT回归算法

需要的Python库:

pandas、PIL、pydotplus、matplotlib

其中pydotplus库会自动调用Graphviz,所以需要去Graphviz官网下载graphviz-2.38.msi安装,再将安装目录下的bin添加到系统环境变量,最后重启计算机。

由于用Python3实现GBDT回归算法代码量比较多,我这里就不列出详细代码了,感兴趣的同学可以去我的GitHub中看一下,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_Regression

3.2 用sklearn实现GBDT回归算法

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

gbdt = GradientBoostingRegressor(loss='ls', learning_rate=0.1, n_estimators=5, subsample=1
                                 , min_samples_split=2, min_samples_leaf=1, max_depth=3
                                 , init=None, random_state=None, max_features=None
                                 , alpha=0.9, verbose=0, max_leaf_nodes=None
                                 , warm_start=False
                                 )
train_feat = np.array([[1, 5, 20],
                       [2, 7, 30],
                       [3, 21, 70],
                       [4, 30, 60],
                       ])
train_id = np.array([[1.1], [1.3], [1.7], [1.8]]).ravel()
test_feat = np.array([[5, 25, 65]])
test_id = np.array([[1.6]])
print(train_feat.shape, train_id.shape, test_feat.shape, test_id.shape)
gbdt.fit(train_feat, train_id)
pred = gbdt.predict(test_feat)
total_err = 0
for i in range(pred.shape[0]):
    print(pred[i], test_id[i])
    err = (pred[i] - test_id[i]) / test_id[i]
    total_err += err * err
print(total_err / pred.shape[0])

用sklearn中的GBDT库实现GBDT回归算法的难点在于如何更好调制下列参数:

xgboost回归模型cv python boosting回归算法_拟合_23

用sklearn实现GBDT回归算法的GitHub地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_Regression_sklearn

4. GBDT回归任务常见的损失函数

xgboost回归模型cv python boosting回归算法_决策树_24

5. GBDT的正则化

xgboost回归模型cv python boosting回归算法_决策树_25

6. 关于GBDT若干问题的思考

xgboost回归模型cv python boosting回归算法_损失函数_26

xgboost回归模型cv python boosting回归算法_拟合_27

 

7. 总结

在本文中,我们首先引出回归树与梯度提升算法结合的优势;然后详细推导了GBDT回归算法的原理,并用实际案例解释GBDT回归算法;其次不仅用Python3实现GBDT回归算法,还用sklearn实现GBDT回归算法;最后,介绍了GBDT回归任务常见的损失函数、GBDT的正则化 和我对GBDT回归算法的若干问题的思考。GBDT中的树是回归树(不是分类树),GBDT可以用来做回归预测,这也是我们本文讲的GBDT回归算法,但是GBDT调整后也可以用于分类任务。让我们期待一下GBDT分类算法,在分类任务中的表现吧!