CAM、热力图 pytorch可视化卷积层

参考github:https://github.com/sixitingting/CAM/blob/master/pytorch_CAM.py

也就是类激活映射(CAM)原作者所给,想要懂理论的去看论文,本次着重实践。

CAM结果展示:

top1 prediction: mountain bike, all-terrain bike, off-roader

 

后话先说: 我发现现在还有很多朋友搜到这篇文章,但这是我刚开始学的时候写的笔记,很多东西都不太全,最近我更新了一个新的方法,更简单,更实用。如果感兴趣,欢迎查看我的另一篇博文CAM(类激活映射),卷积可视化,神经网络可视化,一个库搞定,真的简单的不能再简单。我相信这篇提供的帮助更多,如果有需要的话,点击看看吧。

----------------------------------------------------------实战开始-------------------------------------------------------

# simple implementation of CAM in PyTorch for the networks such as ResNet, DenseNet, SqueezeNet, Inception

import io
import requests
from PIL import Image
import torch
from torchvision import models, transforms
from torch.autograd import Variable
from torch.nn import functional as F
import numpy as np
import cv2
import json

# input image
LABELS_URL = 'https://s3.amazonaws.com/outcome-blog/imagenet/labels.json'
IMG_URL = 'http://media.mlive.com/news_impact/photo/9933031-large.jpg'
# jsonfile = r'D:\python\Camtest\labels.json'
# with open(jsonfile, 'r') as load_f:
#     load_json = json.load(load_f)

# networks such as googlenet, resnet, densenet already use global average pooling at the end,
# so CAM could be used directly.
model_id = 1
if model_id == 1:
    net = models.squeezenet1_1(pretrained=False)
    pthfile = r'E:\anaconda\app\envs\luo\Lib\site-packages\torchvision\models\squeezenet1_1.pth'
    net.load_state_dict(torch.load(pthfile))
    finalconv_name = 'features'   # this is the last conv layer of the network
elif model_id == 2:
    net = models.resnet18(pretrained=False)
    finalconv_name = 'layer4'
elif model_id == 3:
    net = models.densenet161(pretrained=False)
    finalconv_name = 'features'

net.eval()
print(net)

# hook the feature extractor
features_blobs = []


def hook_feature(module, input, output):
    features_blobs.append(output.data.cpu().numpy())


net._modules.get(finalconv_name).register_forward_hook(hook_feature)

# get the softmax weight
params = list(net.parameters())
weight_softmax = np.squeeze(params[-2].data.numpy())


def returnCAM(feature_conv, weight_softmax, class_idx):
    # generate the class activation maps upsample to 256x256
    size_upsample = (256, 256)
    bz, nc, h, w = feature_conv.shape
    output_cam = []
    for idx in class_idx:
        cam = weight_softmax[idx].dot(feature_conv.reshape((nc, h*w)))
        cam = cam.reshape(h, w)
        cam_img = (cam - cam.min()) / (cam.max() - cam.min())  # normalize
        cam_img = np.uint8(255 * cam_img)
        output_cam.append(cv2.resize(cam_img, size_upsample))
    return output_cam


normalize = transforms.Normalize(
   mean=[0.485, 0.456, 0.406],
   std=[0.229, 0.224, 0.225]
)
preprocess = transforms.Compose([
   transforms.Resize((224, 224)),
   transforms.ToTensor(),
   normalize
])

response = requests.get(IMG_URL)
img_pil = Image.open(io.BytesIO(response.content))
img_pil.save('test.jpg')

img_tensor = preprocess(img_pil)
img_variable = Variable(img_tensor.unsqueeze(0))
logit = net(img_variable)

# download the imagenet category list
classes = {int(key): value for (key, value)
          in requests.get(LABELS_URL).json().items()}
# classes = {int(key): value for (key, value)
#           in load_json.items()}


# 结果有1000类,进行排序
h_x = F.softmax(logit, dim=1).data.squeeze()
probs, idx = h_x.sort(0, True)
probs = probs.numpy()
idx = idx.numpy()

# output the prediction 取前5
for i in range(0, 5):
    print('{:.3f} -> {}'.format(probs[i], classes[idx[i]]))

# generate class activation mapping for the top1 prediction
CAMs = returnCAM(features_blobs[0], weight_softmax, [idx[0]])

# render the CAM and output
print('output CAM.jpg for the top1 prediction: %s'%classes[idx[0]])
img = cv2.imread('test.jpg')
height, width, _ = img.shape
heatmap = cv2.applyColorMap(cv2.resize(CAMs[0],(width, height)), cv2.COLORMAP_JET)
result = heatmap * 0.3 + img * 0.5
cv2.imwrite('CAM.jpg', result)

