3、SHAP计算实战:使用Python的shap库计算单样本与全局SHAP值、force_plot与summary_plot
本章聚焦于SHAP(SHapley Additive exPlanations)的工程落地。你将学习如何通过Python的shap库,从单样本解释(Local Explanation)过渡到全局解释(Global Explanation),并掌握force_plot与summary_plot两种核心可视化工具。这是模型漂移告警体系中,量化特征贡献变化的基础能力。
3.1 环境准备与基础模型训练
在计算SHAP值之前,需要确保安装了shap库,并准备一个训练好的模型。以下以XGBoost回归模型为例,使用经典的波士顿房价数据集(简化版)进行演示。
安装命令:
pip install shap xgboost pandas numpy matplotlib
训练一个基础模型:
import shap
import xgboost as xgb
import pandas as pd
import numpy as np
# 加载数据集(示例使用shap内置数据)
X, y = shap.datasets.boston()
model = xgb.XGBRegressor(n_estimators=100, max_depth=4, random_state=42)
model.fit(X, y)
# 创建SHAP解释器(TreeExplainer适用于树模型)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)
关键概念:
shap_values是一个二维数组,形状为 (样本数, 特征数)。每个元素表示对应特征在该样本中的SHAP值(对预测结果的贡献)。正值表示正向推动预测值,负值表示负向推动。
3.2 单样本SHAP值计算与force_plot
单样本解释用于回答:“为什么模型对这个特定样本给出了这样的预测?” force_plot将SHAP值以“力场”形式可视化,直观展示每个特征如何将预测值从基线值(Base Value)推向最终输出。
3.2.1 计算单个样本的SHAP值
# 选择第一个样本进行解释
sample_idx = 0
sample_data = X.iloc[sample_idx:sample_idx+1]
# 计算该样本的SHAP值
single_shap = explainer.shap_values(sample_data)
print("样本特征值:")
print(sample_data)
print("\nSHAP值(特征贡献):")
print(single_shap)
3.2.2 绘制force_plot
# 获取基线值(期望预测值)
expected_value = explainer.expected_value
# 绘制force_plot
shap.force_plot(expected_value, single_shap[0], sample_data, matplotlib=True)
下图展示了force_plot的核心结构:
实战要点: 在模型漂移监控中,定期对关键样本(如预测误差最大的样本)绘制
force_plot,可以直观发现哪些特征的贡献方向或幅度发生了突变,从而定位漂移根因。
3.3 全局SHAP值计算与summary_plot
全局解释回答:“在整个数据集中,哪些特征最重要?它们如何影响预测?” summary_plot结合了特征重要性(SHAP值绝对值的均值)与特征效应方向(SHAP值的正负分布)。
3.3.1 计算全局SHAP值
# 计算所有训练样本的SHAP值(已在前面完成)
# shap_values 形状为 (506, 13) 对于波士顿数据集
# 计算全局特征重要性(基于SHAP绝对值均值)
shap_importance = np.abs(shap_values).mean(axis=0)
feature_names = X.columns
# 打印特征重要性排序
importance_df = pd.DataFrame({
'feature': feature_names,
'importance': shap_importance
}).sort_values('importance', ascending=False)
print(importance_df.head(5))
3.3.2 绘制summary_plot
# 绘制summary_plot(点图)
shap.summary_plot(shap_values, X, show=False)
下图展示了summary_plot的构成原理:
常见误区:
summary_plot默认使用“特征值高低”着色,而非SHAP值正负。红色点集中在右侧表示:该特征值越高,对预测的正向贡献越大。不要将颜色与SHAP值正负混淆。
3.4 两种可视化工具的对比与选择
| 对比维度 | force_plot | summary_plot |
|---|---|---|
| 解释粒度 | 单样本(Local) | 全局(Global) |
| 核心信息 | 基线值、各特征贡献方向与大小、最终预测值 | 特征重要性排序、特征效应方向、特征值交互影响 |
| 适用场景 | 异常样本分析、个案审计、漂移根因定位 | 模型审计、特征筛选、漂移监控仪表盘 |
| 数据量敏感度 | 低(单样本即可) | 高(需要足够多样本) |
| 输出形式 | 力场图(可交互或静态) | 散点图(可交互或静态) |
3.5 实战案例:从单样本到全局的漂移线索
假设你在监控一个信贷评分模型,发现近期批贷通过率下降。通过以下步骤,可以利用SHAP值快速定位漂移来源:
- 计算全局SHAP值:使用
summary_plot查看当前数据中最重要的特征。发现“收入负债比”和“信用历史长度”排名前二。 - 对比历史基线:将当前
summary_plot与三个月前的对比。如果“收入负债比”的红色点(高特征值)在右侧的密度明显减少,说明高收入负债比的客户现在更难获得高分。 - 深入单样本:选取一个被拒的优质客户(历史信用好但被拒),绘制
force_plot。发现“收入负债比”的负向贡献从-0.5变为-2.3,说明模型对该特征的敏感度发生了变化。 - 告警触发:当多个样本的“收入负债比”SHAP值均值偏移超过阈值(如±0.5),触发模型漂移告警。
代码片段:计算特征SHAP值均值漂移
# 假设 baseline_shap 是历史SHAP值,current_shap 是当前SHAP值
baseline_mean = np.abs(baseline_shap).mean(axis=0)
current_mean = np.abs(current_shap).mean(axis=0)
drift_score = np.abs(current_mean - baseline_mean)
alarm_features = feature_names[drift_score > 0.5]
print(f"漂移告警特征: {list(alarm_features)}")
3.6 本章小结
- 单样本解释:
force_plot提供微观视角,适合个案分析与漂移根因追溯。 - 全局解释:
summary_plot提供宏观视角,适合特征重要性排序与整体漂移监控。 - 工程落地:将SHAP值计算封装为定时任务,对比不同时间窗口的SHAP分布,即可实现量化漂移检测。
- 可视化选择:根据分析粒度(单样本 vs 全局)和受众(技术 vs 业务)灵活选用两种图表。