天天看点

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

1 论文简介

XGBoost的作者是曾在华盛顿大学研究机器学习的大牛陈天齐,XGBoost全称是eXtreme Gradient Boosting,从名称看XGBoost仍然是GBDT,在GBDT基础上优化和增强。XGBoost以出众的效率和较高的预测准确率在各大算法比赛中被广泛使用。这篇文章介绍了XGBoost这种可扩展、端到端的Boosting Tree的新算法。

文章先介绍了Boosting Tree在现在机器学习领域中非常出彩,而XGBoost作为一种先进的Boosting Tree,被各大比赛的选手们广泛使用。2015年的Kaggle和KDDCup中,所有top排名的队伍都使用XGBoost。

XGBoost成功背后的最重要因素是在所有情况下的可扩展性。XGBoost在单台机器上的运行速度比现有流行解决方案快十倍以上,可在分布式或内存有限的环境中扩展到数十亿个实例。XGBoost的可扩展性是由于几个重要的系统和算法优化。文章介绍了这些创新:一种新颖的

稀疏数据感知算法

,用于处理稀疏数据;一种

合理的加权分位数略图(weighted quantile sketch)

能够在近似学习中处理有权重特征的数据。论文还提出了

缓存访问模式

数据压缩和分片

的方法。通过结合这些方法,XGBoost可扩展、并行和分布式处理使学习速度更快,用比现有系统少得多的资源来处理数十亿规模的数据。

2 Boosting Tree简介

2.1 Boosting Tree正则化项优化

文章先简单介绍了Gradient Boosting Tree。使用k棵独立的回归树(CART)使用Boosting集成预测,F包含所有k棵树。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读
xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

这里,q是映射到树叶子节点索引的函数,T是叶子节点数,w是叶子权重,连续值,也叫score,是映射到该节点的预测值。

下图是Boosting Tree集成学习的示例。tree1中,+2是预测男孩对游戏感兴趣的权重w1,tree2中,+0.9是对男孩的权重w2。最终预测结果是两棵树的score简单加和。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

下图是Boosting Tree的目标函数,前一项l是损失项,描述模型拟合训练数据的优劣,从训练数据预测有多好。后一项Ω是正则化项,Ω作为惩罚项减少模型复杂度,能平滑权重系数防止过拟合。这里

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

是叶子数,w是叶子权重,分别是L1和L2的正则化项。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

下图是Boosting tree目标函数的另一种写法。根据加法模型,

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

第t棵树是在第t-1棵树的基础上学习优化而来,根据t-1棵树的预测值,计算残差,再拟合第t棵残差树。

注意:这里

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

残差,是下一轮要预测的目标值。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

是泰勒公式里的

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

这里用到了高数知识泰勒展开公式。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

根据泰勒二阶展开,g和h是损失函数的一阶和二阶导数。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读
xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

对上式去掉常数项,这里t-1棵树的loss已经计算出来,可以作为常数项去除。(待进一步明确)

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

使用公式(2)替换公式(3)中的惩罚项。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

目标函数现在是w的函数。对w求导求极值,令求导结果等于0。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

可得到目标函数的最优值。w越小,树的结构就越好,预测的越好。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

把公式(5)带回到公式(4),计算平方后合并同类项,

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

目标函数(6)是score函数,可用来衡量树结构质量的优劣和预测的好坏。

可以穷举所有的树结构q,利用公式(6)计算每棵树score,利用公式(5)优化叶节点权重,求最小化权重w。但树是无限多的,不可能穷举。实际中使用贪心算法,从一个节点出发,分割后形成一棵树。分割点的选择使用下图公式Gain的计算来选择。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

HL左子树分数,HR右子树分数,第三项是不分割可以得到的分数。减第三项表示分割后比分割前变化量,

最后一项

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

是加入新叶子节点引入的复杂度代价

公式(7)同上,是Gain的另一种写法。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

2.2 Shrinkage和subsampling防止过拟合

