社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

机器学习神器:LightGBM详解

机器学习算法与Python实战 • 4 月前 • 115 次点击  

LightGBM 是微软开发的 boosting 集成模型,和 XGBoost 一样是对 GBDT 的优化和高效实现,原理有一些相似之处,但它很多方面比 XGBoost 有着更为优秀的表现。

1.LightGBM安装

LightGBM作为常见的强大Python机器学习工具库,安装也比较简单。

这些系统下的 XGBoost 安装,大家只要基于 pip 就可以轻松完成了,在命令行端输入命令如下命令即可等待安装完成。

pip install lightgbm

大家也可以选择国内的pip源,以获得更好的安装速度:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple lightgbm

 Windows系统

对于 Windows 系统而言,比较高效便捷的安装方式是:在网址http://www.lfd.uci.edu/~gohlke/pythonlibs/ 中去下载对应版本的的LightGBM安装包,再通过如下命令安装。

pip install lightgbm‑3.3.2‑cp310‑cp310‑win_amd64.whl

2.LightGBM参数手册

在ShowMeAI的前一篇内容 XGBoost工具库建模应用详解[3] 中,我们讲解到了 Xgboost 的三类参数:通用参数学习目标参数Booster参数

而 LightGBM 可调参数更加丰富,包含核心参数学习控制参数IO参数目标参数度量参数网络参数GPU参数模型参数。这里我常修改的便是核心参数,学习控制参数,度量参数等。下面我们对这些模型参数做展开讲解,更多的细节可以参考 LightGBM中文文档[4]

(1) 核心参数

config或者config_file:一个字符串,给出了配置文件的路径。默认为空字符串。


task:一个字符串,给出了要执行的任务。可以为:

  • train或者training:表示是训练任务。默认为train
  • predict或者prediction或者test:表示是预测任务。
  • convert_model:表示是模型转换任务。将模型文件转换成if-else格式。

application或者objective或者app:一个字符串,表示问题类型。可以为:

  • regressionregression_l2mean_squared_errormsel2_rootroot_mean_squred_errorrmse:表示回归任务,但是使用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。可以为gpucpu

  • 建议使用较小的max_bin来获得更快的计算速度。
  • 为了加快学习速度,GPU默认使用32位浮点数来求和。你可以设置gpu_use_dp=True来启动64位浮点数,但是它会使得训练速度降低。

(2) 学习控制参数

  • 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。将它设置为更大的值可以获得更精确的结果,但是会降低训练速度。

(3) 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”(如果存在)。如果有多个(对应于多个验证集),则可以用逗号,来分隔。

(4) 目标参数

  • sigmoid:一个浮点数,用sigmoid函数的参数,默认为0。它用于二分类任务和lambdarank任务。
  • alpha:一个浮点数,用于Huber损失函数和Quantileregression,默认值为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。它用于回归任务。

(5) 度量参数

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。

参数影响与调参建议

以下为总结的核心参数对模型的影响,及与之对应的调参建议。

(1) 对树生长控制

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

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

min_data_in_leaf:每个叶节点的最少样本数量。

  • 它是处理leaf-wise树的过拟合的重要参数。将它设为较大的值,可以避免生成一个过深的树。但是也可能导致欠拟合。

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

(2) 更快的训练速度

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

(3) 更好的模型效果

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

(4) 缓解过拟合问题

  • 使用较小的max_bin
  • 使用较小的num_leaves
  • 使用min_data_in_leafmin_sum_hessian_in_leaf
  • 通过设置bagging_fractionbagging_freq来使用bagging
  • 通过设置feature_fraction来使用特征子抽样。
  • 使用更大的训练数据。
  • 使用lambda_l1lambda_l2min_gain_to_split来使用正则。
  • 尝试max_depth来避免生成过深的树。

3.LightGBM内置建模方式

内置建模方式

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

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

下面是官方的一个简单示例,演示了读取libsvm格式数据(成Dataset格式)并指定参数建模的过程。

