目标检测笔记(九):详细介绍并实现可视化深度学习中每层特征层的网络训练情况

news2024/12/23 13:25:12

文章目录

  • 为什么要解析特征层
  • 如何可视化特征层
  • 可视化结果如何

❤️ 🧡 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 ❤️ 🧡 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝

为什么要解析特征层

在深度学习中,特征层是指神经网络中的一组层,在输入数据经过前几层后,将其分析和抽象为更高层次的特征表示。这些特征层对于网络的性能和训练结果有关键的影响。因此,在深度学习网络的训练过程中,对每一层特征层进行可视化和保存,可以帮助研究者更全面地了解网络内部的运作情况,并通过可视化结果的更新来调整网络的超参数和架构,从而提升网络的性能和训练效果。此外,特征层的可视化结果也可以帮助深度学习研究者和工程师更好地理解网络的决策过程和提高解释性。

如何可视化特征层

以ResNet系列为例,在网络训练过程中,我们找到网络定义的forward函数,如下面代码:

class ResNet(nn.Module):
    def __init__(self,
                 block,
                 blocks_num,
                 num_classes=10,  # 种类修改的地方,是几种就把这个改成几
                 include_top=True,
                 groups=1,
                 width_per_group=64):
        super(ResNet, self).__init__()
        self.include_top = include_top
        self.in_channel = 64
        self.groups = groups
        self.width_per_group = width_per_group
        self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,
                               padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_channel)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, blocks_num[0])
        self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)
        self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)
        self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)
        if self.include_top:
            self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)
            self.fc = nn.Linear(512 * block.expansion, num_classes)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

    def _make_layer(self, block, channel, block_num, stride=1):
        downsample = None
        if stride != 1 or self.in_channel != channel * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(channel * block.expansion))
        layers = []
        layers.append(block(self.in_channel,
                            channel,
                            downsample=downsample,
                            stride=stride,
                            groups=self.groups,
                            width_per_group=self.width_per_group))
        self.in_channel = channel * block.expansion
        for _ in range(1, block_num):
            layers.append(block(self.in_channel,
                                channel,
                                groups=self.groups,
                                width_per_group=self.width_per_group))
        return nn.Sequential(*layers)

    def forward(self, x):
       x = self.conv1(x)
       x = self.bn1(x)
       x = self.relu(x)
       x = self.maxpool(x)
       x = self.layer1(x)
       x = self.layer2(x)
       x = self.layer3(x)
       x = self.layer4(x)
       if self.include_top:
           x = self.avgpool(x)
           x = torch.flatten(x, 1)
           x = self.fc(x)
       return x

def resnet50(num_classes=1000, include_top=True, plot_fm=False, name=""):
    # https://download.pytorch.org/models/resnet50-19c8e357.pth
    return ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top, plot_fm=plot_fm, name=name)

这个代码展示了resnet网络整体架构
在训练阶段我们可以加载模型(创建优化器之前)

model = resnet50()
if pretrained:
    # 加载预训练权重文件
    #model.load_state_dict(torch.load('预训练权重文件路径'))
    pass
model.fc = nn.Linear(model.fc.in_features, num_classes)
criterion = nn.CrossEntropyLoss()

知道了整体网络架构之后,我们可以在网络结构中加入可视化特征的代码,如下所示

import time
import matplotlib.pyplot as plt
import numpy as np


def draw_features(width,height,x,savename):
    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, :, :]
        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)
    fig.clf()
    plt.close()
    print("time:{}".format(time.time()-tic))
