SHAP的升级版:可解释性框架Alibi的相关介绍(一)
shap理论的升级框架,涵盖更广泛,其中反事实部分是笔者比较感兴趣想细究的。
github地址:
https://github.com/SeldonIO/alibi?tab=readme-ov-file
文档地址:
https://docs.seldon.io/projects/alibi/en/stable/index.html
文章目录
-
1 Alibi 0.9.5 文档译文
-
-
1.1 Alibi的可解释性
-
1.2 全局与局部洞察
-
1.3 洞察类型
-
- 1.3.1 全局特征归因
- 1.3.2 累积局部效应
- 1.3.3 部分依赖
- 1.3.4 部分依赖方差
- 1.3.5 排列重要性
- 1.3.6 局部必要特征
- 1.3.7 对比解释方法(相关正例)
- 1.3.8 局部特征归因LFA
- 1.3.9 核SHAP
- 1.3.10 路径依赖树SHAP
- 1.3.11 干预树SHAP
-
1.4 反事实实例(Counterfactual instances)
-
1.5 相似性解释(Similarity explanations)
-
-
2 Alibi的笔者自划重点
-
-
2.1 分析手段分个类
-
- 2.1.1 特征重要性(哪些特征影响了这个预测?)
- 2.1.2 特征依赖(特征如何交互?)
- 2.1.3 规则和模式(模型遵循哪些规则?)
- 2.1.4 反事实(哪些改变会导致不同结果?)
- 2.1.5 模型信心(这个预测有多可信?)
-
2.2 两种SHAP值方法
-
- 2.2.1 KernelSHAP实现
- 2.2.2 TreeSHAP实现
-
2.3 锚点解释
-
2.4 反事实解释
-
- 2.4.1 基本反事实解释(CF)
- 2.4.2 反事实原型(CFProto)
- 2.4.3 对比解释方法(CEM)
- 2.4.4 反事实强化学习(CFRL)
-
2.5 误差分析 - 基于相似性的解释高贡献训练样本挖掘
-
2.6 工程化:解释器的保存与加载
-
1 Alibi 0.9.5 文档译文
翻译地址:
https://docs.seldon.io/projects/alibi/en/stable/overview/high_level.html
与文章【不仅仅是 SHAP,使用 ALIBI 在 Python 中获得更多可解释性】
1.1 Alibi的可解释性
可解释性为我们提供了一组算法,这些算法能够对训练模型的预测提供洞察。它使我们能够回答以下问题:
- 预测如何根据特征输入而变化?
- 对于给定的预测,哪些特征是重要的,哪些不是?
- 需要最小改变哪些特征才能获得你选择的新预测?
- 每个特征如何贡献于模型的预测?
Alibi 提供了一组称为解释器的算法或方法。每个解释器提供某种关于模型的洞察。给定训练模型可用的洞察集依赖于多个因素。

例如,如果模型是回归模型,询问预测如何随某个回归特征变化是有意义的。而询问获得新类别预测所需的最小变化则没有意义。一般来说,给定模型,Alibi 提供的解释器受到以下限制:
- 模型处理的数据类型。每个洞察适用于以下某些或所有数据类型:图像、表格或文本。
- 模型执行的任务。Alibi 为回归或分类模型提供解释器。
- 使用的模型类型。模型类型的示例包括神经网络和随机森林。
安装,Alibi 可以通过 pip 或 conda 安装:
# 基本安装
pip install alibi
# 支持分布式计算
pip install alibi[ray]
# 支持 SHAP
pip install alibi[shap]
或者使用 conda/mamba:
# 基本安装
mamba install -c conda-forge alibi
# 支持分布式计算和 SHAP
mamba install -c conda-forge alibi ray shap
1.2 全局与局部洞察
洞察可以分为两类——局部和全局。直观地说,局部洞察描述模型所做的单个预测。例如,给定一张被模型分类为猫的图像,局部洞察可能给出需要保持不变的特征(像素),以便该图像仍被分类为猫。
另一方面,全局洞察指的是模型在一系列输入上的行为。例如,显示回归预测如何随特定特征变化的图表。这些洞察提供了输入与模型预测之间关系的更一般理解。