# coding: utf-8
import json
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error


# 加载数据集合
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
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

# 构建lgb中的Dataset格式
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,
    'verbose'0
}

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

# 保存模型
print('保存模型...')
# 保存模型到文件中
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)
加载数据...
开始训练...
[1]  valid_0's l2: 0.24288   valid_0's auc: 0.764496
Training until validation scores don't improve for 5 rounds.
[2]  valid_0'
s l2: 0.239307  valid_0's auc: 0.766173
[3]  valid_0'
s l2: 0.235559  valid_0's auc: 0.785547
[4]  valid_0'
s l2: 0.230771  valid_0's auc: 0.797786
[5]  valid_0'
s l2: 0.226297  valid_0's auc: 0.805155
[6]  valid_0'
s l2: 0.223692  valid_0's auc: 0.800979
[7]  valid_0'
s l2: 0.220941  valid_0's auc: 0.806566
[8]  valid_0'
s l2: 0.217982  valid_0's auc: 0.808566
[9]  valid_0'
s l2: 0.215351  valid_0's auc: 0.809041
[10] valid_0'
s l2: 0.213064  valid_0's auc: 0.805953
[11] valid_0'
s l2: 0.211053  valid_0's auc: 0.804631
[12] valid_0'
s l2: 0.209336  valid_0's auc: 0.802922
[13] valid_0'
s l2: 0.207492  valid_0's auc: 0.802011
[14] valid_0'
s l2: 0.206016  valid_0's auc: 0.80193
Early stopping, best iteration is:
[9]  valid_0'
s l2: 0.215351  valid_0's auc: 0.809041
保存模型...
开始预测...
预估结果的rmse为:
0.4640593794679212

设置样本权重

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

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

# coding: utf-8
import json
import lightgbm as lgb
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error
import warnings
warnings.filterwarnings("ignore")

# 加载数据集
print('加载数据...')
df_train = pd.read_csv('./data/binary.train', header=None, sep='\t')
df_test = pd.read_csv('./data/binary.test', header=None, sep='\t')
W_train = pd.read_csv('./data/binary.train.weight', header=None)[0]
W_test = pd.read_csv('./data/binary.test.weight', header=None)[0]

y_train = df_train[0].values
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

num_train, num_feature = X_train.shape

# 加载数据的同时加载权重
lgb_train = lgb.Dataset(X_train, y_train,
                        weight=W_train, free_raw_data=False)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train,
                       weight=W_test, free_raw_data=False)

# 设定参数
params = {
    '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
}

# 产出特征名称
feature_name = ['feature_' + str(col) for col in range(num_feature)]

print('开始训练...')
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                valid_sets=lgb_train,  # 评估训练集
                feature_name=feature_name,
                categorical_feature=[21])
加载数据...
开始训练...
[1]  training's binary_logloss: 0.68205
[2]  training'
s binary_logloss: 0.673618
[3]  training's binary_logloss: 0.665891
[4]  training'
s binary_logloss: 0.656874
[5]  training's binary_logloss: 0.648523
[6]  training'
s binary_logloss: 0.641874
[7]  training's binary_logloss: 0.636029
[8]  training'
s binary_logloss: 0.629427
[9]  training's binary_logloss: 0.623354
[10] training'
s binary_logloss: 0.617593

模型存储与加载

上述建模过程得到的模型对象,可以通过save_model成员函数进行保存。保存好的模型可以通过lgb.Booster加载回内存,并对测试集进行预测。

具体示例代码如下:




    
# 查看特征名称
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)
完成10轮训练...
第7个特征为:
'feature_6'
特征名称:
['feature_0''feature_1''feature_2''feature_3''feature_4''feature_5''feature_6''feature_7''feature_8''feature_9''feature_10''feature_11''feature_12''feature_13''feature_14''feature_15''feature_16''feature_17''feature_18''feature_19''feature_20''feature_21''feature_22''feature_23''feature_24''feature_25''feature_26''feature_27']
特征重要度:
[8, 5, 1, 19, 7, 33, 2, 0, 2, 10, 5, 2, 0, 9, 3, 3, 0, 2, 2, 5, 1, 0, 36, 3, 33, 45, 29, 35]
加载模型用于预测
在测试集上的rmse为:
0.4629245607636925

