LightGBM学习笔记
lightgbm是基于boosting的学习框架,使用的是基于决策树的学习算法,具有更快的训练速度、更低的内存使用、更高的正确率、并且能够支持并行化学习,可以用来处理大规模数据,本文以视频 https://www.bilibili.com/video/BV1Ca4y1t7DS?p=14对lightgbm相关知识进行总结扩充
1. lightgbm概述
1.1 lightgbm介绍
Lightgbm是GDBT的进化版本,从名字就可以看出它是一个轻量级(light)的梯度提升器(GBM),在处理大规模数据集的时候lightgbm依然能够具有良好的性能
lightgbm主要通过使用分布式GBDT并选择基于直方图的决策树算法,来实现更多数据的使用以及通信代价的降低
其核心是在于寻找最优分割点
1.2 lightgbm和xgboost对比
xgboost回忆
xgboost属于boosting家族,是GBDT算法的一个时间,在模型训练过程中是聚焦残差,在目标函数中是用来二阶泰勒展开式并加入了正则项,在决策树的生成过程中采用了精确贪心策略;寻找最佳分裂点的时候,使用了预排序算法,对所有特征按照数值进行预排序,然后遍历所有特征上的分裂点,计算按照这些候选分裂点后的全部样本的目标函数增益,找打最大的那个增益对应的特征和候选分裂点位,从而进行分裂,这样一层一层的完成建树的过程,xgboost训练的时候,是通过加法的方式进行训练,也就是每一次通过聚焦残差训练一棵树出来,之后的结果都是所有树的加权和的表示
xgboost缺点
- 每次寻找最优分裂点的时候都是先进行预排序,计算所有特征的所有分裂点按照这些分裂点位分裂后的全部目标函数增益,如果把整个训练数据装进内存,则会限制训练数据的大小,如果不装进内存,反复读写训练数据又会消耗非常大的时间。
- 预排序方法的时间和空间的消耗都很大
lightgbm中使用直方图算法优点:
XGboost | LightGBM | |
---|---|---|
树的生长策略(如何进行树的节点的分割) | 按层进行分割,这种分割的优点是比较容易进行工程化并行 ,但是不容易学到最优的树 | 按叶子节点进行分割,缺点是容易生成较深的树,所以需要对树的深度进行限制 |
节点分割算法 | 预排序,先按照特征大小进行预排序后再进行分割 | 利用直方图算法 |
内存消耗 | 2*feature的个数*data的个数*4Bytes(因为是浮点型的所以是需要再乘以一个4Bytes) | feature个数*data*1Bytes(统计直方图之后可以直接存储直方图即可,内存上减少了很多) |
计算生长增益时候的复杂度 | O(data*feature个数) | O(bin*feature个数),相对于xgboost降低很多 |
cache-line aware optimization | n/a | 40%speed_up on Higgs data |
Caregorical feature support | n/a | 8x speed_up on Expo data |
1.3lgb中树的生长策略
XGboost中树时按层生长的,同一层的所有节点都做分裂,最后剪枝,虽然这种方法避免了过拟合,但是带来了很多额外开销
lightgmb中只对增益最大的进行切分,降低了模型损失,但是会生成较深的决策树,同时存在过拟合问题,可以通过在模型参数中设置决策树的深度
如图中可以看出,只对增益最大的进行分类,需要注意的是,如第三次分裂的时候是对【所有】的叶子节点计算增益后,来确定选择哪个进行分裂
2 .直方图算法及改进
2.1 直方图算法
XGboost中寻找最优分割点的时候采用的是精确贪心算法(Exact greedy),其过程为
- 对每个特征值按照特征进行排序
- 在每个排好序的特征中寻找最优切分点
- 用最优切分点进行切分
优点:比较精确
缺点:需要遍历所有离散化的值,空间消耗比较大,时间开销大,对内存不友好
Lightgbm中采用的直方图算法
,通过把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图,代替了xgboost中预排序算法。当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点,相比于XGBoost中需要遍历所有离散化的值,而在这里只要需要遍历k个直方图的值
bins为直方图中的一列
直方图构建算法
分桶流程
对于数值型特征和类别特征采用不同的分桶策略
小技巧:直方图做差,叶子的直方图可以由其父亲节点的脂肪去与其兄弟的直方图做差得到,从而降低运算效率
直方图算法过程
2.2 直方图算法改进
直方图存在以下问题
- 由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在实际的数据集上表明,离散化的分裂点对最终的精度影响并不大,甚至会好一些。原因在于decision tree本身就是一个弱学习器,分割点是不是精确并不是太重要,采用Histogram算法会起到正则化的效果,有效地防止模型的过拟合(bin数量决定了正则化的程度,bin越少惩罚越严重,欠拟合风险越高)。 直方图算法可以起到的作用就是可以减小分割点的数量, 加快计算。
- 另一个是效率的问题,我们可以可以通过降低特征维数和样本数来提高效率,为此Lightgbm中提出了GOSS算法(从样本数量上来降低)和EFB算法(从特征维数上来降低)
2.2.1 GOSS算法
lightgbm在计算增益的时候使用的是一阶梯度和二阶梯度,如果样本的梯度越小,样本的训练误差越小,表明样本已训练的很好,直接做法是丢掉这部分已经训练好的样本,但是这样会影响数据的分布,因此在lightgbm中采用了单边的采样方式来适配,即GOSS采样策略,在进行最优节点划分的时候使用不是全部的样本,而是使用的所有的大梯度样本和一部分的小梯度样本
没有使用goss之前计算直方图算法带来的增益
使用goss之后,计算量变小了,因为对于小梯度样本只是采用了部分而非全部,从而降低了计算量
通过上面,我们就通过采样的方式,选出了我们的样本,两个梯度大的6号和7号,然后又从剩下的样本里面随机选了2个梯度小的,4号和2号,这时候我们重点看看基于采样样本的估计直方图长什么样子,毕竟我们从8个里面选出了四个,如果直接把另外四个给删掉的话,这时候会改变数据的分布,但应该怎么做呢? 也就是乘上(1-a)/b来放大梯度小的样本权重,具体算法如下
来放大梯度小的样本的权重到底是怎么算的? 看下图:
2.2.2 EFB算法
EFB算法是通过合并高维稀疏数据中的互斥特征以降低数据维数进而提高效率,在进行合并的时候主要面临两大问题
- 哪些特征是可以合并的? 通过贪心算法实现
- 如何将特征合并为bundle实现降维?合并算法
如何判断哪些特征可以合并? - 首先将所有的特征看成图的各个顶点,将不相互独立的特征用一条边连起来,边的权重就是两个相连接的特征的总冲突值(也就是这两个特征上不同时为0的样本个数)。
- 然后按照节点的度对特征降序排序, 度越大,说明与其他特征的冲突越大
- 对于每一个特征, 遍历已有的特征簇,如果发现该特征加入到特征簇中的矛盾数不超过某一个阈值,则将该特征加入到该簇中。 如果该特征不能加入任何一个已有的特征簇,则新建一个簇,将该特征加入到新建的簇中。
3.LightGBM中的并行计算优化算法
3.1 特征并行
每个工作节点(worker中拥有部分feature),先找到每个节点局部的最优切分点,再通过点对点通信得到全局的最优划分点
在找到全局的最后划分点之后的处理方式为:
传统方法中,因为需要广播所以网络通信量比较大
lightgbm中的方法每个工作节点中都保存了所有的特征集,因此在找到最佳划分点之后不需要广播,减少了网络通信量,但是增加了存储代价
3.2 数据并行
每个worker中拥有所有的特征和部分数据,每个数据先构建局部直方图,在组合得到全局直方图,再总全局直方图中得到全局最优分割点
3.3 投票并行
PV-Tree对原始的lightgbm中投票(寻找最优切分点)的方法进行了改进,先构建局部直方图进行局部投票找到k个最优的局部分割点,再将这几个局部的最优分割点进行聚合,得到全局的结果
每个worker拥有部分数据,它们来独自的构建local-feature,构建local-feature后找到top-feature,然后进行投票得到global top feature,然后利用glocal top feature在local中选择相应的特征再进行聚合得到全局的最优结果,进一步得到全局的最优分割点,再广播给worker得到全局最优划分
4.LightGBM在python中的实现
4.1 lgm的参数解释
Lightgbm支持两种形式的调用接口:原生形式和sklearn接口的形式。
lgb原生库API, sklearn库下的lgbAPI
1.原生形式使用lightgbm
LightGBM的参数主要包括以下几类:
- Core Parameters
- Learning Control Parameters
- IO Parameters
- Objective Parameters
- Metric Parameters
- Network Parameters
- GPU Parameters
(1)Core Parameters参数
Index | Core Parameters | 默认值 | 含义 | 用法 |
---|---|---|---|---|
1 | task | default = train | 数据的用途 | 可以用作train/predict/refit |
2 | objective | default = regression | 学习的任务类型 | 回归:regression/regression_l2/regression_l1;二分类:binary;多分类:multiclass and so on |
3 | boosting | default = gbdt | 选择要用的集成学习算法 | gbdt/gbrt/ rf/ random_forest,/dart/ goss |
4 | data | default = “” | 指定训练集的文件路径 | |
5 | valid | default = “” | 指定验证集的文件路径,支持多个验证集 | |
6 | num_iterations | default = 100 | 训练的迭代次数 | >=0;通常大于100 |
7 | learning_rate | default = 0.1 | 收缩因子,类似于学习率,为每一棵树都添加一个收缩因子,从而减少每棵树的影响 | 通常为0.1,0.01,0.003,0.001 |
(2)Learning Control Parameters
Index | Learning Control Parameters | 默认值 | 含义 | 用法 | 注意事项 |
---|---|---|---|---|---|
1 | max_depth | default = -1 | 控制单棵树的最大深度 | int型,用来避免过拟合;小于0表示对树的深度没有限制 | |
2 | min_data_in_leaf | default = 20 | 控制每个叶子节点的最小样本数 | int型,>=0,用来避免过拟合 | |
3 | min_sum_hessian_in_leaf | default = 1e-3 | 控制每个叶子节点的最小二阶梯度和 | int型,>=0,用来避免过拟合 | |
4 | bagging_fraction | default = 1.0 | 控制抽样的样本数 | double型,[0,1],用来避免过拟合,同时能够加快训练 | 若想进行bagging操作,bagging_freq应该取非零值 |
5 | bagging_freq | default = 0 | 控制进行bagging的频率 | int型,取值为0表示不进行bagging,取值为k表示每k轮迭代进行一次bagging | 若想进行bagging操作,bagging_fraction取值应该小于等于1 |
6 | bagging_seed | default = 3 | 控制bagging的随机种子 | int型 | |
7 | feature_fraction | default = 1.0 | 控制每一轮迭代训练随机选择的特征数 | double型,[0,1],用来避免过拟合,同时能够加快训练 | |
8 | feature_fraction_seed | default = 2 | 控制随机选择特征的随机种子 | int型 | |
9 | early_stopping_round | default = 0 | 控制早停的最近轮数,如果验证集上的某一评价指标在最近的early_stopping_round轮内没有提升,则停止训练 | int型,<=0表示没有早停控制 | |
10 | max_delta_step | default = 0.0 | 控制输出的最大叶子数,其最大叶子数为learning_rate * max_delta_step | double型,<= 0表示没有限制 | |
11 | lambda_l1 | default = 0.0 | L-1范数的正则化系数 | double型,用来控制拟合程度,同时有降维的效果 | |
12 | lambda_l2 | default = 0.0 | L-2范数的正则化系数 | double型,用来控制拟合程度 | |
13 | min_gain_to_split | default = 0.0 | 控制节叶点是否分割的最小增益 | double型 | |
14 | drop_rate | default = 0.1 | 控制dropout时,前面的树模型被用到的概率 | double型,[0.0,1.0],用来降低拟合程度 | 只在boosting = dart需要设置 |
15 | max_drop | default = 50 | 控制在一轮boosting 迭代中,被忽略的树模型的最大值 | int型,<=0 表示没有限制 | 只在boosting = dart需要设置 |
16 | skip_drop | default = 0.5 | 控制在一轮boosting 迭代中,不进行dropout的概率 | double型,[0.0,1.0], | 只在boosting = dart需要设置 |
17 | xgboost_dart_mode | default = false | 控制是否使用xgboost中dart的模式 | bool型 | 只在boosting = dart需要设置 |
18 | drop_seed | default = 4 | 控制dropout的随机种子 | int型 | 只在boosting = dart需要设置 |
19 | top_rate | default = 0.2 | 控制大梯度样本的保留比例 | double型, | 只在boosting = goss时需要设置 |
20 | other_rate | default = 0.1 | 控制小梯度样本的保留比例 | double型 | 只在boosting = goss时需要设置 |
21 | min_data_per_group | default = 100 | 设置每一个类别组的最小样本数 | int型,>0 | 用在类别特征 |
22 | max_cat_threshold | default = 32 | 设置类别特征的最大阈值点 | int型,> |