可解释性也存在重大偏差:可解释性 ≠ 客观性
虽然解释器增强了理解,但它们并不免于偏见:
- 数据偏见 :如果您的数据包含偏见(例如,种族或性别偏差),模型及其解释将反映这些偏见。
- 人类偏见 :解释可能会受到我们自身期望的影响。如果模型的推理与我们预期的不一致,我们可能会不信任它,即使它是正确的。
- 复杂性 ≠ 错误性 :某些解释(例如,接近决策边界的锚点)可能很复杂。不要将复杂性误认为是错误。
🧠 示例:风险分析师使用 SHAP 值来验证模型时,可能会确认它“是合理的”——但仅在他们自己(可能有偏见的)期望的范围内。
1.3 洞察类型
Alibi 提供了几种局部和全局洞察,以便探索和理解模型。以下内容使从业者了解在何种情况下哪些解释器是合适的。
| 解释器 | 范围 | 模型类型 | 任务类型 | 数据类型 | 用途 | 资源 |
|---|---|---|---|---|---|---|
| 累积局部效应 | 全局 | 黑箱 | 分类、回归 | 表格(数值) | 模型预测如何随感兴趣特征变化? | docs, paper |
| 部分依赖 | 全局 | 黑箱、白箱(scikit-learn) | 分类、回归 | 表格(数值、分类) | 模型预测如何随感兴趣特征变化? | docs, paper |
| 部分依赖方差 | 全局 | 黑箱、白箱(scikit-learn) | 分类、回归 | 表格(数值、分类) | 哪些特征在全局上最重要?特征之间的交互程度如何? | docs, paper |
| 排列重要性 | 全局 | 黑箱 | 分类、回归 | 表格(数值、分类) | 哪些特征在全局上最重要? | docs, paper |
| 锚点 | 局部 | 黑箱 | 分类 | 表格(数值、分类)、文本和图像 | 确保预测保持不变的特征集是什么? | docs, paper |
| 相关正例 | 局部 | 黑箱、白箱(TensorFlow) | 分类 | 表格(数值)、图像 | “” | docs, paper |
| 集成梯度 | 局部 | 白箱(TensorFlow) | 分类、回归 | 表格(数值、分类)、文本和图像 | 每个特征对模型预测的贡献是什么? | docs, paper |
| 核SHAP | 局部 | 黑箱 | 分类、回归 | 表格(数值、分类) | “” | docs, paper |
| 路径依赖树SHAP | 局部 | 白箱(XGBoost, LightGBM, CatBoost, scikit-learn 和 pyspark 树模型) | 分类、回归 | 表格(数值、分类) | “” | docs, paper |
| 干预树SHAP | 局部 | 白箱(XGBoost, LightGBM, CatBoost, scikit-learn 和 pyspark 树模型) | 分类、回归 | 表格(数值、分类) | “” | docs, paper |
| 反事实实例 | 局部 | 黑箱(可微分)、白箱(TensorFlow) | 分类 | 表格(数值)、图像 | 重新分类当前预测需要哪些特征的最小变化? | docs, paper |
| 对比解释方法 | 局部 | 黑箱(可微分)、白箱(TensorFlow) | 分类 | 表格(数值)、图像 | “” | docs, paper |
| 基于原型的反事实 | 局部 | 黑箱(可微分)、白箱(TensorFlow) | 分类 | 表格(数值、分类)、图像 | “” | docs, paper |
| 强化学习反事实 | 局部 | 黑箱 | 分类 | 表格(数值、分类)、图像 | “” | docs, paper |
| 相似性解释 | 局部 | 白箱 | 分类、回归 | 表格(数值、分类)、文本和图像 | 根据模型,训练集中与感兴趣实例最相似的实例是什么? | docs, paper |
1.3.1 全局特征归因
全局特征归因方法旨在显示模型输出对输入特征子集的依赖关系。它们提供全局洞察,描述模型在输入空间上的行为。例如,累积局部效应图获得直接可视化特征与预测之间关系的图形。
假设一个训练好的回归模型,根据温度、湿度和风速预测某一天租用的自行车数量。温度特征的全局特征归因图可能是绘制的线图,显示租用的自行车数量与温度之间的关系。可以预期,在特定温度之前,租用量会增加,然后在温度过高后减少。
1.3.2 累积局部效应
Alibi 提供累积局部效应(ALE)图,因为它们提供了最准确的洞察。
我们在葡萄酒质量数据集上说明 ALE 的使用,该数据集是一个以葡萄酒质量为目标变量的表格数值数据集。由于我们希望进行分类任务,因此使用 5 作为阈值将数据分为好和坏类别。我们可以通过简单地使用 Alibi 计算 ALE(请参见笔记本):
from alibi.explainers import ALE, plot_ale
# 模型是二分类器,因此我们只取第一个模型输出,对应于“好”类概率。
predict_fn = lambda x: model(scaler.transform(x)).numpy()[:, 0]
ale = ALE(predict_fn, feature_names=features)
exp = ale.explain(X_train)
# 绘制“酒精特征”的解释
plot_ale(exp, features=['alcohol'], line_kw={'label': 'Probability of "good" class'})
python
因此,我们看到模型预测较高酒精含量的葡萄酒为更好的葡萄酒。

