pytorch09:可视化工具-TensorBoard,实现卷积核和特征图可视化

2024-01-09

在这里插入图片描述

一、TensorBoard简介

当我们在模型训练过程中想要将部分参数进行可视化,例如:精确度、损失等参数,想通过一个可视化工具实时观察,可以使用tensorboard工具。
在这里插入图片描述
在使用pytorch进行一些深度学习测试的过程中,首先将生成的数据使用Python脚本进行保存,将数据保存到电脑硬盘当中,然后使用tensorboard将硬盘中的数据绘制成图,在浏览器端展示出来。

二、TensorBoard安装

打开终端,进入到项目所在的虚拟环境当中输入以下安装命令。
安装命令:

pip install tensorboard -i https://pypi.tuna.tsinghua.edu.cn/simple some-package

三、TensorBoard运行可视化

代码案例:

import numpy as np
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter(comment='test_tensorboard')  # 使用SummaryWriter记录我们想要可视化的数据

for x in range(100):
    writer.add_scalar('y=2x', x * 2, x)
    writer.add_scalar('y=pow(2, x)', 2 ** x, x)

    writer.add_scalars('data/scalar_group', {"xsinx": x * np.sin(x),
                                             "xcosx": x * np.cos(x),
                                             "arctanx": np.arctan(x)}, x)
writer.close()

执行该代码会在当前代码文件夹目录下生成一个runs文件夹,该文件夹目录下保存的就是使用tensorboard中方法保存的可视化文件。
在这里插入图片描述
打开pycharm自带的终端,输入以下命令 tensorboard --logdir=./05-02-代码-TensorBoard简介与安装/lesson-20/runs
注意:logdir后面的文件夹路径一定要输入正确 ,然后会在终端显示一个本地端口号
在这里插入图片描述
点击端口号就能跳转到浏览器,观看到可视化界面,我们代码中的三个函数都进行可绘图可视化。
在这里插入图片描述

四、TensorBoard详细使用

4.1 SummaryWriter

在这里插入图片描述
功能:提供创建event file的高级接口
主要属性:
• log_dir:event file输出文件夹
• comment:不指定log_dir时,文件夹后缀
• filename_suffix:event file文件名后缀
代码实现

flag = 1
if flag:
    log_dir = "./train_log/test_log_dir"  # 指定可视化文件保存路径
    writer = SummaryWriter(log_dir=log_dir, comment='_scalars', filename_suffix="12345678")
    # writer = SummaryWriter(comment='_scalars', filename_suffix="12345678")
    for x in range(100):
        writer.add_scalar('y=pow_2_x', 2 ** x, x)
    writer.close()

文件创建结果
文件指定路径train_log/test_log_dir,生成的event 文件后缀名为12345678,_scalars并没有出现在路径当中,因为我们设置log_dir 指定路径。
在这里插入图片描述


不使用log_dir代码实现

flag = 1
if flag:
    log_dir = "./train_log/test_log_dir"  # 指定可视化文件保存路径
    # writer = SummaryWriter(log_dir=log_dir, comment='_scalars', filename_suffix="12345678")
    writer = SummaryWriter(comment='_scalars', filename_suffix="12345678")
    for x in range(100):
        writer.add_scalar('y=pow_2_x', 2 ** x, x)
    writer.close()

文件创建结果:
创建的文件夹带有comment指定后缀名_scalars。
在这里插入图片描述

4.2 add_scalar()

在这里插入图片描述
功能:记录标量
• tag:图像的标签名,图的唯一标识
• scalar_value:要记录的标量
• global_step:x轴
该方法有一定局限性,只能记录一条曲线,所以一般我们不使用该方法

4.3 add_scalars()

在这里插入图片描述
功能:记录多个标量,可以绘制多条曲线。
• main_tag:该图的标签。
• tag_scalar_dict:key是变量的tag,value是变量的值,使用字典的形式去记录多条曲线。

