XGBoost
摘要
提升树是一种非常高效和广泛应用的机器学习算法。在这篇文章中,我们描述了一个名为XGBoost的可扩展的端到端的提升树模型,数据科学家们在很多机器学习挑战中广泛应用该系统并实现目前最好的结果。我们提出一种新颖的稀疏感知算法用于稀疏数据,一种weighted quantile sketch用于树的学习。更重要的是,我们提供关于缓存访问模式、数据压缩和分片的见解,以构建一个可扩展的提升树模型。通过结合这些方法,XGBoost可采用比现存系统少得多的资源来处理数十亿规模的数据。
1. introduction
机器学习和数据驱动的方法在许多领域变得非常重要。智能垃圾邮件分类器通过从大量的垃圾邮件数据和用户反馈中学习来保护我们的邮箱;广告系统学习将正确的广告与正确的背景相匹配;欺诈检测系统保护银行免受恶意攻击;异常事件检测系统帮助实验物理学家发现新的物理现象。有两个重要因素可以推动这些成功的应用:使用能捕获复杂数据依赖性的有效的(统计)模型,以及能从大型数据集里学习出模型的可扩展的学习系统。
在实际应用的机器学习方法里,Gradient Tree Boosting (GBDT)是一个在很多应用里都很出彩的技术。提升树方法在很多有标准分类基准的情况下表现很出色。LambdaMART:一种用于排序的提升树的变种,在排序问题中实现了目前最优的结果。它除了被用于单独的预测器,还在实际生产中被用于广告点击率预测。它是很多集成方法里的实际选择,此外还用于Netflix这样的比赛。
本文描述的可扩展的提升树机器学习系统已经开源了,它的影响力已经被许多机器学习和数据挖掘的比赛所广泛认可。拿机器学习大赛Kaggle举例:2015年发布的29个获胜方法里有17个用了XGBoost。在这些方案里,有8个仅用了XGBoost,另外的大多数用它结合了神经网络。对比来看,第二流行的方法,深度神经网络,只被用了11次。这个系统的成功性也被KDDCup2015所见证了,前十的队伍都用了XGBoost。此外,据胜出的队伍说,很少有别的集成学习方法效果能超过调好参的XGBoost。
这些结果说明我们的系统能在很广泛的问题里获得很好的结果。这些成功案例包括:网页文本分类、顾客行为预测、情感挖掘、广告点击率预测、恶意软件分类、物品分类、风险评估、大规模在线课程退学率预测。虽然这些问题依赖于特定领域的数据分析和特征工程,但选择XGBoost是这些人的共识,这体现出了我们的系统的影响力和重要性。
XGBoost获取成功最重要的一个因素是在各种场景的可扩展性。该系统在单机上的运行速度比现有的主要解决方案快十倍以上, 在分布式或者内存有限的环境下可以扩展到数十亿个实例。XGBoost的可扩展性是因为几个重要系统和算法的优化,这些创新包括:
应对稀疏数据的新颖树学习算法
一个理论上合理的weighted quantile sketch程序确保处理近似树学习的实例权重
并行和分布计算使学习速度更快,确保更快的进行模型探索
更重要的是,XGBoost利用核外计算,使数据科学家能够在笔记本上处理数亿个实例
最后,更令人兴奋的是将这些技术组合在一起,形成一个端到端系统,它可以用最少的集群资源扩展到更大的数据。
这篇论文的主要贡献:
设计和实现了一种高度可扩展的端到端的提升树系统
提出了一个理论上可证明的weighted quantile sketch 进行高效率计算
提出了一种新颖的基于稀疏感知的并行树学习算法
我们提出了一种有效的缓存感知块结构用于核外树学习。
虽然在并行树推广方面有一些已有的工作,但诸如核外计算,缓存感知和稀疏感知学习等方向尚未被探索。更重要的是,结合所有这些方面的端到端系统为实际使用情况提供了一种新颖的解决方案。这使得数据科学家和研究人员能够构建提升树算法的强大变种。除了这些主要的贡献之外,我们还提出了一个改进正则化学习的方法。
在本文的其余部分安排如下。我们将在第二章节首先回顾一下提升树,并介绍一个正则化的目标。然后,我们在第三部分介绍拆分查找方法,第四部分是系统设计,包括相关的实验结果,为我们提到的每个优化方法提供量化支持。相关工作在第五节讨论。详细的端到端评估在第六部分。最后,我们在第七部分总结这篇论文。
2. 提升树
我们在这一节中回顾了梯度树提升算法。其推导过程与现有梯度提升理论的推导思路相同。具体来说,二阶方法起源于Friedman等人的[12]。我们在正则目标上做了一些小的改进,这在实践中很有帮助。
2.1 正则化学习目标
一个树集成模型使用K个加性函数来预测输出:
q表示每个树结构,它将一个样本映射到对应的叶子结点上;T是叶子节点的个数;表示一个独立的树结构q和相对应的叶子权重。这里每个叶子结点输出一个连续值,我们使用表示第i个叶子结点的值。对于一个样本,我们使用树的决策规则将其分类到叶子节点,然后计算每棵树对应叶子节点的分数和()作为最后的输出。
例如:
我们最小化一下的正则化对象:
上式所加的正则化帮助平滑最终学习的权重避免过拟合。直觉上来说,正则化对象将倾向于挑选出一个简单、可预测的函数。我们的目标函数和相应的学习算法更为简单,更容易并行化。当正则化参数设置为0时,目标函数将回到传统的梯度提升树
2.2 梯度提升树
我们贪婪的添加来最大的提升我们的模型。二阶近似用于快速的优化目标对象
我们去除常数项
等式6可以作为一个评分函数来衡量一个树型结构q的质量。
下图展示了树的评分怎么计算
正常情况下我们不能列举所有的结构q,因此提出一个贪婪算法,他从单个叶子开始,迭代的向树中添加分支
2.3 Shrinkage and Column Subsampling
除了上述所说的添加正则对象之外,还有两种技术被引入来进一步防止过拟合
shrinkage:在提升树的每一步添加一个因子作为权重。 类似于学习率,它降低每个树个体的影响, 留下空间给未来的树提高模型
第二个技术是特征的二次采样,这种技术也被用于随机森林。该方法实际效果好于行的采样,同时也加速计算后面所描述的并行算法
3. 划分查找算法
3.1 Basic Exact Greedy Algorithm
树学习的一个关键问题在于寻找等式7中的最优划分点。一种最简单的方法是寻找所有特征的所有划分点(Exact Greedy Algorithm )。它列举所有连续特征的可能划分点在计算上是苦难的。为了更高效,该算法必须首先排升序排列所有的特征值,按排序顺序访问数据,累积梯度统计。
3.2 近似算法
当数据量十分庞大,以致于不能全部放入内存时,精确的贪婪算法就不可能很有效率,通样的问题也出现在分布式的数据集中,为了高效的梯度提升算法,在这两种背景下,近似的算法被提出使用。
近似算法首先根据特征分布的百分数提出侯选划分点。该算法将连续特征映射到候选点对应的桶进行划分,聚集统计信息,查找最优解决方案。
该算法的主要思想如下:枚举所有特征,根据特征,比如是第 k 个特征的分布的分位数来决定出 l个候选切分点 ,然后根据这些候选切分点把相应的样本映射到对应的桶中,对每个桶的 G,H 进行累加。最后在候选切分点集合上贪心查找
这里给了近似算法的两种变体,主要是根据候选点来源不同进行区分:
全局变体提议所有的划分候选在最初树的构建阶段确定,后期每个叶子使用相同的提议寻找划分点。全局变体需要更多的候选点,因为在每次划分之后候选点不会改善。需要设置更小的分位数间隔,这里用 ϵ 表示,3分位的分位数间隔就是 1/3,产生更多的桶,特征分裂查找基于候选点多,计算较慢,但只需在全局执行一次,全局分桶多次使用。
局部变体在每次分裂时重新分桶:每次分裂重新局部(local)分桶,可以设置较大的 ϵ ,产生更少的桶,每次特征分裂查找基于较少的候选点,计算速度快,但是需要每次节点分裂后重新执行,论文中说该方案更适合树深的场景。
当给定足够的候选点时,全局变体将达到和局部变体相同的精度。同时,我们也发现,当百分位策略给定合理的近似水平时,也可以得到和 Exact Greedy Algorithm 相同的精度
3.3 Weighted Quantile Sketch
近似算法中一个重要的步骤为提出候选划分点。通常情况下,一个特征的分位数使候选分裂点均匀地分布在数据集上。
上式可视作标签为且权重为的平方误差
其实基于带权分位数方案的思想很简单,就是如下图所示引入样本权重
3.4 稀疏感知的划分查找
许多真实世界中的问题,数据可能是稀疏的,主要原因如:存在缺失值、统计中0项、特征工程中one-hot等编码。对于数据的稀疏模式的感知是非常重要的。本文算法提出了在每个树节点提添加一个默认方向。即当一个实例值是缺失时,该实例会被分类到默认方向。最优默认方向从数据中学取
算法展示如下:
众所周知,大多数树学习模型要么只优化稠密数据,要么要对类似于分类编码值类的有限样例进行特定的处理。XGBoost以一种统一的方式处理所有的稀疏模式。更重要的是该方法利用稀疏性使计算复杂度与输入中非缺失样例的数量呈线性关系。
下图展示了稀疏感知和简单实施(不考虑稀疏性)的比较。我们发现稀疏感知比简单算法快50倍。
4.系统设计
4.1 Column Block for Parallel Learning
树学习大多数时间消耗在获取数据的排序上/为了降低排序成本,我们提出了将数据存储在内存单元中(block),每个block的数据以列压缩CSC形式存储,每列通过对应的特征值排序。输入数据仅需要在训练之前计算一次,之后的迭代中可以重复使用。
在exact greedy algorithm中,我们存储整个数据集在单个block中,通过对预排序的条目线性扫描运行划分搜索算法。我们对所有的叶子节点做划分查找,因此对block的一次扫描可以收集所有叶子分支上划分候选点的统计信息。
block结构对近似算法也是有帮助的。可以使用多个block,每个block对应数据集中行的子集,不同的block可以分布在不同的机器上或者核外硬盘上。使用排序结构,分位数查找步骤变成对排序后的列进行线性查找。这对局部近似变体是非常有价值的,因为该变体在每个分支频繁的产生候选点。
搜集每列的统计信息也可以是并行化的,提供划分查找的并行化算法。即列block支持列采样,在每个块中搜集列子集的统计信息。
谈一谈自己的理解
- 这里数据采用CSC的压缩形式进行存储,block中保存排序后的特征值和对样本的引用。由于数据的稀疏性,采用CSC压缩方式可以更为高效的提出特征值。
- 对于同时对不同叶子结点进行划分查找,将不同叶子节点数据放入不同block可以进行并行查找
- 对于列的信息统计,可以在并行对不同的特征进行划分点查找,即特征的并行化处理。
4.2 Cache-aware Access
虽然提出的block结构能够帮助优化划分查找的计算复杂度,但由于在顺序访问特征值时,访问的是一块连续的内存空间。需要间接通过行索引获取样本梯度信息,这并不是连续的内存空间。
对于exact greedy algorithm,我们设置缓存预取算法来解决这个问题。即我们在每个线程中分配一个buffer,读取梯度信息存储在buffer中。这种预取策略帮助降低数据过大时的运行时间。
在近似算法中,对块的大小进行了合理的设置。定义Block的大小为Block中最多的样本数。设置合适的大小是很重要的,设置过大则容易导致命中率低,过小则容易导致并行化效率不高。经过实验,发现比较好
4.3 "核外"块计算
系统目标在于充分利用机器资源进行可扩展的学习。除了处理器和内存之外,利用磁盘空间处理不能放入内存的数据是很重要的。为了确保核外计算,我们将数据划分为多个block,并将block存入磁盘。在计算中使用独立线程预读取block到缓存。因此计算和磁盘读取可以同步进行。但是由于磁盘读取耗时过长,降低开销和增加磁盘IO的吞吐量非常重要。我们主要使用两种技术来改进核外计算。
数据块压缩:对数据块按列压缩,在将数据传输到内存中,最后再由独立的线程解压缩。即将磁盘的读取消耗转换为解压缩所消耗的计算资源。
数据块分区:将数据分片到多个磁盘,每个磁盘分配一个预取线程,将数据fetch到in-memory buffer,再有训练线程从各个缓存中读取数据。训练线程交替读取多块缓存的同时,计算任务也在运转,提升了硬盘总体的吞吐量。
5. 相关工作
6. 端到端评估
这里主要是对XGBoost和其他模型的比较,简单介绍其中的讨论
- columns subsample:文中两次实验发现对列采样的结果比使用全部特征的精度稍低,这可能是因为特征中含有某些重要特征,贪婪的选择特征可能是有益的;但也有实验发现列采样反而会提高泛化精度,这可能是因为列采样帮助预防过拟。
7. XGBoost优缺点
7.1 优点
精度更高:GBDT 只用到一阶泰勒展开,而 XGBoost 对损失函数进行了二阶泰勒展开。
灵活性更强: GBDT 以 CART 作为基分类器,XGBoost 不仅支持 CART 还支持线性分类器,使用线性分类器的 XGBoost 相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。此外,XGBoost 工具支持自定义损失函数,只需函数支持一阶和二阶求导;
正则化:XGBoost目标函数中加入了正则化项,用于控制模型的复杂度。正则项中包含树的叶子节点个数、叶子节点权重的L2范数。正则项降低了模型的方差,使学习出来的模型更加简单,有助于防止过拟合,这也是XGBoost优于传统GBDT的一个特性。
Shrinkage:在提升树的每一步添加一个因子作为权重。 类似于学习率,它降低每个树个体的影响, 留下空间给未来的树提高模型
列采样:这种技术也被用于随机森林。不仅能降低过拟合,还能减少计算
可并行的近似算法: 树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以XGBoost还提出了一种可并行的近似算法,用于高效地生成候选的分割点。这种方法对特征值排序,以加权分位数取候选点
稀疏感知:对于特征的值有缺失的样本,XGBoost采用的稀疏感知算法可以自动学习出它的分裂方向。
XGBoost并行计算:XGBoost的并行是在特征粒度上的。决策树学习最耗时的一个步骤就是对特征的值进行排序。XGBoost以CSC压缩对数据存储入block并进行排序,后面的迭代中重复的使用该结构,减少计算量。在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
缓存访问:由于对样本梯度的访问是不连续的,这里在每个线程设置一个buffer,读取梯度信息存储在buffer中。这种预取策略帮助降低数据过大时的运行时间。
核外块计算:采用独立的线程预读取block到缓存,实现计算和磁盘读取同时进行;同时为了提高效率采用数据块压缩和数据块分割放入不同磁盘,多个线程同时读取多个磁盘提高效率。
7.2 缺点
- 虽然利用预排序和近似算法可以降低寻找最佳分裂点的计算量,但在节点分裂过程中仍需要遍历数据集;
- 预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。
参考:
深入理解XGBoost,优缺点分析,原理推导及工程实现
XGBoost 论文翻译+个人注释