当前位置: 首页 > news >正文

Grad-CAM,即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping)

Grad-CAM,即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping),是一种用于解释卷积神经网络决策的方法。它通过可视化模型对于给定输入的关注区域来提供洞察。

原理:

Grad-CAM的关键思想是将输出类别的梯度(相对于特定卷积层的输出)与该层的输出相乘,然后取平均,得到一个“粗糙”的热力图。这个热力图可以被放大并叠加到原始图像上,以显示模型在分类时最关注的区域。

具体步骤如下:

  1. 选择一个卷积层作为解释的来源。通常,我们会选择网络的最后一个卷积层,因为它既包含了高级特征,也保留了空间信息。
  2. 前向传播图像到网络,得到你想解释的类别的得分。
  3. 计算此得分 相对于我们选择的卷积层 输出的梯度。
  4. 对于该卷积层的每个通道,使用上述梯度的全局平均值对该通道进行加权
  5. 结果是一个与卷积层的空间维度相同的加权热力图

优势

Grad-CAM的优点是它可以用于任何卷积神经网络,无需进行结构修改或重新训练。它为我们提供了一个简单但直观的方式来理解模型对于特定输入的决策。

Code

import torch
import cv2
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from PIL import Imageclass GradCAM:def __init__(self, model, target_layer):self.model = modelself.target_layer = target_layerself.feature_maps = Noneself.gradients = None# Hook layerstarget_layer.register_forward_hook(self.save_feature_maps)target_layer.register_backward_hook(self.save_gradients)def save_feature_maps(self, module, input, output):self.feature_maps = output.detach()def save_gradients(self, module, grad_input, grad_output):self.gradients = grad_output[0].detach()def generate_cam(self, image, class_idx=None):# Set model to evaluation modeself.model.eval()# Forward passoutput = self.model(image)if class_idx is None:class_idx = torch.argmax(output).item()# Zero out gradientsself.model.zero_grad()# Backward pass for target classone_hot = torch.zeros((1, output.size()[-1]), dtype=torch.float32)one_hot[0][class_idx] = 1output.backward(gradient=one_hot.cuda(), retain_graph=True)# Get pooled gradients and feature mapspooled_gradients = torch.mean(self.gradients, dim=[0, 2, 3])activation = self.feature_maps.squeeze(0)for i in range(activation.size(0)):activation[i, :, :] *= pooled_gradients[i]# Create heatmapheatmap = torch.mean(activation, dim=0).squeeze().cpu().numpy()heatmap = np.maximum(heatmap, 0)heatmap /= torch.max(heatmap)heatmap = cv2.resize(heatmap, (image.size(3), image.size(2)))heatmap = np.uint8(255 * heatmap)heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)# Superimpose heatmap on original imageoriginal_image = self.unprocess_image(image.squeeze().cpu().numpy())superimposed_img = heatmap * 0.4 + original_imagesuperimposed_img = np.clip(superimposed_img, 0, 255).astype(np.uint8)return heatmap, superimposed_imgdef unprocess_image(self, image):# Reverse the preprocessing stepmean = np.array([0.485, 0.456, 0.406])std = np.array([0.229, 0.224, 0.225])image = (((image.transpose(1, 2, 0) * std) + mean) * 255).astype(np.uint8)return imagedef visualize_gradcam(model, input_image_path, target_layer):# Load imageimg = Image.open(input_image_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).unsqueeze(0).cuda()# Create GradCAMgradcam = GradCAM(model, target_layer)heatmap, result = gradcam.generate_cam(input_tensor)plt.figure(figsize=(10,10))plt.subplot(1,2,1)plt.imshow(heatmap)plt.title('Heatmap')plt.axis('off')plt.subplot(1,2,2)plt.imshow(result)plt.title('Superimposed Image')plt.axis('off')plt.show()# Load your model (e.g., resnet20 in this case)
# model = resnet20()
# model.load_state_dict(torch.load("path_to_your_weights.pth"))
# model.to('cuda')# Visualize GradCAM
# visualize_gradcam(model, "path_to_your_input_image.jpg", model.layer3[-1])

中文注释详细版