class ResNet(nn.Module):
    def __init__(self,
                 block,
                 blocks_num,
                 num_classes=10,  # 种类修改的地方,是几种就把这个改成几
                 include_top=True,
                 groups=1,
                 width_per_group=64,
                 plot_fm=False,name=""): # plot_fm控制是否要绘制特征图,name控制保存文件的名字
        super(ResNet, self).__init__()
        self.plot_fm = plot_fm
        self.savepath='data/resultck/feature_map_save_{}'.format(name) # 创建保存特征图的位置
        os.makedirs(self.savepath, exist_ok=True)
        self.include_top = include_top
        self.in_channel = 64
        self.groups = groups
        self.width_per_group = width_per_group
        self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,
                               padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_channel)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, blocks_num[0])
        self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)
        self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)
        self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)
        if self.include_top:
            self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)
            self.fc = nn.Linear(512 * block.expansion, num_classes)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

    def _make_layer(self, block, channel, block_num, stride=1):
        downsample = None
        if stride != 1 or self.in_channel != channel * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(channel * block.expansion))
        layers = []
        layers.append(block(self.in_channel,
                            channel,
                            downsample=downsample,
                            stride=stride,
                            groups=self.groups,
                            width_per_group=self.width_per_group))
        self.in_channel = channel * block.expansion
        for _ in range(1, block_num):
            layers.append(block(self.in_channel,
                                channel,
                                groups=self.groups,
                                width_per_group=self.width_per_group))
        return nn.Sequential(*layers)

    def forward(self, x):
        if self.plot_fm:
            x = self.conv1(x)
            draw_features(8,8,x.cpu().detach().numpy(),"{}/f1_conv1.png".format(self.savepath)) # 绘制第一个卷积层所有通道的可视化情况
            print("{}/f1_conv1.png".format(self.savepath))
            x = self.bn1(x)
            draw_features(8, 8, x.cpu().detach().numpy(),"{}/f2_bn1.png".format(self.savepath))
            print("{}/f2_bn1.png".format(self.savepath))
            x = self.relu(x)
            draw_features(8, 8, x.cpu().detach().numpy(),"{}/f3_relu.png".format(self.savepath))
            print("{}/f3_relu.png".format(self.savepath))
            x = self.maxpool(x)
            draw_features(8, 8, x.cpu().detach().numpy(),"{}/f4_maxpool.png".format(self.savepath))
            print("{}/f4_maxpool.png".format(self.savepath))
            x = self.layer1(x)
            draw_features(16, 16, x.cpu().detach().numpy(), "{}/f5_layer1.png".format(self.savepath))
            print("{}/f5_layer1.png".format(self.savepath))
            x = self.layer2(x)
            draw_features(16, 32, x.cpu().detach().numpy(), "{}/f6_layer2.png".format(self.savepath))
            print("{}/f6_layer2.png".format(self.savepath))

            x = self.layer3(x)
            draw_features(32, 32, x.cpu().detach().numpy(), "{}/f7_layer3.png".format(self.savepath))
            print("{}/f7_layer3.png".format(self.savepath))

            x = self.layer4(x)
            draw_features(32, 32, x.cpu().detach().numpy()[:, 0:1024, :, :], "{}/f8_layer4.png".format(self.savepath))
            print("{}/f8_layer4.png".format(self.savepath))

            if self.include_top:
                x = self.avgpool(x)
                plt.plot(np.linspace(1, 2048, 2048), x.cpu().detach().numpy()[0, :, 0, 0])
                plt.savefig("{}/f9_avgpool.png".format(self.savepath))
                plt.clf()
                plt.close()
                x = torch.flatten(x, 1)
                x = self.fc(x)
        else:
            x = self.conv1(x)
            x = self.bn1(x)
            x = self.relu(x)
            x = self.maxpool(x)
            x = self.layer1(x)
            x = self.layer2(x)
            x = self.layer3(x)
            x = self.layer4(x)
            if self.include_top:
                x = self.avgpool(x)
                x = torch.flatten(x, 1)
                x = self.fc(x)
        return x
def resnet50(num_classes=1000, include_top=True, plot_fm=False, name=""):
    # https://download.pytorch.org/models/resnet50-19c8e357.pth
    return ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top, plot_fm=plot_fm, name=name)
if modelName == "resnet50":
    model = resnet50(plot_fm=plot_fm,name=modelName)
    if pretrained:
        # 加载预训练权重文件
        #model.load_state_dict(torch.load('预训练权重文件路径'))
        pass

注意
1. draw_features(8,8,x.cpu()…;这里的8,8意思是这里的通道数是64,也就是8*8。如果不清楚可以通过 print(x.cpu().detach().numpy().shape)查看
2. draw_features(32, 32, x.cpu().detach().numpy()[:, 0:1024, :, :], “{}/f8_layer4.png”.format(self.savepath))这里的1024表示最大通道数。

可视化结果如何

在这里插入图片描述
在这里插入图片描述
通过这样可视化,你可以清楚的看到每层特征的处理情况,以及对比不同网络下不同层的特征训练情况,通过对比可以发现哪种方法更有效。新的特征可视化情况会覆盖原始的特征可视化图,如果要在网页上实时显示,可以利用tensorboard工具来解决。可参考这篇博客:链接
非常感谢您的阅读!如果您觉得这篇文章对您有帮助,请点赞支持,您的支持是我写作的最大动力!同时,欢迎关注我的博客,我将持续分享更多深度学习、计算机视觉等方面的内容!
❤️ 🧡 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 ❤️ 🧡 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝

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

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

相关文章

SpringCloud(三)

文章目录 Eureka注册中心Eureka的结构和作用搭建eureka-server创建eureka-server服务引入eureka依赖编写启动类编写配置文件启动服务 服务注册1)引入依赖2)配置文件3)启动多个user-service实例 服务发现1)引入依赖2)配…

chatgpt赋能python:Python冒泡法排序:一种简单且高效的排序方法

Python 冒泡法排序:一种简单且高效的排序方法 在计算机科学中,排序算法是一种将给定数据集合重新排列为按照一定顺序排列的有序序列的方法。而冒泡排序算法是其中最简单、最基础的一种排序算法。 什么是冒泡排序? 冒泡排序,顾名…

代码随想录第49天

1.买卖股票的最佳时机&#xff1a; 贪心 因为股票就买卖一次&#xff0c;那么贪心的想法很自然就是取最左最小值&#xff0c;取最右最大值&#xff0c;那么得到的差值就是最大利润。 C代码如下&#xff1a; class Solution { public:int maxProfit(vector<int>& …

一键部署个人ChatGPT Web网站

一键部署个人ChatGPT Web网站 githubVercel使用自己的域名 本文将向大家介绍如何通过Github和Vercel这两个具,轻松搭建自己的ChatGPT Web网站&#xff0c;并且我们还可以添加密码保护以防止恶意滥用。 github 首先&#xff0c;我们需要拥有一个Github账号和Vercel账&#xff0…

【Set集合】概述和特点

Set集合概述和特点 Set集合概述 Set作为Collection集合的子接口&#xff0c;没有新增的功能&#xff0c;Collection集合可用的功能Set集合也可以用。 Set集合特点 Set集合存储的元素是无序的&#xff0c;而且不允许存储重复的元素&#xff0c;每当有新的元素存入的时候&#x…

操作系统复习3.2.1-虚拟内存

传统存储的问题 一次性&#xff1a;一次性装入作业才能运行 驻留性&#xff1a;不是所有部分都需要长时间存放在内存中 定义和特征 将要用的部分载入内存&#xff0c;不用的部分调出外存&#xff0c;逻辑上扩大内存 虚拟内存的最大容量为计算机的寻址范围决定 实际容量则为内…

【哈希值】概述和特点

哈希值概述和特点 哈希值概述 哈希值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值 Object类中有一个方法可以获取对象的哈希值&#xff1a; public int hashCode()&#xff1a;返回对象的哈希值 哈希值特点 同一个对象多次调用hashCode()方法返回的哈希值是相…

0x36transferData 数据传输

0x36transferData TransferData服务用于客户端将数据从客户端传输到服务器&#xff08;下载&#xff09;或从服务器传输到客户端&#xff08;上传&#xff09; 。 数据传输方向由前面的RequestDownload或RequestUpload服务定义。 如果客户端发起请求下载&#xff0c;则要下载的…

【01】STM32·HAL库开发-单片机简介 |用处、发展历程、发展趋势、CISC与RISC对比、冯诺依曼和哈佛结构对比

目录 1.单片机是什么&#xff08;了解&#xff09;2.单片机有什么用&#xff08;了解&#xff09;3.单片机发展历程&#xff08;了解&#xff09;4.单片机发展趋势&#xff08;了解&#xff09;5.CISC & RISC&#xff08;了解&#xff09;5.1CISC和RISC举例5.2冯诺依曼结构…

【C++】位图应用 | 布隆过滤器

