极限梯度提升树原理(XGBoost)
原博客:https://daya-jin.github.io/2018/12/01/XGBoost/
XGBoost
模型
K个集成树模型的输出为(加法模型,每次boost只针对上次的残差):
带正则项的目标函数为:
其中
那么,在第t轮迭代时的目标函数可以写成:
根据泰勒公式
设、分别为损失函数关于的一阶偏导与二阶偏导,第t轮的目标函数展开到二阶可写成
省略常数项(上一轮的伪残差)并展开正则项,可写成
当基模型使用树模型时,其数学表达式可写为:
其中将样本映射到某一叶子节点,表示某一叶子节点的输出值。
再设表示第个叶子节点中所有的样本,目标函数可写成:
其中,,则令偏导等于零时的最优参数为,最小化后的损失为,可以看出XGB的总损失跟叶子节点的梯度还有叶子数有关,可以推出XGB在划分数据集(生成树)时的增益:
其中是一个分裂阈值。除了损失函数中的正则项,XGB还支持列采样(column subsampling)来避免过拟合。
从上述推导过程中可以看出,XGB在每次做test时直接求出一个最优解,然后根据这个最优解来作为增益指导生成决策树。
加速
近似分割算法
在DT算法算法中,关键就在于找到一个最佳分割点,如CART对连续型特征的处理,必须先对特征的所有值进行排序,然后再遍历尝试所有特征中所有可能的切分点,找到一个最佳位置,这种操作计算量很大。XGB采用了一种快速的近似分割算法:根据百分位点选出一些候选分割点(proposed candidate points),从这些候选分割点中选取分割位置。这种近似分割算法有有两种实现方式,一种是全局(global)的,在一棵树生成之前就定好候选分割点,然后对这棵树的所有分割均在这些全局分割点上进行;另一种是局部(local)的,在每次分割时都重新推举候选分割点。全局实现的步骤数要少于局部实现,但是却需要更多的候选分割点,因为局部实现能在每次分割后重调(refine)候选分割点。
值得一提的是,在跟据分位点推举候选分割点时,这个百分点指的并不是统计意义上的百分点,而是根据二阶梯度确定的分位点:
其中表示第个特征集,表示二阶特征。那么近似分割算法在第个特征上找到的个候选分割点需要满足以下条件:
其中表示在第个特征上的第个候选分割点。
稀疏感知
在DT算法中分割遇到缺失值时,样本会复制多份进入所有子树;而在XGB中,为带缺失值的样本或从未出现过的值规定了一个默认方向(default direction),这个特定特征下的默认方向也是从数据中学到的。在模型训练,即树的生成时,假设在对第个特征做test,数据中某些样本的第个特征是缺失的,那么在确定分割点与默认方向时,首先尝试将所有缺失值样本划到左边计算一次,再尝试将缺失值样本划到右边计算一次,增益值最大的方向被设为默认方向。
列压缩存储
GBDT算法最耗时的部分就在于数据的列排序与分割点的确定,XGB采用CSC(Compressed Sparse Column)形式,把数据的每一列排好序后存储在一个块(block)单元中,这样一来就可以同时对多个特征(列)做test,即实现了特征间的并行化,甚至在block size小于样本数时还可以实现特征内的并行。不仅如此,列式存储还有几个好处:(1)不同的块可以存储在硬盘甚至是分布式的;(2)不同的块代表不同的特征,便于实现列采样。需要注意的是,由于XGB的稀疏感知特性,某特征下的缺失值并不会被存储。
cache
(cache层的优化这里没看懂,大致是讲行索引与存储地址不一致,通过每个线程开辟一个内部缓冲区来实现预取)
问题
为什么要将GBDT的损失函数使用泰勒二阶展开?
首先,凸优化求最小值的充要条件是一阶导数为,二阶导数大于;目标函数使用二阶导数展开,精确度高于一阶;待补充...
GBDT与XGB的区别
GDBT是以树模型为基模型的GBM,只指出了每一颗新树要去拟合损失函数关于前一轮总模型的负梯度,而对于每一颗树怎么生成并没有指明。GBM将子模型看成是参数,使用梯度下降法来得到一个最优函数。
XGB是GBDT的一种实现,也是将子模型看成是参数,但是使用牛顿法来得到一个最优函数,并且将子模型(树模型)的表达式具体化,直接从损失函数推到了树的划分增益,求出了最优解,并使用最优解指导每棵树的生成。