论文学习笔记 XGBoost: A Scalable Tree Boosting System

今天分享一篇论文《XGBoost: A Scalable Tree Boosting System》,由陈天奇于2016年发表,该算法在Kaggle等比赛中大放异彩,现在在工业界也被广泛应用。

论文学习笔记 XGBoost: A Scalable Tree Boosting System_第1张图片
Tree boosting是高效并被广泛应用的机器学习方法。XGBoost是一种适用于大规模数据的端到端的boosting系统。本文提出了一种新颖的稀疏感知算法和加权分位数快速近似数学习算法。更重要的,本文提供了关于缓存访问模式,数据压缩和分片的见解,以构建一个可扩展的树型增强系统。 通过结合这些见解,XGBoost可以使用比现有系统少得多的资源来支撑数十亿个样本的训练。

摘要中主要强调了两个方面的贡献,一个是结点分裂算法,一个是决策树系统的设计。本文参考论文的行文顺序,会先简要回顾一下Tree boosting,然后会主要针对这两部分,总结一下XGBoost的优势。

  • Tree boosting回顾

Boosting方法是一类应用广泛且非常有效的统计学习方法。它是基于这样一种思想:对于一个复杂任务来说,将多个专家的判断进行适当的综合所得出的判断,要比任何一个专家单独的判断要好。这种思想整体上可以分为两种:
强可学习:如果存在一个多项式的学习算法能够学习它,并且正确率很高,那么就称为强可学习,直接单个模型就搞定常规问题。就好比专家给出的意见都很接近且都是正确率很高的结果,那么一个专家的结论就可以用了,这种情况非常少见。

弱可学习:如果存在一个多项式的学习算法能够学习它,学习的正确率仅比随机猜测略好,那么就称这个概念是弱可学习的。这种情况是比较常见的。

Boosting算法主要是针对弱可学习的分类器来开展优化工作。其关心的问题包括两方面内容:
(1)在每一轮如何改变训练数据的权值和概率分布;
(2)如何将弱分类器组合成一个强分类器,这种思路较好的就是AdaBoost算法。
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第2张图片
Boosting Tree模型采用加法模型与前向分步算法,而且基模型都是决策树模型。前向分步算法是指在叠加新的基模型的基础上同步进行优化,具体而言,就是每一次叠加的模型都去拟合上一次模型拟合后产生的残差。由前向分步算法得到多棵决策树后,再进行加和,就得到了提升树模型。
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第3张图片
XGBoost在Boosting Tree上做了一些改变:

首先在目标函数里添加正则项,用来防止树的结构过于复杂,从而降低模型的过拟合。

其次添加了一个收缩机制,类似梯度下降优化问题中学习率随训练轮数增加而递减。这个可以逐渐收缩新加入的树的权重,让每棵新树对整体的影响不那么大,也给后续新树优化整体模型留下了空间。

XGBoost还参考了随机森林,对样本的特征随机抽样。每次生成树的时候,只用其中一部分抽样的特征。这样子也能比较好的降低过拟合的风险。

  • 创新的结点分裂算法

1.精准的贪心算法
贪心算法就是每次我都希望找到最优的结果,也就是说每次都遍历所有的特征,对每个特征,又遍历所有划分的可能。
split
通过计算每一种split方式的分数,取收益最大的对应的特征来进行结点分裂。这是计算结点分裂的最直觉的方法。
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第4张图片
用贪心算法来寻找最佳划分点,准确度没得说,但是时间复杂度和空间复杂度都太高了,特别是对于连续值的变量来说,简直是一个大灾难。

2.近似算法
作者提出一种近似法分位法,先对数据进行分桶,然后桶内的数据相加起来,作为一个代表来进行计算。我们有两种分桶方式。

全局分桶:可以一开始就对全部的数据进行分桶。后面进行划分的时候只需要使用分桶数据就可以了。
局部分桶:每次需要对当前结点进行划分的时候,对当前结点里面的数据进行分桶。然后再划分。当然这个时间复杂度也会变的比较高。

下面是作者测试对几种不同的切分算法的AUC结果比较图。可以看得出,当eps=0.05,也就是将数据分成20个Bucket的时候,AUC的分数跟精准的贪心算法几乎一样。

