论文精读—XGBoost paper

文章目录

      • 引言
      • 1. XGBoost算法预备知识
      • 2. XGBoost算法结构分
      • 3. XGBoost算法之贪心算法寻找分裂点
      • 4. XGBoost算法之近似算法和加权分桶
      • 5. XGBoost算法之缺失值处理
      • 6. XGBoost算法之其他优化

引言

  Xgboost以CART决策树为子模型,通过Gradient Tree Boosting实现多棵CART树的集成学习,得到最终模型。XGBoost本质上还是一个GBDT,但是力争把速度和效率发挥到极致;XGBoost是一个优化的分布式梯度增强库,旨在实现高效,灵活和便携;XGBoost提供了并行树提升,可以快速准确地解决许多数据科学问题;在工业界大规模数据方面,XGBoost的分布式版本有广泛的可移植性,支持在Hadoop等分布式环境下运行。下面以陈天奇XGBoost论文为框架来讲解,XGBoost论文链接提取码:1234

1. XGBoost算法预备知识

  1. 目标函数方面
    XGBoostGBDT一样同样适用了加法模型与前向分布算法,其区别在于,GBDT通过经验风险最小化来确定下一个决策树参数,XGBoost通过结构风险极小化来确定下一个决策树参数 Θ m \Theta_{m} Θm,通俗理解来说,就是XGBoost目标函数中加入了正则项
    在这里插入图片描述
    其中 Ω ( h m ( x ) ) \Omega(h_m(x)) Ω(hm(x))为第m颗决策树的正则项,这是XGBoostGBDT的一个重要区别,其目标函数可写为:
    在这里插入图片描述
  2. 泰勒展开式方面
    GBDT是泰勒一阶展开式,XGBoost是泰勒二阶展开式
    在这里插入图片描述
    原论文中将 L ( y i ~ , ( ^ y i ) < m − 1 > ) L(\widetilde{y_i},\hat(y_i)^{}) L(yi ,(^yi)<m1>)合并到constant中,因为真实标签 y i ~ \widetilde{y_i} yi 是已知的,当求第 m m m步时,第 m − 1 m-1 m1步的预测值也是知道,所以这个值是一个常量。
  3. 决策树改写
    XGBoostGBDT对决策树进行了改写,两者类似,只不过改写的结果不同
    在这里插入图片描述

2. XGBoost算法结构分

  首先先明白XGBoost目标函数中的正则项 Ω ( h m ( x ⃗ ) ) \Omega(h_m(\vec{x})) Ω(hm(x ))是如何计算的?
在这里插入图片描述
然后将正则化项公式代入目标函数(损失函数)中,得到

在这里插入图片描述
则损失函数可化简为:
在这里插入图片描述
此时假设 w j 和 T 、 G j 、 H j w_j和T、G_j、H_j wjTGjHj 都无关的话,那么损失函数实质上是一个关于 w j w_j wj的一元二次方程。根据一元二次方程求极值公式可得:
在这里插入图片描述
w j ∗ w_j^* wj代入损失函数得,
在这里插入图片描述
这个就是所谓的结构分

上一步的化简,我们假设 w j 和 T 、 G j 、 H j w_j和T、G_j、H_j wjTGjHj都无关, w j w_j wj表示树的叶子节点,所以实质是在假设已知树的结构,而事实上损失函数与 T T T是相关的,甚至和树的结构相关,所以定义 L ∗ L^* L为一种scoring function,来衡量已知树结构情况下目标函数的最小值

这就把损失函数求极值问题转化成衡量已知树结构情况下损失函数的最小值的问题,那么,我们只要知道了树结构,我们就可以解决这个问题。下面举例:在已知树结构下,如何求目标函数的最小值?
在这里插入图片描述