请注意,虽然 ALE 在数值表格数据上是明确定义的,但在分类数据上并不明确。这是因为不清楚两个分类值之间的差异应该是什么。请注意,如果数据集中有混合的分类和数值特征,我们始终可以计算数值特征的 ALE。
优缺点
| 优点 | 缺点 |
|---|---|
| ALE 图易于可视化并直观理解 | 比起 PDP 图或 M 图,解释该方法背后的动机更困难 |
| 作为黑箱算法非常通用 | 需要访问训练数据集 |
| 不会像 PD 图那样在基础特征中挣扎于依赖性 | 分类变量的 ALE 定义不明确 |
| ALE 图计算速度快 |
1.3.3 部分依赖
Alibi 提供部分依赖(PD)图作为 ALE 的替代方案。
优缺点
| 优点 | 缺点 |
|---|---|
| PD 图易于可视化并直观理解(比 ALE 更容易) | 在基础特征中挣扎于依赖性。在不相关的情况下,解释可能不明确。 |
| 作为黑箱算法非常通用 | 可能隐藏异质效应(ICE 来拯救)。 |
| PD 图通常计算速度快。对于基于 scikit-learn 的树模型更快的实现 | |
| PD 图具有因果解释。该关系对模型是因果的,但不一定对现实世界是因果的 | |
| 对分类特征的自然扩展 |
1.3.4 部分依赖方差
Alibi 提供部分依赖方差作为一种方式,以全局衡量特征重要性及特征对之间的交互强度。由于该方法基于部分依赖,因此从业者应注意该方法继承的主要限制(请参见上述讨论)。
优缺点
| 优点 | 缺点 |
|---|---|
| 对特征重要性的计算具有直观的动机 | 特征重要性仅捕获主要效应,忽略可能的特征交互 |
| 作为黑箱算法非常通用 | 即使存在特征交互,也可能无法检测到 |
| 一般计算速度快。对于基于 scikit-learn 的树模型更快的实现 | |
| 提供标准化程序来量化特征重要性(即,与某些树模型的内部特征重要性形成对比) | |
| 支持数值和分类特征 | |
| 可以量化潜在的交互效应的强度 |
1.3.5 排列重要性
Alibi 提供排列重要性作为一种全局特征重要性度量方式。特征重要性的计算基于当特征列中的特征值被打乱时模型性能下降的程度。实践者应注意的一个重要行为是,相关特征的重要性可能在它们之间分配。
优缺点
| 优点 | 缺点 |
|---|---|
| 解释简单明了——特征重要性是当特征变为噪声时模型损失/分数的增加/减少。 | 需要真实标签 |
| 作为黑箱算法非常通用 | 可能对不现实的数据实例产生偏见 |
| 特征重要性考虑所有特征交互 | 重要性度量与损失/分数函数相关 |
| 不需要重新训练模型 |
1.3.6 局部必要特征
局部必要特征告诉我们,为了使模型给出相同的分类,特定实例的哪些特征需要保持不变。在训练好的图像分类模型的情况下,给定实例的局部必要特征将是模型用于做出决策的图像的最小子集。Alibi 提供两种计算局部必要特征的解释器:锚点和相关正例。
1.3.7 对比解释方法(相关正例)
相关正例是实例的特征子集,即使去掉这些特征,分类仍然保持不变。与锚点不同,它们的构建并不旨在最大化覆盖率。创建它们的方法也有很大不同。粗略的想法是定义特征的缺失,然后扰动实例,尽可能多地去除信息,同时保留原始分类。请注意,这些是 CEM 方法的子集,该方法也用于构造相关负例/反事实。
优缺点
| 优点 | 缺点 |
|---|---|
| 可以与白箱(TensorFlow)和某些黑箱模型一起使用 | 找到要从实例中去除的非信息特征值通常并不简单,且需要领域知识 |
| 自编码器损失需要访问原始数据集 | |
| 需要调整超参数 ( beta ) 和 ( gamma ) | |
| 洞察并未告知我们相关正例的覆盖率 | |
| 对于黑箱模型,由于必须数值评估梯度,因此速度较慢 | |
| 仅适用于可微的黑箱模型 |
1.3.8 局部特征归因LFA
局部特征归因(LFA)询问在给定实例中每个特征对其预测的贡献。在图像的情况下,这将突出显示对模型预测最负责任的像素。请注意,这与局部必要特征略有不同,后者寻找保持相同预测所需的最小特征子集。局部特征归因则给每个特征分配一个分数。
局部特征归因的一个良好示例是检测图像分类器是否关注图像的正确特征以推断类别。在他们的论文《我为什么应该信任你?:解释任何分类器的预测》中,Marco Tulio Ribeiro 等人对一小部分狼和哈士奇图像数据集训练了一个逻辑回归分类器。该数据集经过精心挑选,以便只有狼的图片有雪地背景,而哈士奇则没有。LFA 方法揭示了哈士奇在雪地中的错误分类为狼的原因是网络错误地关注了那些有雪地背景的图像。
Alibi 提供四种解释器来计算 LFA:集成梯度、核SHAP、路径依赖树SHAP 和 干预树SHAP。
后面三种在 SHAP 库中实现,Alibi 作为包装器。
对于归因方法的相关性,我们期望属性在某些情况下表现一致。因此,它们应满足以下属性:
- 效率/完整性 :归因之和应等于预测与基线之间的差异。
- 对称性 :对模型有相同影响的变量应具有相等的归因。
- 虚拟/敏感性 :不改变模型输出的变量应具有零归因。
- 可加性/线性 :对两个模型的线性组合的特征归因应等于对每个模型的该特征归因的线性组合。
并非所有 LFA 方法都满足这些方法(例如 LIME),但 Alibi 提供的方法(集成梯度、核SHAP、路径依赖和干预树SHAP)都满足。
1.3.9 核SHAP
核SHAP 是计算模型在实例周围的 Shapley 值的方法。Shapley 值是一种博弈论方法,根据对整体目标的贡献分配奖励。在我们的案例中,特征就是玩家,奖励就是归因。
给定任何特征子集,我们可以询问特征在该集合中的存在如何影响模型输出。我们通过计算包含和不包含特定特征的集合的模型输出来做到这一点。通过考虑所有可能的特征子集的有无,获得该特征的 Shapley 值。
两个问题出现。大多数模型并不是训练为接受可变数量的输入特征。其次,考虑所有可能的缺失特征集合会导致考虑幂集,这在特征数量较多时是不可行的。
为了解决前者,我们从干预条件期望中进行采样。这将缺失特征替换为从训练分布中采样的值。为了解决后者,核SHAP 方法在子集空间上进行采样以获得估计。
Alibi 提供对 SHAP 库的包装器。我们可以使用此解释器计算 sklearn 随机森林模型的 Shapley 值,方法如下(请参见notebook):
from alibi.explainers import KernelShap
# 黑箱模型
predict_fn = lambda x: rfc.predict_proba(scaler.transform(x))
explainer = KernelShap(predict_fn, task='classification')
explainer.fit(X_train[0:100])
result = explainer.explain(x)
plot_importance(result.shap_values[1], features, 1)
python
这给出了以下输出:

