LightGBM三部曲:LightGBM实战调优

一直想把XGBoost和LightGBM的原理及应用梳理完分享给大家,但是内容有点多,也有些存疑的点在慢慢厘清,这篇文章也是断断续续在整理。

今天终于梳理完Lightgbm实战调优啦,希望对大家有帮助。

对LightGBM原理感兴趣的可翻看历史文章:LightGBM三部曲:LightGBM原理LightGBM三部曲:LightGBM参数详解,原生与Sklearn接口全掌握

对XGBoost原理、参数详解、实战感兴趣可翻看历史文章:XGBoost三部曲:XGBoost原理XGBoost三部曲:XGBoost参数详解XGBoost三部曲:XGBoost实战

LigthGBM是微软开发的一款高性能梯度提升框架,是一种集成学习算法,属于Boosting类型,通过叠加多个决策树的预测结果得出最终的预测结果。

因其 训练速度快、内存消耗低、性能优异 而受到数据科学界的广泛青睐。

这个算法基于GBDT,对树模型基础知识不了解的小伙伴可以翻看之前的文章:Python中调用sklearn决策树一文弄懂随机森林的原理和应用一文弄懂GBDT原理和应用

今天,我们就通过三个实战案例,带你完整掌握LightGBM的核心用法和调优技巧。

本文目录

  1. 初识LightGBM——快速上手
  2. 理解核心原理——为何它如此高效

2.1 直方图算法

2.2 单边梯度采样(GOSS)

2.3 互斥特征捆绑(EFB) 3. 实战调优案例——Kaggle竞赛实战

3.1 数据准备与基线模型

3.2 系统性调优策略

3.3 模型解释与特征重要性

3.4 最终模型与预测 4. 常见陷阱与应对策略

  1. 总结

一、初识LightGBM——快速上手picture.image

为什么要选择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不过如此。但 这才是冰山一角 ,接下来让我们深入其核心原理。

二、理解核心原理——为何它如此高效picture.image

1 直方图算法

LightGBM通过将连续特征离散化到直方图中,大大减少了寻找最佳分裂点的计算量,这是其速度优势的主要来源。

2 单边梯度采样(GOSS)

传统GBDT需要计算所有样本的梯度,而GOSS保留梯度大的样本,随机采样梯度小的样本,在不影响精度的情况下大幅提升训练速度。

3 互斥特征捆绑(EFB)

高维特征通常稀疏,且许多特征互斥。EFB将互斥特征捆绑在一起,减少特征维度,进一步提升训练效率。

理解这些原理,是有效调优的基础 。很多人在调参时盲目尝试,就是因为不了解背后的机制。

三、实战调优案例——Kaggle竞赛实战

picture.image

接下来通过一个实际案例( 使用Kaggle的房价预测数据集,目标是预测房屋售价) ,演示完整的LightGBM调优流程。

1 数据准备与基线模型

数据集y为房屋售价,X为影响房屋价格的因素,展示前两行如下:

picture.image

接着对数据进行预处理和基线模型训练,代码如下:

  
#房价预测  
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")

得到结果:

picture.image

如果是分类问题,可以调整一下参数中的 '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)

得到结果:

picture.image

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  
    )

得到结果:

picture.image

调优结果对比

通过系统调优,我们的模型性能得到了显著提升:

  • 基线模型RMSE:0.1333。
  • 调优后RMSE:0.1288099。
  • 性能提升:约3%。

这个提升在Kaggle竞赛中可能意味着几百甚至上千名的排名提升!

四、常见陷阱与应对策略picture.image

在实际使用LightGBM过程中,我总结了一些常见陷阱:

1.过拟合 :表现为训练集表现很好但验证集差。

  • 对策:增加min\_data\_in\_leafreg\_alphareg\_lambda,减少num\_leaves。

    2.欠拟合 :模型表现一直不佳。

  • 对策:增加num\_leavesmax\_depth,提高learning\_rate。

    3.训练不稳定 :每次运行结果差异大。

  • 对策:设置random\_state,增加bagging\_fractionfeature\_fraction。

    4.特征重要性误导 :高基数特征被赋予过高重要性。

  • 对策:使用排列重要性或SHAP值进行验证。

五、总结

picture.image

LightGBM是一个强大而高效的工具,但要充分发挥其潜力,需要:

1.理解核心原理 :明白直方图算法、GOSS和EFB的工作原理。

2.系统化调优 :按照学习率→树复杂度→正则化的顺序调参。

3.模型解释 :使用特征重要性和SHAP值理解模型决策。

4.避免常见陷阱 :识别并解决过拟合、欠拟合等问题。

记住,没有万能的参数配置,只有最适合你数据集的参数 。掌握系统化的调优方法,比盲目尝试成百上千种参数组合更有效。

希望这篇实战指南能帮助你在下一个项目中更高效地使用LightGBM!如果你有任何问题或心得,欢迎在评论区分享讨论。

对风控建模感兴趣的小伙伴欢迎加群讨论。

【部分群限时免费进** 】** 分群讨论学习Python、玩转Python、风控建模【29.9元进】、人工智能、数据分析相关问题,还提供招聘内推信息、优秀文章、学习视频、公众号文章答疑,也可交流工作中遇到的难题。如需添加微信号19967879837,加时备注想进的群,比如风控建模。

picture.image

picture.image

往期回顾:

信贷风控架构一张图

变量筛选—特征包含信息量

一文弄懂卡方分箱的原理和应用

应用决策树生成【效果好】【非过拟合】的策略集

一文囊括风控模型搭建(原理+Python实现),持续更新。。。

不同工作年限风控建模岗薪资水平如何?招聘最看重面试者什么能力?

100天精通风控建模(原理+Python实现)——第32天:集成学习是什么?在风控建模中有哪些应用?

picture.image

picture.image

限时免费加群

19967879837

添加 微信号、手机号

0
0
0
0
评论
未登录
暂无评论