数据, 术→技巧, 机器学习, 法→原理

机器学习算法之LightGBM

钱魏Way · · 72,342 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

上一篇文章介绍了一个梯度提升决策树模型XGBoost,这篇文章我们继续学习一下GBDT模型的另一个进化版本:LightGBM。LigthGBM是boosting集合模型中的新进成员,由微软提供,它和XGBoost一样是对GBDT的高效实现,原理上它和GBDT及XGBoost类似,都采用损失函数的负梯度作为当前决策树的残差近似值,去拟合新的决策树。

LightGBM在很多方面会比XGBoost表现的更为优秀。它有以下优势:

  • 更快的训练效率
  • 低内存使用
  • 更高的准确率
  • 支持并行化学习
  • 可处理大规模数据
  • 支持直接使用category特征

从下图实验数据可以看出,LightGBM比XGBoost快将近10倍,内存占用率大约为XGBoost的1/6,并且准确率也有提升。

看完这些惊人的实验结果以后,对下面两个问题产生了疑惑:XGBoost已经十分完美了,为什么还要追求速度更快、内存使用更小的模型?对GBDT算法进行改进和提升的技术细节是什么?

提出LightGBM的动机

常用的机器学习算法,例如神经网络等算法,都可以以mini-batch的方式训练,训练数据的大小不会受到内存限制。而GBDT在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。尤其面对工业级海量的数据,普通的GBDT算法是不能满足其需求的。

LightGBM提出的主要原因就是为了解决GBDT在海量数据遇到的问题,让GBDT可以更好更快地用于工业实践。

XGBoost的优缺点

精确贪心算法

每轮迭代时,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。

优点:

  • 可以找到精确的划分条件

缺点:

  • 计算量巨大
  • 内存占用巨大
  • 易产生过拟合

Level-wise迭代方式

预排序方法(pre-sorted):首先,空间消耗大。这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如排序后的索引,为了后续快速的计算分割点),这里需要消耗训练数据两倍的内存。其次时间上也有较大的开销,在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。

优点:

  • 可以使用多线程
  • 可以加速精确贪心算法

缺点:

  • 效率低下,可能产生不必要的叶结点

对cache优化不友好

在预排序后,特征对梯度的访问是一种随机访问,并且不同的特征访问的顺序不一样,无法对cache进行优化。同时,在每一层长树的时候,需要随机访问一个行索引到叶子索引的数组,并且不同特征访问的顺序也不一样,也会造成较大的cache miss。

LightGBM在哪些地方进行了优化?

以上与其说是XGBoost的不足,倒不如说是LightGBM作者们构建新算法时着重瞄准的点。解决了什么问题,那么原来模型没解决就成了原模型的缺点。

概括来说,lightGBM主要有以下特点:

  • 基于Histogram的决策树算法
  • 带深度限制的Leaf-wise的叶子生长策略
  • 直方图做差加速
  • 直接支持类别特征(Categorical Feature)
  • Cache命中率优化
  • 基于直方图的稀疏特征优化
  • 多线程优化

决策树算法

