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

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

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

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

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

(1) 核心参数

configconfig_file
task
traintrainingtrainpredictpredictiontestconvert_model
applicationobjectiveapp
regressionregression_l2mean_squared_errormsel2_rootroot_mean_squred_errorrmseregressionregression_l1maemean_absolute_errorhuberfairpoissonquantilequantile_l2mapemean_absolute_precentage_errorgammatweediebinarymulticlassnum_classmulticlassovamulticlass_ovaovaovrone-vs-allnum_classxentropycross_entropyxentlambdacross_entropy_lambdacross_entropylambdaranklambdaranklabel_gain
boostingboostboosting_type
gbdtgbdtrfdartgoss
datatraintrain_data
validtestvalid_datatest_data
num_iterationsnum_iterationnum_treenum_treesnum_roundnum_roundsnum_boost_roundboosting
train()/cv()num_boost_roundnum_class*num_iterations
learning_rateshrinkage_rate
num_leavesnum_leaf
tree_learnertreeserial
serialfeaturedatavoting
num_threadsnum_threadnthreadOpenMP_default
  • 为了更快的速度,应该将它设置为真正的CPU内核数,而不是线程的数量(大多数CPU使用超线程来使每个CPU内核生成2个线程)。
  • 当数据集较小的时候,不要将它设置的过大。
  • 对于并行学习,不应该使用全部的CPU核心,因为这会使得网络性能不佳。
devicecpugpucpu
max_bingpu_use_dp=True

(2) 学习控制参数

max_depthmin_data_in_leafmin_data_per_leafmin_datamin_child_samplesmin_sum_hessian_in_leafmin_sum_hessian_per_leafmin_sum_hessianmin_hessianmin_child_weightfeature_fractionsub_featurecolsample_bytreefeature_fraction_seedfeature_fractionbagging_fractionsub_rowsubsamplebagging_freqsubsample_freqbagging_freqbagging_seedbagging_fraction_seedearly_stopping_roundearly_stopping_roundsearly_stoppingearly_stopping_roundlambda_l1reg_alphalambda_l2reg_lambdamin_split_gainmin_gain_to_splitdrop_rateskip_dropmax_dropuniform_dropxgboost_dart_modedrop_seedtop_rateother_ratemin_data_per_groupmax_cat_thresholdcat_smoothcat_l2top_ktopk

(3) IO参数

max_binmax_bin=255min_data_in_bindata_random_seedoutput_modelmodel_outputmodel_outinput_modelmodel_inputmodel_inoutput_resultpredict_resultprediction_resultpre_partitionis_pre_partitionis_sparseis_enable_sparseenable_sparsetwo_roundtwo_round_loadinguse_two_round_loadingsave_binaryis_save_binaryis_save_binary_fileverbosityverboseheaderhas_headerlabellabel_columnlabel=prefix:label_nameweightweight_columnweight=prefix:weight_namequeryquery_columngourpgroup_columnquery=prefix:query_nameignore_columnignore_featureblacklistignore_column=0,1,2ignore_column=prefix:ign_name1,ign_name2categorical_featurecategorical_columncat_featurecat_columncategorical_feature=0,1,2categorical_feature=prefix:cat_name1,cat_name2predict_raw_scoreraw_scoreis_predict_raw_scorepredict_leaf_indexleaf_indexis_predict_leaf_indexpredict_contribcontribis_predict_contribbin_construct_sample_cntsubsample_for_binnum_iteration_predictpred_early_stoppred_early_stop_freqpred_early_stop_marginuse_missingzero_as_missingnp.naninit_score_filevalid_init_score_file,

(4) 目标参数

sigmoidalphafair_cgaussian_etaposson_max_delta_stepscale_pos_weightboost_from_averageis_unbalanceunbalanced_setmax_positionlabel_gainnum_classnum_classesreg_sqrt

(5) 度量参数

metricbinary_logloss,
l1mean_absolute_errormaeregression_l1l2mean_squared_errormseregression_l2regressionl2_rootroot_mean_squared_errorrmsequantilemapemean_absolute_percentage_errorhuberfairpoissongammagamma_deviancetweediendcgmapmean_average_precisionaucbinary_loglossbinarybinary_errormulti_loglossmulticlasssoftmax或者,或者或者multi_errorxentropycross_entropyxentlambdacross_entropy_lambdakldivkullback_leibler
metric_freqoutput_freq
train_metrictraining_metricis_training_metric
ndcg_atndcg_eval_ateval_at

参数影响与调参建议

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

(1) 对树生长控制

num_leaves
level-wise
min_data_in_leaf
leaf-wise
max_depth

(2) 更快的训练速度

bagging_fractionbagging_freqfeature_fractionmax_binsave_binary

(3) 更好的模型效果

max_binlearning_ratenum_iterationsnum_leavesdart

(4) 缓解过拟合问题

max_binnum_leavesmin_data_in_leafmin_sum_hessian_in_leafbagging_fractionbagging_freqbaggingfeature_fractionlambda_l1lambda_l2min_gain_to_splitmax_depth

内置建模方式

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

lightgbm.Datasetlightgbm.train
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.764496Training 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.80193Early stopping, best iteration is:[9]  valid_0's l2: 0.215351  valid_0's auc: 0.809041保存模型...开始预测...预估结果的rmse为:0.4640593794679212

设置样本权重

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

binary.trainbinary.testlightgbm.Datasetlightgbm.Datasetlightgbm.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

模型存储与加载

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轮...

SKLearn形态预估器接口

LGBMRegressor
# 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.491735Training 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.428946Did not meet early stopping. Best iteration is:[20] valid_0's l1: 0.428946开始预测...预测结果的rmse是:0.4441153344254208

网格搜索调参

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

GridSearchCV
# 配合scikit-learn的网格搜索交叉验证选择最优超参数
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('用网格搜索找到的最优超参数为:')
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 + 1) for 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=(20, 8), 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/#/