【机器学习】Lightgbm 原生接口建模

427 阅读12分钟

参考网址

juejin.cn/post/707757…

总结

这一点和xgboost很像,因为lightgbm和sklearn是2个独立的机器学习算法库,
所以,lightgbm有自己的原生接口建模函数。

同时,为了方便广大的sklearn用户的使用,
LightGBM提供了sklearn风格的API接口,这使得用户可以直接在sklearn的工作流程中使用LightGBM模型,从而方便地进行数据预处理、模型训练和评估。

LightGBM是微软开发的boosting集成学习思想的一种算法,
和XGBoost算法一样,都是对GBDT的优化和高效实现,
原理有一些相似之处,但它很多方面比XGBoost算法有着更为优秀的表现,轻量级,速度快,占用内存小。

LightGBM被命名为“Light”主要是因为它的训练速度非常快,同时内存消耗较低,能够处理大量数据。
具体来说:
1. 训练速度快:相比其他梯度提升框架(如XGBoost),LightGBM在保持训练精度的前提下,能够显著提高训练速度,有时甚至能提高20倍。这主要得益于其独特的算法设计,如逐叶生长的决策树策略等leaf-wise
2. 内存消耗低:LightGBM采用了直方图算法等一系列优化措施,使得其在处理大量数据时,内存消耗相对较低。这使得LightGBM在处理大规模数据集时具有更高的效率和可扩展性。

综上所述,LightGBM被命名为“Light”是为了强调其轻量级、快速和高效的特点。
这些特点使得LightGBM在机器学习任务中,尤其是在处理大规模数据集时,成为一种非常受欢迎的选择。

lightgbm的安装

lightgbm作为常见的强大Python机器学习工具库,安装也比较简单。
lightgbm不在sklearn库中,它是一个单独的库包,和xgboost一样,都是一个单独的库包。
所以,你需要单独安装。
安装起来是非常简单方便的。

大家也可以选择国内的pip源,以获得更好的安装速度:
pip install lightgbm -i https://pypi.tuna.tsinghua.edu.cn/simple 

level-wise 和 leaf-wise

Level-wise是【按层进行生长】的决策树生长策略。
它遍历一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度(深度不大),不容易过拟合。
然而,level-wise实际上是一种·低效·的算法,因为它不加区分的对待同一层中的所有的叶子,
导致了对很多分裂增益较低的叶子进行了不必要的搜索和分裂,从而带来了额外的开销。

相比之下,Leaf-wise则是一种更为·高效·的策略。它每次从当前的所有叶子节点中,
找到分裂增益最大的一个叶子,然后分裂,如此循环。
因此,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。
但是,Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。
为了克服这个问题,一些算法(如LightGBM)在Leaf-wise之上增加了一个最大深度的限制,以在保证高效率的同时防止过拟合。

总的来说,Level-wise和Leaf-wise各有优缺点,具体使用哪种策略取决于具体的应用场景和需求。

XGBoost采用的是Level-wise的生长策略

这种策略能够同时分裂同一层的叶子,从而进行多线程优化,并且不容易过拟合。
然而,这种策略的一个潜在缺点是它不加区分地对待同一层的叶子,
这可能会导致对一些分裂增益较低的叶子进行不必要的搜索和分裂,从而带来额外的计算开销。

LightGBM采用的是Leaf-wise的生长策略

与Level-wise不同,Leaf-wise每次从当前所有叶子中找出分裂增益最大的一个叶子进行分裂,如此循环。
这种策略能更快地找到最优的分裂点,并且可以充分利用信息增益来提高模型的预测能力,从而提高精度。

然而,Leaf-wise的一个潜在缺点是可能会长出比较深的决策树,造成过拟合。

为了克服这个问题,LightGBM在Leaf-wise之上增加了一个最大深度限制,以防止过拟合。

gbdt是二叉树吗

是的,GBDT(Gradient Boosting Decision Tree)是基于二叉树的
它是一种集成模型,基评估器采用CART(分类回归二叉决策树),
并通过Gradient Boosting的方式进行集成。
CART树在构建时,会进行特征选择并根据gini系数和最小平方误差来确定最优切分特征和切分点,从而构建出一棵二叉树。因此,GBDT的基模型(弱分类器)为CART决策树,无论是针对分类问题还是回归问题,都是基于二叉树的。

