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

news2024/11/28 15:58:41

文章目录

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

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

为什么要解析特征层

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

如何可视化特征层

以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/590313.html

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

相关文章

随笔-学会和解

上周六,媳妇儿去加班,回到家已经是晚上8点多了。当天的雨淅沥沥地下了一天,气温很低。 看着她情绪不是很高,也没说啥,赶紧安排吃饭。 我:咋的啦,项目不顺利? 她:还行吧…

【深度学习】【人脸检测模型】SCRFD模型的训练与部署实战

文章目录 Linux安装环境pythoninsightface环境 训练数据集准备todo 训练 Linux安装环境 python 我的cuda版本11.6: $ nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2022 NVIDIA Corporation Built on Tue_Mar__8_18:18:20_PST_2022…

gitlab占用内存太大了如何解决?

大家好,我是雄雄,欢迎关注微信公众号雄雄的小课堂 现在是:2023年5月30日16:58:15 最近在家里自己搞了个服务器,因为这台机器都不用了,从朋友那拿过来,就当服务器用了,看了下,比云服…

python基本操作2(速通版)

关于字符串的基本操作,以实例为主。 目录 一、字符串基本操作 1.基本字符串定义 2.字符串遍历 3字符串切片 二、字符串的常用方法 1.find函数 2.replace函数 3.count函数 4.分割和连接类的函数 5.字符串判断函数 6.去除字符串的 三、元组 1.元组的基本操…

离散数学-集合论

数学基础-离散数学-集合论 集合论是现代各科数学的基础,它起源于十六世纪末期的数集的研究。直到1876-1883年,康托尔发表了一系列有关集合论的文章,奠定了集合论的基础。1904-1908年,策墨罗(Zermelo)提出了集合论的公理系统&…

计算机组成原理-总线-总线的概念、事务和定时

目录 一、总线基本概念 总线特性 二、总线的分类 数据传输格式 2.2按总线的功能 2.2.1片内总线 2.2.2系统总线 2.2.3通信总线 2.3 按时序控制方式 三、系统总线的结构 3.1单总线结构 3.2双总线结构 3.3三总线结构 四、总线的性能指标 五、总线的4个阶段 六、总线的事…

5年测试经验,从月薪9k变1w5,进阶自动化测试真就这么香?

在这个吃技术的IT行业来说,我之前每天做的是最基础的工作,但是随着时间的消磨,我产生了对自我和岗位价值和意义的困惑。 一是感觉自己在浪费时间,另一个就是做了快2年的测试,感觉每天过得浑浑噩噩,薪资也从…

Java --- 云尚办公之微信公众号整合

目录 一、整合微信公众号 1.1、公众号菜单管理 1.2、微信授权登录 1.3、消息推送 一、整合微信公众号 1.1、公众号菜单管理 数据库表: CREATE TABLE wechat_menu (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 编号,parent_id BIGINT(20) DEFAULT NULL CO…

day42|动态规划4-背包问题分割等和子集

0-1背包和完全背包搞清楚即可。 0-1背包问题-一维 背包有最大重量的限制,物品有重量有价值,那么在最大背包的限制下,能够得到的最大价值是多少? 暴力解法 每个物品都有放和不放两种状态,那么遍历所有的组合就可以…

谷歌浏览器 | Chrome DevTools系统学习篇- Elements面板(上)

大家好,文接上上回谷歌浏览器 | Chrome DevTools系统学习篇-概述,和上文谷歌浏览器 | Chrome DevTools系统学习篇-Device Mode。所谓“工欲善其事,必先利其器”,我们进一步来熟悉谷歌开发者工具。今天分享的是Elements元素&#x…

我在亚马逊云平台的学习成长之路

前言 今年是亚马逊云科技成立的第16个年头,也是云计算行业诞生的16周年。作为云计算时代的“领头羊”,亚马逊2006年推出了名为Amazon Web Services(AWS)的新产品。AWS背后的想法是提供一个平台,同亚马逊内部使用的平台一样,将其作…

Three.js--》实现3d地月模型展示

目录 项目搭建 初始化three.js基础代码 创建月球模型 添加地球模型 添加模型标签 今天简单实现一个three.js的小Demo,加强自己对three知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。 项目搭建 本案例还…

230530-论文整理-课题组2

对这些研究有点兴趣颇微。 文章目录 Rethinking Dense Retrieval’s Few-Shot AbilityDecoder-Only or Encoder-Decoder? Interpreting Language Model as a Regularized Encoder-DecoderPLOME: Pre-training with Misspelled Knowledge for Chinese Spelling CorrectionRead…

北邮22信通:复习补充:双向链表的实现

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏: 北邮22信通_青山如墨雨如画的博客-CSDN博客 **说明** 最近复习看到书后有双向链表的题目,编出来供大家…

Mybatis-Plus 进阶开发-自定义乐观锁插件

文章目录 前言0. OptimisticLockerInnerInterceptor 介绍1. Mybatis-plus 实现乐观锁的原理2. 自定义乐观锁插件1. 创建自定义乐观锁插件2. 配置自定义乐观锁插件 3. 总结 👏作者简介:大家好,我是冰点,从业11年,目前在…

jmeter 性能测试工具的使用(Web性能测试)

1、下载 2023Jmeter性能测试项目实战教程,十年测试大佬手把手教你做性能!_哔哩哔哩_bilibili2023Jmeter性能测试项目实战教程,十年测试大佬手把手教你做性能!共计11条视频,包括:1.什么是性能测试以及性能测…

爬虫进阶-反爬破解1(反爬技术简介、HTTP网络基础知识、搭建代理服务)

目录 一、反爬技术简介 二、HTTP网络基础知识 三、搭建代理服务 一、反爬技术简介 (一)破解Web端反爬技术 1.常见的反爬策略方向:同一时间的请求数量、请求的身份信息、浏览器和爬虫的区别 2.浏览器和爬虫的不同:异步数据加…

Maui初体验

创建Maui应用程序 使用vs创建项目,选择maui模板。 生成即可。 体验Font. 下载字体,放在Font文件夹下,或者子文件夹。 将 文件的生成操作改成MauiFont. 注册字体 如果在Font的子文件夹下,则需要编辑项目,修改ItemGrou…

SeaFormer实战:使用SeaFormer实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整算法设置混合精度,DP多卡,EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试热力图可视化展示…

Ansible任务控制与Ansible-Playbook

YAML特点 YAML 文件以 # 为注释符 YAML 文件以 .yml或者.yaml 结尾 YAML 文件以 --- 开始 , 以 ... 结束,但开始和结束标志都是可选的 基本语法 大小写敏感 使用缩进表示层级关系 缩进时是使用Tab键还是使用空格- -定要达到统- ,建议使用空格…