深度学习:可视化方法(模型可视化,训练过程可视化,特征提取可视化)_深度学习模型效果进行可视化-程序员宅基地

技术标签: 人工智能——深度学习  python  深度学习  人工智能  

0.环境说明

python3.8.5+pytorch

1. 模型结构可视化

1.1 netron

step1:在虚拟环境中安装netron

pip install netron

step2: 在虚拟环境中打开netron

在这里插入图片描述

step3:浏览器中输入地址:http://localhost:8080/

step4:选择保存的模型xxx.pt

在这里插入图片描述
在这里插入图片描述

1.2 使用tensorboard

step1:安装tensorboard,最简单的方式就是直接安装一个tensorflow

pip install tensorflow==1.15.0 -i https://mirrors.aliyun.com/pypi/simple

step2:代码中设置

from torch.utils.tensorboard import SummaryWriter
'''设置在模型构建后'''
 writer = SummaryWriter(log_dir='./output/log')
 writer.add_graph(model, torch.empty(10, 4)) #注意这里要结合你具体的训练样本,10是batch_szie可任意,4是训练样本的特征长度需要和训练样本一致
 '''设置在反向传播过程中,记录loss和acc'''
 # 可视化输出
 writer.add_scalar('loss', _loss, train_step)
 writer.add_scalar('acc', _acc, train_step)
 train_step += 1
'''train损失和test损失共同打印在一张图上,add_scalars注意s'''
writer.add_scalars('epoch_loss',{
    'train':train_loss,'test':test_loss},epoch)

step3:

  1. 进入cmd命令行;
  2. 切换当前磁盘到events文件所在的磁盘;
  3. 确保events文件所在的路径没有中文字符串;
  4. 输入命令:tensorboard --logdir C:\Users\...\output\log
    在这里插入图片描述
  5. 浏览器中输入http://localhost:6006/#images
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/83b6b6694ba846f8855c5b1781f3afc2.png

2. 训练过程可视化

2.1 tensorboard

上文已经提及,只需要在训练过程中add即可。

 '''设置在反向传播过程中,记录loss和acc'''
 # 可视化输出
 writer.add_scalar('loss', _loss, train_step)
 writer.add_scalar('acc', _acc, train_step)
 train_step += 1

在这里插入图片描述

2.2 普通代码

if batch_idx % 100 == 0:
            print(f"Train Epoch:{
      epoch} [{
      batch_idx*len(data)}/{
      len(train_loader.dataset)} ({
      100.*batch_idx/len(train_loader):.0f}%)]\tloss:{
      loss.item():.6f}")

效果图:
在这里插入图片描述

3. 特征提取可视化

需要tensorboard配合hook,直接上代码。
model: LeNet
data: MNIST

import enum
import sys
import torch
from torch import nn
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from myutils.metrics import Acc_Score  #自己写的一个计算准确度的类,继承Module

class LeNet_BN(nn.Module):
    def __init__(self,in_chanel) -> None:
        super(LeNet_BN,self).__init__()
        self.feature_hook_img = {
    }

        self.features = nn.Sequential(
        nn.Conv2d(in_chanel, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
        nn.AvgPool2d(kernel_size=2, stride=2),
        nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid())
        self.classifi = nn.Sequential(
        nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
        nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
        nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
        nn.Linear(84, 10))   


    def forward(self,X):
        X = self.features(X)# 特征提取与分类需要分开
        X = self.classifi(X)
        return X

    def add_hooks(self):#可视化钩子
        def create_hook_fn(idx):
            def hook_fn(model,input,output):
                self.feature_hook_img[idx]=output.cpu()
            return hook_fn

        for _idx,_layer in enumerate(self.features):
            _layer.register_forward_hook(create_hook_fn(_idx))

    def add_image_summary(self,writer,step,prefix=None):
        if len(self.feature_hook_img)==0:
            return
        if prefix is None:
            prefix='layer'
        else:
            prefix = f"{
      prefix}_layer"
        for _k in self.feature_hook_img:# 包含原始图像
            _v = self.feature_hook_img[_k][0:1,...]# 只获取第一张图像
            _v = torch.permute(_v,(1,0,2,3))#(1,c,h,w)->(c,1,h,w)# 交换通道,展示每个维度的提取的图像特征
            writer.add_images(f"{
      prefix}_{
      _k}",_v,step)