GBDT、XGBoost和LightGBM之间的关系可以从它们的发展历程和特性来理解

1.  GBDT(Gradient Boosting Decision Tree)是机器学习算法中的一种,
 它采用Gradient Boosting的方法进行集成学习。
 GBDT的基分类器采用CART决策树,并通过串行的方式训练多个基分类器,每个基分类器都会在前一个基分类器的基础上进行优化,从而提升整体的预测精度。
 
2.  XGBoost(eXtreme Gradient Boosting)是GBDT的一个高效实现,
由华盛顿大学的陈天奇博士开发。
XGBoost在GBDT的基础上进行了许多改进,如引入了正则化项、支持并行计算、对缺失值处理等,
从而极大地提升了模型训练速度和预测精度。
可以说,XGBoost是GBDT的一个升级版。

3.  LightGBM(Light Gradient Boosting Machine)是微软开发的一个实现GBDT算法的框架,
它支持高效率的并行训练,具有更快的训练速度、更低的内存消耗和更好的准确率等优点。
与XGBoost相比,LightGBM在算法上进行了一些优化,如采用基于直方图的决策树算法、带深度限制的Leaf-wise的叶子生长策略等,
从而进一步提升了模型的性能。

综上所述,GBDT、XGBoost和LightGBM都是基于Gradient Boosting方法的集成学习算法,
它们之间的关系是:
     GBDT是基础的机器学习算法,
     XGBoost是GBDT的高效实现和升级版,
     而LightGBM则是在XGBoost的基础上进行进一步优化和改进的版本。

L1和L2是机器学习和深度学习中常用的两种损失函数,分别称为L1损失函数和L2损失函数。

L1损失函数,平均绝对误差(MAE),mean absolute error 
它表示的是预测值与目标值之间的绝对差异的总和。
这种损失函数对于异常值具有较强的鲁棒性(包容性),因为它在计算误差时不会考虑误差的平方,所以不会过度放大异常值的影响。

L2损失函数,也被称为最小二乘误差(LSE)或均方误差(MSE)。
它表示的是预测值与目标值之间所有平方差的总和。
与L1损失函数相比,L2损失函数对于异常值更为敏感,因为它在计算误差时会考虑误差的平方,这可能会放大异常值的影响。

在实际应用中,L2损失函数由于其良好的数学性质(例如连续性和可导性)和计算效率,通常被优先考虑。
然而,当数据集中存在异常值时,L1损失函数可能会是一个更好的选择,因为它对异常值的敏感性较低。

我们训练模型,就是要最小化损失函数。
损失函数越小,说明预测值和真实值之间的差异就越小。

损失函数和目标函数的区别

在算法模型中,损失函数和目标函数是两个相关但不同的概念。

损失函数(Loss Function)主要用于衡量模型预测结果与真实值之间的差异。
它通常是一个关于模型参数的函数,通过调整模型参数使损失函数最小化,达到优化模型的目的。
损失函数的具体形式可以根据不同的问题和模型来选择,以最大程度地反映模型的性能。常见的损失函数有均方误差、交叉熵等。

目标函数(Objective Function)是一个更加宽泛的概念,它通常是优化问题中的一个概念,用于定义最终需要优化的任务目标。
目标函数可以是损失函数本身,也可以是在损失函数基础上加上其他正则项或约束条件的复合函数(综合函数)。
目标函数考虑了模型算法的准确性、模型的复杂度、可解释度等因素,是机器学习任务的真正目标。
通过最小化或最大化目标函数,可以训练出性能更好的机器学习模型。

因此,损失函数是目标函数的一种具体形式,目标函数包含了损失函数并可能考虑其他因素。
在机器学习中,理解目标函数和损失函数的区别是非常重要的,通过合理选择目标函数和损失函数,可以使机器学习模型在实际问题中取得更好的性能。

通过原生接口训练模型

LightGBM内置了建模方式,有如下的数据格式与核心训练方法:

-   基于`lightgbm.Dataset`格式的数据。
-   基于`lightgbm.train`接口训练。

必须满足输入的数据结构的类型,
你想用人家内置的方法,你就必须按照人家的要求来,人家要你输入什么样结构的数据,你就必须输入相应的数据结构的数据。
不然,你就别用。
# coding: utf-8
import json
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error
# mse,均方差,预测值和真实值之间的差值的平方的和