scalar 和 scalars代码实现

flag = 1
if flag:
    max_epoch = 100
    writer = SummaryWriter(comment='test_comment', filename_suffix="test_suffix")
    for x in range(max_epoch):
        writer.add_scalar('y=2x', x * 2, x)
        writer.add_scalar('y=pow_2_x', 2 ** x, x)
        writer.add_scalars('data/scalar_group', {"xsinx": x * np.sin(x),
                                                 "xcosx": x * np.cos(x)}, x) #字典存储的两个函数
    writer.close()

可视化结果:
在这里插入图片描述

4.4 add_histogram()

在这里插入图片描述
功能:统计参数直方图与多分位数折线图
• tag:图像的标签名,图的唯一标识。
• values:要统计的参数,可以是权重、偏执或者是梯度。
• global_step:y轴。
• bins:取直方图的bins。
代码实现1:

flag = 1
if flag:
    writer = SummaryWriter(comment='test_comment', filename_suffix="test_suffix")
    for x in range(2):
        np.random.seed(x)
        data_union = np.arange(100)
        data_normal = np.random.normal(size=1000)  #1000个数据正态分布
        writer.add_histogram('distribution union', data_union, x)  # 使用tensorboard绘制直方图
        writer.add_histogram('distribution normal', data_normal, x)
        plt.subplot(121).hist(data_union, label="union")  # 使用plt绘制直方图
        plt.subplot(122).hist(data_normal, label="normal")
        plt.legend()
        plt.show()
    writer.close()

plt直方图两次绘制结果:
在这里插入图片描述
在这里插入图片描述

tensorboard两次绘制结果:
在这里插入图片描述

4.4.1实际项目开发使用

代码实现:

将训练、验证过程中的损失已经精度都使用add_scalars写入进去,同时将每一个epoch的梯度都使用add_histogram方法添加。

# -*- coding:utf-8 -*-

import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
from torch.utils.tensorboard import SummaryWriter
import torch.optim as optim
from matplotlib import pyplot as plt
from lenet import LeNet
from my_dataset import RMBDataset
from common_tools import set_seed

set_seed()  # 设置随机种子
rmb_label = {"1": 0, "100": 1}

# 参数设置
MAX_EPOCH = 10
BATCH_SIZE = 16
LR = 0.01
log_interval = 10
val_interval = 1

# ============================ step 1/5 数据 ============================

split_dir = os.path.join("rmb_split")
train_dir = os.path.join(split_dir, "train")
valid_dir = os.path.join(split_dir, "valid")

norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]

train_transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.RandomCrop(32, padding=4),
    transforms.RandomGrayscale(p=0.8),
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std),
])

valid_transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std),
])

# 构建MyDataset实例
train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
valid_data = RMBDataset(data_dir=valid_dir, transform=valid_transform)

# 构建DataLoder
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)

# ============================ step 2/5 模型 ============================

net = LeNet(classes=2)
net.initialize_weights()

# ============================ step 3/5 损失函数 ============================
criterion = nn.CrossEntropyLoss()  # 选择损失函数

# ============================ step 4/5 优化器 ============================
optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9)  # 选择优化器
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)  # 设置学习率下降策略

# ============================ step 5/5 训练 ============================
train_curve = list()
valid_curve = list()

iter_count = 0

# 构建 SummaryWriter
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")