除了正则化项,Boosting Tree还加入了两项技术防止过拟合。第一项Shrinkage类似于随机梯度下降的步长(学习率),shrinkage是每次tree boosting后增加的学习率权重,能减少个体的影响,为后续的tree boosting留下更多空间,防止在构建某棵树时过拟合。第二项subsampling类似于RF里的列抽样,除了防止过拟合,subsampling还能在后续章节提到的并行计算加速计算。

3 如何寻找分割点的算法

3.1 传统贪心算法(Basic Exact Greedy Algorithm)

传统贪心算法,遍历所有分割点,选择最优分割点。如果有2个特征组合,一共有4对取值,穷举后要计算4次才能选择最优分割点。

传统算法必须先对特征值排序,然后遍历数据计算梯度值。缺点就是效率差,数据可能都不能全放进内存。

3.2 近似算法(Approximate Algorithm)

近似算法和传统贪心算法遍历所有分割点的做法不同,根据特征分位数的分布情况,给出备选特征分割点的建议(proposal)。

近似算法建议有两种方法,

Global方法在建树前对所有候选特征做分割选择,给出建议。并且在下层子树构建时都用建树时同样的选择。Global全局考虑,所以需要更多的候选特征。

Local方法在每次分割后re-propose,是对global分割后的优化,local方法提出的重新优化分割更精确,在更深的子树上分割更精确。但Local需要的候选特征更少。

现有的近似算法中,可以直接使用梯度分位数统计信息,也可以使用其他如二分策略(binning strategy)。梯度分位数统计在后续章节的分布式计算表现出色,甚至可接近传统贪心算法的准确度。

3.3 加权分位数略图 (weighted quantile sketch)

近似算法很重要的一步就是找到备选分割点。通常情况会均匀挑选一个特征的分位数作为候选。集合

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读
xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

表示样本第k个特征的特征值,h表示第n个样本损失函数的二阶梯度。据此定义一个排序函数:

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

(式8)

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

对于每个分割点,其函数值

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

表示用此分位点下包含的二阶导之和的比例

函数目标是找到一组候选的分割点

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

满足条件(式9)。即相邻的两个分割点之间的间隔小于给定的ε。这里的ε是一个近似系数。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

直观上看,这大致表示总共选出1/ε个候选点。这里每一个点都按照其二阶导h进行加权。为了说明为何可以使用二阶导h作为权重,可以把等式3改写成如下形式。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

即gi/hi做label时的L2损失平方乘以hi作为权重。

对于大规模数据集来说,很重要的是寻找符合条件的候选分割点。已有的分位数略图算法可以解决当样本权重相同时的问题。然而,还没有哪个现成的分位数略图算法可以解决权重不同的样本数据。因此,大多数近似算法使用随机抽样或启发式的方法,这些方法要不是有失败的可能,要不就是没有理论支撑。

为了解决这个问题,论文引入了一个新颖的,有理论支撑的分布式加权分位数略图算法,其大意是提出一个可以支持融合与剪枝操作的数据结构。而每一个合并与剪枝操作又能保证一定量级上的准确度。细节算法和证明见原论文中的链接。(待进一步阅读)

3.4 稀疏数据感知分割算法(Sparsity-aware Split Finding)

现实数据出现稀疏矩阵(Sparsity)非常普遍,比如存在缺失值,大量的0值,特征工程使用one hot encoding独热编码。因此,在每个树节点增加缺省方向,让算法知道当数据出现稀疏的情况,仍能快速准确分割。

如下图所示,红色是树节点的缺省方向,样本X2中性别是缺失值,根据缺省方向仍然可以被分割。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

确定缺省方向有两种方法,最优方法也是default使用的是,只收集非缺失值,从非缺失值数据的统计信息学习到最好的缺省方向。

和其他算法相比,XGBoost能很好处理稀疏数据,随着数据量复杂度线性增长。使用稀疏矩阵感知算法的XGBoost,在实验中比其他算法快50倍,

