Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization
神经网络输出可视化
文章目录
- Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization
- 摘要
- 1 引言
- 2 相关研究
- 3 方法论
- 3.1 Grad-CAM泛化CAM
- 3.2 Guided Grad-CAM
- 3.3 反事实解释(Counterfactual Explanations)
- 4 定位能力评估
- 结论
- 源代码
摘要
- 提出Grad-CAM,可为基于卷积神经网络的模型的决策生成“视觉解释”,从而使其更加透明和可解释
- 使用梯度来生成粗略的定位图,突出显示了图像中用于预测的重要区域。
- Grad-CAM适用于各种CNN模型系列,无需进行架构更改或重新训练。
- Guided Grad-CAM,将Grad-CAM和细粒度视觉结合生成高分辨率类区别性可视化。
- 通过Grad CAM识别重要神经元并将其与神经元名称结合的方法,为模型决策提供文本解释。
- 研究衡量Grad-CAM的解释是否帮助用户建立对来自深层网络预测的适当信任,并表明Grad-CAM帮助未训练的用户成功地从较弱的网络中中识别出“更强”的深层网络,即使两者做出相同的预测。
1 引言
CNN缺乏可分解性,无法直接理解为独立组件。可解释性很重要。通常要在准确性和简洁性或可解释性之间进行权衡。本文方法是CAM的推广,适用于各类CNN模型。
图像分类模型中,用于证明任何目标类别合理的“良好”视觉解释应是区分类别(在图像中定位该类别)和高分辨率(捕获细粒度细节)。像素空间梯度可视化具有高分辨率,突出显示了图像中的细粒度细节,但没有类别差异。而诸如CAM或grad-CAM之类的定位方法具有高度的类别区分。
为了结合两个方面的优势,可以将现有的像素空间渐变可视化与Grad-CAM融合,以创建高分辨率和区分类别的Guided Grad-CAM可视化。结果是,即使图像包含多个可能的概念,也可以在高分辨率细节中可视化对应于任何感兴趣决策的图像重要区域。
本文贡献:
- Grad-CAM,一种区分类别的定位技术,该技术可以为任何基于CNN的网络生成直观的解释,而无需更改架构或进行重新培训。
- 我们将Grad-CAM应用于现有的性能最高的分类、字幕和VQA模型。
- 展示了可解释的Grad CAM可视化如何通过发现数据集中的偏差来帮助诊断故障模式的概念证明。
- 介绍了用于图像分类和VQA的ResNets的Grad-CAM可视化效果
- 使用来自Grad-CAM的神经元重要性和神经元名称,并获得模型决策的文字解释。
- Guided Grad-CAM的指导性解释是区分性的,不仅帮助人类建立信任,而且还帮助未受过训练的用户成功地从“较弱”的网络中识别出“更强的”网络, 即使两者做出相同的预测
2 相关研究
相关研究有:CNN可视化、模型信任评估、对齐基于梯度的重要性、弱监督定位。
CAM的一个缺点是它要求特征图直接位于softmax层之前,因此仅适用于在预测之前立即对卷积图执行全局平均池化的特定类型的CNN架构(即,conv特征图→全局平均池化→softmax层)。 与某些任务上的通用网络相比,此类体系结构的精度可能较低。
本文介绍了一种使用梯度信号组合特征图的方法,无需对网络结构中做修改。 可以应用于基于CNN的现成架构。 对于全卷积架构,CAM是Grad-CAM的特例。
本文方法可以一次性实现定位,只需要每个图像一次前向传播和部分反向通过,因此通常效率要高一个数量级。
3 方法论
CNN中的深层表示代表了高级视觉结构。卷积层会自然保持空间信息,因此最后的卷积层在高级语义和详细的空间信息之间具有最佳的承诺。这些层中的神经元在图像中寻找特定于语义类的信息。Grad-CAM使用最后卷积层输入的梯度信息为每个神经元分配重要性,以进行特定的关注决策。
以分类为例,下面是解释:
3.1 Grad-CAM泛化CAM
直到在可视化期间标准化的比例常数为止,的表达式都与Grad-CAM使用的相同。因此,Grad-CAM是CAM的严格概括。这种概括能够从基于CNN的模型中生成可视化的解释,该模型将卷积层与更复杂的交互作用层叠在一起。
3.2 Guided Grad-CAM
虽然Grad-CAM是类别区分的,并且可以定位相关的图像区域,但是它缺乏突出显示细微细节的能力。
指导式反向传播可以对相对于图像的梯度进行可视化,在通过ReLU层反向传播时抑制负梯度。直观上讲,这旨在捕获神经元检测到的像素,而不是抑制神经元检测到的像素。
指导式反向传播 + Grad-CAM可视化 => Guided Grad-CAM,通过逐元素相乘融合(先经双线性插值上采样到输入图像分辨率)。
这种可视化既具有高分辨率,又具有类别区分性。
用反卷积代替指导式反向传播可得到相似的结果,但反卷积可视化具有伪影,而引导式反向传播通常噪音较小。
3.3 反事实解释(Counterfactual Explanations)
通过对Grad-CAM稍加修改,可以得到关于对区域支持对网络预测变更的解释。 那么,删除那些区域中出现的概念将使模型对其预测更有信心,称为反事实解释。
针对卷积层的特征图否定了(c类得分)的梯度。因此,重要性权重变为:
就是掩去负类。
4 定位能力评估
弱监督定位,在图像分类环境下
给定图像,根据网络获取类别预测,然后为每个预测的类别生成Grad-CAM图,并使用最大强度的15%的阈值对它们进行二值化。 这将导致连接的像素段,并在单个最大段周围绘制一个边界框。(最大连通分支)
弱监督语义分割
不得不说,这篇论文实验部分是真的多
结论
- 提出类激活图(Grad-CAM),通过产生直观的解释,使任何基于CNN的模型更加透明。
- 将Grad-CAM与现有的高分辨率可视化技术结合在一起,从而获得高分辨率和区分类别的Guided Grad-CAM可视化。
- 研究表明,这种可视化可以更准确地区分类,更好地揭示分类器的可信赖性,并有助于识别数据集中的偏差。
- 设计了一种通过Grad-CAM识别重要神经元的方法,并提供了一种获取用于模型决策的文本解释的方法。
- Grad-CAM在各种现成的体系结构上有广泛适用性
源代码
pytorch可以使用**钩子函数(hook)**来获得层的输入和输出。
import cv2
import numpy as np
import torchvision.transforms as transforms
from torchvision import models
from PIL import Image
def show_with_grad_cam(path_img, out_img):
# 准备图片,用于前向传播
img_raw = Image.open(path_img).convert('RGB')
img_raw = transforms.ToTensor()(img_raw)
_, H, W = img_raw.size()
# 定义模型,以resnet18为例
model = models.resnet18(pretrained=True)
model.eval()
features_map = [] # 后面用于存放特征图
grads = [] # 后面用于存放梯度
def get_feature_map(module, ip, op):
features_map.append(op.data.numpy())
def get_grad(module, grad_in, grad_out):
grads.append(grad_out[0].detach().data.numpy())
model.layer4[-1].register_forward_hook(get_feature_map)
model.layer4[-1].register_backward_hook(get_grad)
# 计算grad-CAM,前向传播和反向传播
data = img_raw.unsqueeze(0) # 增加一个batch size维度
output = model(data).squeeze() # 最后再消去batch size维度
idx = output.sort(descending=True)[1].data.numpy()[0] # 最可能的类别序号
model.zero_grad()
loss = output[idx]
loss.backward()
fm = features_map[0][0] # 取出特征图,第一个0是从列表取,第二个0是从batch size取
grad = grads[0][0]
weight = np.mean(grad.reshape([grad.shape[0], -1]), axis=1) # 权重是通道上梯度的平均
channel, h, w = fm.shape
cam = weight.dot(fm.reshape(channel, h * w)).reshape((h, w)) # 权值进行矩阵乘
# 热力图和原始图相加
cam_img = (cam - cam.min()) / (cam.max() - cam.min())
cam_img = np.uint8(255 * cam_img)
cam_img = cv2.resize(cam_img, (W, H))
heatmap = cv2.applyColorMap(cam_img, cv2.COLORMAP_JET)
img = cv2.imread(path_img, 1)
cam_img = 0.3 * heatmap + 0.7 * img
cv2.imwrite(out_img, cam_img)
if __name__ == '__main__':
show_with_grad_cam(r'E:\Downloads\a.jpg', r'E:\Downloads\grad-cam.jpg')
效果: