1. 树该怎么长
很有意思的一个事是,我们从头到尾了解了xgboost如何优化、如何计算,但树到底长啥样,我们却一直没看到。很显然,一棵树的生成是由一个节点一分为二,然后不断分裂最终形成为整棵树。那么树怎么分裂的就成为了接下来我们要探讨的关键。对于一个叶子节点如何进行分裂,XGBoost作者在其原始论文中给出了一种分裂节点的方法:枚举所有不同树结构的贪心法
不断地枚举不同树的结构,然后利用打分函数来寻找出一个最优结构的树,接着加入到模型中,不断重复这样的操作。这个寻找的过程使用的就是贪心算法。选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值,你枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。
总而言之,XGBoost使用了和CART回归树一样的想法,利用贪婪算法,遍历所有特征的所有特征划分点,不同的是使用的目标函数不一样。具体做法就是分裂后的目标函数值比单子叶子节点的目标函数的增益,同时为了限制树生长过深,还加了个阈值,只有当增益大于该阈值才进行分裂。从而继续分裂,形成一棵树,再形成一棵树,每次在上一次的预测基础上取最优进一步分裂/建树。
2、如何停止树的循环生成
凡是这种循环迭代的方式必定有停止条件,什么时候停止呢?简言之,设置树的最大深度、当样本权重和小于设定阈值时停止生长以防止过拟合。具体而言,则
- 当引入的分裂带来的增益小于设定阀值的时候,我们可以忽略掉这个分裂,所以并不是每一次分裂loss function整体都会增加的,有点预剪枝的意思,阈值参数为(即正则项里叶子节点数T的系数);
- 当树达到最大深度时则停止建立决策树,设置一个超参数max_depth,避免树太深导致学习局部样本,从而过拟合;
- 样本权重和小于设定阈值时则停止建树。什么意思呢,即涉及到一个超参数-最小的样本权重和min_child_weight,和GBM的 min_child_leaf 参数类似,但不完全一样。大意就是一个叶子节点样本太少了,也终止同样是防止过拟合;
3、XGBoost与GBDT有什么不同
除了算法上与传统的GBDT有一些不同外,XGBoost还在工程实现上做了大量的优化。总的来说,两者之间的区别和联系可以总结成以下几个方面。
- GBDT是机器学习算法,XGBoost是该算法的工程实现
- 基分类器 传统的GBDT采用CART作为基分类器,xgboost的基分类器不仅支持cart树,还支持线性分类器,此时的xgboost相当于带L1和L2正则项的逻辑回归(分类问题)或者线性回归(回归问题)
- 导数信息 GBDT在模型训练时只使用了代价函数的一阶导数信息, xgboost对损失函数(代价函数)做了二阶泰勒展开,可以同时使用一阶和二阶导数,gbdt只用了一阶导数,并且xgboost还支持自定义损失函数,只有损失函数一阶,二阶可导。
- 正则项 在使用CART作为基分类器时,XGBoost在目标函数中加入了正则项来控制模型的复杂度,有利于防止过拟合,从而提高模型的泛化能力,这是gbdt中没有的
- 列抽样 传统的GBDT在每轮迭代时使用全部的数据,XGBoost则采用了与随机森林相似的策略,支持对数据进行采样,xgboost支持列抽样,即特征抽样,与随机森林类似,用于防止过拟合
- 缺失值处理 传统的GBDT没有设计对缺失值进行处理,XGBoost能够自动学习出缺失值的处理策略。对树中的非叶子结点,xgboost在训练过程中会自动学习出它的默认分裂方向,如果测试样本的该特征的特征值缺失,将会划入默认分支中去。
- 并行化 注意不是tree维度的并行(boosting策略类的加法模型都做不到tree级别的并行),而是特征维度的并行,xgboost预先将每个特征按特征值排好序,存储为block结构,分裂结点时,可以采用多线程并行查找每个特征的最佳分裂点,从而提升训练速度
- 处理不平衡 在计算损失函数时,考虑不平衡因素,加权损失。
4、为什么XGBoost要用泰勒二阶展开,优势在哪里?
- XGBoost使用了一阶和二阶偏导, 二阶导数有利于梯度下降的更快更准. 使用泰勒展开取得函数做自变量的二阶导数形式, 可以在不选定损失函数具体形式的情况下, 仅仅依靠输入数据的值就可以进行叶子分裂优化计算, 本质上也就把损失函数的选取和模型算法优化/参数选择分开了. 这种去耦合增加了XGBoost的适用性, 使得它按需选取损失函数, 可以用于分类, 也可以用于回归。
- 精准性 相对于gbdt的一阶泰勒展开,xgboost采用了二阶泰勒展开,可以更为精准的逼近真实的损失函数。
- 可扩展性 损失函数支持自定义,只需要新的损失函数二阶可导
5、XGBOOST为什么可以并行训练?
- 不是tree维度的并行(boosting策略类的加法模型都做不到tree级别的并行,每棵树训练前需要等待前面每棵树训练完成才能开始新一轮的训练)
- 而是特征维度的并行,xgboost预先将每个特征按特征值排好序,存储为block结构,分裂结点时,可以采用多线程并行查找每个特征(一个特征一个block块)的最佳分裂点,从而提升训练速度
6、XGBOOST为什么快?
- 分块并行,一个特征一个block块,训练前每个特征按照特征值的排序,存储为block块结构,后面查找特征分割点时可以重复使用,并且支持并且查找每个特征的分割点
- 候选分位点,每个特征采用常数个分位点作为候选分割点,这里注意有加权,不是直接按从小到大的分位点,而是按照权值的分位点,权值大小为二阶导。(因为可能不同的特征取值都对应同一个预测值,同一个预测值对应同一个损失,同一个二阶导,
- cpu cache命中,使用存储预取的方法,对每个线程分配一个连续的buffer,读取每个block中样本的梯度,并存入连续的buffer 中
- block 处理优化,block预先放入内存中,block按列进行解压缩,将block划分导不同的硬盘提高吞吐(这条不太懂)
7、xgboost防止过拟合的方法
xgboost在设计之初,为了防止过拟合就采取了很多的优化,具体如下:
- 目标函数加入正则项,叶子结点个数+叶子结点权值的l2正则化
- 列抽样,训练时只用一部分特征,(不考虑剩余的block块即可)
- 子采样,下采样 每轮计算可以不采用所有的样本,使得算法更保守
- shrinkage ,可以叫做学习率或者步长,为了给后面的训练留出更多的学习空间
8、xgboost如何处理缺失值
xgboost模型的一个优点就是允许存在缺失值,对缺失值的处理如下:
-
在特征k上选择最佳的 分裂点时,不会对该列特征 missing的样本进行遍历,而只是对该列特征值为 non-missing的样本进行对应的特征值遍历,通过这个技巧减少了为稀疏离散特征寻找 分裂点的时间开销
-
逻辑实现上,为了保证完备性,会将该特征值missing的样本分别分配到左结点和右结点上去,两种情况都计算一遍后,选择分裂后增加最大的那个方向(左方向或者右方向),作为预测时特征值缺失样本的默认分支方向。
-
如果在训练时没有缺失值而在预测时有出现缺失值,那么会自动将缺失值的划分方向放到右结点上去
文章参考:https://www.pianshen.com/article/30521719325/