3. XGBoost算法之贪心算法寻找分裂点

  现在的问题是:如何得到最佳的树的结构,来使得目标函数全局最小?我们可以采用贪心算法来寻找最佳树结构使得目标函数全局最小。贪心算法的思想:对现有的叶结点加入一个分裂,然后考虑分裂之后目标函数降低多少,如果目标函数下降,则说明可以分裂,如果目标函数不下降,则说明该叶结点不宜分裂。
  贪心算法判断叶结点适不适宜分裂具体过程为:对于叶结点的某个特征,假如给定其分裂点,定义划分到左子结点的样本的集合为: I L = i ∣ q ( x ⃗ i ) = L \Iota_{L}={i|q(\vec{x}_i)=L} IL=iq(x i)=L;定义划分到右子结点的样本的集合为: I R = i ∣ q ( x ⃗ i ) = R \Iota_{R}={i|q(\vec{x}_i)=R} IR=iq(x i)=R,则右:
在这里插入图片描述
在这里插入图片描述
  上面一段我们已经知道了如何判断某个叶结点适不适宜分裂,但是这是在假设给定其分裂点的前提下。对于每个叶结点来说,存在很多个分裂点,且可能很多分类点都能够带来收益。那么我们应该如何解决呢?解决的办法是:对于叶结点中的所有可能的分裂点排序后进行一次扫描,然后计算每个分裂点的增益,选取增益最大的分裂点作为本叶结点的最优分裂点。
  贪心算法寻找分裂点伪代码如下:
在这里插入图片描述
  贪心算法的缺点为:分裂点贪心算法尝试所有特征和所有分裂位置,从而求得最优分裂点。当样本太大且特征为连续值时,这种暴力做法的计算量太大,计算效率比较低。并且,贪心算法也不容易进行并行运算。不过,我们在平时用到的XGBoost的单线程版本与sklearn版本都是基于贪心算法来寻找分裂点的。接下来,介绍的近似算法正是对贪心算法这一缺点的改进。

4. XGBoost算法之近似算法和加权分桶

  近似算法针对贪心算法计算量大的缺点,用分桶的思想,使得循环的次数减少(贪心算法每一个点都要循环一次,近似算法每一个桶循环一次)。近似算法的思想为:
在这里插入图片描述
  近似算法寻找分裂点伪代码如下:
在这里插入图片描述
  近似算法伪代码中提到了两种分桶模式:全局分桶与局部分桶。
在这里插入图片描述
分桶时的桶区间间隔大小是个重要的参数。区间间隔越小,则桶越多,则划分的越精细,候选的拆分点就越多。
  XGBoost算法中运用的是加权分桶算法。首先,先定义一个排序函数,
在这里插入图片描述
在这里插入图片描述
  我们已经知道了什么是加权分桶?那么我们应该怎么理解加权分桶呢?
损失函数:
在这里插入图片描述
可以改写为:
在这里插入图片描述
原论文,这里写作减号,我们实际推导下来会发现会是加号,可以这么理解:这里不管是加号还是减号都不会影响函数的最值 4 a c − b 2 4 a \frac{4ac-b^2}{4a} 4a4acb2。改写以后,很像平方损失,可以发现对于第 t t t棵决策树而言,它等价于样本 x i x_i xi的真实label为 g i h i \frac{g_i}{h_i} higi,权重为 h i h_i hi,损失函数为平方损失。因此分桶时每个桶的权重为 h h h,所以分桶时使得 h h h更加均匀会使近似算法效果更好一点。
  原论文中给出了贪心算法、近似算法全局分桶、近似算法局部分桶的方法比较:
在这里插入图片描述
从上图我们可以看到, Global 策略在候选点数多时(eps 小)可以和 Local 策略在候选点少时(eps 大)具有相似的精度。此外我们还发现,在eps取值合理的情况下,分位数策略可以获得与贪心算法相同的精度。全局策略需要更细的分桶才能达到本地策略的精确度, 但全局策略在选取候选切分点集合时比本地策略更简单。在XGBoost系统中, 用户可以根据需求自由选择使用精确贪心算法、近似算法全局策略、近似算法本地策略, 算法均可通过参数进行配置。

