LightGBM
LightGBM(Light Gradient Boosting Machine)是一个基于梯度提升决策树(GBDT)的高效机器学习框架。它是由微软公司开发的,旨在提供更快、更高效的训练和预测性能。LightGBM在许多数据科学竞赛中都表现出色,并被广泛应用于各种实际问题中,如推荐系统、搜索排名等。
LightGBM的主要特点:
更快的训练速度:相比于其他梯度提升树算法,如XGBoost,LightGBM在大数据集上能够更快地训练模型。
更低的内存占用:LightGBM使用了一种名为“基于直方图”的方法,将连续特征分桶,从而减少内存使用。
更好的准确性:通过使用一种称为“叶子优先”的树生长策略,LightGBM能够更好地处理过拟合问题,从而提高模型的泛化能力。
支持类别特征:LightGBM可以直接处理类别特征,无需进行独热编码,这可以减少内存占用并加速训练。
可扩展性:LightGBM支持并行学习以及分布式学习,可以处理非常大规模的数据集。
LightGBM的核心技术:
基于直方图的算法:LightGBM使用直方图方法将连续特征分桶,即将特征值划分到不同的区间,从而降低内存消耗。同时,直方图相减技术能够加速子节点的直方图计算,提高训练速度。
叶子优先的树生长策略:传统的GBDT算法采用“深度优先”的树生长策略,即在每一层上尽可能地生长所有节点。而LightGBM采用“叶子优先”的策略,即优先生长那些能够带来最大损失减少的节点。这种策略可以更好地处理过拟合问题,提高模型的泛化能力。
直接处理类别特征:LightGBM能够直接处理类别特征,而无需进行独热编码。它会根据特征值的出现频率自动对类别特征进行排序,然后采用类似于直方图的方式进行分桶。这种方法能够降低内存占用并加速训练。
并行学习和分布式学习:LightGBM支持并行学习和分布式学习,以便在多核处理器或分布式环境中加速训练过程。它使用以下技术实现这一目标:
-
特征并行:特征并行方法通过划分特征空间来实现并行化。不同的线程或机器负责处理不同的特征子集。这种方法可以加速直方图构建过程,并减少通信开销。
-
数据并行:数据并行方法通过划分数据集来实现并行化。每个线程或机器负责处理数据集的一个子集。然后将局部直方图汇总以构建全局直方图。这种方法可以同时利用多个处理器的计算能力,进一步提高训练速度。
-
投票并行:投票并行方法结合了特征并行和数据并行的优势。每个线程或机器负责处理数据集的一个子集,并构建局部直方图。然后通过投票机制选择最佳分割点,以减少通信开销。
自动参数调优:LightGBM提供了许多可以调整的参数,以满足不同问题的需求。通过使用贝叶斯优化等自动参数调优方法,用户可以在不需要深入了解每个参数的情况下,获得更好的模型性能。
总之,LightGBM是一个高效且强大的梯度提升决策树框架。它在许多方面优于其他GBDT算法,如XGBoost。通过使用直方图方法、叶子优先的树生长策略、直接处理类别特征以及并行学习和分布式学习等技术,LightGBM能够在提高训练速度的同时,保持较高的模型准确性。
为了理解直方图梯度提升(Histogram-based Gradient Boosting)的原理,先回顾一下回归决策树的构建过程。在构建回归树时,我们需要遍历所有特征,对于连续型特征,我们需要按特征值排序,并尝试所有可能的分割方法,计算每种分割方法的误差,然后找到误差最小的分割方式作为当前节点的分割方法。
但是,这种方法在处理大量数据时,计算量会随着样本数量和特征维度的增加而大幅度增长。例如,如果我们有100万个样本和100个数值型特征,我们可能需要尝试100亿次计算才能找到一个节点的最佳分割方法,这样的分割方式在大数据量下会导致训练速度很慢。
为了加速训练过程,我们可以采用一种改进方案:对特征值进行分箱处理。假设仍然有100万个样本和100个数值型特征,这时我们将每个特征列分成100个箱子。现在要找到最佳分割点,我们只需进行1万次计算即可,相比原来的方案,计算量最多减少了10000倍。这种方式使得算法更加高效,训练速度更快
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
# 形状:指的是matplotlib.patches 包里面的一些对象,比如我们常见的箭头,正方形,椭圆等等,也称之为“块”。
n_samples = 50
n_bins = 5
X = np.random.uniform(low=-6.0, high=6.0, size=(n_samples,))
X = np.sort(X)
y = 1 / (1 + np.exp(-X))
# 绘制数据
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(13, 4))
ax[0].scatter(X, y, s=10)
ax[0].set_title('1d regression problem with {0} points'.format(n_samples))
ax[0].set_xlabel('X')
ax[0].set_ylabel('y')
# 绘制标准的分割方式
ax[1].scatter(X, y, s=10)
for i in range(n_samples - 1):
split = (X[i] + X[i - 1]) / 2
ax[1].plot([split, split], [0.0, 1.0], c='r', linewidth=1)
ax[1].set_title('Standard: {0} splits to consider'.format(n_samples - 1))
ax[1].set_xlabel('X')
ax[1].set_ylabel('y')
# 找到最佳分割点
best = (-np.inf, np.inf)
for i in range(n_samples - 1):
split = (X[i] + X[i -