赞
踩
LightGBM是一个梯度Boosting框架,使用基于决策树的学习算法。它可以说是分布式的,高效的,有以下优势:
1)更快的训练效率
2)低内存使用
3)更高的准确率
4)支持并行化学习
5)可以处理大规模数据
在讨论LightGBM时,不可避免的会提到XGboost,关于XGboost的不足之处主要有:
1)每轮迭代时,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。
2)预排序方法的时间和空间的消耗都很大
直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。在XGBoost中需要遍历所有离散化的值,而在这里只要遍历k个直方图的值。
使用直方图算法有很多优点。首先,最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值。
然后在计算上的代价也大幅降低,XGBoost预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从O(#data * #feature) 优化到O(k* #features)。
当然,Histogram算法并不是完美的。由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在不同的数据集上的结果表明,离散化的分割点对最终的精度影响并不是很大,甚至有时候会更好一点。原因是决策树本来就是弱模型,分割点是不是精确并不是太重要;较粗的分割点也有正则化的效果,可以有效地防止过拟合;即使单棵树的训练误差比精确分割的算法稍大,但在梯度提升(Gradient Boosting)的框架下没有太大的影响。
一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。利用这个方法,LightGBM可以在构造一个叶子的直方图后(父节点在上一轮就已经计算出来了),可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍。
Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
Leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。
level_wise:多线程优化,控制模型复杂度,不易过拟合。
leaf-wise:计算代价较小,更精确,易过拟合(map_depth)。
实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化到多维的one-hot编码特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM优化了对类别特征的支持,可以直接输入类别特征,不需要额外的one-hot编码展开。并在决策树算法上增加了类别特征的决策规则。在Expo数据集上的实验,相比0/1展开的方法,训练速度可以加速8倍,并且精度一致。
LightGBM还具有支持高效并行的优点。LightGBM原生支持并行学习,目前*支持特征并行(Featrue Parallelization)和数据并行(Data Parallelization)的两种。还有一种是基于投票的数据并行(Voting Parallelization)。
1)特征并行的主要思想是在不同机器、在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。
2)数据并行则是让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。
LightGBM针对这两种并行方法都做了优化:
●特征并行算法中,通过在本地保存全部数据避免对数据切分结果的通信。
●数据并行中使用分散规约 (Reduce scatter) 把直方图合并的任务分摊到不同的机器,降低通信和计算,并利用直方图做差,进一步减少了一半的通信量。
●基于投票的数据并行(Voting Parallelization)则进一步优化数据并行中的通信代价,使通信代价变成常数级别。在数据量很大的时候,使用投票并行可以得到非常好的加速效果。
使用场景:
特征并行:数据量小,但特征数量多;
数据并行:数据量较大,特征数量少;
投票并行:数据量大,特征数量多。
使用LightGBM,你可以运行不同类型的渐变增强提升方法。你有:GBDT、DART和GOSS,这些可以通过“boosting”参数指定。
该方法是传统梯度提升决策树,也是XGBoost和pGBRT等优秀库背后的算法。但由于其精度高、效率高、稳定性好,目前已得到广泛的应用。
梯度提升决策树(GBDT)基于三个重要原则:
1、弱学习者(决策树)
2、梯度优化
3、提升技术
所以在gbdt方法中,我们有很多决策树(弱学习者)。这些树是按顺序构建的:
首先,第一棵树学习如何适应目标变量
第二棵树学习如何适合残差(差异)之间的预测,第一棵树和地面真相
第三棵树学习如何匹配第二棵树的残差,以此类推。
所有这些树都是通过传播整个系统的误差梯度来训练的。
gbdt的主要缺点是,在每个树节点中找到最佳分割点非常耗时,而且会消耗内存。其他的提升方法试图解决这个问题。
DART梯度提升数,是使用dropout(神经网络中的标准)的方法,来改进模型正则化和处理一些其他不太明显的问题。
也就是说,gbdt存在过度专门化(over-specialization)的问题,这意味着在以后的迭代中添加的树往往只会影响对少数实例的预测,而对其余实例的贡献则可以忽略不计。添加dropout会使树在以后的迭代中更加难以专门化那些少数的示例,从而提高性能。
标准的gbdt是可靠的,但在大型数据集上速度不够快。因此goss提出了一种基于梯度的采样方法来避免搜索整个搜索空间。我们知道,对于每个数据实例,当梯度很小时,这意味着不用担心数据是经过良好训练的,而当梯度很大时,应该重新训练。这里我们有两个方面,数据实例有大的和小的渐变。因此,goss以一个大的梯度保存所有数据,并对一个小梯度的数据进行随机抽样(这就是为什么它被称为单边抽样)。这使得搜索空间更小,goss的收敛速度更快。
Control Parameters(控制参数) | 含义 | 用法 |
---|---|---|
max_depth | 树的最大深度 | 默认-1,无限制;当模型过拟合时,可以考虑首先降低 max_depth |
num_leaves | 树的叶子数目 | 默认31,取值应 <= 2 ^(max_depth), 超过此值会导致过拟合 |
min_data_in_leaf | 叶子最小记录数 | 默认20,小于设置时不再分裂,过拟合时用 |
feature_fraction | 特征抽样,例如 为0.8时,意味着在每次迭代中随机选择80%的参数来建树 | 默认为1, boosting 为 random forest 时用 |
bagging_fraction | 数据抽样 | 默认1,用于加快训练速度和减小过拟合 |
bagging_freq | bagging的频率,k意味着每k次迭代执行bagging | 默认0 |
early_stopping_round | 如果一次验证数据的一个度量在最近的early_stopping_round 回合中没有提高,模型将停止训练 | 加速分析,减少过多迭代 |
lambda | ,正则化参数 | 默认0,0~1 |
min_gain_to_split | 描述分裂的最小增益,小于该值不再分裂 | 默认0,控制树的有用的分裂 |
max_cat_group | 在 group 边界上找到分割点 | 当类别数量很多时,找分割点很容易过拟合时 |
num_iterations | 指定增强迭代的次数(要构建的树) 。建立的树越多,你的模型就越精确,代价是:较长的训练时间和过拟合的可能性更高。 | 默认100,建议使用更小的learning_rate和更大的num_iteration。此外,如果您想要更高的num_iteration,那么您应该使用early_stopping_rounds,以便在无法学习任何有用的内容时停止训练。 |
early_stopping_rounds | 度量标准没有改善时停止训练 | 如果验证度量在最后一轮停止后没有改进,此参数将停止训练。这应该与一些迭代成对地进行定义。如果你把它设置得太大,你就增加了过拟合的变化(但你的模型可以更好)。经验法则是让它占num_iterations的10%。 |
categorical_feature | 处理分类特征方法 | 该方法比常用的单热编码方法具有更好的性能。默认值是“auto”,意思是:让lightgbm决定哪个表示lightgbm将推断哪些特性是绝对的。它并不总是工作得很好,可手动设置分类特性 |
tree-learner | 并行方式 | 默认serial |
max_bin | 最大bin数量 | 默认255 |
max_bin_by_feature | 按特征设置bin数目 | |
min_data_in_bin | bin内最少数据量 | 3 |
pos_bagging_fraction | 正例抽样 | 1 |
neg_bagging_fraction | 负例抽样 | 1 |
Core Parameters(核心参数) | 含义 | 用法 |
---|---|---|
Task | 数据的用途 | 默认train,选择 train 或者 predict |
application | 模型的用途 | 选择 regression: 回归时,binary: 二分类时,multiclass: 多分类时 |
boosting | 要用的算法 | 默认gbdt, 随机森林: rf: random forest, dart: Dropouts meet Multiple Additive Regression Trees, goss: Gradient-based One-Side Sampling |
objective | 目标函数 | 默认回归regression;可选二分类:‘binary’,多分类:‘multiclass’,同时需设置类别数 class |
num_boost_round | 迭代次数 | 通常 100+ |
learning_rate | 如果一次验证数据的一个度量在最近的 early_stopping_round 回合中没有提高,模型将停止训练 | 默认0.1,常用 0.1, 0.001, 0.003… |
num_leaves | 树的最大叶子节点数 | 默认 31 |
device | 学习设备 | 默认cpu ,可选 gpu |
metric | 评价指标 | 回归:mae: mean absolute error , mse: mean squared error ; 二分类:binary_logloss: loss for binary classification ; 多分类 multi_logloss: loss for multi classification |
is_unbalance | 正负样本是否平衡 | True or False,True时,算法将尝试自动平衡占主导地位的标签的权重 |
scale_pos_weight | 正负样本权重 | 默认情况下是1,这意味着假设正负标签都是相等的),在不平衡数据集的情况下,您可以使用以下公式来正确地设置它:sample_pos_weight = number of negative samples / number of positive samples |
自定义度量函数:
def feval_func(preds, train_data):
# Define a formula that evaluates the results
return ('feval_func_name', eval_result, False)
#要使用feval函数代替度量,您应该设置度量参数 metric “None”。
print('Start training...')
lgb_train = lgb.train(...,
metric=None,
feval=feval_func)
LightGBM的调参过程和RF、GBDT等类似,其基本流程如下:
1、首先选择较高的学习率,大概0.1附近,这样是为了加快收敛的速度。这对于调参是很有必要的。
2、对决策树基本参数调参:
2.1 max_depth和num_leaves
2.2 min_data_in_leaf和min_sum_hessian_in_leaf
2.3 feature_fraction和bagging_fraction
3、正则化参数调参
4、最后降低学习率,这里是为了最后提高准确率
下表对应了不同情况下,参数如何调整。 Faster Speed ,better accuracy ,over-fitting 三种目的时,可以调的参数
Faster Speed(速率快) | better accuracy(最好正确率) | over-fitting(过拟合) |
---|---|---|
将 max_bin 设置小一些 | 用较大的 max_bin | max_bin 小一些 |
num_leaves 大一些 | num_leaves 小一些 | |
用 feature_fraction 来做 sub-sampling | 用 feature_fraction | |
用 bagging_fraction 和 bagging_freq | 设定 bagging_fraction 和 bagging_freq | |
training data 多一些 | training data 多一些 | |
用 save_binary 来加速数据加载 | 直接用 categorical feature | 用 gmin_data_in_leaf 和 min_sum_hessian_in_leaf |
用 parallel learning | 用 dart | 用 lambda_l1, lambda_l2 ,min_gain_to_split 做正则化 |
num_iterations 大一些,learning_rate 小一些 | 用 max_depth 控制树的深度 |
import lightgbm as lgb from sklearn.metrics import mean_squared_error from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split iris = load_iris() data = iris.data target = iris.target X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2) # 创建成lgb特征的数据集格式 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, # k 意味着每 k 次迭代执行bagging 'verbose': 1 # <0 显示致命的, =0 显示错误 (警告), >0 显示信息 } # 训练 cv and train gbm = lgb.train(params, lgb_train, num_boost_round=20, valid_sets=lgb_eval, early_stopping_rounds=5) # 保存模型到文件 gbm.save_model('model.txt') # 预测数据集 y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration) # 评估模型 print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5)
import lightgbm as lgb from sklearn.metrics import mean_squared_error from sklearn.model_selection import GridSearchCV from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载数据 iris = load_iris() data = iris.data target = iris.target X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2) # 创建模型,训练模型 gbm = lgb.LGBMRegressor(objective='regression', num_leaves=31, learning_rate=0.05, n_estimators=20) gbm.fit(X_train, y_train, eval_set=[(X_test, y_test)], eval_metric='l1', early_stopping_rounds=5) # 测试集预测 y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_) # 模型评估 print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5) # feature importances print('Feature importances:', list(gbm.feature_importances_)) # 网格搜索,参数优化 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('Best parameters found by grid search are:', gbm.best_params_)
原生API与sklearnAPI接口区别总结
需要注意以下几点:
1、多分类时lgb.train除了’objective’:‘multiclass’,还要指定"num_class":5,而sklearn接口只需要指定’objective’:‘multiclass’。
2、lgb.train中正则化参数为"lambda_l1", “lambda_l2”,sklearn中则为’reg_alpha’, ‘reg_lambda’。
3、迭代次数在sklearn中是’n_estimators’:300,在初始化模型时指定。而在lgb.train中则可在参数params中指定,也可在函数形参中指出。
参考:
https://www.cnblogs.com/jiangxinyang/p/9337094.html
https://blog.csdn.net/qq_24591139/article/details/100085359
https://blog.csdn.net/qq_41940950/article/details/100061562
https://www.cnblogs.com/webRobot/p/10019448.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。