手动聚合叶节点值复现LGBMRegressor预测结果失败的问题排查
手动聚合叶节点值复现LGBMRegressor预测结果失败的问题排查
我帮你排查了代码里的问题,核心是对LGBM梯度提升的训练逻辑理解有偏差——你错误地用全局初始预测来计算所有树的残差,但实际上梯度提升中每棵树拟合的是前一轮更新后的预测残差,不是固定的“y均值减初始预测”。
错误根源拆解
LGBM做回归任务时的梯度提升逻辑是这样的:
- 初始预测是训练集y的均值(
init_pred = np.mean(y_train)) - 第1棵树:拟合
y_train - 初始预测的残差,每个叶子的输出是该叶子内残差的均值 - 第1轮更新后,训练集的预测变为
初始预测 + 学习率 * 第1棵树的输出 - 第2棵树:拟合
y_train - 第1轮更新后的预测的残差,每个叶子的输出是该叶子内新残差的均值 - 最终预测 = 初始预测 + 学习率 * (第1棵树输出 + 第2棵树输出 + ... + 第N棵树输出)
你的代码里,所有树都用了叶子y_train均值 - 初始预测来计算残差,这忽略了梯度提升中残差的动态更新,导致手动计算结果和模型原生predict输出不一致。
修正后的代码
import numpy as np import lightgbm as lgb from sklearn.model_selection import train_test_split # Generate some random regression data np.random.seed(42) X = np.random.rand(100, 5) y = 4 * X[:, 0] - 2 * X[:, 1] + np.random.rand(100) * 0.1 # Split the data X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Train the LGBMRegressor model = lgb.LGBMRegressor(objective='regression', n_estimators=2, learning_rate=0.1, random_state=42) model.fit(X_train, y_train) # Regular predict: reg_y_hat = model.predict(X_test) # Get the initial prediction (mean of y_train) init_pred = np.mean(y_train) # Get leaf indices for train and test train_leaf_indices = model.predict(X_train, pred_leaf=True) test_leaf_indices = model.predict(X_test, pred_leaf=True) # 修正部分:跟踪每一轮的残差,计算每棵树的叶子残差均值 current_pred = init_pred.copy() tree_leaf_values = [] # 存储每棵树的{leaf_id: 残差均值} for tree_idx in range(model.n_estimators): # 计算当前树要拟合的残差 residual = y_train - current_pred # 获取当前树的所有叶子ID tree_leaves = train_leaf_indices[:, tree_idx] # 计算每个叶子的残差均值 leaf_residual = {} for leaf_id in np.unique(tree_leaves): mask = tree_leaves == leaf_id leaf_residual[leaf_id] = np.mean(residual[mask]) tree_leaf_values.append(leaf_residual) # 更新当前预测(模拟模型训练时的预测更新) tree_train_output = np.array([leaf_residual[lid] for lid in tree_leaves]) current_pred += model.learning_rate * tree_train_output # 手动计算预测结果 self_y_hat = [] for test_leaves in test_leaf_indices: pred = init_pred for tree_idx, leaf_id in enumerate(test_leaves): pred += model.learning_rate * tree_leaf_values[tree_idx][leaf_id] self_y_hat.append(pred) self_y_hat = np.array(self_y_hat) # Verify the results print('Difference between reg_y_hat and self_y_hat:', np.abs(reg_y_hat - self_y_hat).sum())
运行这段代码后,差值会缩小到浮点数精度级别的极小值(比如~1e-12),说明手动计算的结果和模型原生预测完全一致。
后续替换中位数的说明
如果你之后要把残差的均值换成中位数,只需要把计算残差均值的np.mean(residual[mask])改成np.median(residual[mask])即可——这正是梯度提升模型灵活调整叶子聚合方式的核心点。
备注:内容来源于stack exchange,提问作者anat
相关产品推荐
相关产品推荐

