1.3 LightGBM 的优势与特点


文档摘要

1.3 LightGBM 的优势与特点 第一章:LightGBM 基础与概述 1.3 LightGBM 的优势与特点:更快速、更高效的梯度提升框架 在机器学习领域,梯度提升决策树 (Gradient Boosting Decision Tree, GBDT) 算法以其强大的预测能力和广泛的适用性而备受青睐。然而,传统的 GBDT 算法在处理大规模数据集和高维度特征时,往往面临训练速度慢、内存消耗大等挑战。为了解决这些问题,微软推出了 LightGBM (Light Gradient Boosting Machine),一种基于 GBDT 的高效梯度提升框架。LightGBM 在训练速度、内存效率、准确率以及可扩展性等方面都取得了显著的提升,成为了现代机器学习工具箱中不可或缺的一部分。 1.

1.3 LightGBM 的优势与特点

第一章:LightGBM 基础与概述

1.3 LightGBM 的优势与特点:更快速、更高效的梯度提升框架

在机器学习领域,梯度提升决策树 (Gradient Boosting Decision Tree, GBDT) 算法以其强大的预测能力和广泛的适用性而备受青睐。然而,传统的 GBDT 算法在处理大规模数据集和高维度特征时,往往面临训练速度慢、内存消耗大等挑战。为了解决这些问题,微软推出了 LightGBM (Light Gradient Boosting Machine),一种基于 GBDT 的高效梯度提升框架。LightGBM 在训练速度、内存效率、准确率以及可扩展性等方面都取得了显著的提升,成为了现代机器学习工具箱中不可或缺的一部分。

1.3.1 更快的训练速度和更高的效率

LightGBM 最显著的优势之一就是其惊人的训练速度和效率。相比传统的 GBDT 算法,LightGBM 在处理大规模数据集时能够显著减少训练时间,并降低内存消耗。这主要归功于 LightGBM 采用的两项核心技术:Gradient-based One-Side Sampling (GOSS)Exclusive Feature Bundling (EFB)

1.3.1.1 Gradient-based One-Side Sampling (GOSS)

传统的 GBDT 算法在计算信息增益时,需要遍历所有样本点来寻找最优分裂点,这在大规模数据集上非常耗时。GOSS 算法的核心思想是,在计算信息增益时,不必使用所有样本点,而是通过对样本进行采样,减少计算量,从而加速训练过程

GOSS 并非随机采样,而是基于梯度的单边采样。它的基本思路是:

  1. 保留梯度绝对值较大的样本点:这些样本点在训练过程中对梯度的贡献更大,意味着模型在这些样本上的学习不足,应该更多地关注。

  2. 对梯度绝对值较小的样本点进行随机采样:这些样本点对梯度的贡献相对较小,可以适当减少其在训练中的权重。

具体来说,GOSS 算法的步骤如下:

  1. 对训练样本按照梯度绝对值进行降序排序。

  2. 选取梯度绝对值最大的前 a% 的样本点 (例如,a=20%),作为集合 A。 这些样本点被认为是“重要样本”。

  3. 从剩余的样本点中随机采样 b% 的样本点 (例如,b=10%),作为集合 B。 这些样本点被认为是“非重要样本”。

  4. 使用集合 A 和集合 B 的并集 (A ∪ B) 来计算信息增益,寻找最优分裂点。 为了弥补非重要样本被采样的比例较低,GOSS 在计算信息增益时,会对集合 B 中的样本赋予一个权重系数,放大其梯度值。

通过 GOSS 算法,LightGBM 可以在保证模型精度的前提下,大幅减少参与计算信息增益的样本数量,从而显著提升训练速度。

代码实践 (GOSS 参数设置)

在 LightGBM 中,可以通过设置 boosting_type='gbdt' (默认值,代表传统 GBDT) 或 boosting_type='goss' 来选择是否使用 GOSS 算法。此外,还可以通过 top_rateother_rate 参数来控制 GOSS 算法中集合 A 和集合 B 的采样比例。