for epoch in range(MAX_EPOCH):

    loss_mean = 0.
    correct = 0.
    total = 0.

    net.train()
    for i, data in enumerate(train_loader):

        iter_count += 1

        # forward
        inputs, labels = data
        outputs = net(inputs)

        # backward
        optimizer.zero_grad()
        loss = criterion(outputs, labels)
        loss.backward()

        # update weights
        optimizer.step()

        # 统计分类情况
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).squeeze().sum().numpy()

        # 打印训练信息
        loss_mean += loss.item()
        train_curve.append(loss.item())
        if (i + 1) % log_interval == 0:
            loss_mean = loss_mean / log_interval
            print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                epoch, MAX_EPOCH, i + 1, len(train_loader), loss_mean, correct / total))
            loss_mean = 0.

        # 记录数据,保存于event file
        writer.add_scalars("Loss", {"Train": loss.item()}, iter_count)
        writer.add_scalars("Accuracy", {"Train": correct / total}, iter_count)

    # 每个epoch,记录梯度,权值
    # net.named_parameters() 返回我们的参数以及对应的名字
    for name, param in net.named_parameters():
        writer.add_histogram(name + '_grad', param.grad, epoch)
        writer.add_histogram(name + '_data', param, epoch)

    scheduler.step()  # 更新学习率

    # validate the model
    if (epoch + 1) % val_interval == 0:

        correct_val = 0.
        total_val = 0.
        loss_val = 0.
        net.eval()
        with torch.no_grad():
            for j, data in enumerate(valid_loader):
                inputs, labels = data
                outputs = net(inputs)
                loss = criterion(outputs, labels)

                _, predicted = torch.max(outputs.data, 1)
                total_val += labels.size(0)
                correct_val += (predicted == labels).squeeze().sum().numpy()

                loss_val += loss.item()

            valid_curve.append(loss.item())
            print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                epoch, MAX_EPOCH, j + 1, len(valid_loader), loss_val, correct / total))

            # 记录数据,保存于event file
            writer.add_scalars("Loss", {"Valid": np.mean(valid_curve)}, iter_count)
            writer.add_scalars("Accuracy", {"Valid": correct / total}, iter_count)

train_x = range(len(train_curve))
train_y = train_curve

train_iters = len(train_loader)
valid_x = np.arange(1, len(valid_curve) + 1) * train_iters * val_interval  # 由于valid中记录的是epochloss,需要对记录点进行转换到iterations
valid_y = valid_curve

plt.plot(train_x, train_y, label='Train')
plt.plot(valid_x, valid_y, label='Valid')

plt.legend(loc='upper right')
plt.ylabel('loss value')
plt.xlabel('Iteration')
plt.show()

训练精度和验证集精度(图1),以及训练损失和验证集损失(图2)
在这里插入图片描述
训练过程中的参数分布
在这里插入图片描述

直方图分布情况
会发现越往下,梯度值越小,因为梯度是loss的导数,loss随着epoch的增加减少,所以越往后梯度也就越小。
在这里插入图片描述

4.5 add_image()

功能:记录图像
在这里插入图片描述
• tag:图像的标签名,图的唯一标识
• img_tensor:图像数据,注意尺度
• global_step:x轴
• dataformats:数据形式,CHW,HWC,HW
代码实现:

flag = 1
if flag:
    writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
    # img 1     random
    fake_img = torch.randn(3, 512, 512)
    writer.add_image("fake_img", fake_img, 1)
    time.sleep(1)
    # img 2     ones
    fake_img = torch.ones(3, 512, 512)
    time.sleep(1)
    writer.add_image("fake_img", fake_img, 2)
    # img 3     1.1
    fake_img = torch.ones(3, 512, 512) * 1.1
    time.sleep(1)
    writer.add_image("fake_img", fake_img, 3)
    # img 4     HW
    fake_img = torch.rand(512, 512)  # 灰度图
    writer.add_image("fake_img", fake_img, 4, dataformats="HW")
    # img 5     HWC
    fake_img = torch.rand(512, 512, 3)
    writer.add_image("fake_img", fake_img, 5, dataformats="HWC")
    writer.close()

展示结果:
在这里插入图片描述

4.6 torchvision.utils.make_grid