if __name__=='__main__':
    # 加载数据
    # device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    tsf = transforms.Compose([transforms.ToTensor()])
    train_data= datasets.MNIST(root='dataset\mnist_train',train=True,transform=tsf,download=True)
    train_data_loader = DataLoader(train_data,batch_size=32,shuffle=True)
    test_data = datasets.MNIST(root='dataset\mnist_test',train=False,transform=tsf,download=True)
    test_data_loader = DataLoader(test_data,batch_size=32,shuffle=32)

    model = LeNet_BN(1)
    model.add_hooks()
    lr=1e-2
    epochs = 10
    loss_f = torch.nn.CrossEntropyLoss()
    acc_f = Acc_Score()
    opt = torch.optim.SGD(model.parameters(),lr)
    writer = SummaryWriter(log_dir='./output/log')
    writer.add_graph(model,torch.empty(10,1,28,28))
    
    for epoch in range(epochs):
        for idx,data in enumerate(train_data_loader):
            X,y = data
            y = y.to(torch.long)
            # 前向传播
            y_pred = model(X)
            train_loss = loss_f(y_pred,y)
            train_acc = acc_f(y_pred,y)
            # 反向传播
            opt.zero_grad()
            train_loss.backward()
            opt.step()
            if (idx+1)%100==0:
                print(f"epoch:{
      epoch} |{
      (idx+1)*32}/{
      len(train_data)}({
      100.*(idx+1)*32/len(train_data):.2f}%)|\tloss:{
      train_loss.item():.3f}\tacc:{
      train_acc.item():.2f}")
                model.add_image_summary(writer,epoch,'train')# 添加本次训练


        test_loss=0
        test_acc=0
        test_numbers = len(test_data)/32
        for data in test_data_loader:
            model.eval()
            X,y = data
            y=y.to(torch.long)
            # print(y)
            # y = y.to(torch.long)
            y_pred = model(X)
            test_loss += loss_f(y_pred,y).item()
            test_acc += acc_f(y_pred,y).item()
        test_loss = test_loss/test_numbers
        test_acc = test_acc/test_numbers
        print('test res:')
        print(f"epoch:{
      epoch} \tloss:{
      test_loss:.3f}\tacc:{
      test_acc:.2f}")
        print('-'*80)
        writer.add_scalars('epoch_loss',{
    'train':train_loss.item(),'test':test_loss},epoch)
        writer.add_scalars('epoch_acc',{
    'train':train_acc.item(),'test':test_acc},epoch)
        
    writer.close()# 关闭
            
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42911863/article/details/126160153

智能推荐

【树】leetcode_105_从前序与中序遍历序列构造二叉树-程序员宅基地

文章浏览阅读66次。【树】leetcode_105_从前序与中序遍历序列构造二叉树/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left

vant-list的具体使用完整版(移动端分页)_van-list 详细页面-程序员宅基地

文章浏览阅读576次,点赞7次,收藏10次。【代码】vant-list的具体使用完整版。_van-list 详细页面

C#连接OPC C#上位机链接PLC程序源码 1.该程序是通讯方式是CSharp通过OPC方式连接PLC_c#opc通信-程序员宅基地

文章浏览阅读565次。本文主要介绍如何使用C#通过OPC方式连接PLC,并提供了相应的程序和学习资料,以便读者学习和使用。OPC服务器是一种软件,可以将PLC的数据转换为标准的OPC格式,允许其他软件通过标准接口读取或控制PLC的数据。此外,本文还提供了一些学习资料,包括OPC和PLC的基础知识,C#编程语言的教程和实例代码。这些资料可以帮助读者更好地理解和应用本文介绍的程序。1.该程序是通讯方式是CSharp通过OPC方式连接PLC,用这种方式连PLC不用考虑什么种类PLC,只要OPC服务器里有的PLC都可以连。_c#opc通信

Hyper-V内的虚拟机复制粘贴_win10 hyper-v ubuntu18.04 文件拷贝-程序员宅基地

文章浏览阅读1.6w次,点赞3次,收藏10次。实践环境物理机:Windows10教育版,操作系统版本 17763.914虚拟机:Ubuntu18.04.3桌面版在Hyper-V中的刚安装好Ubuntu虚拟机之后,会发现鼠标滑动很不顺畅,也不能向虚拟机中拖拽文件或者复制内容。在VMware中,可以通过安装VMware tools来使物理机和虚拟机之间达到更好的交互。在Hyper-V中,也有这样的工具。这款工具可以完成更好的鼠标交互,我的..._win10 hyper-v ubuntu18.04 文件拷贝

java静态变量初始化多线程,持续更新中_类初始化一个静态属性 为线程池-程序员宅基地

