YOLOv5与ViT目标检测中的热力图应用教程

news2025/2/27 14:25:30

文章目录

  • 前言
  • 一、热力图介绍
    • 1、热力图应用说明
    • 2、热力图代码整体思路
    • 3、实验效果
  • 二、heatmap类解读
  • 三、GradCAM、GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM, HiResCAM, LayerCAM等类源码解读
    • 1、GradCAM类源码
    • 2、BaseCAM类源码解读
      • 1、BaseCAM源码
      • 2、forward函数源码解读
        • outputs = self.activations_and_grads(input_tensor)方法
        • targets方法解读
        • 梯度求解源码解读(uses_gradients)
        • 梯度cam计算方法
  • 四、ActivationsAndGradients源码解读
  • 五、yolo与vit的gradcam求解代码
    • 1、computer_heatmap代码说明
    • 2、数据处理与模型加载
    • 3、热力图获取
    • 4、热力图与原图绘制
    • 5、其它
    • 6、ActivationsAndGradients方法修改
  • 六、完整源码
    • yolov5热力图可能报错问题

前言

在计算机视觉领域,理解深度学习模型如YOLOv5和Vision Transformers (ViT)如何进行目标检测至关重要。热力图作为一种强大的可视化工具,通过颜色编码的方式直观展示了模型对图像各部分的关注度,帮助我们洞察模型的决策过程。正好,我有一个transformer与cnn结合的网络,我就介绍基于CNN与transformer结构网络的热力图。本文章将介绍如何构建热力图以及实现细节等内容。

一、热力图介绍

1、热力图应用说明

深度学习热力图是一种可视化工具,它通过高亮显示输入数据中对模型预测结果贡献较大的区域,帮助我们理解复杂的深度学习模型是如何做出决策的。例如,在图像分类任务中,热力图可以指出图片的哪些部分促使模型选择了特定的类别标签。生成热力图常用的方法包括梯度加权类激活映射(Grad-CAM),该方法计算特定类别相对于卷积神经网络中特征图的梯度,以确定图像中重要区域的位置。此外,还有特征图可视化、反向传播方法、遮挡实验以及像LIME和SHAP这样的解释性框架。这些技术不仅增强了模型的透明度和可解释性,还为调试模型和改进算法提供了宝贵的见解。通过利用热力图,研究人员和工程师能够更好地理解和优化他们的深度学习系统。

2、热力图代码整体思路

我是将vit结构融合到了yolov5模型中,正好我需要写一个热力图实验。基于此,我将在本次热力图解读中穿插了yolov5条件热力图以及融合了cnn与transformer模型条件的热力图内容。

3、实验效果

先看实现效果。

在这里插入图片描述

二、heatmap类解读

我使用一个类来实现热力图构建,主要包含模型加载方法self.model,目标self.target = yolov5_target(opt.backward_mode, opt.conf_thr),层提取self.target_layers = [self.model.model[l] for l in opt.layer]与热力图初始化self.cam_model = GradHeat(self.model, self.target_layers) 。以下为初始化代码,如下:

class yolov5_heatmap:
   def __init__(self, opt):
       # weight, device, method, layer, backward_type, conf_threshold, ratio, show_box, renormalize
       # self.conf_threshold, self.ratio, self.show_box, self.renormalize = conf_threshold, ratio, show_box, renormalize
       self.opt=opt
       self.device = torch.device(opt.device)
       if opt.model_mode=='yolo':
           self.model = init_model(opt.weights,self.device)  # 载入模型
       else :
           self.model = init_model_lvf(opt.weights,self.device)  # 载入模型
       self.model_names = self.model.names  # 类别名称
       for p in self.model.parameters():
           p.requires_grad_(True)
       self.model.eval()

       self.target = yolov5_target(opt.backward_mode, opt.conf_thr)  # 对获得值进行加工,使其成为标量 

       self.target_layers = [self.model.model[l] for l in opt.layer]
 
       self.cam_model = GradHeat(self.model, self.target_layers) # 这个就是热力图初始化方法
       self.cam_model.activations_and_grads = ActivationsAndGradients(self.model, self.target_layers, None,opt.model_mode)  # 这个是重构热力图类中的激活方法
       self.colors = np.random.uniform(0, 255, size=(len(self.model_names), 3)).astype(np.int)  # 获得每个不同类的颜色
  

