目录

简介 ||张量 ||Autograd ||建筑模型 ||TensorBoard 支持 ||训练模型 ||模型理解

使用 Captum 进行模型理解

创建时间: 2021年11月30日 |上次更新时间:2024 年 1 月 19 日 |上次验证: Nov 05, 2024

请跟随下面的视频或在 youtube 上观看。在此处下载 Notebook 和相应的文件。

Captum(拉丁语中的“理解”)是一个开放的 source,用于基于 PyTorch 构建的模型可解释性的可扩展库。

随着模型复杂性的增加以及由此导致的 透明度,模型可解释性方法已变得越来越多 重要。模型理解既是一个活跃的研究领域,也是 以及跨行业实际应用的重点领域 使用机器学习。Captum 提供最先进的算法、 包括集成梯度,为研究人员和开发人员提供 通过一种简单的方法来了解哪些功能对 model 的输出。

完整的文档、API 参考和一套教程 具体主题可在 captum.ai 网站上找到。

介绍

Captum 的模型可解释性方法是根据归因。有三种类型的归因 Captum:

  • 特征归因 试图用术语来解释特定的输出 生成它的输入的特征。解释 电影评论在某些词语方面是正面的或负面的 该评论是特征归因的一个示例。

  • Layer Attribution 检查模型的隐藏层的活动 在特定输入之后。检查空间映射 卷积层的输出以响应 图层属性示例。

  • Neuron 归因与图层归因类似,但侧重于 对单个神经元的活动。

在这个交互式笔记本中,我们将了解特征归因和 图层属性。

这三种归因类型中的每一种都有多个归因 与之相关的算法。许多归因算法都属于 两大类:

  • 基于梯度的算法计算 model output、layer output 或 neuron activation 与 输入。集成渐变(用于特征),图层渐变 * 激活 (Activation) 和神经元电导 (Neuron Conductance) 都是基于梯度的 算法。

  • 基于扰动的算法检查输出的变化 响应输入的变化。这 输入扰动可以是定向的或随机的。Occlusion、Feature BlationFeature Permutation 都是 基于扰动的算法。

我们将在下面研究这两种类型的算法。

尤其是在涉及大型模型的情况下,它可能很有价值 以易于将归因数据与输入关联的方式可视化归因数据 正在检查的特征。虽然肯定可以创建您的 使用 Matplotlib、Plotly 或类似工具 Captum 实现自己的可视化效果 提供特定于其属性的增强工具:

  • 模块 (在下面导入为 ) 提供有用的函数,用于可视化与 图像。captum.attr.visualizationviz

  • Captum Insights 是 Captum 之上的一个易于使用的 API,它 提供具有现成可视化效果的可视化小组件 image、text 和任意模型类型。

这两个可视化工具集都将在此中演示 笔记本。前几个示例将重点介绍计算机视觉的使用 案例,但末尾的 Captum Insights 部分将演示 在多模型、视觉对象中实现归因的可视化 问答模型。

安装

在开始之前,您需要有一个 Python 环境,其中包含:

  • Python 版本 3.6 或更高版本

  • 对于 Captum Insights 示例,Flask 1.1 或更高版本以及 Flask-Compress (建议使用最新版本)

  • PyTorch 版本 1.2 或更高版本(建议使用最新版本)

  • TorchVision 版本 0.6 或更高版本(建议使用最新版本)

  • Captum (建议使用最新版本)

  • Matplotlib 版本 3.3.4,因为 Captum 当前使用 Matplotlib 函数,其参数在更高版本中已重命名

要在 Anaconda 或 pip 虚拟环境中安装 Captum,请使用 适用于您的环境的命令如下:

跟:conda

conda install pytorch torchvision captum flask-compress matplotlib=3.3.4 -c pytorch

跟:pip

pip install torch torchvision captum matplotlib==3.3.4 Flask-Compress

