热力图heatmap

这个方法又称Class Activation Map(CAM类激活映射),由于这是基于分类问题的一种可视化技术,并且只有将全连接层改为全局平均池化才能较好的保存图像的空间信息,这对于训练是不利的。2017年提出了Grad-CAM,可以在不改变网络结构的情况下进行初始化。

CAM

CAM论文
在这里插入图片描述
CAM的原理大致如上图所示,生成步骤如下:

  • step1: 输入一张图进入CNN(可以是任何网络)中,最后一层卷积层得到的特征图即为图中下排的特征图;
  • step2: 经过全局平均池化得到每层特征图相应权重 W i Wi Wi
  • step3: 经过一层softmax得到分类结果,查看图中Australian terrier对应的权值;
  • step4: 将每层特征图与其相对应的权值相乘并求和即得到映射图;
  • step5: 将生成的映射图resize到原图的大小,与原图相叠。

Q:为什么使用全局平均池化代替全连接层?
每一层卷积层的feature map认为仍然保留着图像的空间信息,而经过全连接层后,特征图转换为特征向量会丢失它的空间信息。所以CAM中采用全局平均池化层代替全连接层。
Q:为什么使用全局平均池化而不是最大池化?
基于average的CAM可以标出重要影响的所有区域,而基于max的CAM却只能标注出ROI区域的右下方的一个关键点。这个使得基于GMP的CAM在分类效果上与基于GAM的差不多,但是在定位问题上就要显得逊色一点。

Grad-CAM

Grad-CAM论文

其基本思路与CAM一致,也是通过得到特征图对应的权重,最后求一个加权和。它与CAM的主要区别在于求权重w的过程。
Grad-CAM另辟蹊径,用梯度的全局平均来计算权重。事实上,经过严格的数学推导,Grad-CAM与CAM计算出来的权重是等价的。
在这里插入图片描述
Grad-CAM生成步骤如下:

  • step1: 输入一张图进入CNN(可以是任何网络)中,最后一层卷积层得到的特征图即为图中下排的特征图;
  • step2: 求出第k个特征图对应类别c的权重为 α k c \alpha_k^c αkc,公式如下;
  • α k c = 1 Z ∑ i ∑ j ∂ y c ∂ A i j k \alpha_k^c=\frac{1}{Z}\sum\limits_{i}\sum\limits_{j}\frac{\partial y^c}{\partial A_{ij}^k} αkc=Z1ijAijkyc
  • 其中,Z为特征图的像素个数, y c y^c yc是对应类别c的分数(在代码中一般用 l o g i t s logits logits表示,是输入softmax层之前的值), A i j k A_{ij}^k Aijk表示第k个特征图中, ( i , j ) (i,j) (i,j)位置处的像素值。
  • step4: 求得类别对所有特征图的权重后,用 L L L求其加权和;
  • L G r a d − C A M c = R e L U ( ∑ k α k c A k ) L_{Grad-CAM}^c=ReLU(\sum\limits_k\alpha_k^cA^k) LGradCAMc=ReLU(kαkcAk)
  • step5: 将生成的映射图resize到原图的大小,与原图相叠。

注意这里和CAM的另一个区别是,Grad-CAM对最终的加权和加了一个ReLU,加这么一层ReLU的原因在于我们只关心对类别c有正影响的那些像素点,如果不加ReLU层,最终可能会带入一些属于其它类别的像素,从而影响解释的效果。使用Grad-CAM对分类结果进行解释的效果如下图所示:
在这里插入图片描述
除了直接生成热力图对分类结果进行解释,Grad-CAM还可以与其他经典的模型解释方法如导向反向传播相结合,得到更细致的解释。

在这里插入图片描述

参考文献:

  1. 使用matplotlib绘制热图(heatmap)
  2. keras可视化代码
  3. CAM(Class Activation Mapping)通俗篇
  4. 凭什么相信你,我的CNN模型?(篇一:CAM和Grad-CAM)
以下是基于自己创建的CNN回归模型的Grad-CAM可视化类激活的热力图的PyTorch实现代码: ```python import torch import torch.nn.functional as F import cv2 import numpy as np class GradCAM: def __init__(self, model, target_layer): self.model = model self.target_layer = target_layer self.feature_maps = None self.gradient = None self.activation_maps = None def forward(self, x): self.feature_maps = [] self.gradient = [] for name, module in self.model.named_modules(): x = module(x) if name == self.target_layer: x.register_hook(self.gradient_hook) self.feature_maps.append(x) elif "conv" in name: self.feature_maps.append(x) self.activation_maps = x return self.activation_maps def gradient_hook(self, grad): self.gradient.append(grad) def backward(self): gradient = self.gradient[0] feature_maps = self.feature_maps[-1] batch_size, channel, height, width = feature_maps.shape weights = F.adaptive_avg_pool2d(gradient, (1, 1)) weights = weights.view(batch_size, channel) activation_maps = feature_maps.view(batch_size, channel, -1) weights = weights.unsqueeze(-1) cam = torch.bmm(activation_maps, weights) cam = F.relu(cam) cam = cam.view(batch_size, 1, height, width) cam = F.interpolate(cam, size=(224, 224), mode='bilinear', align_corners=False) cam = cam.squeeze() return cam.detach().cpu().numpy() model = YourCNNModel() gradcam = GradCAM(model, 'conv2') # target_layer is the layer you want to visualize # input image img = cv2.imread('image.jpg') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (224, 224)) img = np.transpose(img, (2, 0, 1)) img = np.expand_dims(img, axis=0) img = torch.from_numpy(img).float() # forward pass and backward pass output = model(img) cam = gradcam.forward(img) cam = gradcam.backward() # plot the heat map plt.imshow(img.squeeze().permute(1, 2, 0)) plt.imshow(cam, alpha=0.5, cmap='jet') plt.show() ``` 其中,`GradCAM`类实现了Grad-CAM算法的前向传播和反向传播,`model`是你自己创建的CNN回归模型,`target_layer`是你想要可视化的卷积层名称。在使用时,需要将输入图像转换为PyTorch张量,然后调用`gradcam.forward()`得到卷积层的特征图和梯度,再调用`gradcam.backward()`得到类激活热力图。最后,将输入图像和类激活热力图叠加在一起即可得到可视化结果。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值