5. XGBoost算法之缺失值处理

  这是一个面试中经常问的问题。
  真实场景中,有很多可能导致产生稀疏。如:数据缺失、某个特征上出现很多0项、人工进行one-hot编码导致的大量的0。

  • 理论上,数据缺失和数值0的含义是不同的,数值0是有效的。
  • 实际上,在xgboost中,数值0的处理方式类似缺失值的处理方式,都视为稀疏特征。
    在xgboost中,数值0的处理方式和缺失值的处理方式是统一的。这只是一个计算上的优化,用于加速对稀疏特征的处理速度。
  • 对于稀疏特征,只需要对有效值进行处理,无效值则采用默认的分裂方向。
    注意:每个结点的默认分裂方向可能不同。
  • 在XGBoost算法实现中,允许对数值0做不同的处理。可以将0视作缺失值,也可以将0视作有效值。如果数值0是有真实意义的,则建议将其视作有效值。

  我们看输出可以看到返回的是当前叶结点的最佳分裂点,前面也介绍了几个算法来寻找最佳分类点,所以缺失值处理算法并不是一个新的算法,只是在寻找分裂点的算法中加入了一些手段,使算法能够处理缺失值。XGBoost缺失值处理算法如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  原论文中算法伪代码如下:
在这里插入图片描述

6. XGBoost算法之其他优化

  • 预排序—列块并行学习
    在树的生成过程中,最耗时的一个步骤是在每次寻找最佳分裂点时都需要对特征的值进行排序。而xgboost在训练之前会根据特征对数据进行排序,然后保存到块结构中,并在每个块结构中都采用了稀疏矩阵存储格式(CSC)进行存储,后面的训练过程中会重复的使用块结构,可以大大减小工作量。
    在这里插入图片描述

  • 缓存访问
      列块并行学习的设计可以减少节点分裂时的计算量,在顺序访问特征值时,访问的是一块连续的内存空间,但通过特征值持有的索引(样本索引)访问样本获取一阶、二阶导数时,这个访问操作访问的内存空间并不连续,这样可能造成cpu缓存命中率低,影响算法效率。
      为了解决缓存命中率低的问题,XGBoost 提出了缓存访问算法:为每个线程分配一个连续的缓存区,将需要的梯度信息存放在缓冲区中,这样就实现了非连续空间到连续空间的转换,提高了算法效率。此外适当调整块大小,也可以有助于缓存优化。
    在这里插入图片描述

  • “核外”块计算
      当数据量非常大时,我们不能把所有的数据都加载到内存中。那么就必须将一部分需要加载进内存的数据先存放在硬盘中,当需要时再加载进内存。这样操作具有很明显的瓶颈,即硬盘的IO操作速度远远低于内存的处理速度,肯定会存在大量等待硬盘IO操作的情况。针对这个问题作者提出了“核外”计算的优化方法。具体操作为,将数据集分成多个块存放在硬盘中,使用一个独立的线程专门从硬盘读取数据,加载到内存中,这样算法在内存中处理数据就可以和从硬盘读取数据同时进行。此外,XGBoost 还用了两种方法来降低硬盘读写的开销:

  • 块压缩(Block Compression)。数据按列进行压缩,并且在硬盘到内存的传输过程中自动解压缩。对于行索引,只保存第一个索引值,然后用16位的整数保存与该block第一个索引的差值。作者通过测试在block设置为 2 16 2^{16} 216个样本大小时,压缩比率几乎达到 26 % ∼ 29 % 26\% \sim 29\% 26%29%

  • 块分区(Block Sharding )。块分区是将特征block分区存放在不同的硬盘上,每个硬盘对应一个预取线程,以此来增加硬盘IO的吞吐量。

参考:

  • 机器学习—XGboost的原理、工程实现与优缺点
  • XGBoost paper

如果对您有帮助,麻烦点赞关注,这真的对我很重要!!!如果需要互关,请评论或者私信!
在这里插入图片描述


你可能感兴趣的:(论文解读,XGBoost,paper,论文解读)