该结果与集成梯度的结果相似,尽管由于使用不同的方法和模型,二者之间存在差异。
优缺点
| 优点 | 缺点 |
|---|---|
| 满足多个期望属性 | 核SHAP 由于所需样本数量多而较慢 |
| Shapley 值易于解释和可视化 | 干预条件概率引入不现实的数据点 |
| 作为黑箱方法非常通用 | 需要访问训练数据集 |
1.3.10 路径依赖树SHAP
计算模型的 Shapley 值需要为每个实例特征的幂集计算干预条件期望。对于基于树的模型,我们可以通过通常应用树来近似此分布。然而,对于缺失特征,我们同时沿着树的两个路径进行处理,按训练数据集中经过每条路径的样本比例加权。
树 SHAP 方法同时为所有特征幂集的成员执行此操作,从而实现显著的加速。
使用 Alibi 计算随机森林的路径依赖树SHAP 解释器(请参见notebook),我们使用:
from alibi.explainers import TreeShap
# rfc is a random forest model
path_dependent_explainer = TreeShap(rfc)
path_dependent_explainer.fit() # path dependent Tree SHAP doesn't need any data
result = path_dependent_explainer.explain(scaler.transform(x)) # explain the scaled instance
plot_importance(result.shap_values[1], features, '"Good"')
python

路径依赖树SHAP的输出通常与核SHAP和集成梯度的结果相似,但由于利用了树模型结构,计算效率更高。
优缺点
| 优点 | 缺点 |
|---|---|
| 计算速度快,适合基于树的模型 | 仅适用于基于树的模型 |
| 满足多个期望属性 | 需要访问模型内部结构 |
| 结果易于解释和可视化 | |
| 不需要训练数据集 |
1.3.11 干预树SHAP
干预树SHAP方法与路径依赖树SHAP类似,但它使用训练数据的干预分布来计算条件期望,而不是路径依赖的近似。这样可以更准确地反映特征之间的依赖关系。
Alibi同样支持干预树SHAP,使用方式与路径依赖树SHAP类似。
优缺点
| 优点 | 缺点 |
|---|---|
| 更准确地考虑特征之间的依赖 | 计算成本较高 |
| 结果易于解释和可视化 | 仅适用于基于树的模型 |
| 满足多个期望属性 | 需要训练数据集和模型内部结构 |
1.4 反事实实例(Counterfactual instances)
给定数据集中的一个实例和模型的预测,自然会产生一个问题:该实例需要发生怎样的最小改变,才能得到不同的预测结果?
这样的生成实例被称为反事实 。
反事实解释属于局部解释,因为它们针对单个实例和模型预测。
例如,给定一个在 MNIST 数据集上训练的分类模型和一个数据样本,一个反事实可能是一个与原始图像非常相似但经过修改后被模型分类为不同数字的图像。

图片来源:Samoilescu RF 等人,《通过强化学习实现模型无关且可扩展的反事实解释》,2021年
反事实不仅可以用于调试模型,也可以增强模型功能。举例来说,假设一个模型基于表格数据对客户的财务决策做出判断,一个反事实可以告诉客户需要如何调整行为以获得不同的决策结果。
或者,它可以告诉机器学习工程师,如果推荐的改变涉及无关特征,则模型可能存在错误假设。但实践者仍需警惕偏差问题。
反事实 x_{cf}需要满足:
- 模型对 x_{cf}的预测应接近预定目标输出(例如,期望的类别标签)。
- 反事实 x_{cf}应具有可解释性。
第一个要求很明确,但第二个则需要对“可解释性”有一定定义。
Alibi 提供了四种寻找反事实的方法:
反事实实例(CFI) 、对比解释方法(CEM) 、基于原型的反事实(CFP)和 基于强化学习的反事实(CFRL)。
这些方法对可解释性的定义略有不同,但都强调解的稀疏性,即希望仅改变少量特征,从而限制解决方案的复杂度,使其更易理解。
需要注意的是,仅仅稀疏的特征变化并不能保证生成的反事实看起来像数据分布中的真实样本。
CEM 、CFP 和CFRL 方法还要求反事实必须符合数据分布,以保证其可解释性。