继续训练

LightGBM 为 boosting模型,每一轮训练会增加新的基学习器,LightGBM 还支持基于现有模型和参数继续训练,无需每次从头训练。

如下是典型的示例,我们加载已经训练10轮(即10颗树集成)的lgb模型,在此基础上继续训练(在参数层面做了一些改变,调整了学习率,增加了一些 bagging 等缓解过拟合的处理方法)

# 继续训练
# 从./model/model.txt中加载模型初始化
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model='./model/lgb_model.txt',
                valid_sets=lgb_eval)

print('以旧模型为初始化,完成第 10-20 轮训练...')

# 在训练的过程中调整超参数
# 比如这里调整的是学习率
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                learning_rates=lambda iter: 0.05 * (0.99 ** iter),
                valid_sets=lgb_eval)

print('逐步调整学习率完成第 20-30 轮训练...')

# 调整其他超参数
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                valid_sets=lgb_eval,
                callbacks=[lgb.reset_parameter(bagging_fraction=[0.7] * 5 + [0.6] * 5)])

print('逐步调整bagging比率完成第 30-40 轮训练...')



    
[11] valid_0's binary_logloss: 0.616177
[12] valid_0'
s binary_logloss: 0.611792
[13] valid_0's binary_logloss: 0.607043
[14] valid_0'
s binary_logloss: 0.602314
[15] valid_0's binary_logloss: 0.598433
[16] valid_0'
s binary_logloss: 0.595238
[17] valid_0's binary_logloss: 0.592047
[18] valid_0'
s binary_logloss: 0.588673
[19] valid_0's binary_logloss: 0.586084
[20] valid_0'
s binary_logloss: 0.584033
以旧模型为初始化,完成第 10-20 轮训练...
[21] valid_0's binary_logloss: 0.616177
[22] valid_0'
s binary_logloss: 0.611834
[23] valid_0's binary_logloss: 0.607177
[24] valid_0'
s binary_logloss: 0.602577
[25] valid_0's binary_logloss: 0.59831
[26] valid_0'
s binary_logloss: 0.595259
[27] valid_0's binary_logloss: 0.592201
[28] valid_0'
s binary_logloss: 0.589017
[29] valid_0's binary_logloss: 0.586597
[30] valid_0'
s binary_logloss: 0.584454
逐步调整学习率完成第 20-30 轮训练...
[31] valid_0's binary_logloss: 0.616053
[32] valid_0'
s binary_logloss: 0.612291
[33] valid_0's binary_logloss: 0.60856
[34] valid_0'
s binary_logloss: 0.605387
[35] valid_0's binary_logloss: 0.601744
[36] valid_0'
s binary_logloss: 0.598556
[37] valid_0's binary_logloss: 0.595585
[38] valid_0'
s binary_logloss: 0.593228
[39] valid_0's binary_logloss: 0.59018
[40] valid_0'
s binary_logloss: 0.588391
逐步调整bagging比率完成第 30-40 轮训练...

自定义损失函数

LightGBM 支持在训练过程中,自定义损失函数和评估准则,其中损失函数的定义需要返回损失函数一阶和二阶导数的计算方法,评估准则部分需要对数据的 label 和预估值进行计算。其中损失函数用于训练过程中的树结构学习,而评估准则很多时候是用在验证集上进行效果评估。

# 自定义损失函数需要提供损失函数的一阶和二阶导数形式
def loglikelood(preds, train_data):
    labels = train_data.get_label()
    preds = 1. / (1. + np.exp(-preds))
    grad = preds - labels
    hess = preds * (1. - preds)
    return grad, hess


# 自定义评估函数
def binary_error(preds, train_data):
    labels = train_data.get_label()
    return 'error', np.mean(labels != (preds > 0.5)), False


gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                fobj=loglikelood,
                feval=binary_error,
                valid_sets=lgb_eval)

print('用自定义的损失函数与评估标准完成第40-50轮...')
[41] valid_0's binary_logloss: 0.614429  valid_0's error: 0.268
[42] valid_0's binary_logloss: 0.610689  valid_0's error: 0.26
[43] valid_0's binary_logloss: 0.606267  valid_0's error: 0.264
[44] valid_0's binary_logloss: 0.601949  valid_0's error: 0.258
[45] valid_0's binary_logloss: 0.597271  valid_0's error: 0.266
[46] valid_0's binary_logloss: 0.593971  valid_0's error: 0.276
[47] valid_0's binary_logloss: 0.591427  valid_0's error: 0.278
[48] valid_0's binary_logloss: 0.588301  valid_0's error: 0.284
[49] valid_0's binary_logloss: 0.586562  valid_0's error: 0.288
[50] valid_0's binary_logloss: 0.584056  valid_0's error: 0.288
用自定义的损失函数与评估标准完成第40-50轮...

4.LightGBM预估器形态接口

SKLearn形态预估器接口

和 XGBoost 一样,LightGBM 也支持用 SKLearn 中统一的预估器形态接口进行建模,如下为典型的参考案例,对于读取为 Dataframe 格式的训练集和测试集,可以直接使用 LightGBM 初始化LGBMRegressor进行 fit 拟合训练。使用方法与接口,和 SKLearn 中其他预估器一致。

# coding: utf-8
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV

# 加载数据
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
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

print('开始训练...')
# 初始化LGBMRegressor
gbm = lgb.LGBMRegressor(objective='regression',
                        num_leaves=31,
                        learning_rate=0.05,
                        n_estimators=20)

# 使用fit函数拟合
gbm.fit(X_train, y_train,
        eval_set=[(X_test, y_test)],
        eval_metric='l1',
        early_stopping_rounds=5)

# 预测
print('开始预测...')
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_)
# 评估预测结果
print('预测结果的rmse是:')
print(mean_squared_error(y_test, y_pred) ** 0.5)
加载数据...
开始训练...
[1]  valid_0's l1: 0.491735
Training until validation scores don'
t improve for 5 rounds.
[2]  valid_0's l1: 0.486563
[3]  valid_0'
s l1: 0.481489
[4]  valid_0's l1: 0.476848
[5]  valid_0'
s l1: 0.47305
[6]  valid_0's l1: 0.469049
[7]  valid_0'
s l1: 0.465556
[8]  valid_0's l1: 0.462208
[9]  valid_0'
s l1: 0.458676
[10] valid_0's l1: 0.454998
[11] valid_0'
s l1: 0.452047
[12] valid_0's l1: 0.449158
[13] valid_0'
s l1: 0.44608
[14] valid_0's l1: 0.443554
[15] valid_0'
s l1: 0.440643
[16] valid_0's l1: 0.437687
[17] valid_0'
s l1: 0.435454
[18] valid_0's l1: 0.433288
[19] valid_0'
s l1: 0.431297
[20] valid_0's l1: 0.428946
Did not meet early stopping. Best iteration is:
[20] valid_0'
s l1: 0.428946
开始预测...
预测结果的rmse是:
0.4441153344254208

网格搜索调参

上面提到 LightGBM 的预估器接口,整体使用方法和 SKLearn 中其他预估器一致,所以我们也可以使用 SKLearn 中的超参数调优方法来进行模型调优。

如下是一个典型的网格搜索交法调优超参数的代码示例,我们会给出候选参数列表字典,通过GridSearchCV进行交叉验证实验评估,选出 LightGBM 在候选参数中最优的超参数。

# 配合scikit-learn的网格搜索交叉验证选择最优超参数
estimator = lgb.LGBMRegressor(num_leaves=31)

param_grid = {
    'learning_rate': [0.010.11],
    'n_estimators': [2040]
}