import torch
import cv2
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from PIL import Imageclass GradCAM:def __init__(self, model, target_layer):self.model = model  # 要进行Grad-CAM处理的模型self.target_layer = target_layer  # 要进行特征可视化的目标层self.feature_maps = None  # 存储特征图self.gradients = None  # 存储梯度# 为目标层添加钩子,以保存输出和梯度target_layer.register_forward_hook(self.save_feature_maps)target_layer.register_backward_hook(self.save_gradients)def save_feature_maps(self, module, input, output):"""保存特征图"""self.feature_maps = output.detach()def save_gradients(self, module, grad_input, grad_output):"""保存梯度"""self.gradients = grad_output[0].detach()def generate_cam(self, image, class_idx=None):"""生成CAM热力图"""# 将模型设置为评估模式self.model.eval()# 正向传播output = self.model(image)if class_idx is None:class_idx = torch.argmax(output).item()# 清空所有梯度self.model.zero_grad()# 对目标类进行反向传播one_hot = torch.zeros((1, output.size()[-1]), dtype=torch.float32)one_hot[0][class_idx] = 1output.backward(gradient=one_hot.cuda(), retain_graph=True)# 获取平均梯度和特征图pooled_gradients = torch.mean(self.gradients, dim=[0, 2, 3])activation = self.feature_maps.squeeze(0)for i in range(activation.size(0)):activation[i, :, :] *= pooled_gradients[i]# 创建热力图heatmap = torch.mean(activation, dim=0).squeeze().cpu().numpy()heatmap = np.maximum(heatmap, 0)heatmap /= torch.max(heatmap)heatmap = cv2.resize(heatmap, (image.size(3), image.size(2)))heatmap = np.uint8(255 * heatmap)heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)# 将热力图叠加到原始图像上original_image = self.unprocess_image(image.squeeze().cpu().numpy())superimposed_img = heatmap * 0.4 + original_imagesuperimposed_img = np.clip(superimposed_img, 0, 255).astype(np.uint8)return heatmap, superimposed_imgdef unprocess_image(self, image):"""反预处理图像,将其转回原始图像"""mean = np.array([0.485, 0.456, 0.406])std = np.array([0.229, 0.224, 0.225])image = (((image.transpose(1, 2, 0) * std) + mean) * 255).astype(np.uint8)return imagedef visualize_gradcam(model, input_image_path, target_layer):"""可视化Grad-CAM热力图"""# 加载图像img = Image.open(input_image_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).unsqueeze(0).cuda()# 创建GradCAMgradcam = GradCAM(model, target_layer)heatmap, result = gradcam.generate_cam(input_tensor)# 显示图像和热力图plt.figure(figsize=(10,10))plt.subplot(1,2,1)plt.imshow(heatmap)plt.title('热力图')plt.axis('off')plt.subplot(1,2,2)plt.imshow(result)plt.title('叠加后的图像')plt.axis('off')plt.show()# 以下是示例代码,显示如何使用上述代码。
# 首先,你需要加载你的模型和权重。
# model = resnet20()
# model.load_state_dict(torch.load("path_to_your_weights.pth"))
# model.to('cuda')# 然后,调用`visualize_gradcam`函数来查看结果。
# visualize_gradcam(model, "path_to_your_input_image.jpg", model.layer3[-1])

论文链接:https://openaccess.thecvf.com/content_ICCV_2017/papers/Selvaraju_Grad-CAM_Visual_Explanations_ICCV_2017_paper.pdf

相关文章:

Grad-CAM,即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping)