论文学习笔记 XGBoost: A Scalable Tree Boosting System_第5张图片
算法伪代码:
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第6张图片
3.加权分位法
常规思路而言,针对不同特征依据其特征值直接进行等宽或是等频次进行分桶是一种常规操作。作者在文中提出了一种针对分桶的优化方法,即根据loss的影响权重的等值分桶方法,让数据分桶后,每个桶对loss的影响权重相同。
定义数据集:dk
定义rank函数:
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第7张图片
其中epsilon是一个超参数, 用来衡量划分区间的大小。这个意味这数据集大约被分为1/epsilon个候选点。

4.Sparsity-aware Split Finding算法
真实数据很多都是稀疏的数据,大致有如下原因:
1.数据中有缺失值。
2.统计中频繁出现0。
3.人工特征工程造成,例如one-hot。

遇到这些值的时候,我们需要跳过去,默认将它分到一个边,不用再考虑它的划分情况。默认方向是通过学习数据获得的,其算法如下:
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第8张图片
下图显示了稀疏感知和对Allstate-10K数据集的简单实现的比较。 稀疏感知算法的运行速度比原始版本快50倍。
论文学习笔记 XGBoost: A Scalable Tree Boosting System_第9张图片

  • 系统设计

1.对内存的优化(列分块)

在XGBoost模型计算过程中,特征值的排序与切分点的选择是最耗时的部分,文中提出了一种划分块的优化方法,具体表现为如下流程:

  • 整体训练数据可以看做一个n*m的超大规模稀疏矩阵
  • 按照mini-batch的方式横向分割,可以切成很多个“Block”
  • 每一个“Block”内部采用一种Compress Sparse Column的稀疏短阵格式,每一列特征分别做好升序排列,便于搜索切分点,整体的时间复杂度有效降低
    论文学习笔记 XGBoost: A Scalable Tree Boosting System_第10张图片

2.对CPU Cache的优化

针对一个具体的块(block),其中存储了排序好的特征值,以及指向特征值所属样本的索引指针,算法需要间接地利用索引指针来获得样本的梯度值。由于块中数据是按特征值来排序的,当索引指针指向内存中不连续的样本时,无法充分利用CPU缓存来提速。作者提出来了两种优化思路。

(1)提前取数(Prefetching)

对于精确搜索,利用多线程的方式,给每个线程划分一个连续的缓存空间,当training线程在按特征值的顺序计算梯度的累加时,prefetching线程可以提前将接下来的一批特征值对应的梯度加载到CPU缓存中。

(2)合理设置分块大小

对于近似分桶搜索,按行分块时需要准确地选择块的大小。块太小会导致每个线程的工作量太少,切换线程的成本过高,不利于并行计算;块太大导致缓存命中率低,需要花费更多时间在读取数据上。经过反复实验,作者找到一个合理的分块大小:2^16。

3.对IO的优化

当数据集太大,无法全部加载到内存时,主要的性能瓶颈就变成了磁盘IO,因此需要对IO进行优化。文章中主要提出来了两种优化思路。

(1)Block压缩优化

原始数据在磁盘上是以压缩格式存取的,读取的时候,现场解压 (decompress on-the-fly)

相当于牺牲一部分CPU时间,换取对磁盘IO的读取时间损耗。

(2)Block 分片优化

将压缩后的块存放在多个不同的磁盘中,每个磁盘开一个prefetching线程分别读取数据到各自的缓存,提供给一个training线程使用。

  • 总结

XGBoost在Boosting Tree的基础上优化了损失函数,加入了列采样和收缩机制,降低了过拟合。提出了一种新颖的结点分裂算法:稀疏感知算法和加权分位数快速近似数学习算法,从而大幅减小结点分裂的计算量。优化了系统设计,使得XGBoost可以使用比现有系统少得多的资源来支撑数十亿个样本的训练。

  • 参考文献

XGBoost: A Scalable Tree Boosting System

『 论文阅读』XGBoost原理-XGBoost A Scalable Tree Boosting System

XGBoost算法的原理详析[文献阅读笔记]

[机器学习基础复习] XGBoost论文精读

你可能感兴趣的:(论文学习笔记,学习,boosting,机器学习)