文章浏览阅读156次。前言互联网时代,瞬息万变。一个小小的走错,就有可能落后于别人。我们没办法去预测任何行业、任何职业未来十年会怎么样,因为未来谁都不能确定。只能说只要有互联网存在,程序员依然是个高薪热门行业。只要跟随着时代的脚步,学习新的知识。程序员是不可能会消失的,或者说不可能会没钱赚的。我们经常可以听到很多人说,程序员是一个吃青春饭的行当。因为大多数人认为这是一个需要高强度脑力劳动的工种,而30岁、40岁,甚至50岁的程序员身体机能逐渐弱化,家庭琐事缠身,已经不能再进行这样高强度的工作了。那么,这样的说法是对的么?_类初始化一个静态属性 为线程池

idea 配置maven,其实不用单独下载Maven的。以及设置新项目配置,省略每次创建新项目都要配置一次Maven_安装idea后是不是不需要安装maven了?-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏43次。说来也是惭愧,一直以来,在装环境的时候都会从官网下载Maven。然后再在idea里配置Maven。以为从官网下载的Maven是必须的步骤,直到今天才得知,idea有捆绑的 Maven 我们只需要搞一个配置文件就行了无需再官网下载Maven包以后再在新电脑装环境的时候,只需要下载idea ,网上找一个Maven的配置文件 放到 默认的 包下面就可以了!也省得每次创建项目都要重新配一次Maven了。如果不想每次新建项目都要重新配置Maven,一种方法就是使用默认的配置,另一种方法就是配置 .._安装idea后是不是不需要安装maven了?

随便推点

Android自定义圆角矩形图片ImageView_android 矩形圆角imageview-程序员宅基地

文章浏览阅读2.4k次。//自定义的圆形的ImageView类的实现代码如下:package com.xc.xcskin.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import andro_android 矩形圆角imageview

又见回文 字符串-程序员宅基地

文章浏览阅读67次。又见回文题目描述 “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。现在呢,就是让你判断输入的字符串是否是回文串。输入 有多组输入,每行输入一串字符,保证字符串长度不会大于100000,字符串由大小写英文字母和空格组成,以字符串“2013”作为结束标志。输出 每行输出一个字符串,如果输入是..._c语言又见回文 字符串

switch的参数可以是什么类型?_switch的参数有哪些-程序员宅基地

文章浏览阅读1k次,点赞15次,收藏7次。可以作为switch参数数据类型的有:int、byte、short、char、String、枚举(整数、枚举、字符、字符串),一般括号内参数写的都是int型。不能作为switch参数的有:long、float、double、boolean、复杂的表达式。_switch的参数有哪些

毕设基于微信小程序的小区管理系统的设计ssm毕业设计_ssm基于微信小程序的公寓生活管理系统-程序员宅基地

文章浏览阅读223次。该系统将提供便捷的信息发布、物业报修、社区互动等功能,为小区居民提供更加便利、高效的服务。引言: 随着城市化进程的加速,小区管理成为一个日益重要的任务。因此,设计一个基于微信小程序的小区管理系统成为了一项具有挑战性和重要性的毕设课题。本文将介绍该小区管理系统的设计思路和功能,以期为小区提供更便捷、高效的管理手段。四、总结与展望: 通过本次毕设项目,我们实现了一个基于微信小程序的小区管理系统,为小区居民提供了更加便捷、高效的服务。通过该系统的设计与实现,能够提高小区管理水平,提供更好的居住环境和服务。_ssm基于微信小程序的公寓生活管理系统

如何正确的使用Ubuntu以及安装常用的渗透工具集.-程序员宅基地

文章浏览阅读635次。文章来源i春秋入坑Ubuntu半年多了记得一开始学的时候基本一星期重装三四次=-= 尴尬了 觉得自己差不多可以的时候 就吧Windows10干掉了 c盘装Ubuntu 专心学习. 这里主要来说一下使用Ubuntu的正确姿势Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源GNU/Linux操作系统,Ubuntu 是基于DebianGNU/Linux,支..._ubuntu安装攻击工具包

JNI参数传递引用_jni引用byte[]-程序员宅基地

文章浏览阅读335次。需求:C++中将BYTE型数组传递给Java中,考虑到内存释放问题,未采用通过返回值进行数据传递。public class demoClass{public native boolean getData(byte[] tempData);}JNIEXPORT jboolean JNICALL Java_com_core_getData(JNIEnv *env, jobject thisObj, jbyteArray tempData){ //resultsize为s..._jni引用byte[]

推荐文章

热门文章

相关标签