3. SHAP 计算实战:使用 Python 的 shap 库计算单样本与全局 SHAP 值
本章是 SHAP 值监控的实操起点。我们将使用 Python 的 shap 库,分别计算单样本(局部解释)和全局(整体特征重要性)SHAP 值。理解这两种计算方式,是后续监控特征贡献漂移和阈值漂移的基础。
前置准备:确保已安装 shap 库(
pip install shap),并准备一个训练好的模型(本实战使用 XGBoost 分类器)和对应的特征数据。
3.1 单样本 SHAP 值计算(局部解释)
单样本 SHAP 值回答的是:“对于这一个预测结果,每个特征贡献了多少?” 它基于 Shapley 值理论,为每个特征分配一个边际贡献值。
计算步骤:
- 创建解释器:使用
shap.TreeExplainer(model)为树模型创建解释器。 - 计算单个样本:调用
explainer.shap_values(X_sample)或explainer(X_sample)。 - 可视化:使用
shap.force_plot()或shap.waterfall_plot()展示单个样本的特征贡献。
import shap
import xgboost as xgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 1. 加载数据并训练模型
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = xgb.XGBClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# 2. 创建 TreeExplainer
explainer = shap.TreeExplainer(model)
# 3. 选取测试集第一个样本
X_sample = X_test[:1]
# 4. 计算单样本 SHAP 值
shap_values_single = explainer.shap_values(X_sample)
# 5. 输出结果
print("单样本 SHAP 值形状:", shap_values_single.shape)
print("特征名称:", load_breast_cancer().feature_names)
print("SHAP 值:", shap_values_single[0])
# 6. 可视化(可选,在 Jupyter 中运行)
# shap.force_plot(explainer.expected_value, shap_values_single[0], X_sample, feature_names=load_breast_cancer().feature_names)
关键理解:单样本 SHAP 值的总和加上模型的期望输出(base value)等于该样本的预测值。正 SHAP 值表示该特征将预测值推向正类(1),负值则推向反类(0)。
3.2 全局 SHAP 值计算(特征重要性)
全局 SHAP 值回答的是:“在整个数据集上,哪些特征对模型预测最重要?” 常用两种聚合方式:平均绝对 SHAP 值和SHAP 依赖图。
计算步骤:
- 批量计算:对测试集或全量数据调用
explainer.shap_values(X)。 - 聚合重要性:计算每个特征的平均绝对 SHAP 值(
np.abs(shap_values).mean(axis=0))。 - 可视化:使用
shap.summary_plot()或shap.bar_plot()展示全局特征重要性。
import numpy as np
# 1. 计算整个测试集的 SHAP 值
shap_values_all = explainer.shap_values(X_test)
# 2. 计算全局特征重要性(平均绝对 SHAP 值)
feature_names = load_breast_cancer().feature_names
global_importance = np.abs(shap_values_all).mean(axis=0)
# 3. 排序并输出 Top-5 特征
sorted_idx = np.argsort(global_importance)[::-1]
print("全局特征重要性(Top-5):")
for i in range(5):
idx = sorted_idx[i]
print(f" {i+1}. {feature_names[idx]}: {global_importance[idx]:.4f}")
# 4. 可视化(可选,在 Jupyter 中运行)
# shap.summary_plot(shap_values_all, X_test, feature_names=feature_names)
# shap.bar_plot(shap_values_all, X_test, feature_names=feature_names)
注意:全局 SHAP 值反映的是特征在整个数据集上的平均贡献,不代表每个样本的局部贡献。当监控到全局特征重要性发生漂移时,应进一步检查具体样本的局部 SHAP 值变化。
3.3 单样本 vs 全局 SHAP 对比
| 对比维度 | 单样本 SHAP(局部) | 全局 SHAP(整体) |
|---|---|---|
| 计算对象 | 单个样本 | 整个数据集或测试集 |
| 输出形式 | 每个特征一个 SHAP 值(可正可负) | 每个特征一个平均绝对 SHAP 值(非负) |
| 用途 | 解释单个预测、调试异常样本 | 特征选择、监控特征重要性漂移 |
| 可视化工具 | force_plot, waterfall_plot | summary_plot, bar_plot |
| 对监控的意义 | 检测单个样本的特征贡献异常 | 检测整体特征重要性的分布漂移 |
实战建议:在 MLOps 监控中,建议同时计算单样本和全局 SHAP 值。全局监控用于触发告警(如 Top-3 特征排名变化),单样本监控用于根因分析(如具体哪个样本的特征贡献异常)。