R 公式的工作原理
问题中的r 公式适用于randomForest。随机森林中的每棵树都尝试直接预测目标变量。因此,每棵树的预测都在预期区间内(在您的情况下,所有房价都是正数),整体预测只是所有个体预测的平均值。
ensemble_prediction = mean(tree_predictions)
这就是公式告诉您的内容:只需对所有树 x 进行预测,然后对它们进行平均。
为什么 Python PDP 值很小
但是,在sklearn 中,会为GradientBoostingRegressor 计算部分相关性。在梯度提升中,每棵树都在当前预测时预测损失函数的导数,该导数仅与目标变量间接相关。对于 GB 回归,预测为
ensemble_prediction = initial_prediction + sum(tree_predictions * learning_rate)
对于GB分类的预测概率是
ensemble_prediction = softmax(initial_prediction + sum(tree_predictions * learning_rate))
对于这两种情况,部分依赖被报告为just
sum(tree_predictions * learning_rate)
因此,initial_prediction(对于GradientBoostingRegressor(loss='ls'),它仅等于训练的平均值y)不包含在 PDP 中,这使得预测为负。
至于其值的小范围,您示例中的y_train 很小:平均房价约为2,因此房价可能以百万为单位。
sklearn 公式的实际工作原理
我已经说过,在sklearn 中,部分依赖函数的值是所有树的平均值。还有一个调整:所有不相关的特征都被平均掉了。为了描述平均的实际方式,我将引用sklearn的the documentation:
对于网格中“目标”特征的每个值,部分
依赖函数需要边缘化一棵树的预测
“补”特征的所有可能值。在决策树中
无需参考
训练数据。对于每个网格点,加权树遍历是
执行:如果拆分节点涉及“目标”特征,则
跟随相应的左或右分支,否则两者
接下来是分支,每个分支都由以下部分加权
进入该分支的训练样本。最后,部分
依赖性由所有访问过的叶子的加权平均值给出。为了
将每棵树的结果再次平均。
如果您仍然不满意,请参阅the source code。
一个例子
要看到预测已经在因变量的范围内(但只是居中),你可以看一个非常玩具的例子:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble.partial_dependence import plot_partial_dependence
np.random.seed(1)
X = np.random.normal(size=[1000, 2])
# yes, I will try to fit a linear function!
y = X[:, 0] * 10 + 50 + np.random.normal(size=1000, scale=5)
# mean target is 50, range is from 20 to 80, that is +/- 30 standard deviations
model = GradientBoostingRegressor().fit(X, y)
fig, subplots = plot_partial_dependence(model, X, [0, 1], percentiles=(0.0, 1.0), n_cols=2)
subplots[0].scatter(X[:, 0], y - y.mean(), s=0.3)
subplots[1].scatter(X[:, 1], y - y.mean(), s=0.3)
plt.suptitle('Partial dependence plots and scatters of centered target')
plt.show()
您可以看到部分依赖图很好地反映了居中目标变量的真实分布。
如果您不仅想要单位,还想要与 y 一致的均值,则必须将“丢失”均值添加到 partial_dependence 函数的结果中,然后手动绘制结果:
from sklearn.ensemble.partial_dependence import partial_dependence
pdp_y, [pdp_x] = partial_dependence(model, X=X, target_variables=[0], percentiles=(0.0, 1.0))
plt.scatter(X[:, 0], y, s=0.3)
plt.plot(pdp_x, pdp_y.ravel() + model.init_.mean)
plt.show()
plt.title('Partial dependence plot in the original coordinates');