我大致说下每个内容作用是什么。目标self.target是获得模型输出值,层提取self.target_layers需要查看热力图的层、这个需要activatins激活方法获得激活值、而热力图self.cam_model就是帮忙实现的了,这个热力图模型我是继承了gradcam的热力图,讲解一个来说明热力图方法。

三、GradCAM、GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM, HiResCAM, LayerCAM等类源码解读

我以GradCAM方法来做热力图求解源码解读。实际上,热力图就是需要你想查看层的激活值和梯度,而激活值是随着模型前向传播获得,而梯度是后向传播获得。

1、GradCAM类源码

我们看到GradCAM源码是继承了BaseCAM类,而上面类基本也是基于这个基类。我们不用关心这些东西,我们继续查看BaseCAM类。

import numpy as np
from pytorch_grad_cam.base_cam import BaseCAM
class GradCAM(BaseCAM):
    def __init__(self, model, target_layers, use_cuda=False,
                 reshape_transform=None):
        super(
            GradCAM,
            self).__init__(
            model,
            target_layers,
            use_cuda,
            reshape_transform)

    def get_cam_weights(self,
                        input_tensor,
                        target_layer,
                        target_category,
                        activations,
                        grads):
        return np.mean(grads, axis=(2, 3))

2、BaseCAM类源码解读

1、BaseCAM源码

我直接给出BaseCAM源码,而我们也不需要关心,我们需要对forward内容感兴趣。我后面解读这个内容。同时,我们不得不关注初始化的这个方法self.activations_and_grads = ActivationsAndGradients( self.model, target_layers, reshape_transform),这个也很重要,我后面也会解读这个。

import numpy as np
import torch
import ttach as tta
from typing import Callable, List, Tuple
from pytorch_grad_cam.activations_and_gradients import ActivationsAndGradients
from pytorch_grad_cam.utils.svd_on_activations import get_2d_projection
from pytorch_grad_cam.utils.image import scale_cam_image
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget


class BaseCAM:
    def __init__(self,
                 model: torch.nn.Module,
                 target_layers: List[torch.nn.Module],
                 use_cuda: bool = False,
                 reshape_transform: Callable = None,
                 compute_input_gradient: bool = False,
                 uses_gradients: bool = True) -> None:
        self.model = model.eval()
        self.target_layers = target_layers
        self.cuda = use_cuda
        if self.cuda:
            self.model = model.cuda()
        self.reshape_transform = reshape_transform
        self.compute_input_gradient = compute_input_gradient
        self.uses_gradients = uses_gradients
        self.activations_and_grads = ActivationsAndGradients(
            self.model, target_layers, reshape_transform)

    """ Get a vector of weights for every channel in the target layer.
        Methods that return weights channels,
        will typically need to only implement this function. """

    def get_cam_weights(self,
                        input_tensor: torch.Tensor,
                        target_layers: List[torch.nn.Module],
                        targets: List[torch.nn.Module],
                        activations: torch.Tensor,
                        grads: torch.Tensor) -> np.ndarray:
        raise Exception("Not Implemented")

    def get_cam_image(self,
                      input_tensor: torch.Tensor,
                      target_layer: torch.nn.Module,
                      targets: List[torch.nn.Module],
                      activations: torch.Tensor,
                      grads: torch.Tensor,
                      eigen_smooth: bool = False) -> np.ndarray:

        weights = self.get_cam_weights(input_tensor,
                                       target_layer,
                                       targets,
                                       activations,
                                       grads)
        weighted_activations = weights[:, :, None, None] * activations
        if eigen_smooth:
            cam = get_2d_projection(weighted_activations)
        else:
            cam = weighted_activations.sum(axis=1)
        return cam

    def forward(self,
                input_tensor: torch.Tensor,
                targets: List[torch.nn.Module],
                eigen_smooth: bool = False) -> np.ndarray:

        if self.cuda:
            input_tensor = input_tensor.cuda()

        if self.compute_input_gradient:
            input_tensor = torch.autograd.Variable(input_tensor,
                                                   requires_grad=True)

        outputs = self.activations_and_grads(input_tensor)
        if targets is None:
            target_categories = np.argmax(outputs.cpu().data.numpy(), axis=-1)
            targets = [ClassifierOutputTarget(
                category) for category in target_categories]

        if self.uses_gradients:
            self.model.zero_grad()
            loss = sum([target(output)
                       for target, output in zip(targets, outputs)])
            loss.backward(retain_graph=True)

        # In most of the saliency attribution papers, the saliency is
        # computed with a single target layer.
        # Commonly it is the last convolutional layer.
        # Here we support passing a list with multiple target layers.
        # It will compute the saliency image for every image,
        # and then aggregate them (with a default mean aggregation).
        # This gives you more flexibility in case you just want to
        # use all conv layers for example, all Batchnorm layers,
        # or something else.
        cam_per_layer = self.compute_cam_per_layer(input_tensor,
                                                   targets,
                                                   eigen_smooth)
        return self.aggregate_multi_layers(cam_per_layer)

    def get_target_width_height(self,
                                input_tensor: torch.Tensor) -> Tuple[int, int]:
        width, height = input_tensor.size(-1), input_tensor.size(-2)
        return width, height

    def compute_cam_per_layer(
            self,
            input_tensor: torch.Tensor,
            targets: List[torch.nn.Module],
            eigen_smooth: bool) -> np.ndarray:
        activations_list = [a.cpu().data.numpy()
                            for a in self.activations_and_grads.activations]
        grads_list = [g.cpu().data.numpy()
                      for g in self.activations_and_grads.gradients]
        target_size = self.get_target_width_height(input_tensor)

        cam_per_target_layer = []
        # Loop over the saliency image from every layer
        for i in range(len(self.target_layers)):
            target_layer = self.target_layers[i]
            layer_activations = None
            layer_grads = None
            if i < len(activations_list):
                layer_activations = activations_list[i]
            if i < len(grads_list):
                layer_grads = grads_list[i]

            cam = self.get_cam_image(input_tensor,
                                     target_layer,
                                     targets,
                                     layer_activations,
                                     layer_grads,
                                     eigen_smooth)
            cam = np.maximum(cam, 0)
            scaled = scale_cam_image(cam, target_size)
            cam_per_target_layer.append(scaled[:, None, :])

        return cam_per_target_layer

    def aggregate_multi_layers(
            self,
            cam_per_target_layer: np.ndarray) -> np.ndarray:
        cam_per_target_layer = np.concatenate(cam_per_target_layer, axis=1)
        cam_per_target_layer = np.maximum(cam_per_target_layer, 0)
        result = np.mean(cam_per_target_layer, axis=1)
        return scale_cam_image(result)

    def forward_augmentation_smoothing(self,
                                       input_tensor: torch.Tensor,
                                       targets: List[torch.nn.Module],
                                       eigen_smooth: bool = False) -> np.ndarray:
        transforms = tta.Compose(
            [
                tta.HorizontalFlip(),
                tta.Multiply(factors=[0.9, 1, 1.1]),
            ]
        )
        cams = []
        for transform in transforms:
            augmented_tensor = transform.augment_image(input_tensor)
            cam = self.forward(augmented_tensor,
                               targets,
                               eigen_smooth)

            # The ttach library expects a tensor of size BxCxHxW
            cam = cam[:, None, :, :]
            cam = torch.from_numpy(cam)
            cam = transform.deaugment_mask(cam)

            # Back to numpy float32, HxW
            cam = cam.numpy()
            cam = cam[:, 0, :, :]
            cams.append(cam)

        cam = np.mean(np.float32(cams), axis=0)
        return cam

    def __call__(self,
                 input_tensor: torch.Tensor,
                 targets: List[torch.nn.Module] = None,
                 aug_smooth: bool = False,
                 eigen_smooth: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

组织病理学图像的再识别|文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 Re-identification from histopathology images 组织病理学图像的再识别 01 文献速递介绍 在光学显微镜下评估苏木精和伊红&#xff08;H&E&#xff09;染色切片是肿瘤病理诊断的标准程序。随着全切片扫描仪的出现&#xff0c;能够将玻璃切片数字化为所谓的…

如何用重构解锁高效 Vue 开发之路

文章目录 摘要引言什么是代码重构为什么要减少重复逻辑Vue 示例代码问题场景初始代码的痛点重构后的通用组件 TaskList.vue详细说明 重用通用组件详细说明 模拟数据与运行结果 QA环节总结参考资料 摘要 代码重构是改善代码质量的重要手段&#xff0c;特别是在减少重复逻辑方面…

用户发送请求后服务端i/o工作过程

华子目录 服务端i/o介绍磁盘i/o机械磁盘的寻道时间、旋转延迟和数据传输时间常见的机械磁盘平均寻道时间值常见磁盘的平均延迟时间每秒最大IOPS的计算方法 网络i/o网络I/O处理过程磁盘和网络i/o 一次完整的请求在内部的执行过程 服务端i/o介绍 i/o在计算机中指Input/Output&am…

QT c++ 测控系统 一套报警规则(上)

本文适用于pc based的测控系统的上位机&#xff0c;定义了一套报警规则。 由5个部分组成&#xff1a;自定义4布尔类、在全局文件定义工位错误结构体和结构体变量&#xff0c;其它地方给此变量的当前值成员赋值&#xff0c;报警线程类、数据库保存类、弹框类。 1.自定义4布尔类…

概率论得学习和整理24:EXCEL的各种图形,统计图形

目录 0 EXCEL的各种图形&#xff0c;统计图形 1 统计图形 / 直方图 / 其实叫 频度图 hist最合适(用原始数据直接作图) 1.1 什么是频度图 1.2 如何创建频度图&#xff0c;一般是只选中1列数据&#xff08;1个数组&#xff09; 1.3 如何修改频度图的宽度 1.4 hist图的一个特…

项目二十三:电阻测量(需要简单的外围检测电路,将电阻转换为电压)测量100,1k,4.7k,10k,20k的电阻阻值,由数码管显示。要求测试误差 <10%

资料查找&#xff1a; 01 方案选择 使用单片机测量电阻有多种方法&#xff0c;以下是一些常见的方法及其原理&#xff1a; 串联分压法&#xff08;ADC&#xff09; 原理&#xff1a;根据串联电路的分压原理&#xff0c;通过测量已知电阻和待测电阻上的电压&#xff0c;计算出…

Linux中 vim 常用命令大全详细讲解

文章目录 前言一、Vim 基本操作 &#x1f579;️1.1 打开或创建1.2 退出编辑1.3 模式切换 二、Vim 光标移动命令 ↕️2.1 基本移动2.2 行内移动2.3. 单词移动2.4. 页面移动2.5. 行跳转 三、Vim 文本编辑命令 &#x1f4cb;3.1 插入和删除3.2 复制、剪切与粘贴3.3 替换与修改 四…

ARM架构服务器国产麒麟V10安装nginx

目前ARM架构服务器越来越多的出现在我们的工作中&#xff0c;尤其大数据时代的需要&#xff0c;服务器操作系统linux国产化进程的推进。本人已经编写了很多ARM架构下安装java环境&#xff0c;安装mysql&#xff0c;安装redis等等的文档。现在我们演示一下国产麒麟V10安装nginx-…

【SQL】语句练习

1. 更新 1.1单表更新 例1: 所有薪水低于30000的员工薪水增加10% SQL命令&#xff1a; update employee set salarysalary*1.1 where salary < 30000; 1.2多表更新 例1: 将下图两表张三的语文成绩从95修改为80 SQL命令&#xff1a; update exam set score80 where subjec…

【开源】使用环信UIKit for uniapp 做一个IM即时聊天应用

环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界面、联系人列表及后续界面等功能&#xff0c;帮助开发者根据实际业务需求快速搭建包含 UI 界面的即时通讯应用。 本文教大家使用环信 uniapp UIKit 快…

用 Python Turtle 绘制经典杰瑞鼠:捕捉卡通世界中的小聪明

用 Python Turtle 绘制经典杰瑞鼠&#xff1a;捕捉卡通世界中的小聪明 &#x1f438; 前言 &#x1f438;&#x1f41e;往期绘画>>点击进所有绘画&#x1f41e;&#x1f40b; 效果图 &#x1f40b;&#x1f409; 代码 &#x1f409; &#x1f438; 前言 &#x1f438; 杰…

Excel拆分脚本

Excel拆分 工作表按行拆分为工作薄 工作表按行拆分为工作薄 打开要拆分的Excel文件&#xff0c;使用快捷键&#xff08;AltF11&#xff09;打开脚本界面&#xff0c;选择要拆分的sheet&#xff0c;打开Module&#xff0c;在Module中输入脚本代码&#xff0c;然后运行脚本 Su…

ModStartCMS v9.1.0 数据Grid样式优化,富文本格式刷支持,精简代码

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

2024年12月16日Github流行趋势

项目名称&#xff1a;PDFMathTranslate 项目维护者&#xff1a;Byaidu reycn hellofinch Wybxc YadominJinta项目介绍&#xff1a;基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&#xff0c;提供 CLI/GUI/Docker。项目star数…

3-机器人视觉-机器人抓取与操作

文章目录 3机器人视觉目录 1. 传感器和标定摄像头模型Intrinsic MatrixExtrinsic Matrix 标定内参标定手眼标定和外参标定 力传感器&其它传感器其它传感器 2. 神经网络和图像处理2D特征处理常见架构 训练流程推理流程部署流程2D 图像任务3D Point Cloud FeaturePointNet Ap…

Java String详解(二)

上一篇博客&#xff1a;Java String详解&#xff08;一&#xff09; 写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blo…

Qt之点击鼠标右键创建菜单栏使用(六)

Qt开发 系列文章 - menu&#xff08;六&#xff09; 目录 前言 一、示例演示 二、菜单栏 1.MenuBar 2.Menu 总结 前言 QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08;menubar&#xff09;、多个工具栏(toolbars)、一个状态栏(status…

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(一)

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(一) 你好&#xff0c;我是拉依达。 感谢所有阅读关注我的同学支持&#xff0c;目前博客累计阅读 27w&#xff0c;关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析&#xff08;持续更新&#xff09;-CSDN博客》已经是 Lin…

IntelliJ IDEA(2024版) 的安装、配置与使用教程:常用配置、创建工程等操作(很详细,你想要的都在这里)

IDEA的安装、配置与使用&#xff1a; Ⅰ、IDEA 的安装&#xff1a;1、IDEA 的下载地址(官网)&#xff1a;2、IDEA 分为两个版本&#xff1a;旗舰版 (Ultimate) 和 社区版 (Community)其一、两个不同版本的安装文件&#xff1a;其二、两个不同版本的详细对比&#xff1a; 3、IDE…

MybatisPlus-配置加密

配置加密 目前配置文件中的很多参数都是明文&#xff0c;如果开发人员发生流动&#xff0c;很容易导致敏感信息的泄露。所以MybatisPlus支持配置文件的加密和解密功能。 我们以数据库的用户名和密码为例。 生成秘钥 首先&#xff0c;我们利用AES工具生成一个随机秘钥&#…