4 系统实现

4.1 块存储

数据按列排序后在内存中以块(block)存储

排序很费时间,因此数据按列排序后在内存中以块存储,块中数据按列排序后以列压缩(CSC)方式存储。所有样本数据在计算前只需做一次排序,后续迭代多次使用排序结果。

传统贪心算法里,把所有数据集存储在一个block里,分割时可对预先排序的数据线性查找。因此一次扫描获得的数据统计信息,可以在后续各级分支计算上多次使用。

下图描述了这个过程。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

在近似算法中,块结构也非常有用。整个数据使用多个块,每个块是整个数据集的子集。多个块可以分布在多台机器上,也可以从内存放在磁盘上。块中预排序的数据,分位数的计算分割也可以线性扫描。Local propose可以很方便在一个分支上频繁的做优化建议,二分搜索(binary search)也可在合并算法上线性计算。

在并行分割搜索时,可并行为每列数据收集统计信息,更有效率。块结构也能在列抽样(subsampling)操作时很方便选取列数据。

传统贪心算法的复杂度是

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

,d是树的最大深度,K棵树,

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

是数据中非缺失的样本数。如果使用块结构,Tree Boosting的复杂度是

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

,这里

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

是一次预处理排序,因此块结构节省了logn系数的开销。

近似算法中,二分查找算法的复杂度是

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

,这里q是特征数,通常特征数量是32到100,logq还是有一定开销。如果使用块结构,复杂度是

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

,B是块中能包含的最大行数。块结构也节省了logq系数的开销。

4.2 缓存访问机制

块结构方法简化了节点分割计算时的复杂度,但会频繁读取不连续内存里的行数据,内存不连续降低了节点分割计算的速度。

缓存访问机制能解决传统贪心算法里访问不连续内存数据的问题。先把梯度统计信息数据放在每个线程的缓存里,能直接在内存中读写,减少运行开销。

下图显示了在大数据量比较(10M)中,缓存访问机制比无缓存快2倍速度。值得注意的数据量1M时,缓存访问机制没有明显的速度提升。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

近似算法里,调整合适的块结构大小能减少运行开销。如果选择太小的块结构不能充分发挥并行特性,太大的块结构可能使梯度统计信息不能装入cache,缓存无法命中。考虑这两种情况要选择合适的块大小。从下图两个数据集给出的实验结果,选择每个块中包括

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

样本的块大小效果最好,同时平衡了缓存命中和并行。

xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读
xgboost实例_我对《XGBoost: A Scalable Tree Boosting System》的论文解读

4.3 磁盘数据的块压缩和数据分片技术

当数据不能全部装入内存,数据以块存储在磁盘上。除了CPU和内存,也充分对磁盘读取进行优化。

主要有2个技术,块压缩技术对磁盘上的block按列做压缩。

数据分片技术,把数据分片到多个磁盘上,每个磁盘分别存储数据并读取到内存中。这两个技术都增加了磁盘吞吐量。

5 总结

论文读毕,深感作者考虑全面。

论文介绍XGBoost在目标函数设计上增加了L1和L2正则项,限制叶子节点数目,降低叶子权重。同时Shrinkage能降低步长,消除个别树过拟合,为后续树提升留出更多空间。

在算法设计上,论文提出近似算法,global全局考虑分割点,local细节优化global后的分割结果。加权分位数从分位数统计信息提升近似算法性能。缺省方向在实战中为稀疏数据树的分割提供帮助。

在系统实现上,XGBoost 提出了块结构,Cache存取,磁盘数据的压缩和分片技术。

综合看,XGBoost从CPU(算法设计),MEM(Cache),Disk(压缩和分片)各方面考虑,极尽优化之能。XGBoost既快又准,横扫各大比赛成为大杀器令人称道。

相关文献:

XGBoost: A Scalable Tree Boosting System

》 : Tianqi Chen, Carlos Guestrin

XGBoost Documentation