CV10_模型、特征图、CAM热力图可视化

1.1 模型可视化

安装下包: 

pip install netron

安装完成后,在终端输入netron:

进入这个链接:

选择你要可视化的模型的权重文件:(以ResNet34为例)

可视化结果:

点击查看更详细的信息:

1.2 特征图可视化

将如下代码粘贴进模型文件中:

def draw_features(width,height,x,savename):#width,height是画图的坐标
    tic = time.time()
    fig = plt.figure(figsize=(16,16))
    fig.subplots_adjust(left=0.05,right=0.95,bottom=0.05,top=0.95,wspace=0.05,hspace=0.05)
    for i in range(width*height):
        plt.subplot(height,width,i+1)
        plt.axis("off")
        #plt.tight_layout()
        img =x[0,i,:,:] #取第1个batch,从特征矩阵x里面提取第i个特征图
        pmin = np.min(img)
        pmax = np.max(img)
        img = (img-pmin)/(pmax-pmin+0.000001) #批量归一化
        plt.imshow(img,cmap='gray')
        print("{}/{}".format(i,width*height))
    fig.savefig(savename,dpi=100) #以100DPI的分辨率保存图像
    # 关闭并清理figure对象,释放内存。
    fig.clf()
    fig.close()
    print("time:{}".format(time.time()-tic)) #计算所用时间

接下来,找到模型的主类,在初始化函数函数和前向传播做如下修改:

放在需要可视化的特征层中,注意通道数的大小。

下面8,8和通道数64有关联,那么8x8就是通道数64

draw_features(8, 8, x.cpu().detach().numpy(), "{}/f1_conv1.png".format(self.savepath))
print("{}/f1_conv1.png".format(self.savepath))

每训练一张图片就会输出一张可视化特征图:

1.3 CAM热力图可视化

简介

Grad-CAM(Gradient-weighted Class Activation Mapping)是一种用于视觉解释的技术,它能够生成热力图来可视化深度学习模型在做决策时关注输入图像的哪些部分。这种方法特别适用于卷积神经网络(CNNs),帮助理解模型做出特定分类决策的原因。Grad-CAM的基本原理结合了卷积层的特征图(Feature Maps)和分类任务的梯度信息。以下是Grad-CAM工作流程的简要说明:

  1. 前向传播:首先,将输入图像送入CNN模型进行前向传播,直到获得最后一层卷积层的输出,这些输出被称为特征图(Feature Maps),每个特征图对应于输入图像的不同特征响应。

  2. 计算梯度:接着,选择模型中的一个特定类别输出(比如模型预测概率最高的类别,或是我们感兴趣的类别),计算这个类别输出相对于最后一层卷积层特征图的梯度。这些梯度反映了特征图中的各个位置对最终分类分数的贡献程度。

  3. 加权求和:然后,利用计算出的梯度对特征图进行加权。具体来说,对于每个特征图,我们将特征图中的每个像素的值乘以其对应的梯度值,这样做是为了强调那些对目标类别影响较大的特征区域。之后,对所有特征图进行加权求和,得到一个单通道的热力图。这个过程可以看作是用梯度作为权重,对特征图进行全局平均池化。

  4. ReLU操作:为了确保热力图只显示模型关注的区域(正值区域),会对上述加权求和的结果应用ReLU函数,即保留正值,将负值设为零。这样得到的热力图就只突出了模型认为对分类重要的区域。

  5. 上采样(可选):最后,为了与原始输入图像大小匹配,通常会对生成的热力图进行上采样。这样可以直接将热力图叠加在输入图像上,直观地看到模型在做决策时关注的图像区域。

通过Grad-CAM,我们可以直观地看到模型“看”到了什么,这对于理解模型行为、诊断错误分类、以及提升模型的可解释性非常有帮助。

安装包:

pip install grad-cam

import numpy as np
import cv2
import torchvision.models as models
import torchvision.transforms as transforms

import pytorch_grad_cam
from pytorch_grad_cam.utils.image import show_cam_on_image


resnet18 = models.resnet18(pretrained=True)

resnet18.eval()
target_layers = [resnet18.layer4[1].bn2]
origin_img = cv2.imread('./img.jpg')
rgb_img = cv2.cvtColor(origin_img,cv2.COLOR_BGR2RGB)

trans = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize(224),
    transforms.CenterCrop(224)
])