功能:制作网格图像 在这里插入图片描述
• tensor:图像数据, B C H*W形式 ,pytorch训练图像格式
• nrow:行数(列数自动计算)
• padding:图像间距(像素单位),也就是网格线的宽度
• normalize:是否将像素值标准化,0~225之间
• range:标准化范围
• scale_each:是否单张图维度标准化
• pad_value:padding的像素值,也就是网格线的颜色

代码实现:

flag = 1
if flag:
    writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")

    split_dir = os.path.join("rmb_split")
    train_dir = os.path.join(split_dir, "train")

    transform_compose = transforms.Compose([transforms.Resize((32, 64)), transforms.ToTensor()])
    
    train_data = RMBDataset(data_dir=train_dir, transform=transform_compose)
    train_loader = DataLoader(dataset=train_data, batch_size=16, shuffle=True)
    data_batch, label_batch = next(iter(train_loader))  # 取出一个batch数据,data_batch图片数据以及数据标签

    img_grid = vutils.make_grid(data_batch, nrow=4, normalize=True, scale_each=True)
    writer.add_image("input img", img_grid, 0)
    writer.close()

展示结果:
在这里插入图片描述

4.7 卷积核和特征图可视化

4.7.1 AlexNet卷积核可视化

代码实现:
对单个卷积核的三个通道图展示,以及对64个卷积核直接进行可视化。

flag = 1
if flag:
    writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
    alexnet = models.alexnet(pretrained=True)  # 获取一个预训练好的alexnet
    kernel_num = -1  # 当前第几个卷积核层
    vis_max = 1  # 最大可视化卷积层
    for sub_module in alexnet.modules():  # 递归当前网络下的所有网络层
        if isinstance(sub_module, nn.Conv2d):  # 判断当前递归的网络层是否是卷积层
            kernel_num += 1
            if kernel_num > vis_max:  # 只展示前两个卷积层
                break
            kernels = sub_module.weight  # 获取当前卷积核的权重
            c_out, c_int, k_w, k_h = tuple(kernels.shape)
            # 对c_out单独可视化
            for o_idx in range(c_out):
                kernel_idx = kernels[o_idx, :, :, :].unsqueeze(1)  # make_grid需要 BCHW,这里拓展C维度
                kernel_grid = vutils.make_grid(kernel_idx, normalize=True, scale_each=True, nrow=c_int)
                writer.add_image('{}_Convlayer_split_in_channel'.format(kernel_num), kernel_grid, global_step=o_idx)

            # 对64个卷积核直接进行可视化
            kernel_all = kernels.view(-1, 3, k_h, k_w)  # b,3, h, w
            kernel_grid = vutils.make_grid(kernel_all, normalize=True, scale_each=True, nrow=8)  # c, h, w
            writer.add_image('{}_all'.format(kernel_num), kernel_grid, global_step=322)
            print("{}_convlayer shape:{}".format(kernel_num, tuple(kernels.shape)))
    writer.close()

展示结果:
在这里插入图片描述

4.7.2 特征图可视化

代码实现:
将经过第一层卷积之后的特征图展示出来;

# ----------------------------------- 特征图可视化 -----------------------------------
# flag = 0
flag = 1
if flag:
    writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")

    # 数据
    path_img = "3.jpg"  # your path to image
    normMean = [0.49139968, 0.48215827, 0.44653124]
    normStd = [0.24703233, 0.24348505, 0.26158768]

    # 图片预处理
    img_transforms = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(normMean, normStd)
    ])

    # 将图片转换成RGB格式
    img_pil = Image.open(path_img).convert('RGB')
    if img_transforms is not None:
        img_tensor = img_transforms(img_pil)

    # 增加一个维度
    img_tensor.unsqueeze_(0)  # chw --> bchw

    # 模型
    alexnet = models.alexnet(pretrained=True)

    # forward
    convlayer1 = alexnet.features[0]  # 获取第一个卷积层
    fmap_1 = convlayer1(img_tensor)  # 将图片交给第一个卷积层进行处理

    # 预处理 希望batch能够大一些
    fmap_1.transpose_(0, 1)  # bchw=(1, 64, 55, 55) --> (64, 1, 55, 55)
    fmap_1_grid = vutils.make_grid(fmap_1, normalize=True, scale_each=True, nrow=8)

    writer.add_image('feature map in conv1', fmap_1_grid, global_step=322)
    writer.close()