结果:0.678 -> mountain bike, all-terrain bike, off-roader
          0.088 -> bicycle-built-for-two, tandem bicycle, tandem
         0.042 -> unicycle, monocycle
         0.038 -> horse cart, horse-cart
         0.019 -> lakeside, lakeshore
         output CAM.jpg for the top1 prediction: mountain bike, all-terrain bike, off-roader

以上是所有代码,对容易遇到的问题做个解释:

1.由于网络问题下载不下来json文件:

urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='media.mlive.com', port=80):

方法:手动把文件下载下来,放在同一个项目下,用被注释掉的代码:

jsonfile = r'D:\python\Camtest\labels.json'   # 换成自己的地址
with open(jsonfile, 'r') as load_f:
    load_json = json.load(load_f)




# 并把下面内容打开
classes = {int(key): value for (key, value)
          in load_json.items()}

2.由于网络问题加载不出来预训练的网络,我就加载不出来,所以 pretrained=False,并去下载预训练好的参数,下载过程详见:https://blog.csdn.net/u014264373/article/details/85332181

3.关于hook住中间特征不懂得去百度: register_forward_hook

4.结果只展示了top5的预测结果。

小白教程

坚持已实践为主,手把手带你做项目,打比赛,写论文。凡原创文章皆提供理论讲解,实验代码,实验数据。只有实践才能成长的更快,关注我们,一起成长进步~

 

  • 15
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 36
    评论
Grad-CAM(Gradient-weighted Class Activation Mapping)是一种可视化卷积神经网络(CNN)中模型对输入图像的分类决策的方法。它可以根据模型的输出和梯度信息,生成一个热力图,用来表示输入图像中哪些区域对于模型分类结果的影响较大。 以下是使用 PyTorch 实现 Grad-CAM 热力图可视化的简单步骤: 1. 加载预训练模型,设置为 evaluation 模式。 ```python import torch import torchvision.models as models model = models.resnet18(pretrained=True) model.eval() ``` 2. 对输入图像进行预处理,并将其送入模型中进行前向传播。 ```python from PIL import Image import torchvision.transforms as transforms # 加载输入图像 img_path = 'example.jpg' img = Image.open(img_path) # 图像预处理 preprocess = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) input_tensor = preprocess(img) input_batch = input_tensor.unsqueeze(0) # 模型预测 with torch.no_grad(): output = model(input_batch) ``` 3. 计算模型输出关于目标类别的梯度。 ```python # 目标类别的下标 target_class = 2 # 计算目标类别的梯度 model.zero_grad() output[:, target_class].backward() # 获取目标类别的输出和特征图 target_output = model.features[-1].forward(input_batch) target_grads = model.features[-1].weight.grad # 计算特征图上每个像素点的权重 weights = target_grads.mean(dim=(2, 3)).squeeze() ``` 4. 根据权重和特征图计算 Grad-CAM 热力图。 ```python import numpy as np # 取出特征图 features = target_output.squeeze() # 特征图上每个像素的权重乘以对应像素的值 cam = np.zeros(features.shape[1:], dtype=np.float32) for i, w in enumerate(weights): cam += w * features[i, :, :] # 对热力图进行归一化处理 cam = np.maximum(cam, 0) cam = cam / cam.max() # 将热力图可视化到原始图像上 from matplotlib import pyplot as plt # 加载原始图像 img = Image.open(img_path) # 将热力图缩放到原始图像的大小 cam = np.uint8(255 * cam) cam = np.array(Image.fromarray(cam).resize(img.size, resample=Image.BILINEAR)) # 叠加热力图和原始图像 heatmap = plt.get_cmap('jet')(cam)[:,:,:3] heatmap = np.float32(heatmap) heatmap = heatmap / heatmap.max() superimposed_img = heatmap + np.float32(img) superimposed_img = superimposed_img / superimposed_img.max() # 可视化热力图和叠加后的图像 fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].imshow(cam, cmap='jet') ax[0].set_title('Grad-CAM heatmap') ax[1].imshow(superimposed_img) ax[1].set_title('Original image with heatmap') plt.show() ``` 这样就完成了 Grad-CAM 热力图的生成和可视化。需要注意的是,在计算模型输出关于目标类别的梯度时,需要将模型设置为 eval 模式,并关闭 autograd 引擎的计算图记录功能,以避免梯度计算对模型参数的影响。
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tina姐

我就看看有没有会打赏我

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

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

打赏作者

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

抵扣说明:

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

余额充值