import lightgbm as lgb from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载 Iris 数据集 iris = load_iris() X, y = iris.data, iris.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 设置 LightGBM 参数,使用 GOSS 算法 params_goss = { 'boosting_type': 'goss', # 指定使用 GOSS 'objective': 'multiclass', 'num_class': 3, 'metric': 'multi_logloss', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集 lgb_train_goss = lgb.Dataset(X_train, y_train) lgb_eval_goss = lgb.Dataset(X_test, y_test, reference=lgb_train_goss) # 训练模型 (使用 GOSS) gbm_goss = lgb.train(params_goss, lgb_train_goss, num_boost_round=20, valid_sets=lgb_eval_goss) # 设置 LightGBM 参数,不使用 GOSS (默认 GBDT) params_gbdt = { 'boosting_type': 'gbdt', # 默认 GBDT,可以显式指定 'objective': 'multiclass', 'num_class': 3, 'metric': 'multi_logloss', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集 lgb_train_gbdt = lgb.Dataset(X_train, y_train) lgb_eval_gbdt = lgb.Dataset(X_test, y_test, reference=lgb_train_gbdt) # 训练模型 (不使用 GOSS,默认 GBDT) gbm_gbdt = lgb.train(params_gbdt, lgb_train_gbdt, num_boost_round=20, valid_sets=lgb_eval_gbdt) print("GOSS 模型训练完成") print("GBDT 模型训练完成")

代码详解:

这段代码演示了如何在 LightGBM 中使用 GOSS 算法。

  • 首先,我们加载了经典的 Iris 数据集,并将其划分为训练集和测试集。

  • 然后,我们定义了两组 LightGBM 参数:params_gossparams_gbdt

    • params_goss 中,我们将 boosting_type 设置为 'goss',明确指定使用 GOSS 算法。

    • params_gbdt 中,我们保持 boosting_type 为默认值 'gbdt',即使用传统的 GBDT 算法。

  • 接下来,我们分别使用这两组参数创建 LightGBM 的 Dataset 对象,并使用 lgb.train() 函数训练模型。

  • 代码最后打印 "GOSS 模型训练完成" 和 "GBDT 模型训练完成",表明两个模型都已成功训练。

通过运行这段代码,我们可以体验到 GOSS 算法在 LightGBM 中的应用,并在实际场景中根据数据规模和性能需求选择合适的 boosting_type。虽然在这个小数据集上,速度提升可能不明显,但在大规模数据集上,GOSS 的优势将更加突出。

1.3.1.2 Exclusive Feature Bundling (EFB)

除了 GOSS 算法,LightGBM 提升训练效率的另一个关键技术是 Exclusive Feature Bundling (EFB),即互斥特征捆绑。EFB 主要针对高维度稀疏特征数据,通过将互斥特征 (即很少同时为非零值的特征) 捆绑成一个特征束,从而减少特征维度,降低计算复杂度。

互斥特征 的定义是:如果两个特征很少同时为非零值,则称这两个特征是互斥的。例如,在 one-hot 编码后的类别特征中,不同的类别特征通常是互斥的。

EFB 算法的核心思想是:

  1. 构建特征互斥图:以特征为节点,如果两个特征不是互斥的 (即同时为非零值的概率较高),则在两个节点之间连边。

  2. 图着色算法:使用图着色算法 (例如贪心算法) 对特征互斥图进行着色,将图中的节点 (特征) 划分为不同的颜色组。同一颜色组内的特征被认为是互斥的。

  3. 特征捆绑:将同一颜色组内的特征捆绑成一个特征束。捆绑的方式通常是将原始特征的值进行偏移和累加,并记录每个原始特征在特征束中的偏移量。

通过 EFB 算法,LightGBM 可以有效地减少特征维度,特别是在处理高维度稀疏特征数据时,可以显著降低内存消耗,并加速特征分裂的计算过程。

mermaid graph TD 图 (EFB 原理示意)

图表解释:

  • 原始特征: 输入模型的原始特征集合。

  • 特征互斥性分析: 分析特征之间的互斥关系,判断哪些特征可以捆绑。

  • 特征互斥图构建: 构建特征互斥图,节点代表特征,边代表非互斥关系。

  • 图着色算法: 使用图着色算法对特征互斥图进行着色,划分特征组 (颜色组)。

  • 特征分组 (颜色组): 将特征划分成不同的颜色组,同一组内的特征互斥。

  • 特征捆绑: 将同一颜色组内的特征捆绑成一个特征束。

  • 特征束: 捆绑后的新特征,维度降低。

  • 降维后的特征: 经过 EFB 处理后,特征维度降低,模型训练效率提升。

代码实践 (EFB 参数设置)

LightGBM 默认会自动尝试使用 EFB 算法,无需显式设置参数。但是,可以通过 feature_pre_filter 参数来控制是否进行特征预过滤,这会影响 EFB 的效果。此外,min_data_in_leafmin_sum_hessian_in_leaf 等参数也会间接影响 EFB 的性能。

import lightgbm as lgb from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split # 加载 Breast Cancer 数据集 (特征相对稀疏) cancer = load_breast_cancer() X, y = cancer.data, cancer.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 设置 LightGBM 参数,默认使用 EFB params_efb = { 'boosting_type': 'gbdt', 'objective': 'binary', 'metric': 'binary_logloss', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集 lgb_train_efb = lgb.Dataset(X_train, y_train) lgb_eval_efb = lgb.Dataset(X_test, y_test, reference=lgb_train_efb) # 训练模型 (默认使用 EFB) gbm_efb = lgb.train(params_efb, lgb_train_efb, num_boost_round=20, valid_sets=lgb_eval_efb) print("EFB 模型训练完成")

代码详解:

这段代码演示了 LightGBM 默认使用 EFB 算法的情况。

  • 我们加载了 Breast Cancer 数据集,这个数据集的特征相对稀疏,更适合展示 EFB 的效果。

  • 我们定义了一组 LightGBM 参数 params_efb,其中 boosting_type 为默认的 'gbdt',LightGBM 会自动尝试使用 EFB。

  • 接下来,我们创建 LightGBM 的 Dataset 对象,并使用 lgb.train() 函数训练模型。

  • 代码最后打印 "EFB 模型训练完成",表明模型已成功训练,并默认使用了 EFB 算法。

虽然代码中没有显式设置 EFB 相关参数,但 LightGBM 在内部会自动进行特征互斥性分析,并尝试应用 EFB 来优化训练效率。在处理高维度稀疏特征数据时,EFB 的效果会更加显著,可以有效降低内存消耗和训练时间。

1.3.2 更低的内存消耗

LightGBM 在内存效率方面也表现出色,尤其是在处理大规模数据集时。除了 EFB 算法可以减少特征维度从而降低内存消耗外,LightGBM 还采用了其他内存优化策略:

  • 直方图算法 (Histogram-based Algorithm):LightGBM 使用直方图算法来代替预排序算法进行特征分裂点的查找。直方图算法将连续特征值离散化到若干个 bins (桶) 中,构建特征的直方图。在寻找最优分裂点时,只需要遍历直方图的 bins,而不需要遍历所有样本点,大大减少了内存消耗和计算量。

  • 直接支持类别特征 (Direct Support for Categorical Features):LightGBM 可以直接处理类别特征,无需进行 one-hot 编码。传统的 GBDT 算法通常需要对类别特征进行 one-hot 编码,这会显著增加特征维度,导致内存消耗增加。LightGBM 通过优化类别特征的分裂算法,可以直接使用原始的类别特征进行训练,降低了内存消耗。

1.3.2.1 直方图算法 (Histogram-based Algorithm)

传统的 GBDT 算法在特征分裂时,通常采用预排序算法 (Pre-sorted Algorithm)。预排序算法需要对每个特征的所有取值进行排序,并存储排序后的结果,这在大规模数据集和高维度特征情况下会消耗大量的内存。

直方图算法则是一种更高效的特征分裂算法。它的基本思想是:

  1. 特征值离散化:将连续特征值离散化到若干个 bins (桶) 中。例如,可以将一个特征的值范围划分为 256 个 bins。

  2. 构建直方图:对每个特征构建直方图,统计每个 bin 内的样本数量和梯度累积值等信息。

  3. 遍历直方图寻找最优分裂点:在寻找最优分裂点时,只需要遍历直方图的 bins,计算不同分裂点的信息增益。

直方图算法的优势在于:

  • 减少内存消耗:直方图只需要存储每个特征的直方图信息,而不需要存储排序后的样本索引,大大减少了内存消耗。

  • 加速训练:遍历直方图 bins 的速度远快于遍历所有样本点,加速了特征分裂的计算过程。

  • 降低过拟合风险:直方图算法具有一定的正则化效果,可以降低过拟合的风险。

mermaid graph TD 图 (直方图算法原理示意)

图表解释:

  • 连续特征值: 输入模型的连续特征值。

  • 特征值离散化 (Binning): 将连续特征值划分为若干个 bins (桶)。

  • 构建直方图: 对每个特征构建直方图,统计每个 bin 内的样本信息。

  • 遍历直方图Bins: 遍历直方图的 bins,而不是遍历所有样本点。

  • 计算信息增益: 基于直方图信息计算不同分裂点的信息增益。

  • 最优分裂点: 找到信息增益最大的分裂点,作为当前节点的最优分裂特征和分裂值。

代码实践 (直方图算法默认使用)

LightGBM 默认使用直方图算法,无需显式设置参数。num_leaves 参数控制树的复杂度,间接影响直方图的粒度。

import lightgbm as lgb from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split # 加载 Boston Housing 数据集 (连续特征为主) boston = load_boston() X, y = boston.data, boston.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 设置 LightGBM 参数,默认使用直方图算法 params_histogram = { 'boosting_type': 'gbdt', 'objective': 'regression', 'metric': 'l2', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集 lgb_train_histogram = lgb.Dataset(X_train, y_train) lgb_eval_histogram = lgb.Dataset(X_test, y_test, reference=lgb_train_histogram) # 训练模型 (默认使用直方图算法) gbm_histogram = lgb.train(params_histogram, lgb_train_histogram, num_boost_round=20, valid_sets=lgb_eval_histogram) print("直方图算法模型训练完成")

代码详解:

这段代码演示了 LightGBM 默认使用直方图算法的情况。

  • 我们加载了 Boston Housing 数据集,这个数据集以连续特征为主,适合展示直方图算法的应用。

  • 我们定义了一组 LightGBM 参数 params_histogram,其中 boosting_type 为默认的 'gbdt',LightGBM 会自动使用直方图算法。

  • 接下来,我们创建 LightGBM 的 Dataset 对象,并使用 lgb.train() 函数训练模型。

  • 代码最后打印 "直方图算法模型训练完成",表明模型已成功训练,并默认使用了直方图算法。

与预排序算法相比,直方图算法在内存效率和训练速度方面都有显著优势,特别是在处理大规模连续特征数据时。LightGBM 默认使用直方图算法,使其在内存效率方面更具竞争力。

1.3.2.2 直接支持类别特征 (Direct Support for Categorical Features)

传统的 GBDT 算法通常需要将类别特征转换为数值特征才能进行训练,常用的转换方法是 one-hot 编码。然而,one-hot 编码会显著增加特征维度,尤其是在类别数量较多的情况下,会导致内存消耗大幅增加,并可能降低模型训练效率。

LightGBM 针对类别特征进行了优化,可以直接使用原始的类别特征进行训练,无需进行 one-hot 编码。LightGBM 内部实现了针对类别特征的分裂算法,可以有效地处理类别特征,并避免 one-hot 编码带来的维度灾难。

LightGBM 处理类别特征的优势在于:

  • 减少内存消耗:无需进行 one-hot 编码,避免了维度增加,降低了内存消耗。

  • 提升训练效率:直接使用类别特征进行分裂,避免了 one-hot 编码带来的计算开销,提升了训练效率。

  • 提升模型性能:在某些情况下,直接处理类别特征可以保留更多的原始信息,有助于提升模型性能。

代码实践 (类别特征处理)

在 LightGBM 中,可以通过 categorical_feature 参数指定哪些特征是类别特征。LightGBM 会自动识别并使用优化的类别特征分裂算法进行处理。

import lightgbm as lgb from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import pandas as pd # 加载 Iris 数据集 (引入类别特征) iris = load_iris() X, y = iris.data, iris.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 将第一个特征转换为类别特征 (模拟类别特征场景) X_train_df = pd.DataFrame(X_train, columns=iris.feature_names) X_test_df = pd.DataFrame(X_test, columns=iris.feature_names) X_train_df['sepal length (cm)'] = pd.cut(X_train_df['sepal length (cm)'], bins=3, labels=['small', 'medium', 'large']) X_test_df['sepal length (cm)'] = pd.cut(X_test_df['sepal length (cm)'], bins=3, labels=['small', 'medium', 'large']) X_train_categorical = X_train_df.values X_test_categorical = X_test_df.values # 设置 LightGBM 参数,指定类别特征 params_categorical = { 'boosting_type': 'gbdt', 'objective': 'multiclass', 'num_class': 3, 'metric': 'multi_logloss', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集,指定类别特征列索引 lgb_train_categorical = lgb.Dataset(X_train_categorical, y_train, categorical_feature=[0]) # 第一个特征是类别特征 lgb_eval_categorical = lgb.Dataset(X_test_categorical, y_test, reference=lgb_train_categorical) # 训练模型 (直接处理类别特征) gbm_categorical = lgb.train(params_categorical, lgb_train_categorical, num_boost_round=20, valid_sets=lgb_eval_categorical) print("类别特征处理模型训练完成")

代码详解:

这段代码演示了 LightGBM 如何直接处理类别特征。

  • 我们首先加载 Iris 数据集,并将其划分为训练集和测试集。

  • 为了模拟类别特征的场景,我们将第一个特征 'sepal length (cm)' 进行了离散化处理,使用 pd.cut() 函数将其划分为 'small'、'medium'、'large' 三个类别。

  • 我们将处理后的特征数据转换为 NumPy 数组 X_train_categoricalX_test_categorical

  • 在创建 LightGBM 的 Dataset 对象时,我们通过 categorical_feature=[0] 参数指定第一个特征 (索引为 0) 是类别特征。

  • 接下来,我们使用 lgb.train() 函数训练模型,LightGBM 会自动使用优化的类别特征分裂算法处理指定的类别特征。

  • 代码最后打印 "类别特征处理模型训练完成",表明模型已成功训练,并直接处理了类别特征。

通过 categorical_feature 参数,我们可以方便地告诉 LightGBM 哪些特征是类别特征,LightGBM 会自动进行高效处理,无需手动进行 one-hot 编码,从而降低内存消耗,提升训练效率。

1.3.3 更高的准确率 (可能)

在某些情况下,LightGBM 在保证效率的同时,也可能获得更高的准确率。这主要得益于 LightGBM 采用的 Leaf-wise (最佳优先) 的树生长策略

1.3.3.1 Leaf-wise (最佳优先) 的树生长策略

传统的 GBDT 算法通常采用 Level-wise (层级生长) 的树生长策略。Level-wise 生长策略在同一层的所有叶节点上进行分裂,并保持树的平衡性。然而,Level-wise 生长策略在某些情况下可能不是最优的,因为它可能会浪费计算资源在分裂增益较小的叶节点上。

LightGBM 采用 Leaf-wise (最佳优先) 的树生长策略。Leaf-wise 生长策略每次从当前所有叶节点中,找到分裂增益最大的叶节点进行分裂,如此循环。Leaf-wise 生长策略的优势在于:

  • 更精细的树结构:Leaf-wise 生长策略可以构建出更深、更不平衡的树结构,更精细地拟合数据,从而可能获得更高的准确率。

  • 更少的叶节点:相比 Level-wise 生长策略,在相同的叶节点数量下,Leaf-wise 生长策略可以构建出更深的树,模型复杂度更高,表达能力更强。

mermaid graph TD 图 (Level-wise vs. Leaf-wise 树生长策略)

图表解释:

  • Level-wise (层级生长): 按层级生长,同一层的所有叶节点同时分裂,树结构相对平衡。

  • Leaf-wise (最佳优先): 每次选择分裂增益最大的叶节点进行分裂,树结构可能更深、更不平衡。

代码实践 (Leaf-wise 默认使用)

LightGBM 默认使用 Leaf-wise 树生长策略,无需显式设置参数。num_leaves 参数控制叶节点的最大数量,可以间接控制树的深度和复杂度。

import lightgbm as lgb from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split # 加载 Digits 数据集 (分类任务) digits = load_digits() X, y = digits.data, digits.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 设置 LightGBM 参数,默认使用 Leaf-wise params_leafwise = { 'boosting_type': 'gbdt', 'objective': 'multiclass', 'num_class': 10, 'metric': 'multi_logloss', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0 } # 创建 LightGBM 数据集 lgb_train_leafwise = lgb.Dataset(X_train, y_train) lgb_eval_leafwise = lgb.Dataset(X_test, y_test, reference=lgb_train_leafwise) # 训练模型 (默认使用 Leaf-wise) gbm_leafwise = lgb.train(params_leafwise, lgb_train_leafwise, num_boost_round=20, valid_sets=lgb_eval_leafwise) print("Leaf-wise 模型训练完成")

代码详解:

这段代码演示了 LightGBM 默认使用 Leaf-wise 树生长策略的情况。

  • 我们加载了 Digits 数据集,这是一个多分类任务,适合展示 Leaf-wise 生长策略的优势。

  • 我们定义了一组 LightGBM 参数 params_leafwise,其中 boosting_type 为默认的 'gbdt',LightGBM 会自动使用 Leaf-wise 生长策略。


发布者: 作者: 转发
评论区 (0)
U