XGBoost分块并行与缓存优化

XGBoost的并行不是树粒度的而是特征粒度的,随机森林就是树粒度的并行。

寻找分裂点的时候,算法中先是遍历所有特征再遍历每个特征下的所有值。

遍历特征下所有值时要求值是排序好的,这样就可以使用差加速。
如果不排序,那么计算分类时候的损失函数减少量就没法达到O(1)的复杂度,因为二叉树的分裂是> x,分到a子树这样的形式。

在这里插入图片描述

在建树的过程中,最耗时是找最优的切分点,而这个过程中,最耗时的部分是将数据排序。为了减少排序的时间,提出Block结构存储数据。

  • Block中的数据以稀疏格式CSC进行存储
  • Block中的特征进行排序(不对缺失值排序,排序只有一次
  • Block 中特征还需存储指向样本的索引,这样才能根据特征的值来取梯度。
  • 一个Block中存储一个或多个特征的值

个人理解是,这个block是原样本的一种映射,在这个block里,"样本"是按照列存储的,其实他存储的是列,而不是样本。因为样本是按照行来组织的,block中存储的是原样本的各个排序后的列。

所以就要有列中的每个元素与原样本之间的映射关系,因为在分裂节点的选择时,不仅要遍历某个特征(即列)中的所有元素,还要用到原样本的梯度(一阶导和二阶导), 所以就要通过列中的元素找到原样本。

按照block存储的好处就是,不同列之间可以并行查找,并且因为预排序了所以使得分裂节点查找时更快。坏处是空间大了一倍

缓存优化

在分块并行中,block存储了排序的列,并建立和原来样本的一一映射,这样可以通过索来找到原始样本获得梯度,但是原始样本是存放是按照列值的原始顺序的(相邻内存的样本他们对应的列值可能不是连续的,而我们现在根据排序后的列值来找原样本,那么肯定会出现在内存上的跳跃式查找,就非连续内存访问,可能导致CPU cache命中率降低。

CPU cache命中率低对于加权分位数选择分裂点的方法没太大影响,因为其选择分裂点的时候本来就是跳跃着选的,但是对于精确贪心算法的效率影响就非常大了,因为其要遍历所有样本。

下图中,calculate 上下两个部分表示连续列值上计算G和H但是其对应的样本不连续。
红色字说明了连续列值对应的样本不连续性。
在这里插入图片描述
原论文中说

A naive implementation of split enumeration introduces immediate read/write dependency between the accumulation and the non-continuous memory fetch operation

通过降低读写的依赖性来解决cache miss的问题
1.对于精确贪心算法,对每个线程分配一个连续的缓存空间,预取接下来要读取的数据,这样就降低了直接从内存读取并且cache miss消耗的时间。
2. 对于近似分割算法,选取适当的block大小即可(2^16 * each_sample_size)

参考资料

https://www.hrwhisper.me/machine-learning-xgboost/
https://arxiv.org/pdf/1603.02754.pdf

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值