在您设置的环境中重新启动此笔记本,然后您就可以 去!

第一个例子

首先,让我们举一个简单的直观示例。我们将从 ResNet 开始 在 ImageNet 数据集上预训练的模型。我们将得到一个测试输入,并且 使用不同的特征归因算法来检查 输入图像会影响输出,并查看此内容的有用可视化效果 输入某些测试图像的归因映射。

首先,一些导入:

import torch
import torch.nn.functional as F
import torchvision.transforms as transforms
import torchvision.models as models

import captum
from captum.attr import IntegratedGradients, Occlusion, LayerGradCam, LayerAttribution
from captum.attr import visualization as viz

import os, sys
import json

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

现在,我们将使用 TorchVision 模型库下载预训练的 ResNet的。由于我们没有训练,因此我们会将其置于 现在。

model = models.resnet18(weights='IMAGENET1K_V1')
model = model.eval()

您获得此交互式笔记本的位置也应该有一个包含文件的文件夹。imgcat.jpg

test_img = Image.open('img/cat.jpg')
test_img_data = np.asarray(test_img)
plt.imshow(test_img_data)
plt.show()

我们的 ResNet 模型是在 ImageNet 数据集上训练的,并期望图像 设置为特定大小,并将通道数据标准化为特定的 的值范围。我们还将提取人类可读标签列表 对于我们的模型识别的类别 - 它也应该在文件夹中。img

# model expects 224x224 3-color image
transform = transforms.Compose([
 transforms.Resize(224),
 transforms.CenterCrop(224),
 transforms.ToTensor()
])

# standard ImageNet normalization
transform_normalize = transforms.Normalize(
     mean=[0.485, 0.456, 0.406],
     std=[0.229, 0.224, 0.225]
 )

transformed_img = transform(test_img)
input_img = transform_normalize(transformed_img)
input_img = input_img.unsqueeze(0) # the model requires a dummy batch dimension

labels_path = 'img/imagenet_class_index.json'
with open(labels_path) as json_data:
    idx_to_labels = json.load(json_data)

现在,我们可以问这个问题:我们的模型怎么看这张图片 代表?

output = model(input_img)
output = F.softmax(output, dim=1)
prediction_score, pred_label_idx = torch.topk(output, 1)
pred_label_idx.squeeze_()
predicted_label = idx_to_labels[str(pred_label_idx.item())][1]
print('Predicted:', predicted_label, '(', prediction_score.squeeze().item(), ')')

我们已经确认,ResNet 认为我们的猫图像实际上是 猫。但是为什么模型认为这是猫的图像呢?

为了回答这个问题,我们求助于 Captum。

使用集成梯度的特征归因

特征归因将特定输出归因于 输入。它使用特定的输入 - 这里是我们的测试图像 - 来生成 每个输入特征对特定 output 功能。

综合 Gradients 是 Captum 中提供的功能归因算法。综合 Gradients 通过以下方式为每个输入特征分配重要性分数 近似模型输出梯度的积分,其中 尊重输入。

在我们的例子中,我们将获取输出的特定元素 vector - 即表示模型对其 chosen category - 并使用 Integrated Gradients 来了解哪些部分 的输入图像。

一旦我们从 Integrated Gradients 获得了重要性映射,我们将使用 可视化工具,以有用的方式表示 importance 映射。Captum 的函数提供了一个 用于自定义归因数据显示的多种选项。 在这里,我们传入一个自定义的 Matplotlib 颜色映射表。visualize_image_attr()

使用调用运行 cell 将 通常需要一两分钟。integrated_gradients.attribute()

# Initialize the attribution algorithm with the model
integrated_gradients = IntegratedGradients(model)

# Ask the algorithm to attribute our output target to
attributions_ig = integrated_gradients.attribute(input_img, target=pred_label_idx, n_steps=200)

# Show the original image for comparison
_ = viz.visualize_image_attr(None, np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
                      method="original_image", title="Original Image")

