create_counterfactual: 通过反事实解释模型
Wachter 等人 (2017) 提出的用于模型可解释性的反事实方法的实现。
from mlxtend.evaluate import create_counterfactual
概述
反事实是用于解释与蕴含相关的场景的实例:“如果在假设情境下不是 x,那么就不是 y”。例如,“如果我没有努力学习,我的成绩会更差。”
在机器学习的背景下,我们可以将反事实视为来自训练集的实例,我们通过人工修改其特征来改变模型的预测。修改训练示例的特征有助于解释模型的行为。
请注意,这个创建反事实的实现与模型无关,适用于任何支持 predict
(以及理想情况下 predict_proba
)方法的 scikit-learn 估计器。
特别是,create_counterfactual
实现了 Wachter 等人 (2017) [1] 描述的方法。C. Molnar 的《可解释机器学习》一书 [2] 中也提供了该方法的良好、简短的描述。
简而言之,Wachter 等人的方法最小化损失
左边的项,,最小化模型对反事实的预测,即,以及期望预测(由用户指定)之间的平方差,。请注意,是用于权衡左边这项相对于第二项重要性的超参数,.
第二项,,计算给定实例和生成反事实之间的距离。简而言之,第二项将使生成的反事实与原始实例相似。相比之下,第一项最大化模型对反事实的预测与期望预测(例如,不同的类别标签)之间的差异。
距离函数实现为每个特征维度的绝对差值除以中位数绝对偏差 (MAD) 进行缩放
MAD 使用中位数作为中心来衡量给定特征的离散程度
使用 create_counterfactual
函数的一般步骤如下。
- 选择一个您想要解释的实例,并为此实例指定期望的预测(这通常与其原始预测不同)。
- 选择超参数的值
- 优化损失使用
create_counterfactual
函数 - 可选地,正如作者所建议的,您可以通过增加重复步骤 2 和 3,直到达到用户定义的阈值,即
- 当:
- 增加
- 当:
参考文献
- [1] Wachter, S., Mittelstadt, B., & Russell, C. (2017). Counterfactual explanations without opening the black box: Automated decisions and the GDPR. Harv. JL & Tech., 31, 841.,https://arxiv.org/abs/1711.00399
- [2] Christoph Molnar (2018). Interpretable Machine Learning,第 6.1 章
示例 1 -- 简单的 Iris 示例
为简单起见,本示例演示了如何使用 create_counterfactual
函数解释 Iris 数据集中的一个数据实例。
假设我们在 Iris 数据集上训练了一个逻辑回归模型,并选取第 16 个训练点,我们希望通过反事实来解释其预测。
from mlxtend.data import iris_data
from sklearn.linear_model import LogisticRegression
X, y = iris_data()
clf = LogisticRegression()
clf.fit(X, y)
x_ref = X[15]
print('True label:', y[15])
print('Predicted label:', clf.predict(x_ref.reshape(1, -1))[0])
print('Predicted probas:', clf.predict_proba(x_ref.reshape(1, -1)))
print('Predicted probability for label 0:', clf.predict_proba(x_ref.reshape(1, -1))[0][0])
True label: 0
Predicted label: 0
Predicted probas: [[9.86677291e-01 1.33226960e-02 1.28980184e-08]]
Predicted probability for label 0: 0.9866772910539873
从上面可以看出,预测类别 0 的概率得分为 98.6%。现在,我们将通过设置 y_desired=2
将预测推向类别 2。此外,我们通过 y_desired_proba=1.
将类别 2 的概率设置为 100%。
from mlxtend.evaluate import create_counterfactual
res = create_counterfactual(x_reference=x_ref,
y_desired=2,
model=clf,
X_dataset=X,
y_desired_proba=1.,
lammbda=1, # hyperparameter
random_seed=123)
print('Features of the 16th training example:', x_ref)
print('Features of the countefactual:', res)
print('Predictions for counterfactual:\n')
print('Predicted label:', clf.predict(res.reshape(1, -1))[0])
print('Predicted probas:', clf.predict_proba(res.reshape(1, -1)))
Features of the 16th training example: [5.7 4.4 1.5 0.4]
Features of the countefactual: [5.72271344 3.99169005 6.45305374 0.40000002]
Predictions for counterfactual:
Predicted label: 2
Predicted probas: [[1.41639932e-04 3.13292297e-01 6.86566063e-01]]
从上面可以看出,反事实与原始训练示例相对相似,即只有第 3 个特征发生了显著变化(从 1.5 到 6.45)。预测标签已从类别 0 变为类别 2。
从解释的角度来看,这意味着增加 Iris-setosa 花瓣的长度可能会使其更类似于 Iris-virginica 花。
示例 2 -- 带有决策区域和阈值停止准则的简单 Iris 示例
本示例与示例 1 类似;但是,它基于一个只包含花瓣长度和花瓣宽度特征的二维 Iris 数据集,以便可以通过决策区域图绘制结果。
from mlxtend.plotting import plot_decision_regions
import matplotlib.pyplot as plt
X, y = iris_data()
X = X[:, 2:]
clf = LogisticRegression()
clf.fit(X, y)
LogisticRegression()
# Plotting decision regions
ax = plot_decision_regions(X, y, clf=clf, legend=2)
scatter_highlight_defaults = {'c': 'red',
'edgecolor': 'yellow',
'alpha': 1.0,
'linewidths': 2,
'marker': 'o',
's': 80}
ax.scatter(*X[15],
**scatter_highlight_defaults)
plt.show()
上方图中突出显示的大点显示了第 16 个训练数据点。
以下代码将使用与示例 1 相同的设置创建一个反事实
counterfact = create_counterfactual(x_reference=X[15],
y_desired=2,
model=clf,
X_dataset=X,
y_desired_proba=1.0,
lammbda=1,
random_seed=123)
ax = plot_decision_regions(X, y, clf=clf, legend=2)
ax.scatter(*counterfact,
**scatter_highlight_defaults)
plt.show()
从上面可以看出,反事实主要沿着 x 轴(花瓣长度)移动,使得参考点和反事实之间的预测从类别 0 变为类别 2。
以下图表是基于使用不同 lambda 值重复此过程的结果
for i in [0.4, 0.5, 1.0, 5.0, 100]:
counterfact = create_counterfactual(x_reference=X[15],
y_desired=2,
model=clf,
X_dataset=X,
y_desired_proba=1.0,
lammbda=i,
random_seed=123)
ax = plot_decision_regions(X, y, clf=clf, legend=2)
ax.scatter(*counterfact,
**scatter_highlight_defaults)
plt.show()
正如我们所见,值越大,,损失函数中的第一项就越占主导地位。
就越占主导地位。
应用 Wachter 等人的阈值概念,
- 可选地,正如作者所建议的,您可以通过增加重复步骤 2 和 3,直到达到用户定义的阈值,即
- 当:
- 增加
我们可以定义一个用户定义的阈值并按如下方式实现它
import numpy as np
desired_class_2_proba = 1.0
for i in np.arange(0, 10000, 0.1):
counterfact = create_counterfactual(x_reference=X[15],
y_desired=2,
model=clf,
X_dataset=X,
y_desired_proba=desired_class_2_proba,
lammbda=i,
random_seed=123)
predicted_class_2_proba = clf.predict_proba(counterfact.reshape(1, -1))[0][2]
if not i:
print('Initial lambda:', i)
print('Initial diff:', np.abs(predicted_class_2_proba - desired_class_2_proba))
if not np.abs(predicted_class_2_proba - desired_class_2_proba) > 0.3:
break
ax = plot_decision_regions(X, y, clf=clf, legend=2)
ax.scatter(*counterfact,
**scatter_highlight_defaults)
print('Final lambda:', i)
print('Final diff:', np.abs(predicted_class_2_proba - desired_class_2_proba))
plt.show()
Initial lambda: 0.0
Initial diff: 0.9999998976132334
Final lambda: 1.1
Final diff: 0.2962621523225484
API
create_counterfactual(x_reference, y_desired, model, X_dataset, y_desired_proba=None, lammbda=0.1, random_seed=None)
Wachter 等人提出的反事实方法的实现。
参考文献
- Wachter, S., Mittelstadt, B., & Russell, C. (2017). Counterfactual explanations without opening the black box: Automated decisions and the GDPR. Harv. JL & Tech., 31, 841.,https://arxiv.org/abs/1711.00399
参数
-
x_reference
: 类似数组,形状=[m_features]要解释的数据实例(训练示例)。
-
y_desired
: intx_reference
的期望类别标签。 -
model
: 估计器实现
.predict()
和/或predict_proba()
的 (scikit-learn) 估计器。- 如果model
支持predict_proba()
,则默认将其用于第一个损失项,(lambda * model.predict[_proba](x_counterfact) - y_desired[_proba])^2
- 否则,方法将回退到predict
。 -
X_dataset
: 类似数组,形状=[n_examples, m_features]用于选取初始反事实作为优化过程起点的(训练)数据集。
-
y_desired_proba
: 浮点数 (默认: None)指定
y_desired
期望类别概率的浮点数,范围在 [0, 1] 之内。- 如果y_desired_proba=None
(默认),第一个损失项是(lambda * model(x_counterfact) - y_desired)^2
,其中y_desired
是类别标签 - 如果y_desired_proba
不为 None,第一个损失项是(lambda * model(x_counterfact) - y_desired_proba)^2
-
lammbda
: 第一个损失项的权重参数,(lambda * model(x_counterfact) - y_desired[_proba])^2
-
random_seed
: int (默认=None)如果为 int,random_seed 是随机数生成器用于从
X_dataset
中选择初始反事实的种子。
ython