树模型(七):LightGBM

1 XGB vs LGB

2 直方图算法

2.1 缘起

XGBoost的Exact greedy算法流程

  1. 对每个特征都按照特征值进行排序
  2. 在每个排好序的特征都寻找最优切分点
  3. 用最优切分点进行切分

这个算法比较精确,但是缺点明显:

  1. 空间消耗大。需要保存数据的特征值。XGBoost采用Block结构,存储指向样本的索引,需要消耗两倍的内存。
  2. 时间开销大。在寻找最优切分点时,要对每个特征都进行排序,还要对每个特征的每个值都进行了遍历,并计算增益。
  3. 对Cache不友好。使用Block块预排序后,特征对梯度的访问是按照索引来获取的,是一种随机访问,而不同特征访问顺序也不一样,容易照成命中率低的问题。同时,在每一层长树的时候,需要随机访问一个行索引到叶子索引的数组,并且不同特征访问的顺序也不一样,也会造成较大的Cache miss。

使用直方图算法进行划分点的查找可以很好的克服这些缺点

2.2 算法描述

直方图算法(Histogram algorithm)的做法是把连续的浮点特征值离散化为k个整数(其实又是分桶的思想,而这些桶称为bin)比如[0,0.1)→0, [0.1,0.3)→1。

关键点(个人理解)

  • pre-bin,先分箱后分裂
  • LightGBM采用的直方图方法与XGBoost的”加权分位图”类似,直方图的边界即分裂值

同时,将特征根据其所在的bin进行梯度累加。这样,遍历一次数据后,直方图累积了需要的梯度信息,然后可以直接根据直方图,寻找最优的切分点。

流程描述(似乎loss计算那块有些问题,不过不影响理解直方图)

结合伪代码一起看

仔细看上面的伪代码,相信你有几个问题:

  • 如何将特征映射到bin呢?即如何分桶?
  • 如何构建直方图?直方图算法累加的g是什么?
  • 构建完直方图如何找最优特征,有用到二阶信息么?

2.2.1 分桶(bin)策略

对于数值型特征

  • 如果特征取值数比max_bin数量少,直接取distinct_values的中点放置
  • 特征取值比max_bin来得大,说明几个特征取值要共用一个bin
    • 计算mean size for one bin
    • 标记一个特征取值数超过mean,因为这些特征需要单独一个bin
    • 剩下的特征取值中平均每个bin的取值个数

对于类别特征

  • 首先对特征取值按出现的次数排序(大到小)
  • 取前min(max_bin, distinct_values_int.size())中的每个特征做第3步(这样可能忽略一些出现次数很少的特征取值)
  • 然后用𝑏𝑖𝑛_2_𝑐𝑎𝑡𝑒𝑔𝑜𝑟𝑖𝑐𝑎𝑙_bin_2_categorical_(vector类型)记录b对应的特征取值,以及用𝑐𝑎𝑡𝑒𝑔𝑜𝑟𝑖𝑐𝑎𝑙_2_𝑏𝑖𝑛_categorical_2_bin_(unordered_map类型) 将特征取值到哪个bin和一一对应起来。这样,以后就能很方便的进行bin到特征取值和特征取值到bin的转化。

2.2.2 做差加速

直方图算法还可以进一步加速:一个叶子节点的直方图可以由它的父亲节点的直方图与其兄弟的直方图做差得到

原来构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的#bin个桶。使用这个方法,构建完一个叶子的直方图后,可以用非常微小的代价得到它兄弟的直方图,相当于速度提升了一倍

2.3 小结

可以看出,直方图算法的有点有:

  • 可以减少内存占用,比如离散为256个Bin时,只需要用8位整形就可以保存一个样本被映射为哪个Bin(这个bin可以说就是转换后的特征),对比预排序的Exact greedy算法来说(用int_32来存储索引+ 用float_32保存特征值),可以节省7/8的空间。
  • 计算效率也得到提高,预排序的Exact greedy对每个特征都需要遍历一遍数据,并计算增益,复杂度为𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑑𝑎𝑡𝑎。而直方图算法在建立完直方图后,只需要对每个特征遍历直方图即可,复杂度为𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑏𝑖𝑛𝑠)
  • 提高缓存命中率,因为它访问梯度是连续的(直方图)。
  • 此外,在数据并行的时候,直方图算法可以大幅降低通信代价。(数据并行、特征并行在本文后面讲解)

当然也有不够精确的缺点:
由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在不同的数据集上的结果表明,离散化的分割点对最终的精度影响并不是很大,甚至有时候会更好一点。原因是决策树本来就是弱模型,分割点是不是精确并不是太重要;较粗的分割点也有正则化的效果,可以有效地防止过拟合;即使单棵树的训练误差比精确分割的算法稍大,但在梯度提升(Gradient Boosting)的框架下没有太大的影响。

3 直方图算法的改进

直方图算法仍有优化的空间,建立直方图的复杂度为𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑑𝑎𝑡𝑎)O(#feature×#data),如果能降低特征数或者降低样本数,训练的时间会大大减少。以往的降低样本数的方法中,要么不能直接用在GBDT上,要么会损失精度。而降低特征数的直接想法是去除弱的特征(通常用PCA完成),然而,这些方法往往都假设特征是有冗余的,然而通常特征是精心设计的,去除它们中的任何一个可能会影响训练精度。因此LightGBM提出了GOSS算法和EFB算法。

3.1 GOSS算法——(行)减少训练样本数

使用GOSS进行采样,使得训练算法更加的关注没有充分训练(under-trained)的样本,并且只会稍微的改变原有的数据分布。

3.1.1 直观理解

GOSS全称为Gradient-based One-Side Sampling,中文译为”基于梯度的单边采样”。具体采样方式是,对于权值大的样本全部进行保留,而对于权值小的样本,可以选择部分丢弃,是谓“单边采样”。但是这个权值为什么选择”梯度”呢?因为GOSS认为,一个样本如果其梯度值很小,则表明它的训练误差小,也就是说它很好地被训练了,这时再拟合它的意义已经不大了;并且在分裂点计算时,它对Loss下降的贡献也不大。因此,可以将梯度小的样本抛弃。

但是这样又带来一个问题,就是改变了原始样本的分布(准确来说是影响了梯度的分布),容易过拟合。为了弥补这个缺限,Goss会对抛弃的样本集合的梯度累计值进行一定程度的比例放大。

3.1.2 算法描述

GOSS的做法伪代码描述如下:

即:

  1. 根据梯度的绝对值将样本进行降序排序
  2. 选择前𝑎×100%的样本,这些称为样本A
  3. 剩下的数据(1−𝑎)×100%的数据中,随机抽取𝑏×100%的数据,这些称为样本B
  4. 在计算增益的时候,放大样本B中的梯度(1−𝑎)/𝑏倍

原有的在特征j值为d处分数据带来的增益可以定义为:

V j ∣ O ( d ) = 1 n O ( ( ∑ x i ∈ O : x i j ≤ d g i ) 2 n l ∣ O j ( d ) + ( ∑ x i ∈ O : x i j > d g i ) 2 n r ∣ O j ( d ) ) V_{j|O}(d) = \frac{1}{n_O}\left(\frac{(\sum_{x_i\in O:x_{ij} \le d}g_i)^2}{n_{l|O}^j(d)} + \frac{(\sum_{x_i\in O:x_{ij} \gt d}g_i)^2}{n_{r|O}^j(d)} \right) VjO(d)=nO1(nlOj(d)(xiO:xijdgi)2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值