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 函数的一般步骤如下。

  1. 选择一个您想要解释的实例,并为此实例指定期望的预测(这通常与其原始预测不同)。
  2. 选择超参数的值
  3. 优化损失使用 create_counterfactual 函数
  4. 可选地,正如作者所建议的,您可以通过增加重复步骤 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()

png

上方图中突出显示的大点显示了第 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()

png

从上面可以看出,反事实主要沿着 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()

png

png

png

png

png

正如我们所见,值越大,,损失函数中的第一项就越占主导地位。

就越占主导地位。

应用 Wachter 等人的阈值概念,

  1. 可选地,正如作者所建议的,您可以通过增加重复步骤 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

png

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 : int

    x_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