LightGBM原理与实践简记
写在前面:
LightGBM 用了很久了,但是一直没有对其进行总结,本文从 LightGBM 的使用、原理及参数调优三个方面进行简要梳理。
开箱即用
quickstart
使用 LightGBM 官方接口,核心步骤
- 定义参数
- 构造数据
- train
- predict
# 1.定义参数
config = json.load(open("configs/lightgbm_config.json", 'r'))
# 2. 构造数据
index = int(len(features)*0.9)
train_fts, train_lbls = features[:index], labels[:index]
val_fts, val_lbls = features[index:], labels[index:]
train_data = lgb.Dataset(train_fts, label=train_lbls)
val_data = lgb.Dataset(val_fts, label=val_lbls)
# 3. train
bst = lgb.train(params=config, train_set=train_data, valid_sets=[val_data])
# 4. predict
lgb.predict(val_data)
# lightgbm_config.json
{
"objective":"binary",
"task":"train",
"boosting":"gbdt",
"num_iterations":500,
"learning_rate":0.1,
"max_depth":-1,
"num_leaves":64,
"tree_learner":"serial",
"num_threads":0,
"device_type":"cpu",
"seed":0,
"min_data_in_leaf":100,
"min_sum_hessian_in_leaf":0.001,
"bagging_fraction":0.9,
"bagging_freq":1,
"bagging_seed":0,
"feature_fraction":0.9,
"feature_fraction_bynode":0.9,
"feature_fraction_seed":0,
"early_stopping_rounds":10,
"first_metric_only":true,
"max_delta_step":0,
"lambda_l1":0,
"lambda_l2":1,
"verbosity":2,
"is_unbalance":true,
"sigmoid":1,
"boost_from_average":true,
"metric":[
"binary_logloss",
"auc",
"binary_error"
]
}
sklearn 接口
import lightgbm as lgb
# 1. config
"""
objective parameter:
‘regression’ for LGBMRegressor
‘binary’ or ‘multiclass’ for LGBMClassifier
‘lambdarank’ for LGBMRanker.
"""
lgb_clf = lgb.LGBMModel(
objective = 'binary',
metric = 'binary_logloss,auc',
learning_rate = 0.1,
bagging_fraction = 0.8,
feature_fraction = 0.9,
bagging_freq = 5,
n_estimators = 300,
max_depth = 4,
is_unbalance = True
)
# 2. fit
# 3. predict
增量学习
在处理大规模数据时,数据无法一次性载入内存,使用增量训练。
主要通过两个参数实现:
- init_model
- keep_training_booster
详细方法见 增量学习/训练
原理
在LightGBM,Xgboost一直是kaggle的屠榜神器之一,但是,一切都在进步~
回顾Xgboost
贪心算法生成树,时间复杂度\(O(ndKlogn)\),\(d\) 个特征,每个特征排序需要\(O(nlogn)\),树深度为\(K\)
- pre-sorting 对特征进行预排序并且需要保存排序后的索引值(为了后续快速的计算分裂点),因此内存需要训练数据的两倍。
- 在遍历每一个分割点的时候,都需要进行分裂增益的计算,
Level-wise 生长,并行计算每一层的分裂节点
- 提高了训练速度
- 但同时也因为节点增益过小增加了很多不必要的分裂,增加了计算量
LightGBM
- 基于 Histogram 的决策树算法
- 带深度限制的 Leaf-wise 的叶子生长策略
- 直方图做差加速
- 直接支持类别特征(Categorical Feature)
- Cache命中率优化
- 基于直方图的稀疏特征优化
- 多线程优化
直方图算法
- 将连续的浮点特征离散成
个离散值,并构造宽度为
的
。默认k为 255
- 遍历训练数据,统计每个离散值在直方图中的累计统计量。
- 在进行特征选择时,只需要根据直方图的离散值,遍历寻找最优的分割点。
内存优化:
- int32存下标,float32存数据 -> 8位存储
- 内存消耗可以降低为原来的
。
时间优化:
- \(O(nd)\)变为\(O(kd)\)
Leaf-wise 生长
Leaf-wise(按叶子生长)生长策略
- 每次从当前所有叶子中找到分裂增益最大(一般也是数据量最大)的一个叶子
- 然后分裂,如此循环。
- 同 Level-wise 相比,在分裂次数相同的情况下,Leaf-wise 可以降低更多的误差,得到更好的精度。Leaf-wise 的缺点是可能会长出比较深的决策树,产生过拟合。因此 LightGBM 在 Leaf-wise 之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。
类别型特征支持
xgboost使用one-hot编码,LightGBM 采用了 Many vs Many 的切分方式,实现过程如下【7】:
将类别的取值当做bin,有多少个取值就是多少个bin(去除样本极少的bin)
统计该特征中的各取值上的样本数,按照从样本数从大到小排序,去除样本占比小于1%的类别值
对于剩余的特征值(可以理解为一个特征值对应一个桶),统计各个特征值对应的样本的一阶梯度之和,二阶梯度之和,根据正则化系数,算得各个桶的统计量: 一阶梯度之和 / (二阶梯度之和 + 正则化系数);
根据该统计量对各个桶进行从大到小排序;在排序好的桶上,进行最佳切点查找
并行支持
- 特征并行:在不同机器在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。
- 数据并行:让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。
不均衡数据处理
- 二分类
is_unbalance=True
,表示 正样本的权重/负样本的权重 等于负样本的样本数/正样本的样本数- 或设置
scale_pos_weight
,代表的是正类的权重,可以设置为 number of negative samples / number of positive samples
- 多分类
class weight
- 自定义 facal loss【9】
参数调优
参数说明
核心参数
boosting / boost / boosting_type
用于指定弱学习器的类型,默认值为 ‘gbdt’,表示使用基于树的模型进行计算。还可以选择为 ‘gblinear’ 表示使用线性模型作为弱学习器。
‘gbdt’,使用梯度提升树
‘rf’,使用随机森林
‘dart’,不太了解,官方解释为 Dropouts meet Multiple Additive Regression Trees
‘goss’,使用单边梯度抽样算法,速度很快,但是可能欠拟合。
objective / application
“regression”,使用L2正则项的回归模型(默认值)。
“regression_l1”,使用L1正则项的回归模型。
“mape”,平均绝对百分比误差。
“binary”,二分类。
“multiclass”,多分类。
num_class
多分类问题的类别个数
增量训练
keep_training_booster=True # 增量训练
超参
调优
调优思路与方向
- 树结构参数
- max_depth :3-8
- num_leaves:最大值是
2^(max_depth)
- min_data_in_leaf
- 训练速度参数
- learning_rate 和 n_estimators,结合early_stopping使用
- max_bin:变量分箱的数量,默认255。调大则准确,但容易过拟合;调小可以加速
- 防止过拟合
- lambda_l1 和 lambda_l2:
L1
和L2
正则化,对应XGBoost
的reg_lambda
和reg_alpha
- min_gain_to_split:如果你设置的深度很深,但又无法向下分裂,
LGBM
就会提示warning
,无法找到可以分裂的了,说明数据质量已经达到了极限了。参数含义和XGBoost
的gamma
是一样。比较保守的搜索范围是(0, 20)
,它可以用作大型参数网格中的额外正则化 - bagging_fraction:训练每棵树的训练样本百分比
- feature_fraction:训练每棵树时要采样的特征百分比
- lambda_l1 和 lambda_l2:
自动调参
使用Optuna
,定义优化目标函数:
- 定义训练参数字典
- 创建模型,训练
- 定义指标
import optuna # pip install optuna
from sklearn.metrics import log_loss
from sklearn.model_selection import StratifiedKFold
from optuna.integration import LightGBMPruningCallback
def objective(trial, X, y):
param_grid = {
"n_estimators": trial.suggest_categorical("n_estimators", [10000]),
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3),
"num_leaves": trial.suggest_int("num_leaves", 20, 3000, step=20),
"max_depth": trial.suggest_int("max_depth", 3, 12),
"min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 200, 10000, step=100),
"max_bin": trial.suggest_int("max_bin", 200, 300),
"lambda_l1": trial.suggest_int("lambda_l1", 0, 100, step=5),
"lambda_l2": trial.suggest_int("lambda_l2", 0, 100, step=5),
"min_gain_to_split": trial.suggest_float("min_gain_to_split", 0, 15),
"bagging_fraction": trial.suggest_float(
"bagging_fraction", 0.2, 0.95, step=0.1
),
"bagging_freq": trial.suggest_categorical("bagging_freq", [1]),
"feature_fraction": trial.suggest_float(
"feature_fraction", 0.2, 0.95, step=0.1
),
}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=1121218)
cv_scores = np.empty(5)
for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
model = lgbm.LGBMClassifier(objective="binary", **param_grid)
model.fit(
X_train,
y_train,
eval_set=[(X_test, y_test)],
eval_metric="binary_logloss",
early_stopping_rounds=100,
callbacks=[
LightGBMPruningCallback(trial, "binary_logloss")
],
)
preds = model.predict_proba(X_test)
preds = model.predict_proba(X_test)
# 优化指标logloss最小
cv_scores[idx] = log_loss(y_test, preds)
return np.mean(cv_scores)
调优
study = optuna.create_study(direction="minimize", study_name="LGBM Classifier")
func = lambda trial: objective(trial, X, y)
study.optimize(func, n_trials=20)
搜索完成后,调用best_value
和bast_params
属性,调参就出来了。
print(f"\tBest value (rmse): {study.best_value:.5f}")
print(f"\tBest params:")
for key, value in study.best_params.items():
print(f"\t\t{key}: {value}")
-----------------------------------------------------
Best value (binary_logloss): 0.35738
Best params:
device: gpu
lambda_l1: 7.71800699380605e-05
lambda_l2: 4.17890272377219e-06
bagging_fraction: 0.7000000000000001
feature_fraction: 0.4
bagging_freq: 5
max_depth: 5
num_leaves: 1007
min_data_in_leaf: 45
min_split_gain: 15.703519227860273
learning_rate: 0.010784015325759629
n_estimators: 10000
得到这个参数组合后,我们就可以拿去跑模型了,看结果再手动微调,这样就可以省很多时间了。
特征重要性
lgb_clf.feature_importances_
references
【1】详解LightGBM两大利器:基于梯度的单边采样(GOSS)和互斥特征捆绑(EFB)https://zhuanlan.zhihu.com/p/366234433
【2】LightGBM的参数详解以及如何调优. https://cloud.tencent.com/developer/article/1696852
【3】LightGBM 中文文档. https://lightgbm.cn/
【4】决策树(下)——XGBoost、LightGBM(非常详细)https://zhuanlan.zhihu.com/p/87885678
【5】http://www.showmeai.tech/article-detail/195
【6】https://zhuanlan.zhihu.com/p/99069186
【7】lightgbm离散类别型特征为什么按照每一个类别里对应样本的一阶梯度求和/二阶梯度求和排序? - 一直学习一直爽的回答 - 知乎 https://www.zhihu.com/question/386888889/answer/1195897410
【9】LightGBM with the Focal Loss for imbalanced datasets
LightGBM原理与实践简记的更多相关文章
- Atitit 管理原理与实践attilax总结
Atitit 管理原理与实践attilax总结 1. 管理学分类1 2. 我要学的管理学科2 3. 管理学原理2 4. 管理心理学2 5. 现代管理理论与方法2 6. <领导科学与艺术4 7. ...
- Atitit.ide技术原理与实践attilax总结
Atitit.ide技术原理与实践attilax总结 1.1. 语法着色1 1.2. 智能提示1 1.3. 类成员outline..func list1 1.4. 类型推导(type inferenc ...
- Atitit.异步编程技术原理与实践attilax总结
Atitit.异步编程技术原理与实践attilax总结 1. 俩种实现模式 类库方式,以及语言方式,java futuretask ,c# await1 2. 事件(中断)机制1 3. Await 模 ...
- Atitit.软件兼容性原理与实践 v5 qa2.docx
Atitit.软件兼容性原理与实践 v5 qa2.docx 1. Keyword2 2. 提升兼容性的原则2 2.1. What 与how 分离2 2.2. 老人老办法,新人新办法,只新增,少修改 ...
- Atitit 表达式原理 语法分析 原理与实践 解析java的dsl 递归下降是现阶段主流的语法分析方法
Atitit 表达式原理 语法分析 原理与实践 解析java的dsl 递归下降是现阶段主流的语法分析方法 于是我们可以把上面的语法改写成如下形式:1 合并前缀1 语法分析有自上而下和自下而上两种分析 ...
- Atitit.gui api自动化调用技术原理与实践
Atitit.gui api自动化调用技术原理与实践 gui接口实现分类(h5,win gui, paint opengl,,swing,,.net winform,)1 Solu cate1 Sol ...
- Atitit.提升语言可读性原理与实践
Atitit.提升语言可读性原理与实践 表1-1 语言评价标准和影响它们的语言特性1 1.3.1.2 正交性2 1.3.2.2 对抽象的支持3 1.3.2.3 表达性3 .6 语言设计中的权 ...
- Atitit 网络爬虫与数据采集器的原理与实践attilax著 v2
Atitit 网络爬虫与数据采集器的原理与实践attilax著 v2 1. 数据采集1 1.1. http lib1 1.2. HTML Parsers,1 1.3. 第8章 web爬取199 1 2 ...
- Atitit.软件兼容性原理与实践 v3 q326.docx
Atitit.软件兼容性原理与实践 v3 q326.docx 1. 架构兼容性1 2. Api兼容性1 2.1. 新api vs 修改旧的api1 3. Web方面的兼容性(js,html)1 3 ...
随机推荐
- 【Android开发】URL[] 转成 bitmap[]
public static Bitmap[] getBitmapFromURL(String[] path) throws MalformedURLException { Bitmap[] b = n ...
- smdms超市订单管理系统之登录功能
一.超市订单管理系统准备阶段 Supermarket order management system 创建数据库 数据库代码放置如下 点击查看数据库address代码 CREATE TABLE `sm ...
- Windows 10搭建FTP服务器
1 开启FTP服务 控制面板 -> 程序和功能 -> 启用或关闭Windows功能 找到下面选项的勾选 2 添加FTP站点 在开始菜单里面输入 IIS 搜索并打开 IIS管理器 展开左侧菜 ...
- 中文版Postman
作为软件开发从业者,API 调试是必不可少的一项技能,在这方面 Postman 做的非常出色.但是在整个软件开发过程中,API 调试只是其中的一部分,还有很多事情 Postman 无法完成,或者无法高 ...
- QQ浏览器X5内核问题汇总 转
常常被人问及微信中使用的X5内核的问题,其实我也不是很清楚,只知道它是基于android 4.2的webkit,版本号是webkit 534.今天正好从X5团队拿到了一份问题汇总,梳理下发出来,给各位 ...
- Java语言学习day34--8月09日
##13Math类的方法_1 A:Math类中的方法 /* * static double sqrt(double d) * 返回参数的平方根 */ public static void functi ...
- node.js - mysql
今天结束的挺早,因为今天的内容还可以不是很难,今天全程是学了一些关于mysql数据库和sql查询语句的内容包括在node终端里面怎么来连接数据库.经过今天的一个学习,我感觉离那个地步越来越近了,就是那 ...
- CentOS 下 MySQL 服务搭建
1. 卸载旧 MySQL 查看 rpm 包 rpm-qa | grep mysql 如果存在,使用如下命令卸载 rpm -e 查找是否存在mysql 相关目录 find / -name mysql 卸 ...
- 原生实现C#和Lua相互调用-Unity3D可用
引言 本篇简单介绍如何在C#中执行Lua脚本,传递数据到Lua中使用,以及Lua中调用C#导出的方法等.在Unity中开发测试,并打IL2CPP的Android包在模拟器上运行通过.Lua版本 ...
- 重磅!业界首个云原生批量计算项目Volcano正式晋级为CNCF孵化项目
摘要:4月7日,云原生计算基金会(CNCF)宣布,由华为云捐献的业界首个云原生批量计算项目Volcano正式晋级为CNCF孵化项目. 4月7日,云原生计算基金会(CNCF)宣布,由华为云捐献的业界首个 ...