利用梯度上升可视化卷积核:基于torch实现

news2025/1/11 14:47:37

利用梯度上升可视化卷积核

文章目录

  • 前言
      • 基本原理
      • 版本和包
      • 结果展示
  • 简单绘图
  • 修改源码绘图
      • 方法一
    • 方法二(推荐)
  • 报错解决
  • 总结


前言

基于梯度上升的可视化是一种常用的技术,用于理解卷积神经网络(CNN)中的卷积核是如何对输入图像进行特征提取的。该方法可以通过最大化卷积层输出的激活值来生成图像,从而使得卷积核对特定特征更加敏感。

基本原理

以下是基于梯度上升可视化卷积核的基本原理步骤:

  1. 选择目标卷积层和卷积核:首先,选择你希望可视化的目标卷积层和对应的卷积核。通常选择靠近网络顶部的卷积层,因为这些层对于更高级别的特征有更强的响应。
  2. 定义损失函数:为了最大化卷积层的输出激活值,需要定义一个损失函数。通常使用该卷积层的平均激活值作为损失函数。
  3. 随机生成图像:从随机噪声图像开始,作为初始输入图像。
  4. 前向传播:将生成的图像输入到CNN网络中,并进行前向传播,记录目标卷积层的输出激活值。
  5. 反向传播:计算损失函数对于输入图像的梯度,即目标卷积层输出激活值对于输入图像的影响。
  6. 更新输入图像:根据梯度信息更新输入图像,使得激活值增大。
  7. 重复步骤4-6:重复进行前向传播、反向传播和图像更新,直到达到预定的迭代次数或满足特定的停止条件。
  8. 可视化结果:最终生成的图像即为可视化结果,它代表了卷积核对于特定特征的响应模式。

通过这个过程,可以逐步调整输入图像,使得卷积核对于特定特征更加敏感。这种方法可以帮助我们理解卷积神经网络学习到的特征,以及卷积层如何对输入图像进行处理。


版本和包

python=3.7
torch=1.7
flashtorch=0.1.3

其中flashtorch就是用来求取中间层梯度上升的包

结果展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

简单绘图

官方代码如下所示:
(以vgg16为例子)

import torchvision.models as models
from flashtorch.activmax import GradientAscent

model = models.vgg16(pretrained=True)
g_ascent = GradientAscent(model.features)

# specify layer and filter info
conv5_1 = model.features[24]
conv5_1_filters = [45, 271, ]## 这里调节要展示的卷积核(绘出子图的个数),最大值是对应权重的通道数

vis = g_ascent.visualize(conv5_1, conv5_1_filters, title="VGG16: conv5_1",return_output=True)

以上代码,用vis即可画图。
vis =【output45,output271】的列表,每个都包含很多层,源代码是用的最后一层进行绘图(下图为源码截图):
在这里插入图片描述


修改源码绘图

方法一

由于官方代码画不出来图,所以修改from flashtorch.activmax import GradientAscent 的源码,把plt返回即可:
找到GradientAscent函数(gradient_ascent.py) 第211行
在这里插入图片描述
修改为:

if return_output:
    return self.output, self.plt

方法二(推荐)

最好的方法是:在同级目录下新建文件grad_as.py
直接把以下代码复制到grad_as.py里:

import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn as nn

from flashtorch.utils import (apply_transforms,
                              format_for_plotting,
                              load_image,
                              standardize_and_clip)


