类别激活热力图grad-cam(pytorch)实战跑图

写在前面

类激活热力图:用于检查图像哪一部分对模型的最终输出有更大的贡献。具体某个类别对应到图片的那个区域响应最大,也就是对该类别的识别贡献最大

pytorch-grad-cam库代码GitHub代码
如果只想跑个图的话不用下!

作用:一是清晰直观的看看到底影响检测结果的特征;而是cv论文里出图真的很好看
本篇只是跑了代码给的猫狗图,下一篇要写如何可视化其他类别实战

先上跑完结果


使用的是resnet50,可以看出其关注度不仅仅只有狗,还有后面的背景,这会对以后的检测结果造成影响

实战

1.安装pytorch-grad-cam

在pycharm终端中输入

pip install grad-cam

我之前安装过了,页面如下

2.复制全部代码

'''
1)导入相关的包并加载模型
'''

from pytorch_grad_cam import GradCAM, ScoreCAM, GradCAMPlusPlus, AblationCAM, XGradCAM, EigenCAM
from pytorch_grad_cam.utils.image import show_cam_on_image, \
                                         deprocess_image, \
                                         preprocess_image
from torchvision.models import resnet50
import cv2
import numpy as np
import os

os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"

# 1.加载模型
model = resnet50(pretrained=True) #预先训练
# 2.选择目标层
# target_layer = model.layer4[-1]
target_layer = [model.layer4]
'''
Resnet18 and 50: model.layer4[-1]
VGG and densenet161: model.features[-1]
mnasnet1_0: model.layers[-1]
ViT: model.blocks[-1].norm1
'''
#------------------------------
'''
2)构建输入图像的Tensor形式,使其能传送到model里面去计算
'''



image_path = '../../examples/both.png'
rgb_img = cv2.imread(image_path, 1)[:, :, ::-1]   # 1是读取rgb
                                                 #imread返回从指定路径加载的图像
rgb_img = cv2.imread(image_path, 1) #imread()读取的是BGR格式
rgb_img = np.float32(rgb_img) / 255



# preprocess_image作用:归一化图像,并转成tensor
input_tensor = preprocess_image(rgb_img, mean=[0.485, 0.456, 0.406],
                                             std=[0.229, 0.224, 0.225])   # torch.Size([1, 3, 224, 224])
# Create an input tensor image for your model..
# Note: input_tensor can be a batch tensor with several images!


#----------------------------------------
'''
3)初始化CAM对象,包括模型,目标层以及是否使用cuda等
'''
# Construct the CAM object once, and then re-use it on many images:
cam = GradCAM(model=model, target_layers=target_layer, use_cuda=False)
'''
4)选定目标类别,如果不设置,则默认为分数最高的那一类
'''
# If target_category is None, the highest scoring category
# will be used for every image in the batch.
# target_category can also be an integer, or a list of different integers
# for every image in the batch.
target_category = None
#指定类:target_category = 281

'''
5)计算cam
'''
# You can also pass aug_smooth=True and eigen_smooth=True, to apply smoothing.
grayscale_cam = cam(input_tensor=input_tensor, target_category=target_category)  # [batch, 224,224]

#----------------------------------
'''
6)展示热力图并保存
'''
# In this example grayscale_cam has only one image in the batch:
# 7.展示热力图并保存, grayscale_cam是一个batch的结果,只能选择一张进行展示
grayscale_cam = grayscale_cam[0]
visualization = show_cam_on_image(rgb_img, grayscale_cam)  # (224, 224, 3)
cv2.imwrite(f'first_try.jpg', visualization)

> 步骤:先把博客中代码复制,然后按照本博客写的修改即可,也可以直接拉到下面复制我的代码,然后按照说明修改路径

3.自己使用代码需要修改的部分

1、图片路径

第34行,将单引号地址改为你要用的图片地址

image_path = '../../examples/both.png'

不会的请详细参看错误1内容,写的很清楚
注意:路径中不要有中文

2、结果图名称

