这是由于与随机森林算法一起使用时该方法的特殊性;引用相关 Github 线程shap explainer expected_value 中的响应与模型期望值不同:
这是因为 sklearn 如何在它构建的树模型中记录训练样本。随机森林使用数据的随机子样本来训练每棵树,而在 sklearn 中使用随机子样本来记录模型中的叶样本权重。由于 TreeExplainer 使用记录的叶样本权重来表示训练数据集,因此它将取决于训练期间使用的随机抽样。这将导致像您所看到的那样的小变化。
我们实际上可以验证其他算法不存在这种行为,比如梯度提升树:
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
import numpy as np
import shap
shap.__version__
# 0.37.0
X, y = make_regression(n_samples=1000, n_features=10, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
gbt = GradientBoostingRegressor(random_state=0)
gbt.fit(X_train, y_train)
mean_pred_gbt = np.mean(gbt.predict(X_train))
mean_pred_gbt
# -11.534353657511172
gbt_explainer = shap.TreeExplainer(gbt)
gbt_explainer.expected_value
# array([-11.53435366])
np.isclose(mean_pred_gbt, gbt_explainer.expected_value)
# array([ True])
但是对于 RF,我们确实得到了一个“小的变化”,正如主要 SHAP 开发人员在上面的线程中提到的:
rf = RandomForestRegressor(random_state=0)
rf.fit(X_train, y_train)
rf_explainer = shap.TreeExplainer(rf)
rf_explainer.expected_value
# array([-11.59166808])
mean_pred_rf = np.mean(rf.predict(X_train))
mean_pred_rf
# -11.280125877556388
np.isclose(mean_pred_rf, rf_explainer.expected_value)
# array([False])