原始MNIST数字7实例,以及使用1)反事实实例方法和2)基于原型的反事实方法生成的反事实
前三种方法(CFI、CEM、CFP)构造反事实的方式非常相似:它们定义一个损失函数,鼓励生成既可解释又属于目标类别的反事实实例,然后通过梯度下降在特征空间中搜索满足条件的反事实。
主要差别在于,CEM 和CFP 方法还训练了自编码器来确保生成的反事实符合数据分布。

使用和不使用训练数据自编码器的梯度下降反事实生成过程
这三种方法实际只适用于单通道灰度图像,对于多通道图像效果不佳。若要获得多通道图像的高质量反事实,建议使用CFRL 。
CFRL 方法与上述三种方法不同,它通过强化学习训练一个模型(称为“actor”),该模型接收实例并生成反事实。
强化学习中,actor根据奖励函数学习采取行动;在这里,actor尝试生成被正确分类且符合数据分布且稀疏的反事实。训练过程不需要访问模型内部,只需通过采样获得预测结果。最终得到的模型可以实时生成满足约束的可解释反事实。
此外,CFRL支持在生成反事实时考虑约束条件(如不可变特征),这对于建议用户可执行的改变非常重要。例如,建议贷款申请者年龄变小显然不现实。
后续会着重研究这一个模块。
1.5 相似性解释(Similarity explanations)
| 解释器 | 范围 | 模型类型 | 任务类型 | 数据类型 | 用途 | 资源 |
|---|---|---|---|---|---|---|
| 相似性解释 | 局部 | 白箱 | 分类、回归 | 表格(数值、分类)、文本和图像 | 找出训练集中与待解释实例最相似的样本,用以解释模型的预测 | 文档, 论文 |
相似性解释是一种基于实例的解释方法,通过查找训练集中与测试实例最相似的数据点来解释模型对该测试实例的预测。该解释类型可以被理解为模型通过引用训练集中相似且具有相同预测的实例来“证明”其预测——例如,“我之所以将这张图片分类为‘金毛寻回犬’,是因为它与训练集中我也分类为‘金毛寻回犬’的图片最为相似。”

相似性解释说明模型为何将图像分类为‘金毛寻回犬’,因为训练集中最相似的实例也被归为‘金毛寻回犬’。
2 Alibi的笔者自划重点
2.1 分析手段分个类
把Alibi整个大功能大概分个类:
2.1.1 特征重要性(哪些特征影响了这个预测?)
| 方法 | 优点 | 最适合 |
|---|---|---|
| Integrated Gradients | 通过沿路径累积梯度将重要性归因于输入特征 | 神经网络(TF/Keras);图像的像素级解释;文本的标记级解释 |
| Kernel SHAP | 特征归因的博弈论方法;可以处理任何黑盒模型 | 当您需要具有理论保证的一致特征归因时;适用于分布式计算 |
| Tree SHAP | 专为树模型设计的SHAP实现 | 树模型(XGBoost, LightGBM等)当速度重要时 |
| Permutation Importance | 通过打乱特征值来衡量特征重要性的简单方法 | 快速全局特征重要性评估;适用于任何模型类型 |
2.1.2 特征依赖(特征如何交互?)
| 方法 | 优点 | 最适合 |
|---|---|---|
| ALE(累积局部效应) | 显示特征如何平均影响预测,考虑特征相关性 | 在处理特征相关性的同时理解全局特征效应 |
| PDP(部分依赖) | 显示特征对预测的边际效应 | 当特征相对独立时理解全局特征效应 |
| PD Variance | 通过测量部分依赖内的方差来识别特征重要性和交互 | 量化特征交互和全局重要性 |
2.1.3 规则和模式(模型遵循哪些规则?)
| 方法 | 优点 | 最适合 |
|---|---|---|
| Anchor Explanations | 提供锚定预测的决策规则 | 从复杂模型中提取人类可理解的规则;适用于表格、文本和图像数据 |
| ProtoSelect | 选择原型示例来代表类别 | 构建可解释的基于原型的分类器;精简数据集 |
2.1.4 反事实(哪些改变会导致不同结果?)
| 方法 | 优点 | 最适合 |
|---|---|---|
| Counterfactual (CF) | 生成最小改变以获得不同预测 | 简单反事实生成适用于可微分模型 |
| Prototype Counterfactuals (CFProto) | 使用类别原型来指导反事实生成 | 使用原型创建更可解释的反事实 |
| Counterfactuals with RL (CFRL) | 使用强化学习生成多样化的反事实 | 模型无关的批量反事实生成,具有灵活的约束 |
| CEM(对比解释) | 生成相关正例(最小充分特征)和负例(最小排除特征) | 理解预测的充分和必要特征 |
2.1.5 模型信心(这个预测有多可信?)
| 方法 | 优点 | 最适合 |
|---|---|---|
| Trust Scores | 比较到最近类别的距离与预测类别的距离 | 分类信心评估 |
| Linearity Measure | 测量模型在测试实例附近的线性程度 | 理解预测附近的模型行为 |
2.2 两种SHAP值方法
SHAP值基于合作博弈论,提供了一种统一的特征重要性度量。它们表示每个特征对预测的贡献,考虑了所有可能的特征组合。
Alibi实现了两种SHAP变体:
- KernelSHAP:模型无关的实现,适用于任何黑盒模型
- TreeSHAP:针对树模型(XGBoost、LightGBM等)的优化实现
2.2.1 KernelSHAP实现
KernelSHAP通过采样不同的特征联盟并使用加权线性回归模型来近似SHAP值。
from alibi.explainers import KernelShap
# 初始化解释器
explainer = KernelShap(
predictor=model.predict, # 预测函数
feature_names=feature_names, # 可选的特征名称
task='classification', # 'classification'或'regression'
seed=0 # 用于可重复性
)
# 在背景数据上拟合解释器
explainer.fit(
background_data=X_train, # 背景数据集用于基线值
summarise_background=True, # 减少背景数据大小以提高性能
n_background_samples=100 # 使用的背景样本数量
)
# 生成解释
explanation = explainer.explain(
X=X_test[:5], # 要解释的实例
nsamples=500 # 用于SHAP估计的样本数量
)

