如何手动计算scikit-learn GradientBoostingRegressor各树节点的square_error与value值?
如何手动计算scikit-learn GradientBoostingRegressor各树节点的square_error与value值?
我来帮你理清楚这里的逻辑,你之前的计算出错是因为混淆了梯度提升树中「单棵树的预测目标」和「模型整体的预测结果」,咱们一步步拆解:
首先明确梯度提升回归树(以平方误差损失为例)的核心逻辑:
- 初始预测是训练集标签的均值(也就是
gbm.init_.predict(X_train)的结果,等价于y_train.mean()) - 每一棵后续的树,都是用来拟合前一轮预测的残差(即
残差 = 真实标签 - 前一轮累计预测值) - 单棵回归树的每个节点的
value,是该节点内样本残差的均值(平方误差损失下,最优回归值就是样本均值) - 节点的
square_error(即不纯度)是该节点内样本的均方误差,也就是mean((残差 - 节点value)^2)
接下来用你的第一棵树的根节点为例,手动计算:
步骤1:计算初始预测与第一棵树的残差
# 获取初始预测(默认是训练集y的均值) init_pred = gbm.init_.predict(X_train) # 第一棵树要拟合的残差 residuals_1 = y_train - init_pred
步骤2:计算根节点的value和square_error
根节点包含所有训练样本,直接计算即可:
# 根节点的value:所有残差的均值(因为init_pred是y_train的均值,这个值会接近0) root_value = residuals_1.mean() # 根节点的square_error:残差的均方误差(等于y_train的方差) root_square_error = np.mean((residuals_1 - root_value)**2)
这个结果应该和你用plot_tree看到的第一棵树根节点的数值完全一致。
为什么你之前的计算不对?
你之前的代码:
error = np.mean((y_train - gbm.estimators_[0][0].predict(X_train))**2) value = -2 * (y_train - gbm.estimators_[0][0].predict(X_train))
问题出在两点:
gbm.estimators_[0][0].predict(X_train)返回的是第一棵树的节点value(也就是对应样本所在节点的残差均值),但模型的整体预测是init_pred + gbm.learning_rate * 树的预测值,你直接用y_train减去树的预测值,相当于把树的预测当成了最终预测,完全不符合梯度提升的逻辑。- 你计算的
value用了-2*(y - pred),这是平方误差损失的负梯度,但梯度提升中树拟合的是负梯度(也就是残差),但节点的value是该节点内负梯度的均值,不是每个样本的负梯度。
拓展:计算任意节点的数值
如果要计算非根节点的数值,比如第一棵树的某个子节点,需要先找到该节点包含的样本:
- 获取树的结构信息:
tree = gbm.estimators_[0][0].tree_,其中tree.children_left[i]/tree.children_right[i]是节点i的左右子节点索引,tree.feature[i]是分裂特征索引,tree.threshold[i]是分裂阈值。 - 根据这些结构信息,写出递归或循环逻辑筛选出该节点对应的样本掩码。
- 用掩码筛选出对应残差,再计算均值(value)和均方误差(square_error)即可。
备注:内容来源于stack exchange,提问作者Sole Galli
相关产品推荐
相关产品推荐

