一直想把XGBoost和LightGBM的原理及应用梳理完分享给大家,但是内容有点多,也有些存疑的点在慢慢厘清,这篇文章也是断断续续在整理。
今天终于梳理完Lightgbm实战调优啦,希望对大家有帮助。
对LightGBM原理感兴趣的可翻看历史文章:LightGBM三部曲:LightGBM原理、LightGBM三部曲:LightGBM参数详解,原生与Sklearn接口全掌握。
对XGBoost原理、参数详解、实战感兴趣可翻看历史文章:XGBoost三部曲:XGBoost原理、XGBoost三部曲:XGBoost参数详解、XGBoost三部曲:XGBoost实战。
LigthGBM是微软开发的一款高性能梯度提升框架,是一种集成学习算法,属于Boosting类型,通过叠加多个决策树的预测结果得出最终的预测结果。
因其 训练速度快、内存消耗低、性能优异 而受到数据科学界的广泛青睐。
这个算法基于GBDT,对树模型基础知识不了解的小伙伴可以翻看之前的文章:Python中调用sklearn决策树、一文弄懂随机森林的原理和应用、一文弄懂GBDT原理和应用。
今天,我们就通过三个实战案例,带你完整掌握LightGBM的核心用法和调优技巧。
本文目录
- 初识LightGBM——快速上手
- 理解核心原理——为何它如此高效
2.1 直方图算法
2.2 单边梯度采样(GOSS)
2.3 互斥特征捆绑(EFB) 3. 实战调优案例——Kaggle竞赛实战
3.1 数据准备与基线模型
3.2 系统性调优策略
3.3 模型解释与特征重要性
3.4 最终模型与预测 4. 常见陷阱与应对策略
总结
一、初识LightGBM——快速上手
为什么要选择LightGBM?
与XGBoost等传统GBDT工具相比,LightGBM具有明显优势:
- 训练速度提升数倍,内存消耗大幅降低。
- 准确率相当甚至更高。
- 支持并行和GPU学习。
- 能够处理大规模数据。
基础使用:只需5行代码
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
# 加载数据
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2)
# 创建模型并训练
model = lgb.LGBMClassifier()
model.fit(X_train, y_train)
# 评估模型
score = model.score(X_test, y_test)
print(f"模型准确率: {score:.4f}")
得到结果:
模型准确率: 0.9649
看到这里,你可能觉得LightGBM不过如此。但 这才是冰山一角 ,接下来让我们深入其核心原理。
二、理解核心原理——为何它如此高效
1 直方图算法
LightGBM通过将连续特征离散化到直方图中,大大减少了寻找最佳分裂点的计算量,这是其速度优势的主要来源。
2 单边梯度采样(GOSS)
传统GBDT需要计算所有样本的梯度,而GOSS保留梯度大的样本,随机采样梯度小的样本,在不影响精度的情况下大幅提升训练速度。
3 互斥特征捆绑(EFB)
高维特征通常稀疏,且许多特征互斥。EFB将互斥特征捆绑在一起,减少特征维度,进一步提升训练效率。
理解这些原理,是有效调优的基础 。很多人在调参时盲目尝试,就是因为不了解背后的机制。
三、实战调优案例——Kaggle竞赛实战
接下来通过一个实际案例( 使用Kaggle的房价预测数据集,目标是预测房屋售价) ,演示完整的LightGBM调优流程。
1 数据准备与基线模型
数据集y为房屋售价,X为影响房屋价格的因素,展示前两行如下:
接着对数据进行预处理和基线模型训练,代码如下:
#房价预测
import os
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import KFold
# 加载数据
os.chdir(r'F:\公众号\0_内容101_200\118_lightgbm')
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')
# 简单数据预处理
def preprocess_data(df):
# 处理缺失值
for col in df.columns:
if df[col].dtype == 'object':
df[col].fillna(df[col].mode()[0], inplace=True)
else:
df[col].fillna(df[col].median(), inplace=True)
# 对分类变量编码
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
df[col] = pd.factorize(df[col])[0]
return df
train_data = preprocess_data(train_data)
test_data = preprocess_data(test_data)
# 准备特征和目标变量
X = train_data.drop(['SalePrice', 'Id'], axis=1)
y = np.log(train_data['SalePrice']) # 对价格取对数
test_ids = test_data['Id']
test_data = test_data.drop(['Id'], axis=1)
# 创建基线模型
params = {
'objective': 'regression',
'metric': 'rmse',
'boosting_type': 'gbdt',
'n_estimators': 1000,
'learning_rate': 0.05,
'num_leaves': 31,
}
# 使用交叉验证
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = []
for train_idx, val_idx in kf.split(X):
X_train, X_val = X.iloc[train_idx], X.iloc[val_idx]
y_train, y_val = y.iloc[train_idx], y.iloc[val_idx]
model = lgb.LGBMRegressor(**params)
model.fit(X_train, y_train,
eval_set=[(X_val, y_val)],
early_stopping_rounds=100,
verbose=False)
val_pred = model.predict(X_val)
score = np.sqrt(mean_squared_error(y_val, val_pred))
scores.append(score)
print(f"基线模型RMSE: {np.mean(scores):.4f} ± {np.std(scores):.4f}")
如需文中数据,可加群获取,或者在公众号中回复“波士顿房价”获取
得到结果:
基线模型RMSE: 0.1333 ± 0.0193
2 系统性调优策略
调优不是盲目尝试,而是有策略的搜索过程,以下是经过实践验证的调优顺序:
import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import KFold
import matplotlib.pyplot as plt
import seaborn as sns
def systematic_hyperparameter_tuning(X, y, test_data, test_ids):
"""
系统性LightGBM超参数调优完整流程
"""
# 初始化基础参数
base_params = {
'objective': 'regression',
'metric': 'rmse',
'boosting_type': 'gbdt',
'n_estimators': 1000,
'verbose': -1,
'random_state': 42
}
print("开始系统性参数调优...")
# 第一步:调整学习率
print("\n=== 第一步:调整学习率 ===")
best_lr, best_lr_score = tune_learning_rate(X, y, base_params.copy())
base_params['learning_rate'] = best_lr
print(f"最佳学习率: {best_lr}, 对应RMSE: {best_lr_score:.6f}")
# 第二步:调整树复杂度参数
print("\n=== 第二步:调整树复杂度参数 ===")
best_tree_params, best_tree_score = tune_tree_complexity(X, y, base_params.copy())
base_params.update(best_tree_params)
print(f"最佳树复杂度参数: {best_tree_params}")
print(f"对应RMSE: {best_tree_score:.6f}")
# 第三步:调整正则化参数
print("\n=== 第三步:调整正则化参数 ===")
best_reg_params, best_reg_score = tune_regularization(X, y, base_params.copy())
base_params.update(best_reg_params)
print(f"最佳正则化参数: {best_reg_params}")
print(f"对应RMSE: {best_reg_score:.6f}")
# 第四步:最终模型训练
print("\n=== 第四步:训练最终模型 ===")
final_score, final_predictions = train_final_model(X, y, test_data, base_params.copy())
print(f"最终模型RMSE: {final_score:.6f}")
return base_params, final_predictions
def tune_learning_rate(X, y, params, learning_rates=[0.005, 0.01, 0.05, 0.1]):
"""
调整学习率 - 实际调用实现
"""
best_score = float('inf')
best_lr = params.get('learning_rate', 0.05)
for lr in learning_rates:
params['learning_rate'] = lr
# 使用交叉验证评估
cv_scores = cross_validate_model(X, y, params)
current_score = np.mean(cv_scores)
print(f"学习率 {lr}: RMSE = {current_score:.6f}")
if current_score < best_score:
best_score = current_score
best_lr = lr
return best_lr, best_score
def tune_tree_complexity(X, y, params):
"""
调整树复杂度参数 - 实际调用实现
"""
param_grid = {
'num_leaves': [15, 31, 63],
'max_depth': [5, 8, -1], # -1 表示无限制
'min_data_in_leaf': [20, 50, 100]
}
best_score = float('inf')
best_params = {}
print("开始树复杂度参数搜索...")
# 简化的参数搜索(实际项目中可使用更高效的方法)
for num_leaves in param_grid['num_leaves']:
for max_depth in param_grid['max_depth']:
for min_data in param_grid['min_data_in_leaf']:
params['num_leaves'] = num_leaves
params['max_depth'] = max_depth
params['min_data_in_leaf'] = min_data
# 交叉验证评估
cv_scores = cross_validate_model(X, y, params)
current_score = np.mean(cv_scores)
print(f"num_leaves={num_leaves}, max_depth={max_depth}, min_data={min_data}: RMSE = {current_score:.6f}")
if current_score < best_score:
best_score = current_score
best_params = {
'num_leaves': num_leaves,
'max_depth': max_depth,
'min_data_in_leaf': min_data
}
return best_params, best_score
def tune_regularization(X, y, params):
"""
调整正则化参数 - 实际调用实现
"""
param_grid = {
'reg_alpha': [0, 0.01, 0.1, 1],
'reg_lambda': [0, 0.01, 0.1, 1],
'feature_fraction': [0.7, 0.8, 0.9, 1.0],
'bagging_fraction': [0.7, 0.8, 0.9, 1.0]
}
best_score = float('inf')
best_params = {}
print("开始正则化参数搜索...")
# 由于参数组合较多,这里使用简化搜索策略
# 先搜索 reg_alpha 和 reg_lambda
for reg_alpha in param_grid['reg_alpha']:
for reg_lambda in param_grid['reg_lambda']:
params['reg_alpha'] = reg_alpha
params['reg_lambda'] = reg_lambda
cv_scores = cross_validate_model(X, y, params)
current_score = np.mean(cv_scores)
print(f"reg_alpha={reg_alpha}, reg_lambda={reg_lambda}: RMSE = {current_score:.6f}")
if current_score < best_score:
best_score = current_score
best_params.update({
'reg_alpha': reg_alpha,
'reg_lambda': reg_lambda
})
# 然后搜索 feature_fraction 和 bagging_fraction
params.update(best_params)
best_score_stage2 = float('inf')
for feature_frac in param_grid['feature_fraction']:
for bagging_frac in param_grid['bagging_fraction']:
params['feature_fraction'] = feature_frac
params['bagging_fraction'] = bagging_frac
params['bagging_freq'] = 5 if bagging_frac < 1.0 else 0
cv_scores = cross_validate_model(X, y, params)
current_score = np.mean(cv_scores)
print(f"feature_fraction={feature_frac}, bagging_fraction={bagging_frac}: RMSE = {current_score:.6f}")
if current_score < best_score_stage2:
best_score_stage2 = current_score
best_params.update({
'feature_fraction': feature_frac,
'bagging_fraction': bagging_frac,
'bagging_freq': 5 if bagging_frac < 1.0 else 0
})
return best_params, best_score_stage2
def cross_validate_model(X, y, params, n_splits=5):
"""
交叉验证评估模型性能
"""
kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
scores = []
for train_idx, val_idx in kf.split(X):
X_train, X_val = X.iloc[train_idx], X.iloc[val_idx]
y_train, y_val = y.iloc[train_idx], y.iloc[val_idx]
# 创建数据集
train_dataset = lgb.Dataset(X_train, label=y_train)
val_dataset = lgb.Dataset(X_val, label=y_val, reference=train_dataset)
# 训练模型
model = lgb.train(
params,
train_dataset,
num_boost_round=params.get('n_estimators', 1000),
valid_sets=[val_dataset],
callbacks=[
lgb.early_stopping(stopping_rounds=100, verbose=False),
#lgb.record_evaluation(period=0)
]
)
# 预测并计算分数
val_pred = model.predict(X_val)
score = np.sqrt(mean_squared_error(y_val, val_pred))
scores.append(score)
return scores
def train_final_model(X, y, test_data, params):
"""
使用最优参数训练最终模型
"""
# 使用全部训练数据
train_dataset = lgb.Dataset(X, label=y)
print("训练最终模型...")
final_model = lgb.train(
params,
train_dataset,
num_boost_round=params.get('n_estimators', 1000),
#callbacks=[lgb.record_evaluation(period=100)]
)
# 在训练数据上评估(使用交叉验证更准确)
cv_scores = cross_validate_model(X, y, params)
final_score = np.mean(cv_scores)
# 预测测试集
test_predictions = final_model.predict(test_data)
# 特征重要性可视化
plot_feature_importance(final_model, X.columns)
return final_score, test_predictions
def plot_feature_importance(model, feature_names, top_n=20):
"""
可视化特征重要性
"""
importance = model.feature_importance(importance_type='gain')
feature_imp = pd.DataFrame({
'feature': feature_names,
'importance': importance
}).sort_values('importance', ascending=False)
plt.figure(figsize=(10, 8))
sns.barplot(x='importance', y='feature', data=feature_imp.head(top_n))
plt.title(f'LightGBM 特征重要性 Top {top_n}')
plt.tight_layout()
plt.show()
return feature_imp
# ==================== 实际调用示例 ====================
# 前面我们已经加载并预处理了数据
# X, y, test_data, test_ids 已经准备好
# 实际调用系统性调优
if __name__ == "__main__":
# 这里需要先加载数据(示例代码)
# 数据加载和预处理的代码...
# 调用系统性调优
best_params, final_predictions = systematic_hyperparameter_tuning(
X, y, test_data, test_ids
)
print("\n" + "="*50)
print("调优完成!")
print("最佳参数组合:")
for key, value in best_params.items():
print(f" {key}: {value}")
# 生成提交文件
submission = pd.DataFrame({
'Id': test_ids,
'SalePrice': np.exp(final_predictions) # 如果之前对y取了对数,这里需要指数变换
})
submission.to_csv('optimized_submission.csv', index=False)
print("预测结果已保存到 optimized_submission.csv")
得到结果:
如果是分类问题,可以调整一下参数中的 'objective'和'metric'。
3 模型解释与特征重要性
理解模型决策过程同样重要,接下来看下入模变量重要性排序,代码如下:
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib import rcParams
config = {
"font.family": 'serif',
"mathtext.fontset": 'stix', # matplotlib渲染数学字体时使用的字体,和Times New Roman差别不大
"font.serif": ['SimSun'], # 宋体
'axes.unicode_minus': False # 处理负号,即-号
}
rcParams.update(config)
# 特征重要性可视化
def plot_feature_importance(model, feature_names, top_n=20):
importance = model.feature_importances_
indices = np.argsort(importance)[::-1]
plt.figure(figsize=(10, 8))
sns.barplot(x=importance[indices[:top_n]], y=[feature_names[i] for i in indices[:top_n]])
plt.title('LightGBM特征重要性 Top {}'.format(top_n))
plt.tight_layout()
plt.show()
# 调用函数
plot_feature_importance(model, X.columns)
得到结果:
4 最终模型与预测
最后,应用最佳参数训练模型,代码如下:
# 使用调优后的参数训练模型
from bayes_opt import BayesianOptimization
import warnings
warnings.filterwarnings('ignore')
def bayesian_optimization_lightgbm(X, y, init_points=10, n_iter=20):
"""
使用贝叶斯优化进行LightGBM参数调优
"""
def lgb_cv(num_leaves, max_depth, min_data_in_leaf,
reg_alpha, reg_lambda, feature_fraction,
bagging_fraction, learning_rate):
"""
LightGBM交叉验证目标函数
"""
# 将参数转换为合适的数据类型和范围
params = {
'objective': 'regression',
'metric': 'rmse',
'boosting_type': 'gbdt',
'num_leaves': int(num_leaves),
'max_depth': int(max_depth),
'min_data_in_leaf': int(min_data_in_leaf),
'reg_alpha': max(reg_alpha, 0),
'reg_lambda': max(reg_lambda, 0),
'feature_fraction': min(max(feature_fraction, 0.1), 1.0),
'bagging_fraction': min(max(bagging_fraction, 0.1), 1.0),
'bagging_freq': 5,
'learning_rate': learning_rate,
'verbose': -1,
'random_state': 42
}
# 交叉验证
cv_scores = cross_validate_model(X, y, params, n_splits=3) # 使用3折加速
return -np.mean(cv_scores) # 贝叶斯优化默认最大化目标函数
# 定义参数边界
pbounds = {
'num_leaves': (20, 150),
'max_depth': (3, 15),
'min_data_in_leaf': (10, 100),
'reg_alpha': (0, 2),
'reg_lambda': (0, 2),
'feature_fraction': (0.5, 1.0),
'bagging_fraction': (0.5, 1.0),
'learning_rate': (0.01, 0.2)
}
# 创建优化器
optimizer = BayesianOptimization(
f=lgb_cv,
pbounds=pbounds,
random_state=42
)
print("开始贝叶斯优化...")
optimizer.maximize(
init_points=init_points,
n_iter=n_iter
)
# 获取最佳参数
best_params = optimizer.max['params']
# 转换参数类型
best_params['num_leaves'] = int(best_params['num_leaves'])
best_params['max_depth'] = int(best_params['max_depth'])
best_params['min_data_in_leaf'] = int(best_params['min_data_in_leaf'])
# 添加固定参数
best_params.update({
'objective': 'regression',
'metric': 'rmse',
'boosting_type': 'gbdt',
'bagging_freq': 5,
'verbose': -1,
'random_state': 42
})
return best_params, -optimizer.max['target']
# 调用贝叶斯优化
if __name__ == "__main__":
# 使用贝叶斯优化
bayesian_params, bayesian_score = bayesian_optimization_lightgbm(X, y)
print(f"\n贝叶斯优化最佳参数: {bayesian_params}")
print(f"贝叶斯优化最佳分数: {bayesian_score:.6f}")
# 使用贝叶斯优化的参数训练最终模型
final_score, final_predictions = train_final_model(
X, y, test_data, bayesian_params
)
得到结果:
调优结果对比
通过系统调优,我们的模型性能得到了显著提升:
- 基线模型RMSE:0.1333。
- 调优后RMSE:0.1288099。
- 性能提升:约3%。
这个提升在Kaggle竞赛中可能意味着几百甚至上千名的排名提升!
四、常见陷阱与应对策略
在实际使用LightGBM过程中,我总结了一些常见陷阱:
1.过拟合 :表现为训练集表现很好但验证集差。
-
对策:增加
min\_data\_in\_leaf、reg\_alpha、reg\_lambda,减少num\_leaves。2.欠拟合 :模型表现一直不佳。
-
对策:增加
num\_leaves、max\_depth,提高learning\_rate。3.训练不稳定 :每次运行结果差异大。
-
对策:设置
random\_state,增加bagging\_fraction和feature\_fraction。4.特征重要性误导 :高基数特征被赋予过高重要性。
-
对策:使用排列重要性或SHAP值进行验证。
五、总结
LightGBM是一个强大而高效的工具,但要充分发挥其潜力,需要:
1.理解核心原理 :明白直方图算法、GOSS和EFB的工作原理。
2.系统化调优 :按照学习率→树复杂度→正则化的顺序调参。
3.模型解释 :使用特征重要性和SHAP值理解模型决策。
4.避免常见陷阱 :识别并解决过拟合、欠拟合等问题。
记住,没有万能的参数配置,只有最适合你数据集的参数 。掌握系统化的调优方法,比盲目尝试成百上千种参数组合更有效。
希望这篇实战指南能帮助你在下一个项目中更高效地使用LightGBM!如果你有任何问题或心得,欢迎在评论区分享讨论。
对风控建模感兴趣的小伙伴欢迎加群讨论。
【部分群限时免费进** 】** 分群讨论学习Python、玩转Python、风控建模【29.9元进】、人工智能、数据分析相关问题,还提供招聘内推信息、优秀文章、学习视频、公众号文章答疑,也可交流工作中遇到的难题。如需添加微信号19967879837,加时备注想进的群,比如风控建模。
往期回顾:
一文囊括风控模型搭建(原理+Python实现),持续更新。。。
不同工作年限风控建模岗薪资水平如何?招聘最看重面试者什么能力?
100天精通风控建模(原理+Python实现)——第32天:集成学习是什么?在风控建模中有哪些应用?
限时免费加群
19967879837
添加 微信号、手机号