KernelShap解释器处理复杂数据格式,并提供总结背景数据以改进性能的功能。它还可以通过将分类变量视为一组来处理分类变量。
2.2.2 TreeSHAP实现
TreeSHAP是一种针对XGBoost、LightGBM和sklearn树模型的精确SHAP值计算的优化算法。
from alibi.explainers import TreeShap
# 初始化解释器
explainer = TreeShap(
predictor=xgb_model, # 树模型
model_output='raw', # 输出类型:'raw'、'probability'、'log_loss'
feature_names=feature_names # 可选的特征名称
)
# 拟合解释器(可选背景数据用于干预方法)
explainer.fit(
background_data=X_train, # 用于干预方法的背景数据
summarise_background=True # 总结背景以提高性能
)
# 生成解释
explanation = explainer.explain(X_test[:5])

TreeSHAP提供两种方法:
- 路径依赖(默认无背景数据):解释模型机制
- 干预(提供背景数据时):解释特征对结果的影响
2.3 锚点解释
锚点解释提供人类可读的IF-THEN规则,这些规则“锚定”了一个预测——意味着当这些条件成立时,模型的预测几乎可以保证保持不变。可以将这些视为预测的充分条件。
例如,一个锚点可能会告诉你:
IF 婚姻状况 = 分居 AND 性别 = 女性
THEN 预测 = 收入 <= 50K(精确度为95%)
这意味着每当这两个条件满足时,模型预测“收入 <= 50K”的置信度为95%,不受其他特征的影响。
锚点解释巧妙地结合了两种算法:
- Beam Search:高效探索可能的特征组合
- KL-LUCB(Kaufmann和Kalyanakrishnan,2013):一种多臂老虎机算法,平衡探索和利用
目标是找到优化以下两点的规则:
- 精确度:当规则适用时,预测与被解释实例相同的频率是多少?
- 覆盖率:这个规则在数据集中适用的频率是多少?

Alibi实现了三种类型的锚点解释器:
| 类型 | 数据格式 | 关键特性 | 用例 |
|---|---|---|---|
| AnchorTabular | 具有分类和数值特征的结构化数据 | 处理混合数据类型,离散化数值特征 | 信用评分、客户流失、医疗诊断 |
| AnchorText | 文本数据 | 识别重要单词或短语 | 情感分析、垃圾邮件检测 |
| AnchorImage | 图像数据 | 识别重要图像片段 | 目标识别、医学成像 |
举例:
- 金融服务:用规则如“IF 负债比率 > 0.5 AND 收入 < 50K THEN 申请拒绝”解释贷款审批决策
- 医疗保健:用规则如“IF 血压 > 140 AND 年龄 > 65 THEN 高风险”解释疾病风险预测
- 文本分类:用规则如“IF 文本包含 ‘优秀’ AND ‘推荐’ THEN 正面评论”解释情感分析
- 图像识别:通过突出保证预测的区域解释目标检测
有一些局限性:
- 离散化影响:对于数值特征,离散化方法影响解释
- 计算成本:寻找最优锚点在高维数据上可能计算密集
- 覆盖率和精确度权衡:简单规则通常覆盖率较高但精确度较低
- 训练数据依赖:解释受训练数据分布影响
2.4 反事实解释
反事实解释识别如何修改输入以改变模型的预测。例如:
- “你的贷款被拒绝是因为你的收入是45,000美元,但如果收入是52,000美元,贷款就会被批准。”
- “模型将这张图片分类为‘狗’,但如果这些特定像素不同,它会被分类为‘狼’。”
| 方法 | 最适用于 | 关键优势 | 局限性 |
|---|---|---|---|
| CF | 简单数据集,初步探索 | 快速,实现简单 | 可能产生不现实的反事实 |
| CFProto | 混合数据类型,分类特征处理 | 分类变量,生成更现实的反事实 | 需要定义原型(训练数据) |
| CEM | 理解关键特征 | 识别充分和必要特征 | 解释更复杂 |
| CFRL | 复杂关系,严格约束 | 尊重特征关系,高度可定制 | 需要更多计算,训练阶段 |
2.4.1 基本反事实解释(CF)
Alibi中的基本反事实方法基于Wachter等人(2017年)的研究,专注于找到最小改动以翻转预测,同时保持修改后的实例现实可行。
应用在图像数据多些
# 初始化反事实解释器
cf = Counterfactual(predict_fn=model, shape=(1,28,28,1))
# 生成反事实解释
explanation = cf.explain(X_test[0:1])
# 访问反事实示例
counterfactual = explanation.data['cf']
在底层,该方法使用优化过程来平衡两个竞争目标:
- 接近度:使反事实尽可能接近原始实例
- 分类:确保反事实达到目标预测
优化使用以下损失函数:
损失 = λ * L1/L2距离(原始, 反事实) + MSE(目标概率, 预测)
2.4.2 反事实原型(CFProto)
CFProto通过使用类原型指导搜索,扩展了基本反事实方法,使生成的反事实更现实,更接近实际数据分布。