crop_img = trans(rgb_img)
#使用ImageNet数据集的均值和标准差。.unsqueeze(0)增加一个维度,使得张量变为batch形式,符合PyTorch模型输入要求。
net_input = transforms.Normalize((0.485,0.456,0.406),(0.229,0.224,0.225))(crop_img).unsqueeze(0)

#乘以255是为了将这些值转换回[0, 255]区间,将张量的数据类型转换为8位无符号整型
'''
原本的张量或数组的形状是(channels, height, width),这是常见的深度学习模型中的图像表示方式(通道优先)。
而OpenCV以及许多图像处理库通常期待的格式是(height, width, channels)(高度、宽度、通道顺序)。
因此,通过调用.transpose(1, 2, 0),我们把通道维度从第0位移动到最后,使其适应OpenCV的显示需求。
'''
canvas_img = (crop_img * 255).byte().numpy().transpose(1,2,0)
canvas_img = cv2.cvtColor(canvas_img,cv2.COLOR_RGB2BGR)

cam = pytorch_grad_cam.GradCAMPlusPlus(model=resnet18,target_layers=target_layers)
grayscale_cam = cam(net_input)
grayscale_cam = grayscale_cam[0,:] #从热力图数组中取出第一张图像的热力图

src_img = np.float32(canvas_img)/255 #将显示用的图像转换为浮点数并归一化到[0,1]区间,便于后续的热力图叠加。

#在原始图像上叠加热力图。use_rgb=False表明热力图是以灰度形式叠加的,而非彩色。
visualization_img = show_cam_on_image(src_img,grayscale_cam,use_rgb=False)
cv2.imshow('feature map',visualization_img)
cv2.waitKey(0) #等待用户按键,按任意键后关闭显示窗口

运行结果如下: 

接下来用我们自己的模型来生成热力图:

# Author:SiZhen
# Create: 2024/7/15
# Description: 热力图
import numpy as np
import cv2
import torchvision.models as models
import torchvision.transforms as transforms

import pytorch_grad_cam
from pytorch_grad_cam.utils.image import show_cam_on_image

from model import resnet34 #导入模型

net = resnet34() #实例化


net.eval()
target_layers = [net.layer4[1].bn2]
origin_img = cv2.imread('./img.jpg')
rgb_img = cv2.cvtColor(origin_img,cv2.COLOR_BGR2RGB)

trans = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize(224),
    transforms.CenterCrop(224)
])

crop_img = trans(rgb_img)
#使用ImageNet数据集的均值和标准差。.unsqueeze(0)增加一个维度,使得张量变为batch形式,符合PyTorch模型输入要求。
net_input = transforms.Normalize((0.485,0.456,0.406),(0.229,0.224,0.225))(crop_img).unsqueeze(0)

#乘以255是为了将这些值转换回[0, 255]区间,将张量的数据类型转换为8位无符号整型
'''
原本的张量或数组的形状是(channels, height, width),这是常见的深度学习模型中的图像表示方式(通道优先)。
而OpenCV以及许多图像处理库通常期待的格式是(height, width, channels)(高度、宽度、通道顺序)。
因此,通过调用.transpose(1, 2, 0),我们把通道维度从第0位移动到最后,使其适应OpenCV的显示需求。
'''
canvas_img = (crop_img * 255).byte().numpy().transpose(1,2,0)
canvas_img = cv2.cvtColor(canvas_img,cv2.COLOR_RGB2BGR)

cam = pytorch_grad_cam.GradCAMPlusPlus(model=net,target_layers=target_layers)
grayscale_cam = cam(net_input)
grayscale_cam = grayscale_cam[0,:] #从热力图数组中取出第一张图像的热力图

src_img = np.float32(canvas_img)/255 #将显示用的图像转换为浮点数并归一化到[0,1]区间,便于后续的热力图叠加。

#在原始图像上叠加热力图。use_rgb=False表明热力图是以灰度形式叠加的,而非彩色。
visualization_img = show_cam_on_image(src_img,grayscale_cam,use_rgb=False)
cv2.imshow('feature map',visualization_img)
cv2.waitKey(0) #等待用户按键,按任意键后关闭显示窗口


运行结果如下:

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于自己创建的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()`得到类激活热力图。最后,将输入像和类激活热力图叠加在一起即可得到可视化结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值