class GradientAscent:
    """Provides an interface for activation maximization via gradient descent.

    This class implements the gradient ascent algorithm in order to perform
    activation maximization with convolutional neural networks (CNN).

    `Activation maximization <https://pdfs.semanticscholar.org/65d9/94fb778a8d9e0f632659fb33a082949a50d3.pdf>`_
    is one form of feature visualization that allows us to visualize what CNN
    filters are "looking for", by applying each filter to an input image and
    updating the input image so as to maximize the activation of the filter of
    interest (i.e. treating it as a gradient ascent task with activation as the
    loss). The implementation is inspired by `this demo <https://blog.keras.io/category/demo.html>`_
    by Francois Chollet.

    Args:
        model: A neural network model from `torchvision.models
            <https://pytorch.org/docs/stable/torchvision/models.html>`_,
            typically without the fully-connected part of the network.
            e.g. torchvisions.alexnet(pretrained=True).features
        img_size (int, optional, default=224): The size of an input image to be
            optimized.
        lr (float, optional, default=1.): The step size (or learning rate) of
            the gradient ascent.
        use_gpu (bool, optional, default=False): Use GPU if set to True and
            `torch.cuda.is_available()`.

    """ # noqa

    ####################
    # Public interface #
    ####################

    def __init__(self, model, img_size=224, lr=1., use_gpu=False):
        self.model = model
        self._img_size = img_size
        self._lr = lr
        self._use_gpu = use_gpu

        self.num_layers = len(list(self.model.named_children()))
        self.activation = None
        self.gradients = None

        self.handlers = []

        self.output = None
        self.plt = None

    @property
    def lr(self):
        return self._lr

    @lr.setter
    def lr(self, lr):
        self._lr = lr

    @property
    def img_size(self):
        return self._img_size

    @img_size.setter
    def img_size(self, img_size):
        self._img_size = img_size

    @property
    def use_gpu(self):
        return self._use_gpu

    @use_gpu.setter
    def use_gpu(self, use_gpu):
        self._use_gpu = use_gpu

    def optimize(self, layer, filter_idx, input_=None, num_iter=30):
        """Generates an image that maximally activates the target filter.

        Args:
            layer (torch.nn.modules.conv.Conv2d): The target Conv2d layer from
                which the filter to be chosen, based on `filter_idx`.
            filter_idx (int): The index of the target filter.
            num_iter (int, optional, default=30): The number of iteration for
                the gradient ascent operation.

        Returns:
            output (list of torch.Tensor): With dimentions
                :math:`(num_iter, C, H, W)`. The size of the image is
                determined by `img_size` attribute which defaults to 224.

        """

        # Validate the type of the layer

        if type(layer) != nn.modules.conv.Conv2d:
            raise TypeError('The layer must be nn.modules.conv.Conv2d.')

        # Validate filter index

        num_total_filters = layer.out_channels
        self._validate_filter_idx(num_total_filters, filter_idx)

        # Inisialize input (as noise) if not provided

        if input_ is None:
            input_ = np.uint8(np.random.uniform(
                150, 180, (self._img_size, self._img_size, 3)))
            input_ = apply_transforms(input_, size=self._img_size)

        if torch.cuda.is_available() and self.use_gpu:
            self.model = self.model.to('cuda')
            input_ = input_.to('cuda')

        # Remove previous hooks if any

        while len(self.handlers) > 0:
            self.handlers.pop().remove()

        # Register hooks to record activation and gradients

        self.handlers.append(self._register_forward_hooks(layer, filter_idx))
        self.handlers.append(self._register_backward_hooks())

        # Inisialize gradients

        self.gradients = torch.zeros(input_.shape)

        # Optimize

        return self._ascent(input_, num_iter)

    def visualize(self, layer, filter_idxs=None, lr=1., num_iter=30,
                  num_subplots=4, figsize=(4, 4), title='Conv2d',
                  return_output=False):
        """Optimizes for the target layer/filter and visualizes the output.

        A method that combines optimization and visualization. There are
        mainly 3 types of operations, given a target layer:

        1. If `filter_idxs` is provided as an integer, it optimizes for the
            filter specified and plots the output.
        2. If `filter_idxs` is provided as a list of integers, it optimizes for
            all the filters specified and plots the output.
        3. if `filter_idx` is not provided, i.e. None, it randomly chooses
            `num_subplots` number of filters from the layer provided and
            plots the output.

        It also returns the output of the optimization, if specified with
        `return_output=True`.

        Args:
            layer (torch.nn.modules.conv.Conv2d): The target Conv2d layer from
                which the filter to be chosen, based on `filter_idx`.
            filter_idxs (int or list of int, optional, default=None): The index
                or indecies of the target filter(s).
            lr (float, optional, default=.1): The step size of optimization.
            num_iter (int, optional, default=30): The number of iteration for
                the gradient ascent operation.
            num_subplots (int, optional, default=4): The number of filters to
                optimize for and visualize. Relevant in case 3 above.
            figsize (tuple, optional, default=(4, 4)): The size of the plot.
                Relevant in case 1 above.
            title (str, optional default='Conv2d'): The title of the plot.
            return_output (bool, optional, default=False): Returns the
                output(s) of optimization if set to True.


        Returns:
            For a single optimization (i.e. case 1 above):
                output (list of torch.Tensor): With dimentions
                    :math:`(num_iter, C, H, W)`. The size of the image is
                    determined by `img_size` attribute which defaults to 224.
            For multiple optimization (i.e. case 2 or 3 above):
                output (list of list of torch.Tensor): With dimentions
                    :math:`(num_subplots, num_iter, C, H, W)`. The size of the
                    image is determined by `img_size` attribute which defaults
                    to 224.

        """

        self._lr = lr

        if (type(filter_idxs) == int):
            self._visualize_filter(layer,
                                   filter_idxs,
                                   num_iter=num_iter,
                                   figsize=figsize,
                                   title=title)
        else:
            num_total_filters = layer.out_channels

            if filter_idxs is None:
                num_subplots = min(num_total_filters, num_subplots)
                filter_idxs = np.random.choice(range(num_total_filters),
                                               size=num_subplots)

            self._visualize_filters(layer,
                                    filter_idxs,
                                    num_iter,
                                    len(filter_idxs),
                                    title=title)

        if return_output:
            return self.output, self.plt

    def deepdream(self, img_path, layer, filter_idx, lr=.1, num_iter=20,
                  figsize=(4, 4), title='DeepDream', return_output=False):
        """Creates DeepDream.

        It applies the optimization on the image provided. The image is loaded
        and made into a torch.Tensor that is compatible as the input to the
        network.

        Read the original blog post by Google for more information on
        `DeepDream <https://ai.googleblog.com/2015/06/inceptionism-going-deeper-into-neural.html>`_.

        Args:
            img_path (str): A path to the image you want to apply DeepDream on
            layer (torch.nn.modules.conv.Conv2d): The target Conv2d layer from
                which the filter to be chosen, based on `filter_idx`.
            filter_idx (int): The index of the target filter.
            lr (float, optional, default=.1): The step size of optimization.
            num_iter (int, optional, default=30): The number of iteration for
                the gradient ascent operation.
            figsize (tuple, optional, default=(4, 4)): The size of the plot.
                Relevant in case 1 above.
            title (str, optional default='Conv2d'): The title of the plot.
            return_output (bool, optional, default=False): Returns the
                output(s) of optimization if set to True.

        Returns:
            output (list of torch.Tensor): With dimentions
                :math:`(num_iter, C, H, W)`. The size of the image is
                determined by `img_size` attribute which defaults to 224.

        """ # noqa

        input_ = apply_transforms(load_image(img_path), self.img_size)

        self._lr = lr
        output = self.optimize(layer, filter_idx, input_, num_iter=num_iter)

        plt.figure(figsize=figsize)
        plt.axis('off')
        plt.title(title)

        plt.imshow(format_for_plotting(
            standardize_and_clip(output[-1],
                                 saturation=0.15,
                                 brightness=0.7))); # noqa

        if return_output:
            return output

    #####################
    # Private interface #
    #####################

    def _register_forward_hooks(self, layer, filter_idx):
        def _record_activation(module, input_, output):
            self.activation = torch.mean(output[:, filter_idx, :, :])

        return layer.register_forward_hook(_record_activation)

    def _register_backward_hooks(self):
        def _record_gradients(module, grad_in, grad_out):
            if self.gradients.shape == grad_in[0].shape:
                self.gradients = grad_in[0]

        for _, module in self.model.named_modules():
            if isinstance(module, nn.modules.conv.Conv2d) and \
                    module.in_channels == 3:
                return module.register_backward_hook(_record_gradients)

    def _ascent(self, x, num_iter):
        output = []

        for i in range(num_iter):
            self.model(x)

            self.activation.backward()

            self.gradients /= (torch.sqrt(torch.mean(
                torch.mul(self.gradients, self.gradients))) + 1e-5)

            x = x + self.gradients * self._lr
            output.append(x)

        return output

    def _validate_filter_idx(self, num_filters, filter_idx):
        if not np.issubdtype(type(filter_idx), np.integer):
            raise TypeError('Indecies must be integers.')
        elif (filter_idx < 0) or (filter_idx > num_filters):
            raise ValueError(f'Filter index must be between 0 and \
                             {num_filters - 1}.')

    def _visualize_filter(self, layer, filter_idx, num_iter, figsize, title):
        self.output = self.optimize(layer, filter_idx, num_iter=num_iter)

        plt.figure(figsize=figsize)
        plt.axis('off')
        plt.title(title)

        plt.imshow(format_for_plotting(
            standardize_and_clip(self.output[-1],
                                 saturation=0.15,
                                 brightness=0.7))); # noqa

    def _visualize_filters(self, layer, filter_idxs, num_iter, num_subplots,
                           title):
        # Prepare the main plot

        num_cols = 4
        num_rows = int(np.ceil(num_subplots / num_cols))

        fig = plt.figure(figsize=(16, num_rows * 5))
        plt.title(title)
        plt.axis('off')

        self.output = []

        # Plot subplots

        for i, filter_idx in enumerate(filter_idxs):
            output = self.optimize(layer, filter_idx, num_iter=num_iter)

            self.output.append(output)

            ax = fig.add_subplot(num_rows, num_cols, i+1)
            ax.set_xticks([])
            ax.set_yticks([])
            ax.set_title(f'filter {filter_idx}')

            ax.imshow(format_for_plotting(
                standardize_and_clip(output[-1],
                                     saturation=0.15,
                                     brightness=0.7)))

        plt.subplots_adjust(wspace=0, hspace=0); # noqa
        self.plt = plt```

然后使用以下代码绘图:

import torchvision.models as models
from grad_as import GradientAscent

model = models.vgg16(pretrained=True) # 这里可以改成其它模型

g_ascent = GradientAscent(model.features)


# specify layer and filter info
conv5_1 = model.features[24]  # 相应的这里也要改,可以用print(model)代码查看模型结构进行修改
conv5_1_filters = [45, 271, ]

vis,plt = g_ascent.visualize(conv5_1, conv5_1_filters, title="VGG16: conv5_1",return_output=True)

plt.show()



报错解决

报错如下

from importlib_resources import path
ImportError: cannot import name 'path' from 'importlib_resources'

找到报错处的源文件,尝试在代码中手动导入 path,而不是从 importlib_resources 直接导入:

from importlib_resources import path

替换为:

from importlib_resources import files

def path(package, resource):
    return files(package).joinpath(resource)

这样,代码将使用 files 函数来导入 path,而不是直接从 importlib_resources 中导入。


总结

以上代码均经过本人亲测可用。

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

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

相关文章

Kali常用配置(持续更新)

1. 同步系统时间 命令&#xff1a;dpkg-reconfigure tzdata &#xff0c;这个命令可以同时更新系统时间和硬件时间。 然后选择区域和城市&#xff0c;中国可以先选择Asia&#xff0c;然后选择Shanghai 2.更换系统数据源 # vim /etc/apt/sources.list #不是root用户的话需要…

ElasticSearch文档分析

ElasticSearch文档分析 包含下面的过程&#xff1a; 将一块文本分成适合于倒排索引的独立的 词条将这些词条统一化为标准格式以提高它们的“可搜索性”&#xff0c;或者 recall 分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里&#xff1a; 字符过滤器 首先&a…

PMCW体制雷达系列文章(1) – PMCW体制雷达综述

说明 相位调制连续波(Phase-modulated continuous wave, PMCW)雷达&#xff0c;或又被称为数字雷达&#xff0c;近年来开始被应用于汽车雷达领域。而且因其特有的一些优势(精度高、抗干扰能力强等)被认为是车载毫米波雷达的发展趋势之一(从目前占主导的调频连续波(Frequency-mo…

美国材料与试验协会ASTM发布新版玩具安全标准 ASTM F963-23

美国材料与试验协会ASTM发布新版玩具安全标准 ASTM F963-23 2023年10月13日&#xff0c;美国材料与试验协会&#xff08;ASTM&#xff09;发布了新版玩具安全标准ASTM F963-23 ​根据CPSIA的规定&#xff0c;当ASTM将ASTM F963的拟定修订意见通知CPSC时&#xff0c;若CPSC认为…

QRadioButton、QCheckBox样式表

QRadioButton、QCheckBox样式表 实现效果Chapter1 QRadioButton样式表详细描述示例效果源码样式表 Chapter2 QRadioButton样式表 实现效果 QRadioButton{spacing: 2px;color: white; } QRadioButton::indicator {width: 60px;height: 35px; } QRadioButton::indicator:unchecke…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(三)

员工分页查询和账号启用禁用功能 1. 员工分页查询1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计 1.2 代码开发1.2.1 设计DTO类1.2.2 封装PageResult1.2.3 Controller层1.2.4 Service层接口1.2.5 Service层实现类1.2.6 Mapper层 1.3 功能测试1.4 代码完善 2. 启用禁用员工账号…

《QT从基础到进阶·二十》QThreadPool线程池的使用

什么情况下比较适合用线程池&#xff1f; 比如我有上百个任务要同时处理&#xff0c;难道开上百个线程&#xff1f;NO&#xff01;&#xff01;&#xff01; 有了线程池的加持&#xff0c;自动给任务分配线程处理&#xff0c; 多线程不再是真爱~ 线程池创建&#xff1a; 1、自…

CAS、Synchronized 原理

什么是CASCAS应用原子类自旋锁CAS的ABA问题 Synchronized 原理锁升级优化锁消除优化锁粗化优化 什么是CAS 什么是CAS&#xff1f;Compare and swap &#xff1a;比较和交换 一个CAS操作涉及&#xff1a; 我们假设内存中的原数据V&#xff0c;旧的预期值A&#xff0c;需要修改…

第六十一周周报

学习目标&#xff1a; 代码 实验 论文 学习时间&#xff1a; 2023.11.4-2023.11.10 学习产出&#xff1a; 代码 1、修改CelebA64数据集的代码&#xff0c;实验暂时没跑完 2、添加CB模块&#xff0c;实验暂时没跑完 3、修改ViTGAN的CIPS Generator位置编码为傅里叶编码 …

【LeetCode笔试题】26.删除有序数组中的重复项

问题描述 给你一个非严格递增排列的数组nums&#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持一致。然后返回nums中唯一元素的个数。 考虑nums的唯一元素的数量为k&#xff0c;你需要…

FPGA与STM32_FSMC总线通信实验

FPGA与STM32_FSMC总线通信实验 内部存储器IP核的参数设置创建IP核FPGA代码STM32标准库的程序 STM32F407 上自带 FSMC 控制器&#xff0c;通过 FSMC 总线的地址复用模式实现STM32 与 FPGA 之间的通信&#xff0c;FPGA 内部建立 RAM 块&#xff0c;FPGA 桥接 STM32 和 RAM 块&…

面向对象--------三巨头

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ ა 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶个人主页&am…

后台管理系统实用提示框,JavaScript实现(成功,失败,提示弹窗)

本篇就给大家分享一下超级好用的JavaScript提示框&#xff0c;使其开发中节省大量代码&#xff01;&#xff01;&#xff01; 由于本篇运用到了jQuery技术&#xff0c;所以在写之前一定记得引入jQuery库 目录 首先呢我们需要创建html元素 设置css样式&#xff0c;直接引入…

【华为OD:C++机试】Day-4

目录 &#x1f337;1. 排队游戏&#xff1a; &#x1f337;2. 购物&#xff1a; &#x1f337;3. 划分字符串&#xff1a; &#x1f337;4. MELON 的难题&#xff1a; &#x1f337;5. 荒岛求生&#xff1a; &#x1f337;6. 通过软盘拷贝文件&#xff1a; &#x1f337;7. 数字…

基于springboot乐器视频学习网站设计与实现(源码齐全可用)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

Linux常用命令——bzmore命令

在线Linux命令查询工具 bzmore 查看bzip2压缩过的文本文件的内容 补充说明 bzmore命令用于查看bzip2压缩过的文本文件的内容&#xff0c;当下一屏显示不下时可以实现分屏显示。 语法 bzmore(参数)参数 文件&#xff1a;指定要分屏显示的.bz2压缩包。 在线Linux命令查询…

使用JavaScript编写游戏平台数据爬虫程序

目录 一、引言 二、准备工作 三、爬取数据 四、数据处理与存储 五、数据分析与利用 六、结论与展望 一、引言 随着网络技术的发展&#xff0c;数据已经成为企业、研究机构和个人的重要资源。数据可以帮助我们了解市场趋势、用户需求&#xff0c;甚至可以用于机器学习和人…

代码随想录图论部分-695. 岛屿的最大面积|1020. 飞地的数量

695. 岛屿的最大面积 题目&#xff1a;给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0&#xff08;代表水&#xff0…

什么是安全平行切面

安全平行切面的定义 通过嵌入在端—管—云内部的各层次切点&#xff0c;使得安全管控与业务逻辑解耦&#xff0c;并通过标准化的接口为安全业务提供内视和干预能力的安全基础设施。安全平行切面是一种创新的安全体系思想&#xff0c;是实现“原生安全”的一条可行路径。 为什…

Milvus Cloud——LLM Agent 现阶段出现的问题

LLM Agent 现阶段出现的问题 由于一些 LLM&#xff08;GPT-4&#xff09;带来了惊人的自然语言理解和生成能力&#xff0c;并且能处理非常复杂的任务&#xff0c;一度让 LLM Agent 成为满足人们对科幻电影所有憧憬的最终答案。但是在实际使用过程中&#xff0c;大家逐渐发现了通…