其关键优势在于,CFProto可以优雅地处理分类变量,通过距离度量将它们映射到数值,并可以通过以下方式融入领域知识:
- 基于编码器的指导:在潜在空间中将反事实引导至学习的类原型
- 自动编码器正则化:使反事实保持在数据流形上
- 分类映射:使用专门的距离度量正确处理分类变量
# 初始化基于原型的反事实解释器
cfproto = CounterfactualProto(
predict=model,
shape=X_train.shape[1:],
cat_vars={0: 3, 2: 2}, # 分类变量及其类别数
beta=0.1,
gamma=100, # 自动编码器正则化
theta=100 # 原型正则化
)
# 在数据上训练以学习类原型
cfproto.fit(X_train)
# 生成反事实
explanation = cfproto.explain(X_test[0:1])
python

该方法特别适用于具有混合特征类型的表格数据,而基本反事实方法可能会生成不现实的特征组合。
2.4.3 对比解释方法(CEM)
CEM采用不同的方法,寻找两种互补类型的解释:
- 相关负例(PN):如果存在,会改变预测的特征
- 相关正例(PP):最小化地足以维持当前预测的特征
# 初始化CEM以生成相关负例
cem_pn = CEM(
predict=model,
mode="PN", # 相关负例
shape=X_train.shape[1:],
kappa=0.1,
beta=0.1,
gamma=100, # 自动编码器正则化
no_info_val=0.0 # 被认为不包含信息的值
)
# 生成相关负例
explanation_pn = cem_pn.explain(X_test[0:1])
# 初始化CEM以生成相关正例
cem_pp = CEM(
predict=model,
mode="PP", # 相关正例
shape=X_train.shape[1:],
kappa=0.1,
beta=0.1
)
# 生成相关正例
explanation_pp = cem_pp.explain(X_test[0:1])

CEM的优化与标准反事实不同,在于它对待特征的方式:
- 对于PN:特征只能从其“无信息”基线进一步修改
- 对于PP:特征只能从其“无信息”基线更接近地修改
这种方法识别驱动预测的关键特征,而不仅仅是任何会翻转预测的改动集合。
2.4.4 反事实强化学习(CFRL)
CFRL代表Alibi中最先进的反事实方法,使用强化学习生成高质量的反事实,尊重特征约束和关系。

CFRL解决了其他方法的关键局限性:
- 通过从数据中学习,尊重复杂的特征关系
- 通过在潜在空间中操作,生成现实的反事实
- 自然地处理分类特征和约束
- 支持自定义距离函数和奖励结构
# 初始化带有表格后端的CFRL解释器
cfrl = CounterfactualRL(
predictor=predictor,
feature_names=feature_names,
categorical_indices=categorical_indices,
immutable_features=immutable_features,
backend="tensorflow" # 或 "pytorch"
)
# 训练强化学习代理
cfrl.fit(X_train)
# 生成反事实
explanation = cfrl.explain(X_test[0:1])