default_cmap = LinearSegmentedColormap.from_list('custom blue',
                                                 [(0, '#ffffff'),
                                                  (0.25, '#0000ff'),
                                                  (1, '#0000ff')], N=256)

_ = viz.visualize_image_attr(np.transpose(attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)),
                             np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
                             method='heat_map',
                             cmap=default_cmap,
                             show_colorbar=True,
                             sign='positive',
                             title='Integrated Gradients')

在上图中,您应该看到 Integrated Gradients 为我们提供了 图像中猫的位置周围的最强信号。

使用遮挡的特征属性

基于梯度的归因方法有助于从各个方面理解模型 直接计算出相对于输入的输出变化。基于扰动的归因方法更直接地解决这个问题,方法是 引入对 input 的更改以衡量对 output 的影响。遮挡就是这样一种方法。 它涉及替换输入图像的各个部分,并检查 对 output 信号的影响。

下面,我们设置 Occlusion 归因。与配置 卷积神经网络,您可以指定目标的大小 区域和步长来确定单个 测量。我们将可视化 Occlusion 属性的输出 其中 ,显示两者的热图 按地区划分的正面和负面归因,以及通过掩盖原始 具有正面归因区域的图片。遮罩给出了非常 模型发现我们的猫照片区域的指导性视图 最“像猫”。visualize_image_attr_multiple()

occlusion = Occlusion(model)

attributions_occ = occlusion.attribute(input_img,
                                       target=pred_label_idx,
                                       strides=(3, 8, 8),
                                       sliding_window_shapes=(3,15, 15),
                                       baselines=0)


