梯度提升集成:LightGBM与XGBoost组合预测
LightGBM和XGBoost是两种高效的梯度提升决策树算法,常用于回归和分类任务,通过逐步优化模型来提升预测精度,并支持并行计算以加速训练过程,我们的组合模型通过分别训练LightGBM和XGBoost模型,然后对它们的预测结果取平均值,以进一步提高预测的准确性和稳健性。
1. 代码实现流程图
2. 代码实现
2.1 数据读取
import pandas as pd
df = pd.read_excel('Auto MPG.xlsx')
df.head()
数据涉及城市循环燃油消耗,以每加仑英里数 (miles per gallon, mpg) 为单位,根据 3 个多值离散属性和 5 个连续属性来预测mpg。
2.2 缺失值处理
df.isnull().sum()
horsepower列存在6条缺失值,采用基于K最近邻的缺失值填充算法,假设相似的样本在特征空间中具有相似的特征值,从而使得填充后的数据尽可能保持原有的分布特性。
# 采用KNN算法填补缺失值
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=3)
imputed = imputer.fit_transform(df)
data = pd.DataFrame(imputed, columns=df.columns, index = df.index)
data.isnull().sum()
2.3 数据集划分
from sklearn.model_selection import train_test_split
# 首先将数据集划分为训练集和测试集
X_temp, X_test, y_temp, y_test = train_test_split(df.iloc[:,0:7], df['mpg'], test_size=0.2, random_state=42)
# 然后将训练集进一步划分为训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.125, random_state=42) # 0.125 x 0.8 = 0.1
# 输出数据集的大小
print(f"训练集维度: {X_train.shape}")
print(f"验证集维度: {X_val.shape}")
print(f"测试集维度: {X_test.shape}")
函数train_test_split函数是可以直接进行数据集的训练集和测试集分割的,然而,它不能直接将数据分为训练集、验证集和测试集三部分,于是利用函数对数据集进行两次分割,首先将数据划分为训练集和测试集,然后将训练集进一步分割为训练集和验证集,并保证训练集、验证集、测试集的比例约为7:1:2。
2.4 归一化目标变量
from sklearn.preprocessing import MinMaxScaler
def normalize_dataframe(y_train, y_val, y_test):
scaler = MinMaxScaler()
scaler.fit(np.array(y_train).reshape(-1, 1)) # 在训练集上拟合归一化模型 MinMaxScaler输入数据形状为二维数组
train = pd.DataFrame(scaler.transform(np.array(y_train).reshape(-1, 1)), index=y_train.index)
val = pd.DataFrame(scaler.transform(np.array(y_val).reshape(-1, 1)), index=y_val.index)
test = pd.DataFrame(scaler.transform(np.array(y_test).reshape(-1, 1)), index=y_test.index)
return train, val, test, scaler
train_y, val_y, test_y, scaler_y = normalize_dataframe(y_train, y_val, y_test)
2.5 定义和训练模型
import lightgbm as lgb
import xgboost as xgb
# LightGBM模型参数
params_lgb = {
'learning_rate': 0.02, # 学习率,控制每一步的步长,用于防止过拟合。典型值范围:0.01 - 0.1
'boosting_type': 'gbdt', # 提升方法,这里使用梯度提升树(Gradient Boosting Decision Tree,简称GBDT)
'objective': 'mse', # 损失函数
'metric': 'rmse', # 评估指标
'num_leaves': 127, # 每棵树的叶子节点数量,控制模型复杂度。较大值可以提高模型复杂度但可能导致过拟合
'verbose': -1, # 控制 LightGBM 输出信息的详细程度,-1表示无输出,0表示最少输出,正数表示输出更多信息
'seed': 42, # 随机种子,用于重现模型的结果
'n_jobs': -1, # 并行运算的线程数量,-1表示使用所有可用的CPU核心
'feature_fraction': 0.8, # 每棵树随机选择的特征比例,用于增加模型的泛化能力
'bagging_fraction': 0.9, # 每次迭代时随机选择的样本比例,用于增加模型的泛化能力
'bagging_freq': 4 # 每隔多少次迭代进行一次bagging操作,用于增加模型的泛化能力
}
model_lgb = lgb.LGBMRegressor(**params_lgb)
# XGBoost模型参数
params_xgb = {
'learning_rate': 0.02, # 学习率,控制每一步的步长,用于防止过拟合。典型值范围:0.01 - 0.1
'booster': 'gbtree', # 提升方法,这里使用梯度提升树(Gradient Boosting Tree)
'objective': 'reg:squarederror',# 损失函数
'max_leaves': 127, # 每棵树的叶子节点数量,控制模型复杂度。较大值可以提高模型复杂度但可能导致过拟合
'verbosity': 1, # 控制 XGBoost 输出信息的详细程度,0表示无输出,1表示输出进度信息
'seed': 42, # 随机种子,用于重现模型的结果
'nthread': -1, # 并行运算的线程数量,-1表示使用所有可用的CPU核心
'colsample_bytree': 0.6, # 每棵树随机选择的特征比例,用于增加模型的泛化能力
'subsample': 0.7, # 每次迭代时随机选择的样本比例,用于增加模型的泛化能力
'early_stopping_rounds': None # 早停参数在fit时单独设置
}
model_xgb = xgb.XGBRegressor(**params_xgb)
# 定义平均模型
class AverageModel:
def __init__(self, models):
self.models = models
def fit(self, X, y, X_val, y_val):
for model in self.models:
if isinstance(model, lgb.LGBMRegressor):
model.fit(X, y, eval_set=[(X_val, y_val)], eval_metric='rmse', callbacks=[lgb.early_stopping(stopping_rounds=100)])
elif isinstance(model, xgb.XGBRegressor):
model.fit(X, y, eval_set=[(X_val, y_val)], eval_metric='rmse', early_stopping_rounds=model.get_params()['early_stopping_rounds'], verbose=False)
def predict(self, X):
predictions = []
for model in self.models:
predictions.append(model.predict(X))
return sum(predictions) / len(predictions)
# 创建平均模型
average_model = AverageModel([model_lgb, model_xgb])
# 训练模型
average_model.fit(X_train, train_y, X_val, val_y)
代码定义了一个名为 AverageModel 的类,用于创建一个平均模型,通过集成LightGBM和XGBoost模型的预测结果来提升预测的稳定性和准确性,在 fit 方法中,该类可以同时训练传入的多个模型,并使用验证集进行早期停止策略来防止过拟合,在 predict 方法中,该类将多个模型的预测结果取平均作为最终的预测输出。
这里代码使用了一个平均模型类,将多个模型包装起来,提供了更统一和可重用的接口,方便管理和使用多个模型,当然也可以考虑引入K折交叉验证在每个fold内部独立训练和评估模型,然后手动取平均预测结果,如果希望封装多个模型为一个整体进行训练和预测,可以使用当前代码的方式,如果需要更灵活的控制和理解每个模型在每个fold的表现,可以采取第二种方法进行代码编写。
2.6 预测测试集
y_pred = average_model.predict(X_test)
y_pred
2.7 计算评估指标
from sklearn import metrics
y_pred_list = y_pred.tolist() # 或者 y_pred_array = np.array(y_pred)
mse = metrics.mean_squared_error(test_y, y_pred_list)
rmse = np.sqrt(mse)
mae = metrics.mean_absolute_error(test_y, y_pred_list)
r2 = metrics.r2_score(test_y, y_pred_list)
print("均方误差 (MSE):", mse)
print("均方根误差 (RMSE):", rmse)
print("平均绝对误差 (MAE):", mae)
print("拟合优度 (R-squared):", r2)
2.8 可视化预测结果
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
# 反归一化
train_min = np.min(y_train)
train_max = np.max(y_train)
pred = y_pred * (train_max - train_min) + train_min
y_test = np.array(y_test)
plt.figure(figsize=(8, 8), dpi=300)
# 计算预测值 真实值差值的绝对值
alpha_values = abs(pred-y_test.reshape(-1)) # 值越大alpha越大
# 确保 alpha 值在 0 到 1 之间
alpha_values = np.clip(alpha_values, 0, 1)
plt.scatter(pred, y_test, color='blue', edgecolor='k', s=50, alpha=alpha_values, label='预测值 vs 真实值')
plt.title('预测值与真实值对比图', fontsize=16)
plt.xlabel('预测值', fontsize=14)
plt.ylabel('真实值', fontsize=14)
max_val = max(max(pred), max(y_test))
min_val = min(min(pred), min(y_test))
plt.plot([min_val, max_val], [min_val, max_val], color='red', linestyle='--', linewidth=2, label='x=y')
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
plt.show()
图中横轴表示模型预测的值,纵轴表示真实的标签值,每个点的透明度(alpha 值)根据预测值与真实值的差异大小动态调整,差异越大的点透明度越低,红色虚线表示理想情况下预测值等于真实值的对角线。