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 结构示意图 基线值 E[f(x)] = 22.5 LSTAT +3.2 RM -1.8 最终预测 f(x) = 24.8 特征贡献明细 • LSTAT: +3.2 (正向最大) • RM: -1.8 (负向最大) • CRIM: +0.5 • 其他特征: 合计 +0.4 force_plot 解读要点 1. 红色箭头:正向贡献特征,将预测值向右(更高)推动。 2. 绿色箭头:负向贡献特征,将预测值向左(更低)推动。 3. 箭头长度代表贡献绝对值大小,越长影响越大。
实战要点: 在模型漂移监控中,定期对关键样本(如预测误差最大的样本)绘制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 结构示意图 LSTAT RM CRIM DIS ...其他特征 低 高 SHAP值(对模型输出的影响) 特征值高 特征值低 每个点代表一个样本;X轴位置表示SHAP值(影响方向与大小); 颜色表示该特征值的高低(红高绿低),揭示特征与预测的非线性关系。
常见误区: summary_plot默认使用“特征值高低”着色,而非SHAP值正负。红色点集中在右侧表示:该特征值越高,对预测的正向贡献越大。不要将颜色与SHAP值正负混淆。

3.4 两种可视化工具的对比与选择

对比维度 force_plot summary_plot
解释粒度 单样本(Local) 全局(Global)
核心信息 基线值、各特征贡献方向与大小、最终预测值 特征重要性排序、特征效应方向、特征值交互影响
适用场景 异常样本分析、个案审计、漂移根因定位 模型审计、特征筛选、漂移监控仪表盘
数据量敏感度 低(单样本即可) 高(需要足够多样本)
输出形式 力场图(可交互或静态) 散点图(可交互或静态)

3.5 实战案例:从单样本到全局的漂移线索

假设你在监控一个信贷评分模型,发现近期批贷通过率下降。通过以下步骤,可以利用SHAP值快速定位漂移来源:

  1. 计算全局SHAP值:使用summary_plot查看当前数据中最重要的特征。发现“收入负债比”和“信用历史长度”排名前二。
  2. 对比历史基线:将当前summary_plot与三个月前的对比。如果“收入负债比”的红色点(高特征值)在右侧的密度明显减少,说明高收入负债比的客户现在更难获得高分。
  3. 深入单样本:选取一个被拒的优质客户(历史信用好但被拒),绘制force_plot。发现“收入负债比”的负向贡献从-0.5变为-2.3,说明模型对该特征的敏感度发生了变化。
  4. 告警触发:当多个样本的“收入负债比”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 业务)灵活选用两种图表。