_ = viz.visualize_image_attr_multiple(np.transpose(attributions_occ.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      ["original_image", "heat_map", "heat_map", "masked_image"],
                                      ["all", "positive", "negative", "positive"],
                                      show_colorbar=True,
                                      titles=["Original", "Positive Attribution", "Negative Attribution", "Masked"],
                                      fig_size=(18, 6)
                                     )

同样,我们看到图像区域具有更大的意义 其中包含猫。

使用 Layer GradCAM 进行图层归因

图层归因允许您对隐藏的活动进行归因 layers 添加到输入的特征中。下面,我们将使用 layer 归因算法来检查其中一个 卷积层。

GradCAM 计算目标输出相对于 给定层,每个输出通道的平均值(输出的维度 2), 并将每个通道的平均渐变乘以层 激活。结果对所有通道求和。GradCAM 是 专为卷积网络设计;由于卷积层的活动经常 映射到输入,GradCAM 归因通常会被上采样 并用于屏蔽输入。

图层归因的设置与输入归因类似,不同之处在于 除了模型之外,还必须在 您要检查的模型。如上所述,当我们调用 时, 我们指定感兴趣的目标类。attribute()

layer_gradcam = LayerGradCam(model, model.layer3[1].conv2)
attributions_lgc = layer_gradcam.attribute(input_img, target=pred_label_idx)

_ = viz.visualize_image_attr(attributions_lgc[0].cpu().permute(1,2,0).detach().numpy(),
                             sign="all",
                             title="Layer 3 Block 1 Conv 2")

我们将使用 LayerAttribution 基类中的 convenience 方法对归因数据进行上采样,以便与输入进行比较 图像。interpolate()

upsamp_attr_lgc = LayerAttribution.interpolate(attributions_lgc, input_img.shape[2:])

print(attributions_lgc.shape)
print(upsamp_attr_lgc.shape)
print(input_img.shape)

_ = viz.visualize_image_attr_multiple(upsamp_attr_lgc[0].cpu().permute(1,2,0).detach().numpy(),
                                      transformed_img.permute(1,2,0).numpy(),
                                      ["original_image","blended_heat_map","masked_image"],
                                      ["all","positive","positive"],
                                      show_colorbar=True,
                                      titles=["Original", "Positive Attribution", "Masked"],
                                      fig_size=(18, 6))

诸如此类的可视化可以为您提供新颖的见解,让您了解 隐藏图层响应您的输入。

使用 Captum Insights 实现可视化

Captum Insights 是一个构建在基础上的可解释性可视化小部件 Captum 的 Captum 中,以便于理解模型。Captum Insights 有效 跨图像、文本和其他功能,以帮助用户了解功能 归 因。它允许您可视化多个 input/output 对,并为图像、文本、 和任意数据。

在笔记本的这一部分中,我们将可视化多个图像 使用 Captum Insights 进行分类推理。

首先,让我们收集一些图像,看看模型对它们的看法。 为了多样性,我们将带上我们的猫、一个茶壶和一个三叶虫化石:

imgs = ['img/cat.jpg', 'img/teapot.jpg', 'img/trilobite.jpg']

for img in imgs:
    img = Image.open(img)
    transformed_img = transform(img)
    input_img = transform_normalize(transformed_img)
    input_img = input_img.unsqueeze(0) # the model requires a dummy batch dimension

    output = model(input_img)
    output = F.softmax(output, dim=1)
    prediction_score, pred_label_idx = torch.topk(output, 1)
    pred_label_idx.squeeze_()
    predicted_label = idx_to_labels[str(pred_label_idx.item())][1]
    print('Predicted:', predicted_label, '/', pred_label_idx.item(), ' (', prediction_score.squeeze().item(), ')')

…看起来我们的模型正确地识别了它们 - 但是 当然,我们想更深入地挖掘。为此,我们将使用 Captum Insights widget,我们使用一个对象 导入如下。期望 batch of data, 因此,我们将引入 Captum 的 helper 类。我们将寻找 具体到图像,所以也导入。AttributionVisualizerAttributionVisualizerBatchImageFeature

我们使用以下参数配置 :AttributionVisualizer

  • 要检查的模型数组(在我们的例子中,只有一个)

  • 评分函数,允许 Captum Insights 提取 来自模型的 top-k 预测

  • 一个有序的、人类可读的类列表,其中包含我们的模型所训练的类

  • 要查找的功能列表 - 在我们的示例中,为ImageFeature

  • 数据集,它是一个可迭代对象,返回批量输入 和标签 - 就像你用于训练一样

from captum.insights import AttributionVisualizer, Batch
from captum.insights.attr_vis.features import ImageFeature

# Baseline is all-zeros input - this may differ depending on your data
def baseline_func(input):
    return input * 0

# merging our image transforms from above
def full_img_transform(input):
    i = Image.open(input)
    i = transform(i)
    i = transform_normalize(i)
    i = i.unsqueeze(0)
    return i


input_imgs = torch.cat(list(map(lambda i: full_img_transform(i), imgs)), 0)

visualizer = AttributionVisualizer(
    models=[model],
    score_func=lambda o: torch.nn.functional.softmax(o, 1),
    classes=list(map(lambda k: idx_to_labels[k][1], idx_to_labels.keys())),
    features=[
        ImageFeature(
            "Photo",
            baseline_transforms=[baseline_func],
            input_transforms=[],
        )
    ],
    dataset=[Batch(input_imgs, labels=[282,849,69])]
)

请注意,运行上面的单元格根本不需要太多时间,这与 我们的归属在上面。这是因为 Captum Insights 允许您 在可视 Widget 中配置不同的归因算法,之后 它将计算并显示属性。该过程将 花几分钟时间。

运行下面的单元格将呈现 Captum Insights 小组件。您可以 然后选择 Attributions methods and their arguments(归因方法及其参数)、Filter Model (筛选模型) 基于预测类或预测正确性的响应,请参阅 模型的预测和相关概率,并查看 与原始图像相比的归属。

visualizer.render()

脚本总运行时间:(0 分 0.000 秒)

由 Sphinx-Gallery 生成的图库

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源