强化学习方法特别适用于复杂数据集,简单的优化可能会产生不现实的反事实或陷入局部最优。
2.5 误差分析 - 基于相似性的解释高贡献训练样本挖掘
基于相似性的解释通过识别模型认为与测试实例相似的训练样例,帮助你理解模型的预测结果。
是回答这样一个问题:“哪些训练样例对这一预测影响最大?”
这种方法在以下情况下特别有用:
- 你需要验证模型是否使用了正确的样例进行决策
- 你想检测潜在的数据集偏差或分布偏移
- 你需要提供人类可理解的样例来解释模型行为
Alibi通过梯度相似性方法实现基于相似性的解释,这些方法使用模型的内部梯度来确定哪些训练样例从模型的角度看最相似于测试实例。
与传统比较原始特征的相似性度量不同,梯度相似性比较模型参数对不同输入的响应。当模型处理相似样例时,会产生相似的梯度——这使得梯度比较成为从模型角度衡量相似性的有效方法。
梯度相似性的核心思想是:
- 计算训练实例和测试实例的损失函数梯度
- 使用相似性度量(如点积或余弦相似性)比较这些梯度
- 根据与测试实例的相似性对训练实例进行排序
这种方法捕捉了模型对相似性的内部表示,而不是依赖于可能不符合模型“看待”数据的原始特征相似性。
Alibi提供了三种梯度相似性度量:
| 度量 | 描述 | 用例 | 实现 |
|---|---|---|---|
| grad_dot | 梯度之间的标准点积 | 通用,强调幅度和方向 | dot(X, Y) = X^T Y |
| grad_cos | 梯度之间的余弦相似性 | 仅关注方向,归一化梯度幅度 | cos(X, Y) = X^T Y / ( |
| grad_asym_dot | 非对称点积(基于影响)度量 | 训练样例对测试实例的影响 | asym_dot(X, Y) = X^T Y / |
相似性度量工作流程包括三个主要步骤:
- 初始化 解释器,使用你的模型和损失函数
- 拟合 解释器到你的训练数据
- 解释 测试实例,获取相似性分数和最相似的训练样例
案例
使用 GradientSimilarity 解释器与你的模型的方法:
# 导入必要的模块
from alibi.explainers.similarity.grad import GradientSimilarity
# 定义与你的模型兼容的损失函数
# 对于 TensorFlow
def loss_fn(y_true, y_pred):
return tf.keras.losses.categorical_crossentropy(y_true, y_pred)
# 对于 PyTorch
def loss_fn(y_true, y_pred):
return torch.nn.functional.cross_entropy(y_pred, y_true)
# 初始化解释器
explainer = GradientSimilarity(
predictor=model, # 你的训练模型
loss_fn=loss_fn, # 损失函数
sim_fn='grad_cos', # 相似性函数
task='classification', # 'classification' 或 'regression'
precompute_grads=True, # 存储梯度以加快解释速度
backend='tensorflow', # 'tensorflow' 或 'pytorch'
verbose=True
)
# 在训练数据上拟合解释器
explainer.fit(X_train, Y_train)
# 生成测试实例的解释
explanation = explainer.explain(X_test)
# 访问解释结果
similarity_scores = explanation.data['scores'] # 按降序排列的相似性分数
ordered_indices = explanation.data['ordered_indices'] # 按相似性分数排序的训练样例索引
most_similar = explanation.data['most_similar'] # 每个测试实例的5个最相似训练样例
least_similar = explanation.data['least_similar'] # 每个测试实例的5个最不相似训练样例

对于图像分类模型,
梯度相似性可以揭示哪些训练图像对特定预测影响最大:
import tensorflow as tf
from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input
from alibi.explainers.similarity.grad import GradientSimilarity
# 加载预训练模型
model = ResNet50(weights='imagenet')
# 定义损失函数
def loss_fn(y_true, y_pred):
return tf.keras.losses.categorical_crossentropy(y_true, y_pred)
# 初始化解释器
explainer = GradientSimilarity(
predictor=model,
loss_fn=loss_fn,
sim_fn='grad_cos',
task='classification',
precompute_grads=False,
backend='tensorflow'
)
# 在训练数据上拟合
explainer.fit(X_train, Y_train)
# 解释测试图像
explanation = explainer.explain(X_test[0:1])
# 可视化最相似的训练图像
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 3))
plt.subplot(1, 6, 1)
plt.title("测试图像")
plt.imshow(X_test[0])
plt.axis('off')
for i in range(5):
plt.subplot(1, 6, i+2)
plt.title(f"相似 #{i+1}")
plt.imshow(explanation.data['most_similar'][0][i])
plt.axis('off')

2.6 工程化:解释器的保存与加载
保存和加载解释器在以下几种情况下非常有用:
- 一次训练,多次解释:在数据集上训练解释器,保存它,并在以后重用
- 生产部署:在开发环境中训练解释器,并在生产环境中加载它们
- 分享结果:与同事分享解释器或将其包含在研究出版物中
- 版本控制:为模型治理跟踪不同版本的解释结果
Alibi提供了两个主要函数用于保存和加载解释器:
from alibi.saving import save_explainer, load_explainer
# 保存解释器
save_explainer(explainer, "path/to/save/directory")
# 加载解释器(您必须单独提供预测器)
loaded_explainer = load_explainer("path/to/save/directory", predictor=my_model)
一个比较完整的案例:
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from alibi.explainers import TreeShap
from alibi.saving import load_explainer
# 准备数据并训练模型
X_train = np.random.random((100, 5))
y_train = np.random.randint(0, 2, 100)
X_test = np.random.random((10, 5))
# 训练模型
model = RandomForestClassifier(n_estimators=10)
model.fit(X_train, y_train)
# 创建并拟合解释器
explainer = TreeShap(model, model_output='probability')
explainer.fit(X_train)
# 生成解释结果
explanation = explainer.explain(X_test)
# 保存解释器
explainer.save("my_treeshap_explainer")
# 稍后:使用相同的模型加载解释器
loaded_explainer = load_explainer("my_treeshap_explainer", predictor=model)
# 使用加载的解释器生成新的解释结果
new_explanation = loaded_explainer.explain(X_test)
# 两个解释结果应完全相同
assert np.allclose(explanation.shap_values[0], new_explanation.shap_values[0])

几个特殊的保存内容:
CounterfactualRL 解释器保存神经网络组件(编码器、解码器、执行器、评判器)并处理不同的后端(TensorFlow或PyTorch):
# TensorFlow示例
explainer = CounterfactualRLTabular(
encoder=encoder,
decoder=decoder,
predictor=predictor,
backend="tensorflow"
)
# 保存和加载 - 模型组件以各自格式保存
explainer.save("my_cfrl")
loaded = load_explainer("my_cfrl", predictor=predictor)

目前有些解释器不支持保存:(2025/7/23)
- DistributedAnchorTabular
- CEM
- Counterfactual
- CounterfactualProto
尝试保存这些解释器将引发NotImplementedError
