线性模型SHAP值手动计算与SHAP库结果差异原因问询
我训练了一个预测房价的线性模型,对比手动计算的Shapley值和SHAP库返回值时发现存在细微差异。我理解线性模型的Shapley值计算公式应该是:coeff * features for obs - coeffs * mean(features in training set)
或SHAP文档所述的coef[i] * (x[i] - X.mean(0)[i])(i代表单个特征),想知道为什么两者结果不同。
相关代码
import pandas as pd from sklearn.datasets import fetch_california_housing from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler import shap X, y = fetch_california_housing(return_X_y=True, as_frame=True) X = X.drop(columns = ["Latitude", "Longitude", "AveBedrms"]) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0, ) scaler = MinMaxScaler().set_output(transform="pandas").fit(X_train) X_train = scaler.transform(X_train) X_test = scaler.transform(X_test) linreg = LinearRegression().fit(X_train, y_train) coeffs = pd.Series(linreg.coef_, index=linreg.feature_names_in_) X_test.reset_index(inplace=True, drop=True) obs = 6188 # 手动计算Shapley值 effect = coeffs * X_test.loc[obs] manual_shap = effect - coeffs * X_train.mean()
手动计算结果
MedInc 0.123210 HouseAge -0.459784 AveRooms -0.128162 Population 0.032673 AveOccup -0.001993 dtype: float64
SHAP库计算代码与结果
explainer = shap.LinearExplainer(linreg, X_train) shap_values = explainer(X_test) shap_values[obs]
返回结果:
.values = array([ 0.12039244, -0.47172515, -0.12767778, 0.03473923, -0.00251017]) .base_values = 2.0809714707337523 .data = array([0.25094137, 0.01960784, 0.06056066, 0.07912217, 0.00437137])
当前已设置忽略交互项:
explainer.feature_perturbation
返回:'interventional'
差异原因分析
1. 手动计算的逻辑误差
虽然数学上effect - coeffs * X_train.mean()等价于coeffs * (X_test.loc[obs] - X_train.mean()),但分步计算可能引入额外的浮点数累积误差。建议简化手动计算代码为:
manual_shap = coeffs * (X_test.loc[obs] - X_train.mean())
再对比结果,看差异是否缩小。
2. 特征与系数的匹配问题
需确保coeffs的特征顺序与X_test的列顺序完全一致。虽然代码中用linreg.feature_names_in_对齐了索引,但可通过以下代码验证:
assert list(coeffs.index) == list(X_test.columns), "特征顺序不匹配"
3. SHAP的数值处理细节
SHAP的LinearExplainer在interventional模式下,会直接使用模型系数与训练集特征均值计算SHAP值,但内部可能采用了更精确的数值优化(比如批量计算时的精度处理),这可能导致与手动单样本计算的细微差异。
4. 基准值与预测值的一致性校验
SHAP值的核心要求是:单个样本的SHAP值之和 + base_value = 模型对该样本的预测值。你可以通过以下代码验证:
# 验证SHAP值的正确性 pred_obs = linreg.predict(X_test.loc[obs:obs])[0] base_value = linreg.predict(X_train).mean() shap_sum = shap_values[obs].values.sum() print(f"SHAP和验证:{abs(pred_obs - base_value - shap_sum) < 1e-6}") # 验证手动计算的正确性 manual_sum = manual_shap.sum() print(f"手动计算和验证:{abs(pred_obs - base_value - manual_sum) < 1e-6}")
如果手动计算的验证结果为False,说明你的手动计算中存在特征均值、系数或测试样本值的匹配错误。
内容的提问来源于stack exchange,提问作者Sole Galli