文章目录 1. 位图应用题目一代码实现setrsettest具体代码 题目二位图优缺点总结 2. 布隆过滤器提出背景概念具体实现hash1 hash2 hash3N取值问题settsettset中在与不在那个准确&#xff1f;使用场景及特点具体代码 1. 位图应用 题目一 给40亿个不重复的无符号整数&#xff0c…

压缩感知重构之凸松弛法

算法的重构是压缩感知中重要的一步&#xff0c;是压缩感知的关键之处。因为重构算法关系着信号能否精确重建&#xff0c;国内外的研究学者致力于压缩感知的信号重建&#xff0c;并且取得了很大的进展&#xff0c;提出了很多的重构算法&#xff0c;每种算法都各有自己的优缺点&a…

Linux-地址空间

文章目录 问题引入操作系统宏观认识操作系统与进程程序地址空间进程地址空间问题解释 问题引入 在Linux操作系统中、vim编译器下&#xff0c;出现了变量同地址但不同值的现象。 下面以解释该现象产生的原因为主线&#xff0c;在过程中学习Linux操作系统的知识。 运行代码展示…

chatgpt赋能python:Python分词处理

Python分词处理 随着网络技术的飞速发展&#xff0c;搜索引擎已成为人们了解信息的主要渠道之一&#xff0c;而搜索引擎的核心是关键词匹配&#xff0c;因此分词技术在搜索引擎优化&#xff08;SEO&#xff09;中起着至关重要的作用。Python作为一种强大的编程语言&#xff0c…

【学习日记2023.6.3】之 工作台显示和报表导出

文章目录 12. 工作台显示和报表导出12.1 工作台12.1.1 需求分析和设计12.1.2 代码开发Controller层Service层接口Service层实现类Mapper层 12.1.3 功能测试12.1.4 提交代码 12.2 Apache POI12.2.1 介绍12.2.2 入门案例12.2.2.1 将数据写入Excel文件12.2.2.2 读取Excel文件中的数…

Linux4.7Nginx优化与防盗链

文章目录 计算机系统5G云计算第六章 LINUX Nginx优化与防盗链一、Nginx服务优化和深入优化1.隐藏版本号2.修改用户与组3.缓存时间4.日志切割5.连接超时6.更改进程数7.配置网页压缩8.配置防盗链9.fpm参数优化 计算机系统 5G云计算 第六章 LINUX Nginx优化与防盗链 一、Nginx服…

操作系统复习4.1.0-文件管理结构

定义 一组有意义的信息的集合 属性 文件名、标识符、类型、位置、大小、创建时间、上次修改时间、文件所有者信息、保护信息 操作系统向上提供的功能 创建文件、删除文件、读文件、写文件、打开文件、关闭文件 这6个都是系统调用 创建文件 创建文件时调用Create系统调用…

django中使用celery

Celery介绍&#xff1a; 核心及优点&#xff1a;1.基于分布式系统架构&#xff08;负载均衡避免单点故障&#xff0c;高可用&#xff09; 2.实现了异步任务的调度&#xff08;快速&#xff09; 只需要通过配置文件的修改就可以实现架构的切换所以灵活 django-celery-beat 用…

Oracle中的循环

目录 一、简单循环 1.1LOOP 循环语法&#xff1a; 1.2LOOP 循环示例 二、for循环 2.1for循环语法&#xff1a; 2.2for循环示例 三、while循环 3.1while循环语法 3.2while循环示例 四、GOTO 循环 4.1GOTO 循环语法 4.2GOTO 循环示例 在 Oracle 数据库中&#xff0c;…

储能之动力电池与储能电池区别?

储能之动力电池与储能电池区别 1、概念1.1 动力电池1.2 储能电池 2、应用场景3、动力电池与储能电池的对比3.1 性能要求3.2 循环次数3.3 电池类型方面3.4 成本结构不同 1、概念 1.1 动力电池 动力电池即为工具提供动力来源的电源&#xff0c;多指为电动汽车、电动列车、电动自…

Oracle中ORA-12560:协议适配器错误

平时在长时间未登录Oracle数据库&#xff0c;再次登录时会出现如下错误&#xff1a; 当Oracle登录时出现12560协议适配器错误时&#xff0c;可以通过以下步骤尝试启动相应的服务&#xff1a; 第一步&#xff1a; 打开本地【服务】&#xff0c;点击最顶层的名称输入【O】&…