对于xgboost的理解一直处在半知不解的情况,知道这个东西,大致知道这个原理;
但是让自己说一遍的它的原理的时候,发现根本说不出什么:它就是一个一棵树,用boost的方式串行,让模型上下相关联,关联的方式是用残差。
那具体是怎么构建这棵树的呢?怎么通过残差进行上下关联?怎么计算目标函数的?
带着这个问题查了资料,看到了一篇真的通俗易懂的文章,而且很深入。https://zhuanlan.zhihu.com/p/258564378zhuanlan.zhihu.com
通过自己的理解,再咀嚼后,记录此笔记。
XGBOOST 和GBDT最主要的区别有四个:GBDT的泰勒展开只展开到一阶,xgboost进行了二阶泰勒展开;可以自定义损失函数(MSE,交叉熵等),只要损失函数支持一阶导二阶导即可;
GBDT以CART树作为基分类器,xgboost支持线性分类器;
XGBOOST添加了惩罚项,主要由叶子节点数量和节点的值组成,降低了模型的方差,防止过拟合;
xgboost增加了自动处理缺失值特征的策略。通过把带缺失值样本分别划分到左子树或者右子树,比较两种方案下目标函数的优劣,从而自动对有缺失值的样本进行划分,无需对缺失特征进行填充预处理;
支持并行化处理,是特征粒度上的并行,预先对数据进行排序保存为block结构,迭代使用。各特征信息增益的计算可以并行;
GBDT是给新的基模型寻找新的拟合标签(前面加法模型的负梯度),而xgboost是给新的基模型寻找新的目标函数(目标函数关于新的基模型的二阶泰勒展开)
一、进入模型前的简单案例
在上一篇文章中的案例,通俗的讲解了思路:
训练出第一个模型,计算其预测结果及残差;
然后再以当前模型的残差,作为下一个模型的实际输出,将其与下一个模型的预测结果作对比,得出这个模型的残差;
再迭代到下一个......(看案例中的图更容易理解)
最终的输出结果是所有模型的预测结果之和
这个案例解决了我在看其他人写xgboost案例时看不懂的地方,其他地方写的都是以家庭中每个人的那个作为案例。我看的时候完全不知道它到底想干嘛,最后为什么又是把所有结果加起来,直到我看到了这位作者后才真正直到原理。
二、构建损失函数
个人在学习机器学习和深度学习的时候,除了要了解原理,另外最重要的就是要重点知道该算法的损失函数是怎么计算的!这是每个算法的核心。
假设我们已经训练了
棵树,对于第
个样本的最终预测值是等于:
其中
表示样本特征,
表示第
棵树对样本
的预测结果,最后把这些值都相加在一起就得到了最终的结果
再结合真实的结果label是
,就可以构建损失函数(到当前
棵树累计的损失函数):
其中
表示损失函数,常见的损失函数都可以用,像MSE、交叉熵cross entropy等;
后一项是用来控制复杂度的,类似于l1,l2正则,可以防止过拟合。
对于目标函数我们可以再拆开,类似于动态规划的思想,将
和
拆成与上一个相关联的公式
这其中就是将
变成和上一个相关的
,
变成了
,这两个公式前面一项都是之前计算好的,已知的,所以可以看作是优化如下的损失函数,使其最小:
通过泰勒展开近似损失函数
泰勒展开式为:
将损失函数套用上泰勒展开,
对应
,
对应
,
对应
;
即可得
代入损失函数即可得:
再重复一遍,当前目标函数是训练第
颗树时的函数,
这一项是在计算
棵树为止的预测结果的损失累加值,而这些在计算第
颗树时,其实都是已知的,不参与优化过程。所以上式的
、
和
都是已知的。后两项可以分别用
和
表示,再优化后,损失函数只需优化如下式子即可:
如上所说,
和
是已知的,现在要优化函数的话,需要把
、
参数化。
1、定义树的复杂度
一颗树的复杂度可以用叶节点个数和节点值计算得:
给两个都乘上一个超参数,用来控制他们,
是为了后续方便求导计算:
最终可以再优化得到:
这里的
表示当前这棵树中,样本
被预测后落入在对应节点上。
2、注意:刚才的
遍历的是样本,接下来换一下,变成遍历每个叶子节点
,而每个叶子节点中都包含有样本!!也就是我们放到实际的树上看。
最后得:
这里的
表示的是当前叶子节点
包含的样本的一阶导数之和,
表示的是当前叶子节点
包含的样本的二阶导数之和!!
我们知道
最小值的点为:
所以,所以当树的结构固定,也就是说
固定的话,在中括号中的最佳
为:
将
带入到
中可得,当前树结构下的最佳的目标函数值:
其中
和
表示
和
,都是已知的,和一阶导,二阶导对应的。
三、构建树的形状
在得到了损失函数,就可以开始构建树了,那怎么构建树呢?贪心算法!
没错,先计算当前树的损失函数,再穷举出所有可能性,然后计算所有穷举后的损失函数,然后取损失函数降的最多的那棵树最为当前树的形状,然后再重复......直到损失函数减少的不够多的时候,我们可以设个阈值,就可以停下来!
整个遍历过程就像我们在公式里提到的,需要遍历所有叶子节点,也要遍历每个叶子节点中的样本!!所以才有了后续优化的算法。
Xgboost缺点:在每次迭代的时候都要遍历整个训练数据多次,如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。
预排序方法空间消耗大:因为xgboost采用的是基于特征的并行计算,所以在每次计算之前,都要对特征进行排序。这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如排序后的索引,为了后续快速的计算分割点)。这里需要消耗训练数据两倍的内存
时间消耗大,在遍历每个分割点的时候,都需要进行分裂增益的计算,消耗的时间代价大。
对cache优化不友好,在预排序后,特征对梯度的访问是一种随机访问,并且不同的特征访问的顺序不一样,无法对cache进行优化。同时,在每一层长树的时候,需要随机访问一个行索引到叶子索引的数组,并且不同特征访问的顺序也不一样,也会造成较大的cache miss。
LightGBM的优点:
1、直方图算法:把连续的浮点特征值离散化成k个整数,构造一个宽度为k的直方图(1、可防止过拟合;2、分的太少,模型准确率会下降);遍历数据的时候,根据离散化后的值作为索引在直方图中累计统计量,也就是遍历的时候遍历的是直方图。
2、直方图做差加速:一个叶子的直方图可以由他的父亲点的直方图与兄弟点的直方图做差得到,基于Histogram的决策树算法,如图
3、带深层限制的Leaf-wise的叶子生长策略
Level-wise长下图这样,类似广度优先搜索,同时分裂同一层叶子,容易进行多线程优化,但是算法低效
而Leaf-wise是类似深度优先搜索,误差更低,精度更高,但是可能过拟合
「卓拾书非卓师叔」
作者:卓师叔,爱书爱金融的NLPer
微信公众号:卓师叔
了解更多和AI、金融相关的知识,请关注公众号:卓师叔