第79行

cv2.imwrite(f'first_try.jpg', visualization)

将’'改为你希望的名字即可

4.查看结果图

其实就在代码当前路径的文件夹下
当然也可以复制图片名称first_try.jpg,在保存代码的文件夹里直接查找

点击结果图

运行代码所遇到的问题

错误1:TypeError: ‘NoneType’ object is not subscriptable

1、报错内容:

Traceback (most recent call last):
File “E:/CAM/pytorch-grad-cam/pytorch_grad_cam/utils/using_grad_cam.py”, line 32, in
rgb_img = cv2.imread(image_path, 1)[:, :, ::-1] # 1是读取rgb
TypeError: ‘NoneType’ object is not subscriptable

2、错误代码:

第2)构建输入图像的Tensor形式,使其能传送到model里面去计算,这一步

image_path = './examples/both.png'
rgb_img = cv2.imread(image_path, 1)[:, :, ::-1]   # 1是读取rgb
rgb_img = np.float32(rgb_img) / 255

使用OpenCV读取图片时发生错误,一直显示图片类型是“nonetype”

3、修正:

发现是路径错误

image_path = './examples/both.png'  #错误路径

修改后

image_path = '../../examples/both.png'

附上运行代码与读取的图片位置

关于路径如何正确表示可以看这篇文件路径./和…/

错误2:AttributeError: ‘GradCAM’ object has no attribute ‘activations_and_grads’

报错内容:

Traceback (most recent call last):
File “D:\anaconda\envs\pytorch\lib\site-packages\pytorch_grad_cam\base_cam.py”, line 192, in del
self.activations_and_grads.release()
AttributeError: ‘GradCAM’ object has no attribute ‘activations_and_grads’

错误3:TypeError: init() got an unexpected keyword argument ‘target_layer’

报错内容:

Traceback (most recent call last):
File “E:/CAM/pytorch-grad-cam/pytorch_grad_cam/utils/using_grad_cam.py”, line 60, in
cam = GradCAM(model=model, target_layer=target_layer, use_cuda=False)
TypeError: init() got an unexpected keyword argument ‘target_layer’

针对错误2与错误3的修改:

第一处:

target_layer = model.layer4[-1]

修改为 target_layer = [model.layer4]
第二处:

cam = GradCAM(model=model, target_layer=target_layer, use_cuda=False)

修改为

cam = GradCAM(model=model, target_layers=target_layer, use_cuda=False)

只想跑图出结果的不需要看,想大概熟悉代码与原理的可以看一看

使用pytorch实现grad-cam需了解hook机制,详细看 pytorch的autograd
pytorch的hook应用
个人理解:hook就是储存pytorch所释放的中间变量的“钩子”,哪里需要就钩在哪里 举例更好理解。
hook机制一定要理解,grad-cam中hook是重要代码

基础差的看不懂基础代码,可以哪里不懂点哪里
1、*args**kargs 知识点讲解链接
2、Python自带模块argparse详细使用说明书 Python argparse命令行参数解析包的详细使用说明书

例:采用LeNet-5演示backward_hook在grad-cam中应用,下面是代码过程(具体代码上面链接中有) 1、创建网络net
2、注册forward_hook函数用于提取最后一层特征图;
3、注册backward_hook函数用于提取类向量(one-hot)关于特征图的梯度; 4、对特征图的梯度进行求均值,并对特征图进行加权;
5、可视化heatmap

model._modules.items() 遍历输出每一层
register_hook的作用:即对x求导时,对x的导数进行操作,并且register_hook的参数只能以函数的形式传过去。(保护求导中中间值作用,不然pytorch将直接保存最后结果,中间结果不保存)详细解释

本篇主要参考CAM(类激活映射),卷积可视化,神经网络可视化,一个库搞定,真的简单的不能再简单

来源:半甜田田

物联沃分享整理
物联沃-IOTWORD物联网 » 类别激活热力图grad-cam(pytorch)实战跑图

发表评论