XGBoost使用的是pre-sorted算法,能够更精确的找到数据分隔点。

  • 首先,对所有特征按数值进行预排序。
  • 其次,在每次的样本分割时,用O(#data)的代价找到每个特征的最优分割点。
  • 最后,找到最后的特征以及分割点,将数据分裂成左右两个子节点。

这种pre-sorting算法能够准确找到分裂点,但是在空间和时间上有很大的开销。

  • 由于需要对特征进行预排序并且需要保存排序后的索引值(为了后续快速的计算分裂点),因此内存需要训练数据的两倍。
  • 在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。

LightGBM使用的是histogram算法,占用的内存更低,数据分隔的复杂度更低。其思想是将连续的浮点特征离散成k个离散值,并构造宽度为k的Histogram。然后遍历训练数据,统计每个离散值在直方图中的累计统计量。在进行特征选择时,只需要根据直方图的离散值,遍历寻找最优的分割点。

使用直方图算法有很多优点。首先最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值,而这个值一般用8位整型存储就足够了,内存消耗可以降低为原来的1/8。

然后在计算上的代价也大幅降低,预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从O(#data*#feature)优化到O(k*#features)。

Histogram algorithm

Histogram algorithm应该翻译为直方图算法,直方图算法的思想也很简单,首先将连续的浮点数据转换为bin数据,具体过程是首先确定对于每一个特征需要多少的桶bin,然后均分,将属于该桶的样本数据更新为bin的值,最后用直方图表示。(看起来很高大上,其实就是直方图统计,最后我们将大规模的数据放在了直方图中)

直方图算法有几个需要注意的地方:

  • 使用bin替代原始数据相当于增加了正则化;
  • 使用bin意味着很多数据的细节特征被放弃了,相似的数据可能被划分到相同的桶中,这样的数据之间的差异就消失了;
  • bin数量选择决定了正则化的程度,bin越少惩罚越严重,欠拟合风险越高。

直方图算法需要注意的地方:

  • 构建直方图时不需要对数据进行排序(比XGBoost快),因为预先设定了bin的范围;
  • 直方图除了保存划分阈值和当前 bin 内样本数以外还保存了当前 bin 内所有样本的一阶梯度和(一阶梯度和的平方的均值等价于均方损失);

  • 阈值的选取是按照直方图从小到大遍历,使用了上面的一阶梯度和,目的是得到划分之后△loss 最大的特征及阈值。

Histogram 算法的优缺点:

  • Histogram 算法并不是完美的。由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在实际的数据集上表明,离散化的分裂点对最终的精度影响并不大,甚至会好一些。原因在于 decision tree 本身就是一个弱学习器,采用 Histogram 算法会起到正则化的效果,有效地防止模型的过拟合。
  • 时间上的开销由原来的 O(#data*#features) 降到 O(k*#features)。由于离散化,#bin 远小于 #data,因此时间上有很大的提升。

Histogram 算法还可以进一步加速。一个叶子节点的 Histogram 可以直接由父节点的 Histogram 和兄弟节点的 Histogram 做差得到。一般情况下,构造 Histogram 需要遍历该叶子上的所有数据,通过该方法,只需要遍历 Histogram 的 k 个捅。速度提升了一倍。

决策树生长策略

在 Histogram 算法之上,LightGBM 进行进一步的优化。首先它抛弃了大多数 GBDT 工具使用的按层生长(level-wise)的决策树生长策略,而使用了带有深度限制的按叶子生长(leaf-wise)算法。

XGBoost 采用的是按层生长 level(depth)-wise 生长策略,能够同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合;但不加区分的对待同一层的叶子,带来了很多没必要的开销。因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。

LightGBM 采用 leaf-wise 生长策略,每次从当前所有叶子中找到分裂增益最大(一般也是数据量最大)的一个叶子,然后分裂,如此循环。因此同 Level-wise 相比,在分裂次数相同的情况下,Leaf-wise 可以降低更多的误差,得到更好的精度。Leaf-wise 的缺点是可能会长出比较深的决策树,产生过拟合。因此 LightGBM 在 Leaf-wise 之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

直方图差加速

LightGBM 另一个优化是 Histogram(直方图)做差加速。一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的 k 个桶。利用这个方法,LightGBM 可以在构造一个叶子的直方图后,可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍。

直接支持类别特征

实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化 one-hotting 特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM 优化了对类别特征的支持,可以直接输入类别特征,不需要额外的 0/1 展开。并在决策树算法上增加了类别特征的决策规则。

one-hot 编码是处理类别特征的一个通用方法,然而在树模型中,这可能并不一定是一个好的方法,尤其当类别特征中类别个数很多的情况下。主要的问题是:

  • 可能无法在这个类别特征上进行切分(即浪费了这个特征)。使用 one-hot 编码的话,意味着在每一个决策节点上只能使用 one vs rest(例如是不是狗,是不是猫等)的切分方式。当类别值很多时,每个类别上的数据可能会比较少,这时候切分会产生不平衡,这意味着切分增益也会很小(比较直观的理解是,不平衡的切分和不切分没有区别)。
  • 会影响决策树的学习。因为就算可以在这个类别特征进行切分,也会把数据切分到很多零碎的小空间上,如图 1 左边所示。而决策树学习时利用的是统计信息,在这些数据量小的空间上,统计信息不准确,学习会变差。但如果使用下图右边的分裂方式,数据会被切分到两个比较大的空间,进一步的学习也会更好。

下图右边叶子节点的含义是 X=A 或者 X=C 放到左孩子,其余放到右孩子。

LightGBM 处理分类特征大致流程:

为了解决 one-hot 编码处理类别特征的不足。LightGBM 采用了 Many vs many 的切分方式,实现了类别特征的最优切分。用 LightGBM 可以直接输入类别特征,并产生上图右边的效果。在 1 个 k 维的类别特征中寻找最优切分,朴素的枚举算法的复杂度是 $O(2^k)$,而 LightGBM 采用了如On Grouping For Maximum Homogeneity 的方法实现了 $O(k\log k)$ 的算法。

算法流程下图所示:在枚举分割点之前,先把直方图按每个类别的均值进行排序;然后按照均值的结果依次枚举最优分割点。从下图可以看到,Sum(y)/Count(y) 为类别的均值。当然,这个方法很容易过拟合,所以在 LGBM 中加入了很多对这个方法的约束和正则化。

下图是一个简单的对比实验,可以看到该最优方法在 AUC 上提升了 1.5 个点,并且时间只多了 20%。

下面具体来讲下在代码中如何求解类别特征的最优切分的流程:

  • 离散特征建立直方图的过程:统计该特征下每一种离散值出现的次数,并从高到低排序,并过滤掉出现次数较少的特征值, 然后为每一个特征值,建立一个 bin 容器, 对于在 bin 容器内出现次数较少的特征值直接过滤掉,不建立 bin 容器。
  • 计算分裂阈值的过程:
    • 先看该特征下划分出的 bin 容器的个数,如果 bin 容器的数量小于 4,直接使用 one vs other 方式, 逐个扫描每一个 bin 容器,找出最佳分裂点;
    • 对于 bin 容器较多的情况, 先进行过滤,只让子集合较大的 bin 容器参加划分阈值计算, 对每一个符合条件的 bin 容器进行公式计算(公式如下: 该 bin 容器下所有样本的一阶梯度之和/该 bin 容器下所有样本的二阶梯度之和+正则项(参数 cat_smooth),这里为什么不是 label 的均值呢?其实上例中只是为了便于理解,只针对了学习一棵树且是回归问题的情况,这时候一阶导数是 Y, 二阶导数是 1),得到一个值,根据该值对 bin 容器从小到大进行排序,然后分从左到右、从右到左进行搜索,得到最优分裂阈值。但是有一点,没有搜索所有的 bin 容器,而是设定了一个搜索 bin 容器数量的上限值,程序中设定是 32,即参数 max_num_cat。LightGBM 中对离散特征实行的是 many vs many 策略,这 32 个 bin 中最优划分的阈值的左边或者右边所有的 bin 容器就是一个 many 集合,而其他的 bin 容器就是另一个 many 集合。
    • 对于连续特征,划分阈值只有一个,对于离散值可能会有多个划分阈值,每一个划分阈值对应着一个 bin 容器编号,当使用离散特征进行分裂时,只要数据样本对应的 bin 容器编号在这些阈值对应的 bin 集合之中,这条数据就加入分裂后的左子树,否则加入分裂后的右子树。

直接支持高效并行

LightGBM原生支持并行学习,目前支持特征并行和数据并行的两种。特征并行的主要思想是在不同机器在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。数据并行则是让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。

LightGBM针对这两种并行方法都做了优化,在特征并行算法中,通过在本地保存全部数据避免对数据切分结果的通信;在数据并行中使用分散规约(Reduce scatter)把直方图合并的任务分摊到不同的机器,降低通信和计算,并利用直方图做差,进一步减少了一半的通信量。

基于投票的数据并行则进一步优化数据并行中的通信代价,使通信代价变成常数级别。在数据量很大的时候,使用投票并行可以得到非常好的加速效果。

更具体的内容可以看NIPS2016的文章:A Communication-Efficient Parallel Algorithm for Decision Tree

网络通信优化

XGBoost由于采用pre-sorted算法,通信代价非常大,所以在并行的时候也是采用histogram算法;LightGBM采用的histogram算法通信代价小,通过使用集合通信算法,能够实现并行计算的线性加速。

LightGBM原理

论文地址:LightGBM A Highly Efficient Gradient Boosting

提升树是利用加模型与前向分布算法实现学习的优化过程,它有一些高效实现,如XGBoost, pGBRT,GBDT(Gradient Boosting Decision Tree)等。其中GBDT采用负梯度作为划分的指标(信息增益),XGBoost则利用到二阶导数。他们共同的不足是,计算信息增益需要扫描所有样本,从而找到最优划分点。在面对大量数据或者特征维度很高时,它们的效率和扩展性很难使人满意。解决这个问题的直接方法就是减少特征量和数据量而且不影响精确度,有部分工作根据数据权重采样来加速booisting的过程,但由于GBDT没有样本权重不能应用。

微软开源的LightGBM(基于GBDT的)则很好的解决这些问题,它主要包含两个算法:

单边梯度采样,Gradient-based One-Side Sampling(GOSS)

GOSS(从减少样本角度):排除大部分小梯度的样本,仅用剩下的样本计算信息增益。GBDT虽然没有数据权重,但每个数据实例有不同的梯度,根据计算信息增益的定义,梯度大的实例对信息增益有更大的影响,因此在下采样时,我们应该尽量保留梯度大的样本(预先设定阈值,或者最高百分位间),随机去掉梯度小的样本。我们证明此措施在相同的采样率下比随机采样获得更准确的结果,尤其是在信息增益范围较大时。

互斥特征绑定,Exclusive Feature Bundling(EFB)

  • EFB(从减少特征角度):捆绑互斥特征,也就是他们很少同时取非零值(也就是用一个合成特征代替)。通常真是应用中,虽然特征量比较多,但是由于特征空间十分稀疏,是否可以设计一种无损的方法来减少有效特征呢?特别在稀疏特征空间上,许多特征几乎是互斥的(例如许多特征不会同时为非零值,像one-hot),我们可以捆绑互斥的特征。最后,我们将捆绑问题归约到图着色问题,通过贪心算法求得近似解。

结合使用GOSS和EFB的GBDT算法就是LightGBM。

Gradient-based One-Side Sampling(GOSS)

GOSS是一种在减少数据量和保证精度上平衡的算法。GOSS是通过区分不同梯度的实例,保留较大梯度实例同时对较小梯度随机采样的方式减少计算量,从而达到提升效率的目的。

算法描述

AdaBoost中,样本权重是数据实例重要性的指标。然而在GBDT中没有原始样本权重,不能应用权重采样。幸运的事,我们观察到GBDT中每个数据都有不同的梯度值,对采样十分有用,即实例的梯度小,实例训练误差也就较小,已经被学习得很好了,直接想法就是丢掉这部分梯度小的数据。然而这样做会改变数据的分布,将会影响训练的模型的精确度,为了避免此问题,我们提出了GOSS。

GOSS保留所有的梯度较大的实例,在梯度小的实例上使用随机采样。为了抵消对数据分布的影响,计算信息增益的时候,GOSS对小梯度的数据引入常量乘数。GOSS首先根据数据的梯度绝对值排序,选取top a个实例。然后在剩余的数据中随机采样b个实例。接着计算信息增益时为采样出的小梯度数据乘以(1-a)/b,这样算法就会更关注训练不足的实例,而不会过多改变原数据集的分布。

理论分析

GBDT使用决策树,来学习获得一个将输入空间映射到梯度空间的函数。假设训练集有n个实例${x_1,…,x_n}$,特征维度为s。每次梯度迭时,模型数据变量的损失函数的负梯度方向表示为${g_1,…,g_n}$,决策树通过最优切分点(最大信息增益点)将数据分到各个节点。GBDT通过分割后的方差衡量信息增益。

定义:O表示某个固定节点的训练集,分割特征j的分割点d定义为:

$$V_{j|O}(d)=\frac{1}{n_O}(\frac{(\sum_{\{x_i\in O:x_{ij}\le d\}}g_i)^2}{n^j_{l|O}(d)}+\frac{(\sum_{\{x_i\in O:x_{ij}>d\}}g_i)^2}{n^j_{r|O}(d)})$$

其中,$n_O=\sum I[x_i \in O],n^j_{l|O}=\sum I[x_i \in O:x_i\ge d],n^j_{r|O}=\sum I[x_i \in O:x_i>d]$

遍历每个特征的每个分裂点,找到$d^*_j=argmax_dV_j(d)$并计算最大的信息增益$V_j(d^*_j)$,然后,将数据根据特征$j^*$的分裂点$d^*_j$将数据分到左右子节点。

在GOSS中,

  • 首先根据数据的梯度将训练降序排序。
  • 保留top a个数据实例,作为数据子集A。
  • 对于剩下的数据的实例,随机采样获得大小为b的数据子集B。
  • 最后我们通过以下方程估计信息增益:

$$\tilde{V}_j(d)=\frac{1}{n}(\frac{(\sum_{{x_i\in A:x_{ij}\le d}}g_i +\frac{1-a}{b}\sum_{{x_i\in B:x_{ij}\le d}}g_i)^2}{n^j_{l}(d)}+\frac{(\sum_{{x_i\in A:x_{ij}>d}}g_i +\frac{1-a}{b}\sum_{{x_i\in B:x_{ij}>d}}g_i)^2}{n^j_{r}(d)})\tag{1}$$

此处 GOSS 通过较小的数据集估计信息增益 $\tilde{V}_j(d)$ 将大大地减小计算量。更重要的,我们接下来理论表明 GOSS 不会丢失许多训练精度,胜过随机采样,理论的证明在附加材料。

我们定义 GOSS 近似误差为:

$$\varepsilon(d)=|\tilde{V}_j(d)-V_j(d)|$$

$$\bar{g}_l^j(d)=\frac{\sum_{x_i\in (A\cup A^c)_l}|g_i|}{n_l^j(d)}$$

概率至少是 $1-\delta 1$,有:

$$\varepsilon(d)\le C_{a,b}^2\ln(1/\delta)*\max\{\frac{1}{n_l^j(d)},\frac{1}{n_r^j(d)}\}+2*D*C_{a,b}\sqrt{\frac{\ln(1/\delta)}{n}}\tag{2}$$

其中 $C_{a,b}=\frac{1-a}{\sqrt{b}}\max_{x\in A^c}{|g_i|},D=\max(\bar{g}_l^j(d),\bar{g}_r^j(d))$

根据上述理论,我们得出以下结论:

  • GOSS 的渐近逼近比率 $O(\frac{1}{n_l^j(d)}+\frac{1}{n_r^j(d)}+\frac{1}{\sqrt{n}})$。如果数据分割不是极不平衡(例如 $n_l^j(d)\ge O(\sqrt{n})$ 和 $n_r^j\ge O(\sqrt{n})$),那么不等式(2)中近似误差将由第二项主导,当 n 趋于无穷(数据量很大时),$O(\sqrt{n})$ 将趋于 0,即数据量越大,误差越小,精度越高。
  • 随机采样是 GOSS 在 a=0 的一种情况。多数情况下,GOSS 性能优于随机采样,即以下情况:$C_{0,\beta}>C_{a,\beta-a}$,即 $\frac{\alpha_a}{\sqrt{\beta}}>\frac{1-a}{\sqrt{\beta-a}}$,其中 $\alpha_a=\max_{x_i\in A\cup A^c}|g_i|/\max_{x_i\in A^c}|g_i|$

下面分析 GOSS 的泛化性。考虑 GOSS 泛化误差 $\varepsilon_{gen}^{GOSS}(d)=|\tilde{V}_j(d)-V_*(d)|$,这是 GOSS 抽样的的实例计算出的方差增益与实际样本方差增益之间的差距。变换为 $\varepsilon_{gen}^{GOSS}(d)=|\tilde{V}_j(d)-V_j(d)|+|V_j(d)-V_*(d)|:=\varepsilon_{GOSS}(d)+\varepsilon_{gen}(d)$,因此,在 GOSS 准确的情况下,GOSS 泛化误差近似于全量的真实数据。另一方面,采样将增加基学习器的多样性(因为每次采样获得的数据可能会不同),这将提高泛化性。

Exclusive Feature Bundling(EFB)

EFB 是通过特征捆绑的方式减少特征维度(其实是降维技术)的方式,来提升计算效率。通常被捆绑的特征都是互斥的(一个特征值为零,一个特征值不为零),这样两个特征捆绑起来才不会丢失信息。如果两个特征并不是完全互斥(部分情况下两个特征都是非零值),可以用一个指标对特征不互斥程度进行衡量,称之为冲突比率,当这个值较小时,我们可以选择把不完全互斥的两个特征捆绑,而不影响最后的精度。

EBF 的算法步骤如下:

  • 将特征按照非零值的个数进行排序
  • 计算不同特征之间的冲突比率
  • 遍历每个特征并尝试合并特征,使冲突比率最小化

高位的数据通常是稀疏的,这种稀疏性启发我们设计一种无损地方法来减少特征的维度。特别的,稀疏特征空间中,许多特征是互斥的,例如他们从不同时为非零值。我们可以绑定互斥的特征为单一特征,通过仔细设计特征臊面算法,我们从特征捆绑中构建了与单个特征相同的特征直方图。这种方式的间直方图时间复杂度从 O(#data*#feature) 降到 O(#data*#bundle),由于 #bundle<<#feature,我们能够极大地加速 GBDT 的训练过程而且损失精度。 有两个问题:

  • 怎么判定那些特征应该绑在一起(build bundled)?
  • 怎么把特征绑为一个(merge feature)?

bundle(什么样的特征被绑定)?

**理论1:**将特征分割为较小量的互斥特征群是 NP 难的。

证明:将图着色问题归约为此问题,而图着色是 NP 难的,所以此问题就是 NP 难的。

给定图着色实例 G=(V,E)。以 G 的关联矩阵的每一行为特征,得到我们问题的一个实例有 |V| 个特征。很容易看到,在我们的问题中,一个独特的特征包与一组具有相同颜色的顶点相对应,反之亦然。

理论 1 说明多项式时间中求解这个 NP 难问题不可行。为了寻找好的近似算法,我们将最优捆绑问题归结为图着色问题,如果两个特征之间不是相互排斥,那么我们用一个边将他们连接,然后用合理的贪婪算法(具有恒定的近似比)用于图着色来做特征捆绑。此外,我们注意到通常有很多特征,尽管不是 100%相互排斥的,也很少同时取非零值。如果我们的算法可以允许一小部分的冲突,我们可以得到更少的特征包,进一步提高计算效率。经过简单的计算,随机污染小部分特征值将影响精度最多 $O([(1-\gamma)n]^{-2/3})$,$\gamma$ 是每个绑定中的最大冲突比率,当其相对较小时,能够完成精度和效率之间的平衡。

**算法3:**基于上面的讨论,我们设计了算法 3,伪代码见下图,具体算法:

  • 建立一个图,每个点代表特征,每个边有权重,其权重和特征之间总体冲突相关。
  • 按照降序排列图中的度数来排序特征。
  • 检查排序之后的每个特征,对他进行特征绑定或者建立新的绑定使得操作之后的总体冲突最小。

算法 3 的时间复杂度是 $O(feature^2)$,训练之前只处理一次,其时间复杂度在特征不是特别多的情况下是可以接受的,但难以应对百万维的特征。为了继续提高效率,我们提出了一个更加高效的无图的排序策略:将特征按照非零值个数排序,这和使用图节点的度排序相似,因为更多的非零值通常会导致冲突,新算法在算法 3 基础上改变了排序策略。

merging features(特征合并)

如何合并同一个 bundle 的特征来降低训练时间复杂度。关键在于原始特征值可以从 bundle 中区分出来。鉴于直方图算法存储离散值而不是连续特征值,我们通过将互斥特征放在不同的箱中来构建 bundle。这可以通过将偏移量添加到特征原始值中实现,例如,假设 bundle 中有两个特征,原始特征 A 取值[0,10],B 取值[0,20]。我们添加偏移量 10 到 B 中,因此 B 取值[10,30]。通过这种做法,就可以安全地将 A、B 特征合并,使用一个取值[0,30]的特征取代 AB。算法见算法 4,

EFB 算法能够将许多互斥的特征变为低维稠密的特征,就能够有效的避免不必要 0 值特征的计算。实际,通过用表记录数据中的非零值,来忽略零值特征,达到优化基础的直方图算法。通过扫描表中的数据,建直方图的时间复杂度将从 O(#data) 降到 O(#non_zero_data)。当然,这种方法在构建树过程中需要而额外的内存和计算开销来维持预特征表。我们在 lightGBM 中将此优化作为基本函数,因为当 bundles 是稀疏的时候,这个优化与 EFB 不冲突(可以用于 EFB)。

参考链接:『论文阅读』LightGBM 原理-LightGBM: A Highly Efficient Gradient Boosting Decision Tree

如何使用 LightGBM

LightGBM 调参指导

针对 leaf-wise 树的参数优化:

  • num_leaves:控制了叶节点的数目。它是控制树模型复杂度的主要参数。
  • 如果是 level-wise,则该参数为 $2^{depth}$,其中 depth 为树的深度。但是当叶子数量相同时,leaf-wise 的树要远远深过 level-wise 树,非常容易导致过拟合。因此应该让 num_leaves 小于 $2^{depth}$。在 leaf-wise 树中,并不存在 depth 的概念。因为不存在一个从 leaves 到 depth 的合理映射。
  • min_data_in_leaf:每个叶节点的最少样本数量。它是处理 leaf-wise 树的过拟合的重要参数。将它设为较大的值,可以避免生成一个过深的树。但是也可能导致欠拟合。
  • max_depth:控制了树的最大深度。该参数可以显式的限制树的深度。

针对更快的训练速度:

  • 通过设置 bagging_fraction 和 bagging_freq 参数来使用 bagging 方法
  • 通过设置 feature_fraction 参数来使用特征的子抽样
  • 使用较小的 max_bin
  • 使用 save_binary 在未来的学习过程对数据加载进行加速

获取更好的准确率:

  • 使用较大的 max_bin(学习速度可能变慢)
  • 使用较小的 learning_rate 和较大的 num_iterations
  • 使用较大的 num_leaves(可能导致过拟合)
  • 使用更大的训练数据
  • 尝试 dart

缓解过拟合:

  • 使用较小的 max_bin
  • 使用较小的 num_leaves
  • 使用 min_data_in_leaf 和 min_sum_hessian_in_leaf
  • 通过设置 bagging_fraction 和 bagging_freq 来使用 bagging
  • 通过设置 feature_fraction 来使用特征子抽样
  • 使用更大的训练数据
  • 使用 lambda_l1, lambda_l2 和 min_gain_to_split 来使用正则
  • 尝试 max_depth 来避免生成过深的树

核心参数:

  • config 或者 config_file:一个字符串,给出了配置文件的路径。默认为空字符串。
  • task:一个字符串,给出了要执行的任务。可以为:
    • ‘train’ 或者 ‘training’:表示是训练任务。默认为 ‘train’。
    • ‘predict’ 或者 ‘prediction’ 或者 ‘test’:表示是预测任务。
    • ‘convert_model’:表示是模型转换任务。将模型文件转换成 if-else 格式。
  • application 或者 objective 或者 app:一个字符串,表示问题类型。可以为:
    • ‘regression’ 或 ‘regression_l2’ 或 ‘mean_squared_error’ 或 ‘mse’ 或 ‘l2_root’ 或 ‘root_mean_squred_error’ 或 ‘rmse’:表示回归任务,但是使用 L2 损失函数。默认为 ‘regression’
    • ‘regression_l1’ 或者 mae 或者 mean_absolute_error:表示回归任务,但是使用 L1 损失函数。
    • ‘huber’:表示回归任务,但是使用 huber 损失函数。
    • ‘fair’:表示回归任务,但是使用 fair 损失函数。
    • ‘poisson’:表示 Poisson 回归任务。
    • ‘quantile’:表示 quantile 回归任务。
    • ‘quantile_l2’:表示 quantile 回归任务,但是使用了 L2 损失函数。
    • ‘mape’ 或者 ‘mean_absolute_precentage_error’:表示回归任务,但是使用 MAPE 损失函数
    • ‘gamma’:表示 gamma 回归任务。
    • ‘tweedie’:表示 tweedie 回归任务。
    • ‘binary’:表示二分类任务,使用对数损失函数作为目标函数。
    • ‘multiclass’:表示多分类任务,使用 softmax 函数作为目标函数。必须设置 num_class 参数
    • ‘multiclassova’ 或者 ‘multiclass_ova’ 或者 ‘ova’ 或者 ‘ovr’:表示多分类任务,使用 one-vs-all 的二分类目标函数。必须设置 num_class 参数
    • ‘xentropy’ 或者 ‘cross_entropy’:目标函数为交叉熵(同时具有可选择的线性权重)。要求标签是 [0,1] 之间的数值。
    • ‘xentlambda’ 或者 ‘cross_entropy_lambda’:替代了参数化的 cross_entropy 。要求标签是 [0,1] 之间的数值。
    • ‘lambdarank’:表示排序任务。在 lambdarank 任务中,标签应该为整数类型,数值越大表示相关性越高。label_gain 参数可以用于设置整数标签的增益(权重)
  • boosting 或者 ‘boost’ 或者 ‘boosting_type’:一个字符串,给出了基学习器模型算法。可以为:
    • ‘gbdt’:表示传统的梯度提升决策树。默认值为 ‘gbdt’
    • ‘rf’:表示随机森林。
    • ‘dart’:表示带 dropout 的 gbdt
    • goss:表示 Gradient-based One-Side Sampling 的 gbdt
  • data 或者 train 或者 train_data:一个字符串,给出了训练数据所在的文件的文件名。默认为空字符串。lightgbm 将使用它来训练模型。
  • valid 或者 test 或者 valid_data 或者 test_data:一个字符串,表示验证集所在的文件的文件名。默认为空字符串。lightgbm 将输出该数据集的度量。如果有多个验证集,则用逗号分隔。
  • num_iterations 或者 num_iteration 或者 num_tree 或者 num_trees 或者 num_round 或者 num_rounds 或者 num_boost_round 一个整数,给出了 boosting 的迭代次数。默认为 100。
    • 对于 python/R 包,该参数是被忽略的。对于 python,使用 train()/cv() 的输入参数 num_boost_round 来代替。
    • 在内部,lightgbm 对于 multiclass 问题设置了 num_class*num_iterations 棵树。
  • learning_rate 或者 shrinkage_rate:个浮点数,给出了学习率。默认为 1。在 dart 中,它还会影响 dropped trees 的归一化权重。
  • num_leaves 或者 num_leaf:一个整数,给出了一棵树上的叶子数。默认为 31
  • tree_learner 或者 tree:一个字符串,给出了 tree learner,主要用于并行学习。默认为 ‘serial’。可以为:
    • ‘serial’:单台机器的 tree learner
    • ‘feature’:特征并行的 tree learner
    • ‘data’:数据并行的 tree learner
    • ‘voting’:投票并行的 tree learner
  • num_threads 或者 num_thread 或者 nthread:一个整数,给出了 lightgbm 的线程数。默认为 OpenMP_default。
    • 为了更快的速度,应该将它设置为真正的 CPU 内核数,而不是线程的数量(大多数 CPU 使用超线程来使每个 CPU 内核生成 2 个线程)。
    • 当数据集较小的时候,不要将它设置的过大
    • 对于并行学习,不应该使用全部的 CPU 核心,因为这会使得网络性能不佳
  • device:一个字符串,指定计算设备。默认为 ‘cpu’。可以为 ‘gpu’, ‘cpu’。
    • 建议使用较小的 max_bin 来获得更快的计算速度
    • 为了加快学习速度,GPU 默认使用 32 位浮点数来求和。你可以设置 gpu_use_dp=True 来启动 64 位浮点数,但是它会使得训练速度降低。

学习控制参数:

  • max_depth:一个整数,限制了树模型的最大深度,默认值为-1。如果小于0,则表示没有限制。
  • min_data_in_leaf 或者 min_data_per_leaf 或者 min_data 或者 min_child_samples:一个整数,表示一个叶子节点上包含的最少样本数量。默认值为20
  • min_sum_hessian_in_leaf 或者 min_sum_hessian_per_leaf 或者 min_sum_hessian 或者 min_hessian 或者 min_child_weight:一个浮点数,表示一个叶子节点上的最小 hessian 之和。(也就是叶节点样本权重之和的最小值)默认为1e-3。
  • feature_fraction 或者 sub_feature 或者 colsample_bytree:一个浮点数,取值范围为[0.0,1.0],默认值为0。如果小于1.0,则 lightgbm 会在每次迭代中随机选择部分特征。如0.8表示:在每棵树训练之前选择80%的特征来训练。
  • feature_fraction_seed:一个整数,表示 feature_fraction 的随机数种子,默认为2。
  • bagging_fraction 或者 sub_row 或者 subsample:一个浮点数,取值范围为[0.0,1.0],默认值为0。如果小于1.0,则 lightgbm 会在每次迭代中随机选择部分样本来训练(非重复采样)。如0.8表示:在每棵树训练之前选择80%的样本(非重复采样)来训练。
  • bagging_freq 或者 subsample_freq:一个整数,表示每 bagging_freq 次执行 bagging。如果该参数为0,表示禁用 bagging。
  • bagging_seed 或者 bagging_fraction_seed:一个整数,表示 bagging 的随机数种子,默认为3。
  • early_stopping_round 或者 early_stopping_rounds 或者 early_stopping:一个整数,默认为0。如果一个验证集的度量在 early_stopping_round 循环中没有提升,则停止训练。如果为0则表示不开启早停。
  • lambda_l1 或者 reg_alpha:一个浮点数,表示 L1 正则化系数。默认为0
  • lambda_l2 或者 reg_lambda:一个浮点数,表示 L2 正则化系数。默认为0
  • min_split_gain 或者 min_gain_to_split:一个浮点数,表示执行切分的最小增益,默认为0
  • drop_rate:一个浮点数,取值范围为[0.0,1.0],表示 dropout 的比例,默认为1。该参数仅在 dart 中使用
  • skip_drop:一个浮点数,取值范围为[0.0,1.0],表示跳过 dropout 的概率,默认为5。该参数仅在 dart 中使用
  • max_drop:一个整数,表示一次迭代中删除树的最大数量,默认为50。如果小于等于0,则表示没有限制。该参数仅在 dart 中使用
  • uniform_drop:一个布尔值,表示是否想要均匀的删除树,默认值为 False。该参数仅在 dart 中使用
  • xgboost_dart_mode:一个布尔值,表示是否使用 xgboost dart 模式,默认值为 False。该参数仅在 dart 中使用
  • drop_seed:一个整数,表示 dropout 的随机数种子,默认值为4。该参数仅在 dart 中使用
  • top_rate:一个浮点数,取值范围为[0.0,1.0],表示在 goss 中,大梯度数据的保留比例,默认值为2。该参数仅在 goss 中使用
  • other_rate:一个浮点数,取值范围为[0.0,1.0],表示在 goss 中,小梯度数据的保留比例,默认值为1。该参数仅在 goss 中使用
  • min_data_per_group:一个整数,表示每个分类组的最小数据量,默认值为100。用于排序任务
  • max_cat_threshold:一个整数,表示 category 特征的取值集合的最大大小。默认为32。
  • cat_smooth:一个浮点数,用于 category 特征的概率平滑。默认值为10。它可以降低噪声在 category 特征中的影响,尤其是对于数据很少的类。
  • cat_l2:一个浮点数,用于 category 切分中的 L2 正则化系数。默认为10。
  • top_k 或者 topk:一个整数,用于投票并行中。默认为20。将它设置为更大的值可以获得更精确的结果,但是会降低训练速度。

IO 参数:

  • max_bin:一个整数,表示最大的桶的数量。默认值为255。lightgbm 会根据它来自动压缩内存。如 max_bin=255 时,则 lightgbm 将使用 uint8 来表示特征的每一个值。
  • min_data_in_bin:一个整数,表示每个桶的最小样本数。默认为3。该方法可以避免出现一个桶只有一个样本的情况。
  • data_random_seed:一个整数,表示并行学习数据分隔中的随机数种子。默认为1它不包括特征并行。
  • output_model 或者 model_output 或者 model_out:一个字符串,表示训练中输出的模型被保存的文件的文件名。默认 txt。
  • input_model 或者 model_input 或者 model_in:一个字符串,表示输入模型的文件的文件名。默认空字符串。对于 prediction 任务,该模型将用于预测数据,对于 train 任务,训练将从该模型继续
  • output_result 或者 predict_result 或者 prediction_result:一个字符串,给出了 prediction 结果存放的文件名。默认为 txt。
  • pre_partition 或者 is_pre_partition:一个布尔值,指示数据是否已经被划分。默认值为 False。如果为 true,则不同的机器使用不同的 partition 来训练。它用于并行学习(不包括特征并行)
  • is_sparse 或者 is_enable_sparse 或者 enable_sparse:一个布尔值,表示是否开启稀疏优化,默认为 True。如果为 True 则启用稀疏优化。
  • two_round 或者 two_round_loading 或者 use_two_round_loading:一个布尔值,指示是否启动两次加载。默认值为 False,表示只需要进行一次加载。默认情况下,lightgbm 会将数据文件映射到内存,然后从内存加载特征,这将提供更快的数据加载速度。但是当数据文件很大时,内存可能会被耗尽。如果数据文件太大,则将它设置为 True
  • save_binary 或者 is_save_binary 或者 is_save_binary_file:一个布尔值,表示是否将数据集(包括验证集)保存到二进制文件中。默认值为 False。如果为 True,则可以加快数据的加载速度。
  • verbosity 或者 verbose:一个整数,表示是否输出中间信息。默认值为1。如果小于0,则仅仅输出 critical 信息;如果等于0,则还会输出 error,warning 信息;如果大于0,则还会输出 info 信息。
  • header 或者 has_header:一个布尔值,表示输入数据是否有头部。默认为 False。
  • label 或者 label_column:一个字符串,表示标签列。默认为空字符串。你也可以指定一个整数,如 label=0 表示第0列是标签列。你也可以为列名添加前缀,如 label=prefix:label_name
  • weight 或者 weight_column:一个字符串,表示样本权重列。默认为空字符串。你也可以指定一个整数,如 weight=0 表示第0列是权重列。注意:它是剔除了标签列之后的索引。假如标签列为0,权重列为1,则这里 weight=0。你也可以为列名添加前缀,如 weight=prefix:weight_name
  • query 或者 query_column 或者 gourp 或者 group_column:一个字符串,query/groupID 列。默认为空字符串。你也可以指定一个整数,如 query=0 表示第0列是 query 列。注意:它是剔除了标签列之后的索引。假如标签列为0,query 列为1,则这里 query=0。你也可以为列名添加前缀,如 query=prefix:query_name

ignore_column 或者 ignore_feature 或者 blacklist:一个字符串,表示训练中忽略的一些列,默认为空字符串。可以用数字做索引,如 ignore_column=0,1,2 表示第 0,1,2 列将被忽略。注意:它是剔除了标签列之后的索引。

  • 你也可以为列名添加前缀,如 ignore_column=prefix:ign_name1,ign_name2
  • categorical_feature 或者 categorical_column 或者 cat_feature 或者 cat_column:一个字符串,指定 category 特征的列。默认为空字符串。可以用数字做索引,如 categorical_feature=0,1,2 表示第 0,1,2 列将作为 category 特征。注意:它是剔除了标签列之后的索引。你也可以为列名添加前缀,如 categorical_feature=prefix:cat_name1,cat_name2 在 categorycal 特征中,负的取值被视作缺失值。
  • predict_raw_score 或者 raw_score 或者 is_predict_raw_score:一个布尔值,表示是否预测原始得分。默认为 False。如果为 True 则仅预测原始得分。该参数只用于 prediction 任务。
  • predict_leaf_index 或者 leaf_index 或者 is_predict_leaf_index:一个布尔值,表示是否预测每个样本在每棵树上的叶节点编号。默认为 False。在预测时,每个样本都会被分配到每棵树的某个叶子节点上。该参数就是要输出这些叶子节点的编号。该参数只用于 prediction 任务。
  • predict_contrib 或者 contrib 或者 is_predict_contrib:一个布尔值,表示是否输出每个特征对于每个样本的预测的贡献。默认为 False。输出的结果形状为 [nsamples,nfeatures+1],之所以 +1 是考虑到 bais 的贡献。所有的贡献加起来就是该样本的预测结果。该参数只用于 prediction 任务。
  • bin_construct_sample_cnt 或者 subsample_for_bin:一个整数,表示用来构建直方图的样本的数量。默认为 200000。如果数据非常稀疏,则可以设置为一个更大的值,如果设置更大的值,则会提供更好的训练效果,但是会增加数据加载时间。
  • num_iteration_predict:一个整数,表示在预测中使用多少棵子树。默认为 -1。小于等于 0 表示使用模型的所有子树。该参数只用于 prediction 任务。
  • pred_early_stop:一个布尔值,表示是否使用早停来加速预测。默认为 False。如果为 True,则可能影响精度。
  • pred_early_stop_freq:一个整数,表示检查早停的频率。默认为 10
  • pred_early_stop_margin:一个浮点数,表示早停的边际阈值。默认为 0
  • use_missing:一个布尔值,表示是否使用缺失值功能。默认为 True 如果为 False 则禁用缺失值功能。
  • zero_as_missing:一个布尔值,表示是否将所有的零(包括在 libsvm/sparse 矩阵中未显示的值)都视为缺失值。默认为 False。如果为 False,则将 nan 视作缺失值。如果为 True,则 np.nan 和零都将视作缺失值。
  • init_score_file:一个字符串,表示训练时的初始化分数文件的路径。默认为空字符串,表示 train_data_file+”.init”(如果存在)
  • valid_init_score_file:一个字符串,表示验证时的初始化分数文件的路径。默认为空字符串,表示 valid_data_file+”.init”(如果存在)。如果有多个(对应于多个验证集),则可以用逗号 , 来分隔。
  • 目标函数的参数:

    • sigmoid:一个浮点数,用 sigmoid 函数的参数,默认为 0。它用于二分类任务和 lambdarank 任务。
    • alpha:一个浮点数,用于 Huber 损失函数和 Quantile regression,默认值为 0。它用于 huber 回归任务和 Quantile 回归任务。
    • fair_c:一个浮点数,用于 Fair 损失函数,默认值为 0。它用于 fair 回归任务。
    • gaussian_eta:一个浮点数,用于控制高斯函数的宽度,默认值为 0。它用于 regression_l1 回归任务和 huber 回归任务。
    • posson_max_delta_step:一个浮点数,用于 Poisson regression 的参数,默认值为 7。它用于 poisson 回归任务。
    • scale_pos_weight:一个浮点数,用于调整正样本的权重,默认值为 0 它用于二分类任务。
    • boost_from_average:一个布尔值,指示是否将初始得分调整为平均值(它可以使得收敛速度更快)。默认为 True。它用于回归任务。
    • is_unbalance 或者 unbalanced_set:一个布尔值,指示训练数据是否均衡的。默认为 True。它用于二分类任务。
    • max_position:一个整数,指示将在这个 NDCG 位置优化。默认为 20。它用于 lambdarank 任务。
    • label_gain:一个浮点数序列,给出了每个标签的增益。默认值为 0,1,3,7,15,….它用于 lambdarank 任务。
    • num_class 或者 num_classes:一个整数,指示了多分类任务中的类别数量。默认为 1 它用于多分类任务。
    • reg_sqrt:一个布尔值,默认为 False。如果为 True,则拟合的结果为:$\sqrt{label}$。同时预测的结果被自动转换为:${pred}^2$。它用于回归任务。

    度量参数:

    • metric:一个字符串,指定了度量的指标,默认为:对于回归问题,使用 l2;对于二分类问题,使用 binary_logloss;对于 lambdarank 问题,使用 ndcg。如果有多个度量指标,则用逗号 , 分隔。
      • ‘l1’ 或者 mean_absolute_error 或者 mae 或者 regression_l1:表示绝对值损失
      • ‘l2’ 或者 mean_squared_error 或者 mse 或者 regression_l2 或者 regression:表示平方损失
      • ‘l2_root’ 或者 root_mean_squared_error 或者 rmse:表示开方损失
      • ‘quantile’:表示 Quantile 回归中的损失
      • ‘mape’ 或者 ‘mean_absolute_percentage_error’:表示 MAPE 损失
      • ‘huber’:表示 huber 损失
      • ‘fair’:表示 fair 损失
      • ‘poisson’:表示 poisson 回归的负对数似然
      • ‘gamma’:表示 gamma 回归的负对数似然
      • ‘gamma_deviance’:表示 gamma 回归的残差的方差
      • ‘tweedie’:表示 Tweedie 回归的负对数似然
      • ‘ndcg’:表示 NDCG
      • ‘map’ 或者 ‘mean_average_precision’:表示平均的精度
      • ‘auc’:表示 AUC
      • ‘binary_logloss’ 或者 ‘binary’:表示二类分类中的对数损失函数
      • ‘binary_error’:表示二类分类中的分类错误率
      • ‘multi_logloss’ 或者 ‘multiclass’ 或者 ‘softmax’ 或者 ‘multiclassova’ 或者 ‘multiclass_ova’, 或者 ‘ova’ 或者 ‘ovr’:表示多类分类中的对数损失函数
      • ‘multi_error’:表示多分类中的分类错误率
      • ‘xentropy’ 或者 ‘cross_entropy’:表示交叉熵
      • ‘xentlambda’ 或者 ‘cross_entropy_lambda’:表示 intensity 加权的交叉熵
      • ‘kldiv’ 或者 ‘kullback_leibler’:表示 KL 散度
    • metric_freq 或者 ‘output_freq’:一个正式,表示每隔多少次输出一次度量结果。默认为 1。
      • train_metric 或者 training_metric 或者 is_training_metric:一个布尔值,默认为 False。如果为 True,则在训练时就输出度量结果。
      • ndcg_at 或者 ndcg_eval_at 或者 eval_at:一个整数列表,指定了 NDCG 评估点的位置。默认为 1,2,3,4,5。

      参考链接:

      调参示例:

      import lightgbm as lgb
      
      X = df.iloc[:, :-1]
      y = df.iloc[:, -1]
      category_feature = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
      
      cv_params = {
      'num_leaves': [13, 14, 15],
      #'max_depth': [-1, 4, 6, 8],
      #'learning_rate': [0.07, 0.08, 0.09],
      #'n_estimators': [10, 15, 20],
      #'min_child_samples': [15, 20, 25],
      #'subsample': [0.4, 0.5, 0.6, 0.7],
      #'colsample_bytree': [0.4, 0.5, 0.6, 0.7],
      #'reg_alpha': [0, 1, 2, 3, 5, 8],
      #'reg_lambda': [7, 8, 9, 10],
      #'num_iterations': [30, 40, 50],
      #'min_data_in_leaf': [30, 50, 100, 300, 400],
      #'cat_smooth': [150, 160, 170, 180, 190]
      }
      #cv_params = {'learning_rate': [0.06, 0.07, 0.08, 0.09]}
      other_params = {
      'max_depth': 4,
      'num_leaves': 15,
      'learning_rate': 0.07,
      'cat_smooth': 180,
      'num_iterations': 100,
      'colsample_bytree': 0.7,
      'subsample': 0.4,
      'reg_alpha': 3,
      'reg_lambda': 9,
      }
      model_lgb = lgb.LGBMRegressor(**other_params)
      optimized_lgb = GridSearchCV(estimator=model_lgb, param_grid=cv_params, scoring='r2', cv=5, verbose=1, n_jobs=2)
      optimized_lgb.fit(X, y, categorical_feature=category_feature)
      print('参数的最佳取值:{0}'.format(optimized_lgb.best_params_))
      print('最佳模型得分:{0}'.format(optimized_lgb.best_score_))
      print(optimized_lgb.cv_results_['mean_test_score'])
      print(optimized_lgb.cv_results_['params'])
      

      sklearn 接口形式的 LightGBM 示例

      import lightgbm as lgb
      from sklearn.metrics import mean_squared_error
      from sklearn.model_selection import GridSearchCV
      from sklearn.datasets import load_iris
      from sklearn.model_selection import train_test_split
      
      # 加载数据
      iris = load_iris()
      data = iris.data
      target = iris.target
      X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2)
      
      # 创建模型,训练模型
      gbm = lgb.LGBMRegressor(objective='regression', num_leaves=31, learning_rate=0.05, n_estimators=20)
      gbm.fit(X_train, y_train, eval_set=[(X_test, y_test)], eval_metric='l1', early_stopping_rounds=5)
      
      # 测试机预测
      y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_)
      
      # 模型评估
      print('The rmse of prediction is:', mean_squared_error(y_test, y_pred)**0.5)
      
      # feature importances
      print('Feature importances:', list(gbm.feature_importances_))
      
      # 网格搜索,参数优化
      estimator = lgb.LGBMRegressor(num_leaves=31)
      param_grid = {
      'learning_rate': [0.01, 0.1, 1],
      'n_estimators': [20, 40]
      }
      gbm = GridSearchCV(estimator, param_grid)
      gbm.fit(X_train, y_train)
      print('Best parameters found by grid search are:', gbm.best_params_)
      

      原生形式使用 lightgbm

      import lightgbm as lgb
      from sklearn.metrics import mean_squared_error
      from sklearn.datasets import load_iris
      from sklearn.model_selection import train_test_split
      
      iris = load_iris()
      data = iris.data
      target = iris.target
      X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2)
      
      # 创建成lgb特征的数据集格式
      lgb_train = lgb.Dataset(X_train, y_train)
      lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
      
      # 将参数写成字典下形式
      params = {
          'task': 'train',
          'boosting_type': 'gbdt',  # 设置提升类型
          'objective': 'regression',  # 目标函数
          'metric': {'l2', 'auc'},  # 评估函数
          'num_leaves': 31,  # 叶子节点数
          'learning_rate': 0.05,  # 学习速率
          'feature_fraction': 0.9,  # 建树的特征选择比例
          'bagging_fraction': 0.8,  # 建树的样本采样比例
          'bagging_freq': 5,  # k意味着每k次迭代执行bagging
          'verbose': 1  # <0显示致命的,=0显示错误(警告),>0显示信息
      }
      
      # 训练cv and train
      gbm = lgb.train(params, lgb_train, num_boost_round=20, valid_sets=lgb_eval, early_stopping_rounds=5)
      
      # 保存模型到文件
      gbm.save_model('model.txt')
      
      # 预测数据集
      y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
      
      # 评估模型
      print('The rmse of prediction is:', mean_squared_error(y_test, y_pred)**0.5)
      

      参数速查

      xgb lgb xgb.sklearn lgb.sklearn
      booster=’gbtree’ boosting=’gbdt’ booster=’gbtree’ boosting_type=’gbdt’
      objective=’binary:logistic’ application=’binary’ objective=’binary:logistic’ objective=’binary’
      max_depth=7 num_leaves=2**7 max_depth=7 num_leaves=2**7
      eta=0.1 learning_rate=0.1 learning_rate=0.1 learning_rate=0.1
      num_boost_round=10 num_boost_round=10 n_estimators=10 n_estimators=10
      gamma=0 min_split_gain=0.0 gamma=0 min_split_gain=0.0
      min_child_weight=5 min_child_weight=5 min_child_weight=5 min_child_weight=5
      subsample=1 bagging_fraction=1 subsample=1.0 subsample=1.0
      colsample_bytree=1.0 feature_fraction=1 colsample_bytree=1.0 colsample_bytree=1.0
      alpha=0 lambda_l1=0 reg_alpha=0.0 reg_alpha=0.0
      lambda=1 lambda_l2=0 reg_lambda=1 reg_lambda=0.0
      scale_pos_weight=1 scale_pos_weight=1 scale_pos_weight=1 scale_pos_weight=1
      seed bagging_seed feature_fraction_seed random_state=888 random_state=888
      nthread num_threads n_jobs=4 n_jobs=4
      evals valid_sets eval_set eval_set
      eval_metric metric eval_metric eval_metric
      early_stopping_rounds early_stopping_rounds early_stopping_rounds early_stopping_rounds
      verbose_eval verbose_eval verbose verbose

      参考链接:LightGBM使用

      更多参考

    5 Replies to “机器学习算法之LightGBM”

    1. 非常建议看看这篇代码解读:https://mp.weixin.qq.com/s/XxFHmxV4_iDq8ksFuZM02w

    2. 非常感谢 文章中的一些连接,很重要.

    回复 11 取消回复

    您的邮箱地址不会被公开。 必填项已用 * 标注