跳转到内容

SHAP — 用博弈论给每个特征发工资

是什么

SHAP(SHapley Additive exPlanations)是一个 Python 模型解释库,2017 年由华盛顿大学的 Scott Lundberg 和 Su-In Lee 提出(NeurIPS 2017 论文 A Unified Approach to Interpreting Model Predictions)。

日常类比:把一次模型预测看成一场合作博弈——

  • 队员:每个特征(年龄、收入、负债比 …)
  • 奖金:模型给出的预测值(比如『违约概率 0.73』)
  • 任务:把奖金公平地分给每个队员,分到的钱就是该特征对这次预测的贡献

公平分奖金的方案在 1953 年已经被 Lloyd Shapley(2012 诺贝尔经济学奖得主)解决了,叫 Shapley value。SHAP 把这套博弈论数学搬到机器学习模型解释上。

import shap, xgboost
model = xgboost.XGBClassifier().fit(X, y)
explainer = shap.TreeExplainer(model)
shap_values = explainer(X)
shap.plots.waterfall(shap_values[0]) # 单条样本的瀑布图
shap.plots.beeswarm(shap_values) # 全局蜂群图

GitHub ~23k star,Kaggle 和业界做表格类 ML 时的事实标准解释工具。

为什么重要

模型一旦上生产(信贷、医疗、风控),监管和业务方一定会问:『为什么模型预测它会违约?』

不用 SHAP 之前,常见做法各有缺陷:

  • 看 feature_importances_:只能告诉你『年龄整体重要』,不能解释这一条预测里年龄推高了多少
  • LIME(Ribeiro 2016):开了局部解释先河,但每个解释器对同一模型给的答案不一致,没数学保证
  • 手工写规则:模型一变就全推倒重来

SHAP 同时解决三件事:

  1. 理论:满足 Shapley 公平四公理(局部可加 / 一致 / 缺失 / 对称),是同类方法里唯一有此保证的
  2. 工程:TreeSHAP 把指数级复杂度降到多项式,XGBoost / LightGBM / CatBoost 上能秒级算完
  3. 可视化:summary / force / waterfall / dependence 四张图把抽象的 Shapley 值变成业务方看得懂的故事

核心要点

1. Shapley value 的直觉

把特征想成依次加入团队,每次加入都看预测值变化多少(边际贡献)。考虑所有可能的加入顺序取平均,就是该特征的 Shapley value。

数学上写起来吓人:

φ_i = Σ |S|!(n-|S|-1)!/n! · [v(S∪{i}) - v(S)]

但意思就一句:『所有顺序里它平均带来多少额外预测分』。

2. 三类 Explainer

  • TreeExplainer:树模型专用(XGBoost / LightGBM / CatBoost / sklearn 树),用 TreeSHAP 算法,O(TLD²) 多项式复杂度,速度快、结果精确
  • DeepExplainer / GradientExplainer:深度网络,结合 DeepLIFT 思想用反向传播算近似
  • KernelExplainer:模型无关(任何黑箱),用加权线性回归逼近 Shapley,慢但通用

3. 四公理(Shapley 数学保证)

公理意思
局部可加性所有特征 SHAP 值之和 + 基准 = 当前预测,分毫不差
一致性模型变了,某特征贡献变大,它的 SHAP 值就一定变大
缺失性模型不用的特征,SHAP 值必为 0
对称性两个特征作用相同 → SHAP 值相同

4. 四张关键图

  • summary plot(蜂群):全局重要性 + 每条样本的方向,最常看
  • force plot:单条预测的拉力图,红色推高、蓝色拉低
  • waterfall plot:瀑布图,从基准值一步步加到最终预测
  • dependence plot:某特征值 × 它的 SHAP 值散点,看非线性和交互

实践案例

案例 1:信贷违约模型

explainer = shap.TreeExplainer(xgb_model)
shap_values = explainer(X_test)
shap.plots.waterfall(shap_values[42])

输出告诉你:第 42 个客户被预测违约概率 0.73,因为

  • 负债收入比 0.65 → +0.31
  • 信用历史 2 年 → +0.18
  • 月收入 3500 → +0.09
  • 年龄 28 → -0.05
  • 加起来 + 基准 0.20 = 0.73,正好对上(局部可加性)

业务方拿到这个分解就能写拒绝理由信,监管能复核。

案例 2:调试数据泄露

跑 SHAP summary plot 时如果发现 user_id 的 SHAP 值排第一名——立刻知道训练集泄露了,模型在记 ID 而不是学规律。这是 Kaggle 老炮用 SHAP 的标准排查手法。

案例 3:替代 LIME

explainer = shap.KernelExplainer(model.predict, X_background)
shap_values = explainer.shap_values(X_test[:10])

KernelSHAP 通用但慢,只在小样本+黑箱模型时才用,否则首选 TreeExplainer。

踩过的坑

  1. 特征相关性破坏独立假设:标准 Shapley 假设特征独立,高相关特征会被均摊贡献。用 TreeSHAP 的 feature_perturbation="tree_path_dependent"(默认)可缓解
  2. KernelSHAP 在大样本上跑不动:1 万条样本 × 50 特征要算几十分钟。有树用树,没树才退到 Kernel
  3. SHAP 不是因果:解释的是『模型在做什么』,不是『世界真实因果』。别拿 SHAP 当因果推断
  4. summary plot 容易过度解读:高基数类别特征(如『城市』有 300 个值)常被 SHAP 低估,因为单个城市样本少
  5. 深度模型解释不稳:DeepSHAP 在不同 background 数据集上结果有差异,工程上 captum 在 PyTorch 生态更成熟

适用 vs 不适用场景

适用

  • 表格类 ML(XGBoost / LightGBM / CatBoost / sklearn)做生产解释——首选 TreeSHAP
  • Kaggle 特征工程检查、数据泄露排查
  • 需要给非技术方写预测理由(信贷、保险、医疗)

不适用

  • 因果分析 → 用 DoWhy / EconML
  • 大规模 NLP / CV 模型 → captum / Integrated Gradients 更合适
  • 实时低延迟解释(每条 < 1ms) → 预计算 SHAP 表查询,或用 surrogate model

学到什么

  1. 博弈论数学能解决 ML 工程问题——1953 年 Shapley 设计的奖金分配方案,60 年后被搬来解释神经网络
  2. 统一框架的力量:SHAP 论文证明 LIME / DeepLIFT / Shapley regression 都是同一套 additive attribution 的特例
  3. 理论保证 + 工程优化缺一不可:纯 Shapley 指数复杂度跑不起来,TreeSHAP 把它压到多项式才让工业落地

延伸阅读

关联

  • scikit-learn —— sklearn 估计器是 SHAP 最常解释的对象
  • optuna —— HPO 调出最优模型后,用 SHAP 检查它学到了什么
  • pytorch —— DeepExplainer / GradientExplainer 的运行后端