# 加载数据集合
# 存储在txt文件中,用制表符分割开
print('加载数据...')
df_train = pd.read_csv('./data/regression.train.txt', header=None, sep='\t') # 训练集合
df_test = pd.read_csv('./data/regression.test.txt', header=None, sep='\t')  # 测试集合

# 设定训练集和测试集
y_train = df_train[0].values  # 第0列,是标签列
y_test = df_test[0].values    # 第0列,是标签列

X_train = df_train.drop(0, axis=1).values  # 删除掉第0列之后,剩下的是特征列
X_test = df_test.drop(0, axis=1).values    # 删除掉第0列之后,剩下的是特征列

# 这里,必须把输入,转换成合适的格式
# 构建lgb中的Dataset格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 敲定好一组参数
params = {
        'task': 'train',  # 要执行的环节,比如train, predict等
        'boosting_type': 'gbdt',
        'objective': 'regression',  # 要解决的问题,比如分类,或者回归
        'metric': {'l2', 'auc'},  # 度量指标,如果有多个,用逗号隔开
        'num_leaves': 31,
        'learning_rate': 0.05,
        'feature_fraction': 0.9,  # 随机选择部分特征,0.9就是所有特征的90%,随机选择
        'bagging_fraction': 0.8,
        'bagging_freq': 5,
        'verbose': 0
}

# 训练
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=20,
                valid_sets=lgb_eval,
                early_stopping_rounds=5)

# 保存模型
gbm.save_model('model.txt')

# 预测
print('开始预测...')
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)

# 评估
print('预估结果的rmse为:')
print(mean_squared_error(y_test, y_pred) ** 0.5)

# 查看特征名称
print('完成10轮训练...')
print('第7个特征为:')
print(repr(lgb_train.feature_name[6]))

# 存储模型
gbm.save_model('./model/lgb_model.txt')

# 特征名称
print('特征名称:')
print(gbm.feature_name())

# 特征重要度
print('特征重要度:')
print(list(gbm.feature_importance()))

# 加载模型
print('加载模型用于预测')
bst = lgb.Booster(model_file='./model/lgb_model.txt')

# 预测
y_pred = bst.predict(X_test)

# 在测试集评估效果
print('在测试集上的rmse为:')
print(mean_squared_error(y_test, y_pred) ** 0.5)

图片.png

图片.png

设置样本权重

LightGBM的建模非常灵活,它可以支持我们对于每个样本设置不同的权重学习.
设置的方式也非常简单,我们需要提供给模型一组权重数组数据,长度和样本数一致。

如下是一个典型的例子,其中`binary.train``binary.test`读取后加载为`lightgbm.Dataset`格式的输入,而在`lightgbm.Dataset`的构建参数中可以设置样本权重(这个例子中是numpy array的形态)。再基于`lightgbm.train`接口使用内置建模方式训练。

如何设置参数

1/控制树的生长

图片.png

<1>num_leaves
   叶子节点的数目。它是控制树模型复杂度的主要参数。

   如果是`level-wise`,则该参数为2depth2^{depth}2depth,其中depth为树的深度。
   但是当叶子节点数量相同的时候,leaf-wise树要远远深过level-wise树,非常容易导致过拟合。
   因此应该让num_leaves小于2depth2^{depth}2depth。
   在leaf-wise树中,并不存在depth的概念。因为不存在一个从leaves到depth的合理映射。

<2>min_data_in_leaf
   每个叶子节点的最少样本数量。
   它是处理`leaf-wise`树的过拟合的重要参数。
   将它设为较大的值,可以避免生成一个过深的树。但是也可能导致欠拟合。

<3>max_depth
  树的最大深度。该参数可以显式的限制树的深度。

(2/更快的训练速度

图片.png

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

3/更好的模型效果

图片.png

- 使用较大的`max_bin`(学习速度可能变慢)。
- 使用较小的`learning_rate`和较大的`num_iterations`- 使用较大的`num_leaves`(可能导致过拟合)。叶子节点数量越多,说明分得越细致,这就可能导致过度拟合
- 使用更大的训练数据。
- 尝试`dart`

4/缓解过拟合问题

图片.png

-   使用较小的`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`来避免生成过深的树。

总的来说,为了控制过度拟合,就是控制树的复杂程度。
比如深度,每个叶子节点中样本的最少数量, 叶子节点的数量。