微信登录

可解释性方法 - 局部解释 - 解释单个预测

TensorFlow 可解释性方法 - 局部解释 - 解释单个预测

引言

在深度学习领域,神经网络模型通常被视为“黑盒”,它们能够在许多任务中取得出色的性能,但却难以理解其决策背后的具体原因。然而,在许多实际应用场景中,如医疗诊断、金融风险评估和自动驾驶等,仅仅知道模型的预测结果是不够的,我们还需要了解模型做出该预测的依据,这就引出了模型可解释性的重要性。局部解释方法专注于解释单个预测,帮助我们理解模型在特定输入下的决策过程。本文将介绍如何使用 TensorFlow 实现一些常见的局部解释方法来解释单个预测。

局部解释方法概述

局部解释方法旨在解释模型对于特定输入的预测结果,而不是对整个模型的行为进行全局解释。常见的局部解释方法包括:

  1. 梯度类方法:如梯度加权类激活映射(Grad-CAM)、集成梯度(Integrated Gradients)等,这些方法基于模型的梯度信息来确定输入特征对预测结果的重要性。
  2. 扰动类方法:如 LIME(Local Interpretable Model-agnostic Explanations),通过对输入进行局部扰动并观察模型输出的变化来解释预测。

使用 TensorFlow 实现集成梯度解释单个预测

集成梯度原理

集成梯度是一种基于梯度的局部解释方法,它通过计算从基线输入到实际输入的路径上的梯度积分,来衡量每个输入特征对预测结果的贡献。基线输入通常是一个没有信息的输入,如全零向量。

代码实现

  1. import tensorflow as tf
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. # 加载预训练的模型,这里以简单的 MNIST 分类模型为例
  5. mnist = tf.keras.datasets.mnist
  6. (x_train, y_train), (x_test, y_test) = mnist.load_data()
  7. x_train, x_test = x_train / 255.0, x_test / 255.0
  8. model = tf.keras.models.Sequential([
  9. tf.keras.layers.Flatten(input_shape=(28, 28)),
  10. tf.keras.layers.Dense(128, activation='relu'),
  11. tf.keras.layers.Dropout(0.2),
  12. tf.keras.layers.Dense(10, activation='softmax')
  13. ])
  14. model.compile(optimizer='adam',
  15. loss='sparse_categorical_crossentropy',
  16. metrics=['accuracy'])
  17. model.fit(x_train, y_train, epochs=5)
  18. # 选择一个测试样本进行解释
  19. test_index = 0
  20. test_sample = x_test[test_index]
  21. test_sample = np.expand_dims(test_sample, axis=0)
  22. # 定义基线输入
  23. baseline = np.zeros_like(test_sample)
  24. # 计算积分步数
  25. m_steps = 50
  26. alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)
  27. # 生成沿着路径的输入
  28. def interpolate_inputs(baseline, input, alphas):
  29. alphas_x = alphas[:, tf.newaxis, tf.newaxis, tf.newaxis]
  30. baseline_x = tf.cast(tf.expand_dims(baseline, axis=0), tf.float32)
  31. input_x = tf.cast(tf.expand_dims(input, axis=0), tf.float32)
  32. delta = input_x - baseline_x
  33. interpolated_inputs = baseline_x + alphas_x * delta
  34. return interpolated_inputs
  35. interpolated_inputs = interpolate_inputs(baseline, test_sample, alphas)
  36. # 计算梯度
  37. @tf.function
  38. def compute_gradients(inputs, target_class_idx):
  39. with tf.GradientTape() as tape:
  40. tape.watch(inputs)
  41. logits = model(inputs)
  42. probs = tf.nn.softmax(logits, axis=-1)[:, target_class_idx]
  43. return tape.gradient(probs, inputs)
  44. target_class_idx = np.argmax(model.predict(test_sample))
  45. path_gradients = compute_gradients(interpolated_inputs, target_class_idx)
  46. # 计算积分梯度
  47. integrated_gradients = (tf.math.reduce_mean(path_gradients, axis=0) * (test_sample - baseline))
  48. # 可视化结果
  49. plt.figure(figsize=(10, 5))
  50. plt.subplot(1, 2, 1)
  51. plt.imshow(test_sample[0], cmap='gray')
  52. plt.title('Original Image')
  53. plt.axis('off')
  54. plt.subplot(1, 2, 2)
  55. plt.imshow(np.abs(integrated_gradients[0]), cmap='gray')
  56. plt.title('Integrated Gradients')
  57. plt.axis('off')
  58. plt.show()

代码解释

  1. 加载模型:使用 TensorFlow 加载并训练一个简单的 MNIST 分类模型。
  2. 选择测试样本:从测试集中选择一个样本进行解释。
  3. 定义基线输入:这里使用全零向量作为基线输入。
  4. 生成插值输入:通过在基线输入和实际输入之间进行线性插值,生成一系列沿着路径的输入。
  5. 计算梯度:使用 tf.GradientTape 计算每个插值输入的梯度。
  6. 计算积分梯度:对路径上的梯度进行平均,并乘以输入与基线的差值。
  7. 可视化结果:将原始图像和集成梯度的绝对值可视化,绝对值越大表示该像素对预测结果的贡献越大。

使用 LIME 解释单个预测

LIME 原理

LIME 是一种模型无关的局部解释方法,它通过在待解释的输入附近生成局部扰动样本,并训练一个可解释的简单模型(如线性回归模型)来近似原模型的局部行为。

代码实现

  1. from lime import lime_image
  2. from skimage.segmentation import mark_boundaries
  3. # 定义预测函数
  4. def predict_fn(images):
  5. return model.predict(images.reshape(-1, 28, 28))
  6. # 创建 LIME 解释器
  7. explainer = lime_image.LimeImageExplainer()
  8. # 解释单个预测
  9. explanation = explainer.explain_instance(test_sample[0].astype(np.double), predict_fn, top_labels=5, hide_color=0, num_samples=1000)
  10. # 获取解释结果
  11. temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=True, num_features=5, hide_rest=False)
  12. # 可视化结果
  13. plt.figure(figsize=(10, 5))
  14. plt.subplot(1, 2, 1)
  15. plt.imshow(test_sample[0], cmap='gray')
  16. plt.title('Original Image')
  17. plt.axis('off')
  18. plt.subplot(1, 2, 2)
  19. plt.imshow(mark_boundaries(temp / 2 + 0.5, mask))
  20. plt.title('LIME Explanation')
  21. plt.axis('off')
  22. plt.show()

代码解释

  1. 定义预测函数:定义一个函数,该函数接受图像输入并返回模型的预测结果。
  2. 创建 LIME 解释器:使用 lime_image.LimeImageExplainer 创建一个解释器。
  3. 解释单个预测:调用解释器的 explain_instance 方法,对测试样本进行解释。
  4. 获取解释结果:使用 get_image_and_mask 方法获取解释结果的图像和掩码。
  5. 可视化结果:将原始图像和 LIME 解释结果可视化,掩码部分表示对预测结果有重要贡献的区域。

结论

局部解释方法能够帮助我们理解模型在特定输入下的决策过程,从而提高模型的可解释性。本文介绍了如何使用 TensorFlow 实现集成梯度和 LIME 两种常见的局部解释方法来解释单个预测。集成梯度基于模型的梯度信息,而 LIME 是一种模型无关的方法,通过局部扰动和简单模型来解释预测。在实际应用中,可以根据具体需求选择合适的局部解释方法。