相关文章:
R python︱XGBoost极端梯度上升以及forecastxgb(预测) xgboost(回归)双案例解读
python︱sklearn一些小技巧的记录(训练集划分/pipelline/交叉验证等)
GBDT一个藤上,进化的xgb以及lgb。
比较好的几则练习代码:
- QLMX/data_mining_models
- Anfany/Machine-Learning-for-Beginner-by-Python3
文章目录
- 0 相关理论
- 0.1 内存更小
- 0.2 速度更快
- 0.3 直接支持类别特征(即不需要做one-hot编码)
- 0.4 LightGBM参数调优
- 0.5 调参经验
- 0.6 安装
- 1 二分类参数选择
- 2 多分类参数选择
- 3 回归任务参数设置
- 3.1 案例一
- 3.2 案例二
- 4 其他相关
- 4.1 Spark - LightGBM
- 4.2 LightGBM比赛里用的很多,为何公司里很少?
- 5 排序算法&LightGBM
- 5.1 案例一
- 5.2 案例二
- 6 debug
- 6.1 non-ASCII characters 版本问题
- 7 回归模型中 - > 如何画 预测/实际 对比曲线
0 相关理论
GDBT模型、XGBoost和LightGBM之间的区别与联系
0.1 内存更小
XGBoost 使用预排序后需要记录特征值及其对应样本的统计值的索引,而 LightGBM 使用了直方图算法将特征值转变为 bin 值,且不需要记录特征到样本的索引,将空间复杂度从 公式 降低为 公式 ,极大的减少了内存消耗;
LightGBM 采用了直方图算法将存储特征值转变为存储 bin 值,降低了内存消耗;
LightGBM 在训练过程中采用互斥特征捆绑算法减少了特征数量,降低了内存消耗。
0.2 速度更快
LightGBM 采用了直方图算法将遍历样本转变为遍历直方图,极大的降低了时间复杂度;
LightGBM 在训练过程中采用单边梯度算法过滤掉梯度小的样本,减少了大量的计算;
LightGBM 采用了基于 Leaf-wise 算法的增长策略构建树,减少了很多不必要的计算量;
LightGBM 采用优化后的特征并行、数据并行方法加速计算,当数据量非常大的时候还可以采用投票并行的策略;
LightGBM 对缓存也进行了优化,增加了 Cache hit 的命中率。
对比优势:
- 更快的训练效率,速度较快,是XGBoost速度的16倍,内存占用率为XGBoost的1/6
- 低内存使用
- 更好的准确率(我对比 XGBoost 没太大差别)
- 支持并行学习
- 可处理大规模数据
缺点:
1)可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度限制,在保证高效率的同时防止过拟合
2)基于偏差的算法,会对噪点较为敏感
3)在寻找最优解时,依据的最优切分变量,没有将最优解是全部特征的综合这一理念来考虑
代码语言:txt复制1. 树的切分策略不同:XGB 是 level-wise,而 LGB 是leaf-wise。level-wise 的建树方式对当前层的所有叶节点一视同仁,有些叶节点的分裂收益很小仍然需要进行分裂,增加了计算代价。leaf-wise 方式的精度更高,但容易过拟合,所以要控制树的最大深度。
2. 在选择数据分割点时:XGB 是通过预排序的方式,空间消耗较大;LGB是通过直方图算法,不需要进行预排序,内存占用更低。
3. 在并行策略上,XGB 主要集中在特征并行上,而 LGB 的并行策略包含特征并行、数据并行和投票并行(Data parallel,Feature parallel, Voting parallel)。
Level-wise和Leaf-wise
Level-wise:
在XGBoost中,树是按层生长的,称为Level-wise tree growth,同一层的所有节点都做分裂,最后剪枝,
在Histogram算法之上,LightGBM进行进一步的优化,采用的Leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。
LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合
0.3 直接支持类别特征(即不需要做one-hot编码)
实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化到多维的one-hot编码特征,降低了空间和时间的效率。
而类别特征的使用是在实践中很常用的。
基于这个考虑,LightGBM优化了对类别特征的支持,可以直接输入类别特征,不需要额外的one-hot编码展开。并在决策树算法上增加了类别特征的决策规则。
在Expo数据集上的实验,相比0/1展开的方法,训练速度可以加速8倍,并且精度一致。
0.4 LightGBM参数调优
LightGBM实战总结
n_estimators代表树的棵树
0.5 调参经验
LightGBM实战总结
下表对应了Faster Spread,better accuracy,over-fitting三种目的时,可以调整的参数
0.6 安装
中文文档:
https://lightgbm.apachecn.org/#/docs/3
依赖:
代码语言:javascript复制pip install setuptools wheel numpy scipy scikit-learn -U
为了验证是否安装成功, 可以在 Python 中 import lightgbm 试试:
代码语言:javascript复制import lightgbm as lgb
1 二分类参数选择
【lightgbm, xgboost, nn代码整理一】lightgbm做二分类,多分类以及回归任务(含python源码)
官方参数文档
参数的选择:
代码语言:javascript复制params = {'num_leaves': 60, #结果对最终效果影响较大,越大值越好,太大会出现过拟合
'min_data_in_leaf': 30,
'objective': 'binary', #二分类,定义的目标函数
'max_depth': -1,
'learning_rate': 0.03,
"min_sum_hessian_in_leaf": 6,
"boosting": "gbdt",
"feature_fraction": 0.9, #提取的特征比率
"bagging_freq": 1,
"bagging_fraction": 0.8,
"bagging_seed": 11,
"lambda_l1": 0.1, #l1正则
# 'lambda_l2': 0.001, #l2正则
"verbosity": -1,
"nthread": -1, #线程数量,-1表示全部线程,线程越多,运行的速度越快
'metric': {'binary_logloss', 'auc'}, ##二分类,评价函数选择
"random_state": 2019, #随机数种子,可以防止每次运行的结果不一致
# 'device': 'gpu' ##如果安装的事gpu版本的lightgbm,可以加快运算
}
folds = KFold(n_splits=5, shuffle=True, random_state=2019)
prob_oof = np.zeros((train_x.shape[0], ))
test_pred_prob = np.zeros((test.shape[0], ))
## train and predict
feature_importance_df = pd.DataFrame()
for fold_, (trn_idx, val_idx) in enumerate(folds.split(train)):
print("fold {}".format(fold_ 1))
trn_data = lgb.Dataset(train_x.iloc[trn_idx], label=train_y[trn_idx])
val_data = lgb.Dataset(train_x.iloc[val_idx], label=train_y[val_idx])
clf = lgb.train(params,
trn_data,
num_round,
valid_sets=[trn_data, val_data],
verbose_eval=20,
early_stopping_rounds=60)
prob_oof[val_idx] = clf.predict(train_x.iloc[val_idx], num_iteration=clf.best_iteration)
fold_importance_df = pd.DataFrame()
fold_importance_df["Feature"] = features
fold_importance_df["importance"] = clf.feature_importance()
fold_importance_df["fold"] = fold_ 1
feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0)
test_pred_prob = clf.predict(test[features], num_iteration=clf.best_iteration) / folds.n_splits
threshold = 0.5
for pred in test_pred_prob:
result = 1 if pred > threshold else 0
目标函数采用的是binary
,评价函数采用的是{'binary_logloss', 'auc'}
,可以根据需要对评价函数做调整,可以设定一个或者多个评价函数;'num_leaves'
对最终的结果影响较大,如果值设置的过大会出现过拟合现象。
常用的5折统计有两种:StratifiedKFold和KFold,其中最大的不同是StratifiedKFold分层采样交叉切分,确保训练集,测试集中各类别样本的比例与原始数据集中相同,实际使用中可以根据具体的数据分别测试两者的表现。
2 多分类参数选择
【lightgbm, xgboost, nn代码整理一】lightgbm做二分类,多分类以及回归任务(含python源码)
官方参数文档
代码语言:javascript复制params = {'num_leaves': 60,
'min_data_in_leaf': 30,
'objective': 'multiclass', # 多分类需要注意
'num_class': 33, # 多分类需要注意
'max_depth': -1,
'learning_rate': 0.03,
"min_sum_hessian_in_leaf": 6,
"boosting": "gbdt",
"feature_fraction": 0.9,
"bagging_freq": 1,
"bagging_fraction": 0.8,
"bagging_seed": 11,
"lambda_l1": 0.1,
"verbosity": -1,
"nthread": 15,
'metric': 'multi_logloss', # 多分类需要注意
"random_state": 2019,
# 'device': 'gpu'
}
folds = KFold(n_splits=5, shuffle=True, random_state=2019)
prob_oof = np.zeros((train_x.shape[0], 33))
test_pred_prob = np.zeros((test.shape[0], 33))
## train and predict
feature_importance_df = pd.DataFrame()
for fold_, (trn_idx, val_idx) in enumerate(folds.split(train)):
print("fold {}".format(fold_ 1))
trn_data = lgb.Dataset(train_x.iloc[trn_idx], label=train_y.iloc[trn_idx])
val_data = lgb.Dataset(train_x.iloc[val_idx], label=train_y.iloc[val_idx])
clf = lgb.train(params,
trn_data,
num_round,
valid_sets=[trn_data, val_data],
verbose_eval=20,
early_stopping_rounds=60)
prob_oof[val_idx] = clf.predict(train_x.iloc[val_idx], num_iteration=clf.best_iteration)
fold_importance_df = pd.DataFrame()
fold_importance_df["Feature"] = features
fold_importance_df["importance"] = clf.feature_importance()
fold_importance_df["fold"] = fold_ 1
feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0)
test_pred_prob = clf.predict(test[features], num_iteration=clf.best_iteration) / folds.n_splits
result = np.argmax(test_pred_prob, axis=1)
3 回归任务参数设置
3.1 案例一
【lightgbm, xgboost, nn代码整理一】lightgbm做二分类,多分类以及回归任务(含python源码)
官方参数文档
代码语言:javascript复制params = {'num_leaves': 38,
'min_data_in_leaf': 50,
'objective': 'regression', # 回归设置
'max_depth': -1,
'learning_rate': 0.02,
"min_sum_hessian_in_leaf": 6,
"boosting": "gbdt",
"feature_fraction": 0.9,
"bagging_freq": 1,
"bagging_fraction": 0.7,
"bagging_seed": 11,
"lambda_l1": 0.1,
"verbosity": -1,
"nthread": 4,
'metric': 'mae', # 回归设置
"random_state": 2019,
# 'device': 'gpu'
}
def mean_absolute_percentage_error(y_true, y_pred):
return np.mean(np.abs((y_true - y_pred) / (y_true))) * 100
def smape_func(preds, dtrain):
label = dtrain.get_label().values
epsilon = 0.1
summ = np.maximum(0.5 epsilon, np.abs(label) np.abs(preds) epsilon)
smape = np.mean(np.abs(label - preds) / summ) * 2
return 'smape', float(smape), False
folds = KFold(n_splits=5, shuffle=True, random_state=2019)
oof = np.zeros(train_x.shape[0])
predictions = np.zeros(test.shape[0])
train_y = np.log1p(train_y) # Data smoothing
feature_importance_df = pd.DataFrame()
for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_x)):
print("fold {}".format(fold_ 1))
trn_data = lgb.Dataset(train_x.iloc[trn_idx], label=train_y.iloc[trn_idx])
val_data = lgb.Dataset(train_x.iloc[val_idx], label=train_y.iloc[val_idx])
clf = lgb.train(params,
trn_data,
num_round,
valid_sets=[trn_data, val_data],
verbose_eval=200,
early_stopping_rounds=200)
oof[val_idx] = clf.predict(train_x.iloc[val_idx], num_iteration=clf.best_iteration)
fold_importance_df = pd.DataFrame()
fold_importance_df["Feature"] = features
fold_importance_df["importance"] = clf.feature_importance()
fold_importance_df["fold"] = fold_ 1
feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0)
predictions = clf.predict(test, num_iteration=clf.best_iteration) / folds.n_splits
print('mse %.6f' % mean_squared_error(train_y, oof))
print('mae %.6f' % mean_absolute_error(train_y, oof))
result = np.expm1(predictions) #reduction
result = predictions
在回归任务中对目标函数值添加了一个log平滑,如果待预测的结果值跨度很大,做log平滑很有很好的效果提升。
3.2 案例二
LightGBM_Regression_pm25案例
代码语言:javascript复制def Train(data, modelcount, censhu, yanzhgdata):
model = lgbm.LGBMRegressor(boosting_type='gbdt', objective='regression', num_leaves=1200,
learning_rate=0.17, n_estimators=modelcount, max_depth=censhu,
metric='rmse', bagging_fraction=0.8, feature_fraction=0.8, reg_lambda=0.9)
model.fit(data[:, :-1], data[:, -1])
# 给出训练数据的预测值
train_out = model.predict(data[:, :-1])
# 计算MSE
train_mse = mse(data[:, -1], train_out)
# 给出验证数据的预测值
add_yan = model.predict(yanzhgdata[:, :-1])
# 计算MSE
add_mse = mse(yanzhgdata[:, -1], add_yan)
print(train_mse, add_mse)
return train_mse, add_mse
4 其他相关
4.1 Spark - LightGBM
参考:实战!LightGBM算法原理、训练与预测
原生的Spark版本的LightGBM算法集成在了微软的开源项目MMLSPARK(Microsoft Machine Learning for Apache Spark),该项目是微软在认知工具包(Microsoft Cognitive Toolkit,曾用名 CNTK)的基础上开发的基于Apache Spark大数据框架的实现,由于mmlspark集成了大量了机器学习和深度学习算法,导致依赖该项目的maven后,项目打的jar包巨大(400M ),因此,需要对mmlspark项目进行一定阉割,只保留LightGBM算法(分类,回归均支持)进行重新编译。
笔者在进行预测代码的开发中,踩了好多坑,一把辛酸泪。尝试了不同的预测打分方式,这其中包括了PMML解决方案、MMLSPARK原生预测解决方案以及Java重构的预测解决方案。最终选择了java重构的预测解决方案,放弃前两种解决方案的原因如下:
1、PMML的解决方案会有一定的打分误差,并打分耗时不太满足当前业务
2、MMLSPARK原生预测解决方案中代码依赖了底层的C 动态链接库,并且预测代码有一定的优化空间,打分耗时巨大(每次打分都需要重新初始化C 依赖的一些数据对象)
4.2 LightGBM比赛里用的很多,为何公司里很少?
https://www.zhihu.com/question/344433472/answer/959927756
LightGBM在所有大厂里有会用到,你所说的很少大概是指线上模型?据我所知只有美团和阿里有部分线上模型是用了改进版的Lightgbm在做排序,结合了pair-wise损失。但是用的最多的还是离线模型效果,因为原生的lightgbm虽然使用了缓存加速和直方图做差,不用预排序存储了,但不支持扩展。
这意味着,在超大规模数据集用lightgbm是很不明智的,也不会有公司直接使用。
更多的是来快速地验证数据、想法是否正确可行,是很多团队都会先抽小规模的数据用LightGBM跑一遍,有效果了再做深度模型和算法改进。
最后一点,lightGBM虽然直接支持分类变量,也可以输出分桶,但是特征工程还是非常重要的,也需要一定时间调参。这算不上什么创新应用,自然没有公司刻意推崇。
作者:图灵的猫
链接:https://www.zhihu.com/question/344433472/answer/959927756
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5 排序算法&LightGBM
5.1 案例一
参考:https://lightgbm.apachecn.org/#/docs/5
Below are two rows from MSLR-WEB10K dataset:
代码语言:javascript复制0 qid:1 1:3 2:0 3:2 4:2 … 135:0 136:0
2 qid:1 1:3 2:3 3:0 4:0 … 135:0 136:0
5.2 案例二
lightgbm用于排序
jiangnanboy/learning_to_rank
1.raw_train.txt
代码语言:javascript复制0 qid:10002 1:0.007477 2:0.000000 ... 45:0.000000 46:0.007042 #docid = GX008-86-4444840 inc = 1 prob = 0.086622
0 qid:10002 1:0.603738 2:0.000000 ... 45:0.333333 46:1.000000 #docid = GX037-06-11625428 inc = 0.0031586555555558 prob = 0.0897452 ...
模型的参数:
代码语言:javascript复制train params = {
'task': 'train', # 执行的任务类型
'boosting_type': 'gbrt', # 基学习器
'objective': 'lambdarank', # 排序任务(目标函数)
'metric': 'ndcg', # 度量的指标(评估函数)
'max_position': 10, # @NDCG 位置优化
'metric_freq': 1, # 每隔多少次输出一次度量结果
'train_metric': True, # 训练时就输出度量结果
'ndcg_at': [10],
'max_bin': 255, # 一个整数,表示最大的桶的数量。默认值为 255。lightgbm 会根据它来自动压缩内存。如max_bin=255 时,则lightgbm 将使用uint8 来表示特征的每一个值。
'num_iterations': 200, # 迭代次数,即生成的树的棵数
'learning_rate': 0.01, # 学习率
'num_leaves': 31, # 叶子数
'max_depth':6,
'tree_learner': 'serial', # 用于并行学习,‘serial’: 单台机器的tree learner
'min_data_in_leaf': 30, # 一个叶子节点上包含的最少样本数量
'verbose': 2 # 显示训练时的信息
}
6 debug
6.1 non-ASCII characters 版本问题
代码语言:javascript复制报错:
LightGBMError: Do not support non-ASCII characters in feature name
报错2:
ValueError: DataFrame.dtypes for data must be int, float or bool.
Did not expect the data types in fields xxxx
报错,后面看到light的版本要回退到:2.2.3
7 回归模型中 - > 如何画 预测/实际 对比曲线
代码语言:javascript复制# 绘制不同参数下MSE的对比曲线
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['FangSong'] # 显示中文
mpl.rcParams['axes.unicode_minus'] = False # 显示负号
import matplotlib.pyplot as plt
def plot_true_pred_y(true_y,pred_y,count = 100):
randomnum = np.random.choice(len(pred), count, replace=False)
true_y = Y_test[randomnum]
pred_y = pred[randomnum]
# 对比
plt.figure(figsize=(17, 9))
plt.subplot(2, 1, 1)
plt.plot(list(range(len(pred_y))), pred_y, 'r--', label='预测', lw=2)
plt.scatter(list(range(len(true_y))), true_y, c='b', marker='.', label='真实', lw=2)
plt.xlim(-1, count 1)
plt.legend()
# plt.title('预测和真实值对比[最大树数%d]' % int(tree))
plt.subplot(2, 1, 2)
plt.plot(list(range(len(pred_y))), np.array(pred_y) - np.array(true_y), 'k--', marker='s', label='真实-预测', lw=2)
plt.legend()
plt.title('预测和真实值相对误差')
#plt.savefig(r'C:UsersGWT9Desktopduibi_lightgbm.jpg')
这里,true_y
是实际值,pred_y
为预测值,count = 100
,count为希望显示多少个,因为如果点/线太多,会密密麻麻。
这个是个数约束之后的: