物体定位:Grad-CAM论文笔记——Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization

物体定位:Grad-CAM论文笔记——Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization

综述

论文题目:《Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization》
会议时间:IEEE International Conference on Computer Vision 2017 (ICCV, 2017)

论文地址:http://openaccess.thecvf.com/content_ICCV_2017/papers/Selvaraju_Grad-CAM_Visual_Explanations_ICCV_2017_paper.pdf

关键字:无监督定位

介绍

  Grad-CAM的全称为梯度加权的类别响应图(Gradient-weighted Class Activation Mapping),常用于物体定位,适用于图像分类模型、图像描述模型(image captioning)以及可视化问答系统(visual question answering)等等。

  生成响应图的核心就是获得基于指定类别的梯度,之后利用梯度计算神经元的重要性:
α k c = 1 Z ∑ i ∑ j ∂ y c ∂ A i , j k \alpha_k^c=\frac1Z\sum_i\sum_j\frac{\partial y^c}{\partial A^k_{i,j}} αkc=Z1ijAi,jkyc
其中 α k c \alpha^c_k αkc是第 k k k张特征图 A k A^k Ak对于预测类别 c c c的相对重要性, y c y^c yc表示类别 c c c的预测值(网络的输出,未经过softmax),之后将 α \alpha α与特征图 A A A相乘,再沿通道方向求和得到一张图,最后再传入ReLU激活函数,得到最终的响应图:
L G r a d − C A M c = R e L U ( ∑ k α k c A k ) L^c_{Grad-CAM}=ReLU(\sum_k \alpha^c_k A^k) LGradCAMc=ReLU(kαkcAk)
后续可以利用响应图来执行定位任务,响应图上数据越大,表示原图响应位置上是物体所在区域。

注:

  • Grad-CAM算法核心就在于提取指定类别的梯度,在PyTorch中可用hook模块提取指定中间变量的梯度;
  • 在训练过程中,可用使用类别标签来定位,在测试过程中无标签,可以使用预测值最大的序号作为物体类别来定位;
  • 响应图不能直接用于可视化,可以先经过一次归一化运算,之后使用cv2.applyColorMap将原单通道图像数据转为3色图像数据;
  • 如果网络有BN层或DropOut层,在测试过程中执行定位时,需要将模型设为eval()模式,调整网络运算方式。

代码实现

  代码为个人实现,仅供参考,可视化效果如下:

在这里插入图片描述

import cv2
import torch
import numpy as np
import torch.nn.functional as F
from torchvision import models, transforms


# 定义反向传播和前向传播hook,分别用于提取梯度和特征
# 这里只提取网络模块输出元素的梯度和对应的特征数据
def backward_hook(module, inputs, outputs):
    grad_outputs.append(outputs[0].detach())


def forward_hook(module, inputs, outputs):
    feature_outputs.append(outputs)


# 数据归一化模块
def _normalize(cams):
    min = cams.min()
    max = cams.max()
    return (cams - min) / (max - min)


torch.manual_seed(0)
img_path = 'dog.jpg'
grad_outputs = []
feature_outputs = []
input_size = (224, 224)
# 定义网络模型,注意测试阶段要将模型设置为eval()
net = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1).eval()
# 将定义好的hook加到网络模块上面,注意模块名称
net.layer4.register_full_backward_hook(backward_hook)
net.layer4.register_forward_hook(forward_hook)
tran = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

img = cv2.imread(img_path)
img_re = cv2.resize(img, input_size)
img_re = cv2.cvtColor(img_re, cv2.COLOR_BGR2RGB)

data = tran(img_re).unsqueeze(0)
# 将图片数据传入网络中,做前向传播,此时feature_outputs中已经有了特征数据
out = net(data)
cls_idx = torch.argmax(out).item()
# 提取到网络预测最大的概率序号,当成预测类别,设为c
score = out[:, cls_idx]
net.zero_grad()
# 反向传播,得到关于类别c的梯度,此时储存在grad_outputs中
score.backward(retain_graph=True)
# 梯度求均值->和特征数据相乘->相乘结果沿通道方向求和->经过relu->归一化,得到最终的定位响应图
weight = grad_outputs[-1].squeeze(0).mean(dim=(0)).unsqueeze(0)
grad_cam = (weight * feature_outputs[-1].squeeze(0)).sum(0)
grad_cam = _normalize(F.relu(grad_cam, inplace=True)).cpu().detach().numpy()
# 将响应图放大到和原图一样的大小,之后转为三色图,变成热图
grad_cam = cv2.resize(grad_cam, (img.shape[1], img.shape[0]))
heatmap = cv2.applyColorMap(np.uint8(255 * grad_cam), cv2.COLORMAP_JET)
# 热图和原图加权相加,得到最终的可视化图
heatmap = cv2.addWeighted(img, 0.5, heatmap, 0.5, 0)
cv2.imwrite('hotmap.jpg', heatmap)

注:以上内容仅是笔者的个人见解,若有问题或者不清楚的地方,欢迎沟通交流。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

视觉萌新、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值