展示结果: 在这里插入图片描述

4.8 add_graph()

功能:可视化模型计算图
在这里插入图片描述
• model:模型,必须是 nn.Module
• input_to_model:输出给模型的数据
• verbose:是否打印计算图结构信息
代码实现:

flag = 1
if flag:
    writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
    # 模型
    fake_img = torch.randn(1, 3, 32, 32)
    lenet = LeNet(classes=2)
    writer.add_graph(lenet, fake_img)
    writer.close()

展示结果:
在这里插入图片描述

五、summary网络查看工具

功能:查看模型信息,便于调试,用于观察网络结构很好的工具。
在这里插入图片描述
• model:pytorch模型
• input_size:模型输入size
• batch_size:batch size
• device:“cuda” or “cpu”

代码实现:

    from torchsummary import summary
    lenet = LeNet(classes=2)
    print(summary(lenet, (3, 32, 32), device="cpu"))

输出结果:
在这里插入图片描述

第一层456个参数是如何计算的?
LeNet网络结构如下,可以看出第一层网络input通道数为3,output输出通道数为6,卷积核大小为5x5,所以参数量为6x3x5x5=450,因为加了6个bias,所以总参数量为456.
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pytorch09:可视化工具-TensorBoard,实现卷积核和特征图可视化 的相关文章