Grad-CAM,即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping),是一种用于解释卷积神经网络决策的方法。它通过可视化模型对于给定输入的关注区域来提供洞察。 原理: Grad-CAM的关键思想是将输出类别的梯度(相对于特定卷积…...

程序发布——使用pyinstaller打包识别程序为exe可执行文件 详解

当我们使用python完成项目开发后,必然面对着如何将自己的程序分享给其他人使用,这就离不开程序的打包工作。对于大多数人而言,我们还是使用windows电脑居多,因此我们在大多数场景是需要将程序打包为exe的可执行文件。笔者将在本章节详细介绍使用pyinstaller进行打包的全过程…...

Docker 使用

简介 Docker是一个开源的容器引擎,它有助于更快地交付应用。 Docker可将应用程序和基础设施层隔离,并且能将基础设施当作程序一样进行管理。使用 Docker可更快地打包、测试以及部署应用程序,并可以缩短从编写到部署运行代码的周期。 Docker…...

电脑c盘变红满了怎么清理?4个方法轻松清理!

“我的电脑才用了不到一年,现在就已经满了!电脑c盘变红满了应该怎么清理呢?有什么方法能帮我清理的干净一点吗?希望大家给我出出主意。” 随着我们使用电脑时间的增多,电脑C盘可能会变得满满当当,这会不仅会…...

【UE 材质】实现角度渐变材质、棋盘纹理材质

目标 步骤 一、角度渐变材质 1. 首先通过“Mask”节点将"Texture Coordinate" 节点的R、G通道分离 2. 通过“RemapValueRange”节点将0~1范围映射到-1~1 可以看到此时R通道效果: G通道效果: 继续补充如下节点 二、棋盘纹理材质 原视频链接&…...

[深度学习]1. 深度学习知识点汇总

本文记录了我在学习深度学习的过程中遇到过的不懂的知识点,为了方便翻阅,故将其发表于此,随时更新,供大家参考。 深度学习常见知识点 1. 测试精度和训练精度 在深度学习中,测试精度和训练精度是两个重要的指标&#…...

鲁棒优化入门(6)—Matlab+Yalmip两阶段鲁棒优化通用编程指南(上)

0.引言 上一篇博客介绍了使用Yalmip工具箱求解单阶段鲁棒优化的方法。这篇文章将和大家一起继续研究如何使用Yalmip工具箱求解两阶段鲁棒优化(默认看到这篇博客时已经有一定的基础了,如果没有可以看看我专栏里的其他文章)。关于两阶段鲁棒优化与列与约束生成算法的原…...

golang通过gorm操作sqlite设置主键自增

在 Golang 中使用 GORM 操作 SQLite 数据库时,可以通过以下步骤设置主键自增: 首先,确保已经安装了 GORM 和 SQLite 的驱动程序。你可以使用以下命令安装它们: go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite导入所…...

基于Spring Boot的企业门户网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频: 基于Spring Boot的企业门户网站设计与实现(Javaspring bootMySQL) 使用技术: 前端:html css javascript jQuery ajax thymeleaf 微信小程序 后端:Java springboot…...

Json解析流程

一、拿到了题库 分析一下可以定义的 1、序号,用来区分题目数,每个题有唯一的序号 2、题目,就是下图的Q 3、预设的回答,下图的A 分析完我可以知道有三个字段,分别是int index、string Q、string A。 二、把字段丢到…...

Mybatis 动态SQL – 使用choose标签动态生成条件语句

之前我们介绍了if,where标签的使用;本篇我们需要在if,where标签的基础上介绍如何使用Mybatis提供的choose标签动态生成条件语句。 如果您对if,where标签动态生成条件语句不太了解,建议您先进行了解后再阅读本篇,可以参考: Mybat…...

http接口自动化测试框架实现

目录 一、测试需求描述 二、实现方法 三、Excel表格样式 四、实现代码(代码才是王道,有注释很容易就能看明白的) 一、测试需求描述 对服务后台一系列的http接口功能测试。 输入:根据接口描述构造不同的参数输入值 输出&…...

Android逆向学习(三)vscode修改smali绕过vip

Android逆向学习(三)vscode修改smali绕过vip 写在前面 这是吾爱的第二个作业,主要就是要修改smali代码,其实smali代码我感觉没有必要去学,当然主要是我本来就会汇编语言,基本上汇编语言都是一样的&#x…...

代码随想录训练营第38天|62.不同路径,63.不同路径II

代码随想录训练营第38天|62.不同路径,63.不同路径II 62.不同路径文章思路代码 63.不同路径II文章思路代码 总结 62.不同路径 文章 代码随想录|0062.不同路径 思路 d p [ i ] [ j ] { 1 , i 0 ∧ j 0 d p [ i − 1 ] [ j ] d p [ i ] [ j − 1 ] , e l s e \b…...

BlueStore BlueFS rocksdb 关联性梳理

Tag: ceph 12.2.4 BlueStore空间初始化 BlueStore磁盘空间管理 总述 OSD挂载目录基于文件系统管理,Slow、WAL、DB空间区域基于裸盘管理;Slow区域:此类空间主要用于存储对象数据,由BlueStore管理,其中分配于BlueFS空…...

PgSQL-并行查询系列-介绍[译]

PgSQL-并行查询系列-介绍 现代CPU模型拥有大量的CPU核心。多年来,数据库应用程序都是并发向数据库发送查询的。查询处理多个表的行时,若可以使用多核,则可以客观地提升性能。PgSQL 9.6引入了并行查询的新特性,开启并行查询后可以大…...

Linux以系统服务的方式启动Kafka(其他服务同理)

最终效果: 先回顾命令行的启动方式: kafka的启动 进入kafka的安装目录 1、首先启动zookeeper服务: bin/zookeeper-server-start.sh config/zookeeper.properties2、再启动kafka bin/kafka-server-start.sh config/server.properties &…...

成都瀚网科技有限公司:抖店的评论会消失吗?

抖店是抖音推出的电子商务平台。很多用户在购物后都会对产品进行评价。但有时用户可能会发现抖店评论缺失,让用户产生一些疑惑和困惑。本文将围绕这个问题提供一些答案和解决方案。 1.为什么抖店评论不见了? 首先需要明确的是,抖店评论消失可…...

优先级队列priority_queue以及仿函数的使用

目录 优先级队列priority_queuepriority_queue的模拟实现仿函数 优先级队列priority_queue 优先级队列priority_queue是一种容器适配器,根据严格的弱排序标准,它默认第一个元素总是它所包含的元素中最大的 优先级队列默认使用vector作为底层存储数据的…...

java+ssm+mysql水费管理系统

项目介绍: 使用javassmmysql开发的用户水费管理系统,系统包含超级管理员,系统管理员、用户角色,功能如下: 超级管理员:管理员管理、用户管理、用水管理(用水记录、缴费提醒)、水费…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

Cursor实现用excel数据填充word模版的方法

cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...