gbm = GridSearchCV(estimator, param_grid)

gbm.fit(X_train, y_train)

print('用网格搜索找到的最优超参数为:')
print(gbm.best_params_)
用网格搜索找到的最优超参数为:
{'learning_rate': 0.1, 'n_estimators': 40}

绘图解释

LightGBM 支持对模型训练进行可视化呈现与解释,包括对于训练过程中的损失函数取值与评估准则结果的可视化、训练完成后特征重要度的排序与可视化、基学习器(比如决策树)的可视化。

以下为参考代码:

# coding: utf-8
import lightgbm as lgb
import pandas as pd

try:
    import matplotlib.pyplot as plt
except ImportError:
    raise ImportError('You need to install matplotlib for plotting.')

# 加载数据集
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
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

# 构建lgb中的Dataset数据格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_test = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 设定参数
params = {
    'num_leaves'5,
    'metric': ('l1''l2'),
    'verbose'0
}

evals_result = {}  # to record eval results for plotting

print('开始训练...')
# 训练
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=100,
                valid_sets=[lgb_train, lgb_test],
                feature_name=['f' + str(i + 1for i in range(28)],
                categorical_feature=[21],
                evals_result=evals_result,
                verbose_eval=10)

print('在训练过程中绘图...')
ax = lgb.plot_metric(evals_result, metric='l1')
plt.show()

print('画出特征重要度...')
ax = lgb.plot_importance(gbm, max_num_features=10)
plt.show()

print('画出第84颗树...')
ax = lgb.plot_tree(gbm, tree_index=83, figsize=(208), show_info=['split_gain'])
plt.show()

#print('用graphviz画出第84颗树...')
#graph = lgb.create_tree_digraph(gbm, tree_index=83, name='Tree84')
#graph.render(view=True)
加载数据...
开始训练...
[10] training's l2: 0.217995 training's l1: 0.457448 valid_1's l2: 0.21641   valid_1's l1: 0.456464
[20] training's l2: 0.205099 training's l1: 0.436869 valid_1's l2: 0.201616  valid_1's l1: 0.434057
[30] training's l2: 0.197421 training's l1: 0.421302 valid_1's l2: 0.192514  valid_1's l1: 0.417019
[40] training's l2: 0.192856 training's l1: 0.411107 valid_1's l2: 0.187258  valid_1's l1: 0.406303
[50] training's l2: 0.189593 training's l1: 0.403695 valid_1's l2: 0.183688  valid_1's l1: 0.398997
[60] training's l2: 0.187043 training's l1: 0.398704 valid_1's l2: 0.181009  valid_1's l1: 0.393977
[70] training's l2: 0.184982 training's l1: 0.394876 valid_1's l2: 0.178803  valid_1's l1: 0.389805
[80] training's l2: 0.1828   training's l1: 0.391147 valid_1's l2: 0.176799  valid_1's l1: 0.386476
[90] training's l2: 0.180817 training' s l1: 0.388101 valid_1's l2: 0.175775  valid_1's l1: 0.384404
[100]   training's l2: 0.179171 training's l1: 0.385174 valid_1's l2: 0.175321  valid_1's l1: 0.382929

参考资料

[1]

图解机器学习 | LightGBM模型详解: https://www.showmeai.tech/article-detail/195

[2]

图解python | 安装与环境设置](https://www.showmeai.tech/article-detail/65: https://www.showmeai.tech/article-detail/65

[3]

XGBoost工具库建模应用详解: https://www.showmeai.tech/article-detail/204

[4]

LightGBM中文文档: https://lightgbm.apachecn.org/#/

【机器学习】特征提取:传统算法 vs 深度学习
神经网络是如何学习预测的?
机器学习周刊【第二期】:300个机器学习应用案例
叫板GPT-4的Gemini,我做了一个聊天网页,可图片输入,附教程



    Python社区是高质量的Python/Django开发社区
    本文地址:http://www.python88.com/topic/166096
     
    115 次点击