随机推荐

  • 视图的内边距和边距之间的区别

    视图的边距和填充有什么区别 帮助我记住的含义padding 我想到一件有很多的大衣厚棉垫 我在外套里面 但我和我的棉衣是在一起的 我们是一个单位 但要记住margin 我想 嘿嘿 给我一点余地吧 这是我和你之间的空白 不要进入我的舒适区 我
  • jOOQ 和缓存?

    我正在考虑从 Hibernate 迁移到 jOOQ 但我不确定是否可以不使用缓存 休眠有一个一级 二级缓存 https stackoverflow com questions 337072 what are first and second
  • Apache CXF LoggingInInterceptor 已弃用 - 可以使用什么替代?

    我在 Spring Boot 的帮助下使用 Apache CXFcxf spring boot starter jaxws3 2 7版本的插件 我的目的是自定义日志拦截器 但是当我创建以下类时 public class CustomLogg
  • 在 C++ 中打印浮点数的二进制表示形式[重复]

    这个问题在这里已经有答案了 可能的重复 C 中浮点数转换为二进制 https stackoverflow com questions 2746380 float to binary in c 我想在 C 中打印出浮点数的二进制表示形式 不太
  • 将 MongoCursor 从 ->find() 转换为数组

    jokes collection gt find 我如何转换 jokes进入数组 你可以使用 PHP 的iterator to array http php net manual en function iterator to array
  • Roundcube问题:与存储服务器的连接失败

    我在 Roundcube 中收到此错误 连接到存储服务器失败 行 我已经检查了所有内容 配置 数据库用户名密码 服务器详细信息都是干净的 谁能告诉我可能是什么问题 这里我给出了整个配置文件
  • asp.net 中的 Convert.ToDateTime 问题

    我有一个应用程序在西班牙服务器上运行没有任何问题 当我将应用程序上传到在线服务器 英文窗口 时 我收到 Convert ToDateTime 和 Convert ToInt32 的异常 类型为 输入字符串不是有效的 Datetime Int
  • 在 Symfony/Doctrine 中删除记录时执行一些清理

    将 Symfony 1 4 5 与 Doctrine 结合使用 我有一个模型 其中包含上传的图像作为其中一列 创建和更新记录很好 使用 doSave 方法来处理上传和对文件的任何更改 我遇到的问题是 如果记录被删除 我希望它也删除关联的文件
  • 如何控制表格视图滚动速度?

    我想要控制表视图滚动速度 如何以编程方式做到这一点 请帮忙 提前致谢 简森 雅各布 您可以设置tableView decelerationRate财产 它是一个浮点值 决定用户抬起手指后的减速率 并且 您的应用程序可以使用UIScrollV
  • iPhone - 将字典写入文件:处理错误

    使用以下命令将 NSDictionary 保存到文件时 BOOL writeToFile NSString path atomically BOOL flag 可以返回 YES 或 NO 有一些编写接受 NSError 参数的文件的方法 对
  • JQuery - 摆脱 .serialize() 中的 %5B%5D

    我正在使用 AJAX 提交序列化表单 数据传递到action php最终包含 5B 5D 而不是 是否有办法取回 或者数据能够以相同的方式处理 即像数组一样 action php 该表格通过以下方式序列化 var form data for
  • 如何使用 Tensorflow 2/ Keras 保存和恢复训练具有多个模型部分的 GAN

    我目前正在尝试添加一个功能来中断和恢复通过此示例代码创建的 GAN 的训练 https machinelearningmastery com how to develop an auxiliary classifier gan ac gan
  • BeautifulSoup 获取列表的 href - 需要简化脚本 - 替换多处理

    我有以下汤 下一个 我想从中提取 href some url 我想提取 href some url 以及此页面上列出的页面的完整列表 https www catholic hierarchy org diocese laa html htt
  • 【计算机开题报告】题库管理系统

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 国内外研究现状 题库管理系统的发展现状 试题库一词 源于20世纪60年代的英国 是在一个教育研究课题上提出的 试题库是指测验试题的有序集合 是适合于具备一
  • 【计算机开题报告】基于JAVA的酒店管理系统的设计与实现

    1 毕业设计 论文 综述 随着社会经济和科技的迅速发展 人们对衣食住行的要求也逐渐提高 酒店 宾馆在服务行业中扮演着越来越重要的角色 本课程设计的内容旨在以管理系统的方式给人们出行提供酒店预订服务 从而能够更方便快捷的帮助酒店工作人员办理客
  • 手机未来发展的趋势与挑战

    随着科技的飞速发展 手机已经成为我们生活中不可或缺的一部分 然而 随着5G 人工智能等新技术的不断涌现 手机的未来发展面临着前所未有的机遇和挑战 本文将探讨手机未来发展的趋势与挑战 一 手机未来发展的趋势 5G技术的普及 随着5G技术的不断
  • 鸿鹄云商B2B2C:JAVA实现的商家间直播带货商城系统概览

    saas云平台 打造全行业全渠道全场景的saas产品 为经营场景提供一体化解决方案 门店经营区域化 网店经营一体化 本地化 全方位 一站式服务 为多门店提供统一运营解决方案 提供丰富多样的营销玩法覆盖所有经营场景 助力商家成功 系统稳定压倒
  • 【Leetcode】49. 字母异位词分组

    49 字母异位词分组 题目链接 代码一 代码二 题目链接 Leetcode 49 字母异位词分组 代码一 func groupAnagrams strs string string 存放字典序相同的字符串切片 hash map string
  • APP加固技术及其应用

    文章目录 引言 APP加固的概念 APP加固的方案 APP加固在实际开发中的应用 总结 引言 在移动应用开发过程中 APP加固技术起到了非常重要的作用 APP加固是将apk文件进行混淆加密 以防止别人反编译获取我们的源码和资源文件 目前市场
  • pytorch09:可视化工具-TensorBoard,实现卷积核和特征图可视化

    目录 一 TensorBoard简介 二 TensorBoard安装 三 TensorBoard运行可视化 四 TensorBoard详细使用 4 1 SummaryWriter 4 2 add scalar 4 3 add scalars