请添加图片描述

本文主要讲解TensorBoard的使用及其原理,为了专业性以及严谨度采用结合官方文档进行解释,这可能会增加学习时间成本,读者可以找一个时间块进行研究学习。加油加油加油,下面针对文档的翻译,会在特定的部分进行解释。以及如何执行学习使用这些代码。和论文精读博客一样。引用格式为博主自身理解。未➕引用格式则为原文翻译。

这里是torch官方文档的链接。个人推荐同时打开当前博客和官方文档的链接一起学习效率加倍哦
https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html

0. 模型数据以及训练行为通过TensorBoard 可视化

在60分钟快速入门中,我们向您展示了如何加载数据,通过定义为nn.Module子类的模型进行输入,对这个模型进行训练数据的训练,并在测试数据上对其进行测试为了看到正在发生的事情,我们在模型训练时打印出一些统计数据,以便了解训练是否在进展。然而,我们能做得更好:PyTorch与TensorBoard集成,这是一个专门用于可视化神经网络训练运行结果的工具。本教程展示了一些其功能,使用可通过torchvision.datasets读入PyTorch的Fashion-MNIST数据集。

划重点,TensorBoard就是让神经网络训练的行为结果可视化的这样的一个工具
那么具体的这个TensorBoard做什么了呢?
就是你神经网络训练的行为结果可视化,你需要有一个网络啊,并且你得用数训练他啊,所以就是搭建一个网络训练,但是你也不知道到底咋个样子了,如果是传统的方式就需要最终运行结束对中间结果进行打印,而使用tensorboard就可以实时的进行可视化各种各样的行为。

下面就是官方教程中的中主要内容,本文也是透过这几点对这个TensorBoard进行分析学习。

在本教程中,我们将学习如何:

1. 读取数据并进行适当的转换(与前一个教程几乎相同)。
2. 设置TensorBoard。
3. 写入TensorBoard。
4. 使用TensorBoard检查模型架构。
5. 使用TensorBoard创建上一个教程中我们创建的可视化的交互版本,代码更少。

具体来说,在第五点上,我们将看到:

检查我们训练数据的几种方法。

跟踪我们的模型在训练过程中的表现。

评估我们的模型在训练完成后的表现。

我们将从与 CIFAR-10 教程中类似的样例代码开始:

下面代码为展示TensorBoard如何使用需要的基础环境代码。都是深度学习常用的,读者还是好好看看:

# imports
import matplotlib.pyplot as plt # 画图库
import numpy as np # 矩阵计算

import torch
import torchvision # 计算机视觉库
import torchvision.transforms as transforms # 对图片进行处理

import torch.nn as nn
import torch.nn.functional as F # 包含各种激活函数
import torch.optim as optim # 优化器

# transforms #主要就是变形,对拿到的图进行处理,便于后续的模型使用
transform = transforms.Compose( # compose将几种操作组合起来
    [transforms.ToTensor(), # 将数据类型变层tensor
'''
这个转换将 PIL 图像或者 NumPy ndarray转换为torch.Tensor类
型。它会将图像数据从 0-255 的数值范围缩放到 0-1 的浮点数范围,
并且将数据的形状从(H, W, C)转换为(C, H, W),其中 H 是高度,W
 是宽度,C 是通道数。 这里这里改变了维度的位置
'''

    transforms.Normalize((0.5,), (0.5,))]) # 标准化
'''
这个转换对张量图像进行标准化,即对图像进行归一化处理,让图像的数值
范围变为给定的平均值(mean)和标准差(std)。在这个例子中,mean
 和 std 都设置为 0.5。对于每个通道,归一化操作公式为:(input - 
 mean)
'''
# 在之前的博文中"pytorch项目实战-分类模型李宏毅 21 机器学习第三次作业代码详解 CNN图片分类任务"
# 讲解过一个常用的数据集构建方式在datasets下的DatasetFolder。常用方式。
'''
DatasetFolder是torchvision.datasets中一个非常实用的类,它允
许您加载自定义数据集,这些数据集的目录结构已经按类别组织好了。每个
类别的数据应该存储在自己的子目录中。DatasetFolder自动为每个子目
录中的文件分配标签,这些标签是从子目录的名称中推导出来的。

而torch官方教程汇总给出的直接使用dataset+数据集名称的方式则是直接调用
其存在的数据集torchvision.datasets中提供了许多内置的数据集类,如
FashionMNIST,这些类为经常被使用到的公共数据集提供了专门的加载、下载功
能以及预定义的转换操作等。具体有哪些数据集可以参考官方的文档。
'''
# 实际上其参数还是很好理解的

# datasets './data', 存放数据集的位置,是否下载,是否作为训练数据集,transform=transform获取后对数据的处理方式
trainset = torchvision.datasets.FashionMNIST('./data', 
    download=True,
    train=True,
    transform=transform)
testset = torchvision.datasets.FashionMNIST('./data',
    download=True,
    train=False,
    transform=transform)

# dataloaders # 将数据变成可迭代的对象。其提供了各种各样分批以及擦是否打乱等操作。
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                        shuffle=True, num_workers=2)


testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                        shuffle=False, num_workers=2)

# constant for classes 一共10个类别
classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
        'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot')

# helper function to show an image
# (used in the `plot_classes_preds` function below)
def matplotlib_imshow(img, one_channel=False):
    if one_channel:
        img = img.mean(dim=0)
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    if one_channel:
        plt.imshow(npimg, cmap="Greys")
    else:
        plt.imshow(np.transpose(npimg, (1, 2, 0)))

这个matplotlib_imshow函数是为了方便地使用matplotlib来展示一个通过PyTorch
tensor表示的图像。我们逐步解释这个函数:

  1. 判断是否处理单通道图像: # 判断下是不是要将图片以灰度图形式进行展示

    • if one_channel:判断是否只展示一个通道(灰度图像)。如果是,使用.mean(dim=0)计算所有通道的平均值,使得原先可能有多个颜色通道的图像变为单通道(灰度图)。按照第一个维度执行均值操作,加入是3,4,5均值计算就从3这个维度开始做。所以将三个数值取均值。
  2. 反归一化:# 就是单纯的默认数值是经过归一化处理的,所以反其道而行之一下,让数值映射为1然后0.55灰度取值呗就能出图像了

    • img = img / 2 + 0.5:这行代码是对图像进行反归一化操作。归一化时,通常会将图像的像素值从[0,255]缩放到[0,1](如果是用transforms.ToTensor()转换的话),然后可能会进一步应用一次归一化以使数据的分布大致符合均值为0,标准差为1的分布。这里的反归一化操作是假设原先的归一化是用均值0.5和标准差0.5对图像进行的处理。反归一化的目的是让图像的像素值恢复到[0,1]的范围。
  3. 转换为Numpy数组:

    • npimg = img.numpy():这行代码将PyTorch张量转换为Numpy数组,因为matplotlib期望的是Numpy数组作为输入。
  4. 展示图像:

    • 接下来,根据是否单通道,分别用plt.imshow()展示图像。
      • 如果是单通道(灰度图像),直接调用plt.imshow(npimg, cmap="Greys"),其中cmap="Greys"指定了使用灰度色彩映射。
      • 对于非单通道(彩色)图像,因为matplotlib期望图像的维度顺序为(高度, 宽度, 通道数),即(H,W,C),而PyTorch张量的顺序是(通道数, 高度,
        宽度),即(C,H,W),所以需要使用np.transpose(npimg, (1, 2, 0))将张量的维度顺序改变成matplotlib所期望的形式再展示。这里和上文中改通道Totensor()的操作相反

综上所述,这个函数提供了一种方便的方法来利用matplotlib展示由PyTorch张量表示的图像,无论是单通道还是多通道图像,都可以通过适当的处理展示出来。
仅仅就是将tensor还原成灰度或其他颜色的图而已。就是为了展示数据的样子

数据有了现在看看网络的定义:

class Net(nn.Module): # 网络定义继承了nn.Module
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5) #卷积层输入通道1输出6然后5*5卷积
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 4 * 4, 120)# 开始全连接了
        self.fc2 = nn.Linear(120, 84) 
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 4 * 4) # 对图数据进行拉伸成向量
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()

还剩下啥啊,损失函数优化器:

criterion = nn.CrossEntropyLoss() # 损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #梯度下降方式

设置好了环境之后开始轮到咱们这个主力出场了

1. TensorBoard 设置

现在我们将设置TensorBoard,从torch.utils导入tensorboard并定义一个SummaryWriter,它是我们写入TensorBoard信息的关键对象。

实际上就是利用python存取文件的特性,在执行的过程中生成文件存入到特定的文件夹中,在调用函数进行读取。这样就能使实际监控需要看的数据特性,而不必等待全部的代码都执行结束才能看到整体的数据走向。

from torch.utils.tensorboard import SummaryWriter
'''
SummaryWriter 实际上是 torch.utils.tensorboard 模块中的一个。当使用 SummaryWriter 类创建一个实例时,可以指定一个目录路径作为参数,TensorBoard 将把日志文件保存在这个目录下。这个目录路径通常被视为 “
日志目录”,不过如果这个目录在创建 SummaryWriter 实例时不存在,SummaryWriter 会自动创建它。
'''

# 默认的`log_dir`是 "runs" - 在这里我们会更具体一些
writer = SummaryWriter('runs/fashion_mnist_experiment_1') # 可以提供绝对路径,实例化这个类,通过相对路径和绝对路径都可以

注意,仅这一行代码就创建了一个runs/fashion_mnist_experiment_1文件夹。

将需要保存的数据存在这里展示。实际上,SummaryWriter('runs/fashion_mnist_experiment_1')这行代码创建的runs/fashion_mnist_experiment_1文件夹用于存储在TensorBoard中需要展示的数据。
当使用SummaryWriter的各种方法(如add_scalaradd_imageadd_graph等)写入数据时,这些数据会被序列化并保存到指定的log_dir目录下,即这里的runs/fashion_mnist_experiment_1中。TensorBoard程序会读取这个目录下的文件,解析里面存储的数据,然后在Web界面上以图表或其他形式将这些数据展示出来。
这种机制使得开发者可以==实时地监控训练过程(==例如,查看损失和精确率的变化)、分析模型结构、观察参数和激活的分布,以及实现数据的可视化等,帮助开发者更深入地理解模型的行为和训练过程中的各种动态情况。
简而言之,通过在runs/fashion_mnist_experiment_1路径下保存数据,TensorBoard提供了一种直观、方便的方式来查看和分析模型的训练和性能。

通过使用SummaryWriter提供的各种方法来存储变量(比如损失值、准确率、权重分布、输入输出图像等)到日志文件中,然后TensorBoard能够读取这些日志文件并将存储的数据以可视化形式展示出来。这种方式相比于直接使用Matplotlib绘图有几个显著的优势:

  1. 实时监控:TensorBoard允许您在模型训练过程中实时监控各种指标的变化(通过存储日志文件的方式保存数据读取并且在其他平台处理成图),这对于调整参数、发现过拟合或欠拟合等问题非常有帮助。而Matplotlib通常是在训练过程结束后,基于已收集的全部数据绘制图像,不便于实时观察。
    通过torch.utils.tensorboard.SummaryWriter将变量存储为日志文件,然后使用 TensorBoard 进行实时展示,可以很好地解决 Matplotlib 需要等到程序运行完才能绘图的问题。

  2. 交互性:TensorBoard提供了一个基于Web的交互界面,您可以在浏览器中查看图表和数据,能够轻松地进行缩放、过滤、比较等操作。Matplotlib虽然也能制作出高质量的图像,但其交互性较弱。

  3. 多维度数据展示:TensorBoard支持展示多种形式的数据,包括标量(如损失和准确率)、图像、音频、文本、图结构(网络结构)以及参数分布等。而在Matplotlib中,要实现这些功能需要更多的代码和处理。

  4. 面向实验管理:TensorBoard方便地对多次运行或实验进行记录和比较。您可以在不同时间点运行模型,然后在TensorBoard中并列比较这些运行的指标和输出,这对于实验管理和模型调参特别有用。

总结来说,TensorBoard为深度学习模型提供了一种高效、直观且交互式的监控和可视化工具。它特别适合于需要实时跟踪模型训练进程、分析模型结构以及比较不同模型性能的场景。而Matplotlib则在制作静态、发布质量的图表方面更有优势。两者可以根据需要和场景选择使用。

2. Writing to TensorBoard

现在让我们写入一些图像到我们的TensorBoard中——具体来说,是使用make_grid创建一个网格图像。

这段想用一个mat的图和tensorboard的图同时输出,展示器tensorboard的优越性。首先看一下iter和next函数的例子方便理解代码

my_list = [1, 2, 3] # 可迭代对象

# 获取迭代器
it = iter(my_list)

# 使用 next 遍历迭代器
print(next(it))  # 输出: 1
print(next(it))  # 输出: 2
print(next(it))  # 输出: 3

# 下一次调用 next,迭代器已经没有元素,将抛出 StopIteration
# print(next(it))  # 引发 StopIteration 异常

#######使用for也能事项相同的功能
my_list = [1, 2, 3]
for item in my_list:
    print(item)

正文代码如下

# 获取一些随机的训练图像
dataiter = iter(trainloader)  # 可以迭代的对象。trainloader可迭代对象 ,iter对可迭代对象进行分批次取出。其实上文中已经看到了通过数据集构建的可迭代对象trainloader
images, labels = next(dataiter) # 使用next对迭代器对象换成下一个。其组合方式和for类似.

# 创建图像网格,其主要就是做一个表格将一个图塞进一个表格便于展示
img_grid = torchvision.utils.make_grid(images)

'''
官方一点的解释
torchvision.utils.make_grid 函数的主要目的是将多个图像(
通常是一个批次的图像)整理成一个网格形式的大图像,以便于进行
展示和对比。这种 “表格” 形式的展示可以让我们一眼看到多张图像
,非常适合于训练过程中查看数据样本,或者查看模型生成 / 修改
的图像效果。
'''
# 可以看到其主要迭代对象是一个loader就决定了其每次拿出来的都是一个batchsie的图。所以trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,四张图,塞进四个表格中
# 展示图像
matplotlib_imshow(img_grid, one_channel=True) # 灰度图展示方式
 # 注意这个要是在jupyter 环境下可以直接进行输出,举个例子a=4 在
 # jupyyer下就是会输出4但是pycharm和vscode就不会有任何输出,
 # 所以要是在vscode中需要加上plt.show() 
 
# 写入到tensorboard 将数据img_grid写入到之前创建的实例文件
writer.add_image('four_fashion_mnist_images', img_grid)


'''
'four_fashion_mnist_images' 在这个上下文中作为一个参数传递给 
writer.add_image() 方法,它的作用是标识或命名你要加入到 Tenso
rBoard 中的图像,可以理解为这张图像的标题或标签。
'''

下面这段话是执行代码的重点*********************************

jupyter环境下,按行执行代码后在jupyter的终端输入tensorboard --logdir=runs 即可开启tensorboard,注意runs是存放日志的文件夹。
如果要是在vscode就先关闭plt输出的图。,程序停止在终端下输入 --logdir=runs,即可得到结果:(不要关闭终端再去输入tensorboard不要关闭终端再去输入tensorboard --logdir=runs不要关闭终端再去输入tensorboard --logdir=runs
首先代码会弹出独立窗口展示matlplot的图如下所示


请添加图片描述

关闭这个图,x掉他。然后在终端输入如下代码:

tensorboard --logdir=runs

然后打开浏览器按照终端输出的网址打开即可得到
比较显眼的就是一个表格插入四种图,并且设定的标题都在。使用matplot直接输出的结果如下:

请添加图片描述

上述代码演示了如何从训练数据加载器中获取一批随机图像,并使用torchvision.utils.make_grid函数创建一个图像网格。然后使用matplotlib_imshow函数显示这个图像网格(假设您之前定义了这个函数,用于将PyTorch张量转换成matplotlib能够显示的格式)。最后,通过writer.add_image将这个图像网格添加到TensorBoard中,这样就可以在TensorBoard的界面上查看这个图像网格了。

执行tensorboard --logdir=runs命令将启动TensorBoard服务器,您可以通过浏览器访问 http://localhost:600* 给出的端口号不同别乱复制来查看TensorBoard界面和其中的图像网格。

在执行到这的时候电脑模型奇妙的numpy的包和matplot的冲突,同时导入的话,torchvison.dataset.数据集的函数死活是跳不出来循环♻️。改用jupyter一切正常都能执行。但是俺就不信那个xie。debug花费一天。终于发现了这个莫名其妙的解法。

3. Inspect the model using TensorBoard

上文中使用TensorBoard检查了一下数据的形态,可以在执行的过程中看数据发生的变化。其还可以通过TensorBoard检查模型。执行方法和之前的看数据方式一样,在这个TensorBoard页面选择Graphs既可以看到模型的架构图。请添加图片描述

请添加图片描述

其主要功能如下:

  1. 可视化模型结构
  • TensorBoard 提供了一种可视化模型架构的图形化表示。这让您能够理解模型的层次结构和操作流程,查看各层之间的连接,以及识别模型的组件结构。
  1. 追踪训练过程
  • 使用 TensorBoard,你可以追踪训练过程中的各种度量,如损失函数、准确率等随训练迭代变化的数据。这有助于评估模型的训练效果和性能,并进行调优。
  1. 参数分布和梯度
  • 查看模型参数(如权重和偏置)的分布和训练过程中的梯度变化。这对于识别训练问题,如过拟合或梯度消失/爆炸,是十分有用的。
  1. 特征表示
  • 分析模型中各层的输出,可以帮助理解模型是如何学习特征的,也有助于识别模型是否在注意到关键的信息。
  1. 查看嵌入
  • 如果模型包含嵌入层,TensorBoard 允许对嵌入空间进行可视化,帮助理解不同类别或标签之间的关系和聚类特性。

如何在 TensorBoard 中检查模型:

  1. 在你的 PyTorch 脚本中,使用 torch.utils.tensorboard 中的 SummaryWriter 来记录模型和想要追踪的信息。
  2. 在模型定义后,可以用 SummaryWriter.add_graph() 方法将模型结构记录下来。
  3. 在训练循环中,用 .add_scalar() 方法来追踪损失和其他指标。
  4. 训练完成后,启动 TensorBoard 指向日志文件所在的目录。
  5. 在浏览器中打开 TensorBoard,通过不同的选项卡来“inspect”或检查模型的各个方面。

通过以上步骤,您可以充分利用 TensorBoard 来检查、评估和分析您的模型,从而有针对性地进行调整和优化。

继续双击 “Net” 查看其展开,看到组成模型的各个操作的详细视图。
在这里插入图片描述

4. Adding a “Projector” to TensorBoard

TensorBoard 有一个非常方便的功能,用于将高维数据(如图像数据)可视化为低维空间;接下来我们将讨论这个。
这个技术就是将高维度的数据进行压缩到低维度进行展示,我们可以通过 add_embedding 方法可视化高维数据的低维表示:

# 定义一个辅助函数,用于从数据集中随机选择 n 个数据点及其对应的标签
def select_n_random(data, labels, n=100):
    '''
    Selects n random datapoints and their corresponding labels from a dataset
    '''
    # 断言数据和标签的长度相等,确保数据的一致性
    assert len(data) == len(labels) # 确保每个数据都有标签

    # 生成一个随机排列的索引,用于随机选择数据
    perm = torch.randperm(len(data)) # 判断下数据的长度,按照找个长度生成一个随机的索引序列
    '''
   比如len后得到4就会生成一个4231类似的随机序列
    '''
    # 使用随机索引选择 n 个数据和标签
    return data[perm][:n], labels[perm][:n] # 用这个索引序列取到前100个数据 。看不懂我在下面放了一个例子

# 使用定义的函数从 trainset 中随机选择图像和标签
images, labels = select_n_random(trainset.data, trainset.targets) 
# ********* 看一个函数,首先先看这个函数接受了什么,以及返回了什么。训练集中的数据以及标签,返回的也是一样的东西,再看函数内部。

# 根据选中的标签获取每个图像的类别标签
class_labels = [classes[lab] for lab in labels]

# 将选中的图像转换为一维特征向量,准备进行嵌入可视化
features = images.view(-1, 28 * 28) # 这个就是将高维度数据向量划作为特征

# 使用 TensorBoard 的 add_embedding 方法记录嵌入
writer.add_embedding(features,
                    metadata=class_labels,  # 将类别标签作为元数据
                    label_img=images.unsqueeze(1))  # 增加一个维度,以适配 add_embedding 方法的输入要求
writer.close()  # 关闭 writer,确保数据被写入日志文件

简单的讲讲就是使用 torch.randperm(len(data)),并且 len(data) 的值是 4,那么
torch.randperm(4) 将会生成一个长度为 4 的随机序列,序列中包含 0 到 3
的每个数字各一次,但顺序是随机的。例如,它可能生成 [2, 4, 3, 1] 这样的序列,或者任何其它0到3的随机排列,比如您提到的
4231(考虑到索引从0开始,实际上应该是 [3, 2, 1, 0] 这样的形式)。

在这里,torch.randperm 产生的随机排列序列被用来索引 datalabels,从而随机选择 n
个数据点和它们对应的标签。这种方法便于从数据集中抽取小部分数据进行实验或分析,而不用处理整个数据集,尤其在数据集较大时非常有用。

data[a, b, c, d]labels[1, 2, 3, 4],且
torch.randperm(4) 生成了随机序列 [3, 2, 1, 0] 为例,使用随机序列索引数据后将得到:

  • 随机选择的 data[d, c, b, a]
  • 对应的 labels[4, 3, 2, 1]

如果我们只取前 n(例如 n=2)个数据点,那么结果将是前两个元素:

  • 随机选择的数据点:[d, c]
  • 对应的标签:[4, 3]

将上述代码复制到之前的代码后面,执行后关闭plt图然后在终端中输入tensorboard --logdir=runs。在 TensorBoard 的 “Projector” 标签页中。

请添加图片描述
你可以看到这 100 张图像 —— 每张图像都是 784 维 —— 被投影到三维空间中。此外,这个投影是可交互的:你可以点击并拖动以旋转三维投影。最后,为了使可视化更易于观察,提供几个小建议:在左上角选择 “color: label”,以及启用 “夜间模式”,这将使图像因为背景为白色而更易于看清:

不要觉得疑惑784时如何压缩成三维空间的呢???? 人家给你提供了多种技术的选择,默认是使用pca。一起来看看吧。各位可以按需进行选择哦:还有一点拖动找个图可以调整观看数据的角度

请添加图片描述

5. Tracking model training with TensorBoard

上面解释了如何看对数据降维操作,后面则是如何追踪模型的训练行为。在之前的示例中,我们仅仅每 2000 次迭代就打印一次模型的运行损失。现在,我们将改为将运行损失记录到 TensorBoard,并通过 plot_classes_preds 函数查看模型的预测情况。

就是记录损失并且将这个损失变成日志文件,从而通过tensorboard可视化 让我们通过一个具体的例子来解释这段代码是如何工作的: 文中给出的代码:

def images_to_probs(net, images): #训练模型将结果展示出来。看不懂这个函数的底下有一个例子
    '''
    Generates predictions and corresponding probabilities from a trained
    network and a list of images
    
	'''
    output = net(images) #把图送入网络
    # convert output probabilities to predicted class
    _, preds_tensor = torch.max(output, 1) # 预测的数值 ,及其索引信息.只要了索引
    preds = np.squeeze(preds_tensor.numpy()) # 改变索引数值从tensor 变成np并且删掉了维度为1的维度。
    return preds, [F.softmax(el, dim=0)[i].item() for i, el in zip(preds, output)] # (preds, output)]索引和计算结果向量。然后对计算结果向量进行softmax通过索引取到这个概率值。


def plot_classes_preds(net, images, labels): # 使用plot进行画图。人家选4也是因为分批了的原因
    '''
   使用训练好的网络以及一批图像和标签生成 matplotlib 图形,该图形显示了网络的顶级预测及其概率,
   同时显示了实际标签,并根据预测是否正确来对这些信息进行着色。使用了 “images_to_probs” 函数。
    '''
    preds, probs = images_to_probs(net, images) # 预测的类别和概率值
    # plot the images in the batch, along with predicted and true labels
    fig = plt.figure(figsize=(12, 48)) # 这里全面采用的是matplt。图的大小指定figure的宽和高,单位为英寸
    for idx in np.arange(4): # 从0-3。
        ax = fig.add_subplot(1, 4, idx+1, xticks=[], yticks=[])
        matplotlib_imshow(images[idx], one_channel=True)
        ax.set_title("{0}, {1:.1f}%\n(label: {2})".format(
            classes[preds[idx]],
            probs[idx] * 100.0,
            classes[labels[idx]]),
                    color=("green" if preds[idx]==labels[idx].item() else "red")) # 预测对了就是绿色不对就是红色
    return fig

假设有一个简单的神经网络 net,这个网络的任务是对图像进行分类。我们有一批图像
images,想要使用这个网络来预测每个图像的类别。

  1. 送入网络得到输出

    output = net(images)
    ```假设 `output` 是一个形状为 `[4, 3]` 的张量,表示这批中有4张图像,网络对于每张图像给出了属于3个类别的概率。比如:
    ```python
    tensor([[0.2, 0.5, 0.3],
            [0.1, 0.8, 0.1],
            [0.4, 0.4, 0.2],
            [0.6, 0.2, 0.2]])
    
  2. 找到每张图像的预测类别

    _, preds_tensor = torch.max(output, 1)
    

    torch.max(output, 1) 将会沿着张量的第1维(类别概率维度)寻找最大值,返回每张图像最可能的类别及其索引(即预测类别)。
    在我们的例子中,preds_tensor 将会是:

    tensor([1, 1, 0, 0])
    

    表示对于4张图像,模型预测的类别分别是类别1、类别1、类别0、类别0。

  3. 转换为 NumPy 数组并调整维度

    preds = np.squeeze(preds_tensor.numpy())
    

    使用 numpy()np.squeeze() 将预测结果从 PyTorch 张量转换为 NumPy 数组,并移除所有单一维度。如果 preds_tensor[1, 1, 0, 0],那么 preds 也将是 [1, 1, 0, 0]

  4. 计算每个预测的概率

    [F.softmax(el, dim=0)[i].item() for i, el in zip(preds, output)]
    

    这行代码遍历 output 中每个元素(每个类别的概率向量)和对应的 preds 预测索引,使用 F.softmax 计算 softmax 概率(使其和为1),然后通过 [i].item() 获取与预测索引相对应的概率值,将其转换为 Python 数值。

    以第一张图像为例,假设 el[0.2, 0.5, 0.3]i1(因为预测的类别是1):

    • F.softmax(el, dim=0) 将计算 [0.2, 0.5, 0.3] 的 softmax 值,可能得到 [0.24, 0.42, 0.34](仅为示例,实际值会有所不同)。
    • [i].item() 对于第一个元素,将会选择索引1的值,即 0.42,作为这张图像属于类别1的概率。

这样,对于每张图像,我们不仅得到了最可能的类别,还得到了模型对其进行该类别预测的置信度(即概率)。

最后,让我们使用之前教程中相同的模型训练代码来训练模型,但这次每 1000 批次将结果写入 TensorBoard,而不是打印到控制台;这是通过使用 add_scalar 函数来完成的。
另外,在我们训练的过程中,我们将生成一张图像,展示在该批次包含的四张图像上模型的预测结果与实际结果的对比。

这个就很简单的网络

running_loss = 0.0 # 初始化损失
for epoch in range(1):  # loop over the dataset multiple times

    for i, data in enumerate(trainloader, 0): # 对一个批次的数据进行编码

        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data #取出一个batch的数据

        # zero the parameter gradients
        optimizer.zero_grad() # 优化器中的梯度信息晴空

        # forward + backward + optimize
        outputs = net(inputs) # 网路开始预测
        loss = criterion(outputs, labels) # 计算损失
        loss.backward() # 计算梯度
        optimizer.step() # 开始优化

        running_loss += loss.item() #损失开始累积
        if i % 1000 == 999:    # every 1000 mini-batches...

            # ...log the running loss # 把计算的数据写进去
            writer.add_scalar('training loss', 
                            running_loss / 1000,
                            epoch * len(trainloader) + i)

            # ...log a Matplotlib Figure showing the model's predictions on a
            # random mini-batch # 把结果图也搞进去tensorboard
            writer.add_figure('predictions vs. actuals',
                            plot_classes_preds(net, inputs, labels),
                            global_step=epoch * len(trainloader) + i)
            running_loss = 0.0
print('Finished Training')


可以看到最终和展示两部分的结果

        writer.add_figure('predictions vs. actuals',
                        plot_classes_preds(net, inputs, labels),
                        global_step=epoch * len(trainloader) + i)
                        这个代码工作内容,绿色预测正确的红色的则是错误的

请添加图片描述
这个则是损失曲线的代码

        writer.add_scalar('training loss', 
                        running_loss / 1000,
                        epoch * len(trainloader) + i)

在这里插入图片描述

另外,我们可以在整个学习过程中查看模型对任意批次所做的预测。查看 “Images” 标签页,并向下滚动至 “predictions vs. actuals” 可视化下方可以看到这些内容;这告诉我们,例如,在仅仅 3000 次训练迭代后,模型已经能够区分视觉上不同的类别,如衬衫、运动鞋和外套,尽管它此时的自信度并不像训练后期那样高:

6. Assessing trained models with TensorBoard

绘制一个召回曲线的例子。基本都差不多各位可以酌情选择理解。

  1. 获取一个 test_size x num_classes 张量中的概率预测
  2. 获取一个 test_size 张量中的预测
    运行大约需要10秒钟
class_probs = []
class_label = []
with torch.no_grad():
    for data in testloader:
        images, labels = data
        output = net(images)
        class_probs_batch = [F.softmax(el, dim=0) for el in output]

        class_probs.append(class_probs_batch)
        class_label.append(labels)

test_probs = torch.cat([torch.stack(batch) for batch in class_probs])
test_label = torch.cat(class_label)

辅助函数

def add_pr_curve_tensorboard(class_index, test_probs, test_label, global_step=0):
    '''
    输入一个从 0 到 9 的“class_index”,并绘制相应的精确率-召回率曲线
    '''
    tensorboard_truth = test_label == class_index
    tensorboard_probs = test_probs[:, class_index]

    writer.add_pr_curve(classes[class_index],
                        tensorboard_truth,
                        tensorboard_probs,
                        global_step=global_step)
    writer.close()

绘制所有的 PR 曲线

for i in range(len(classes)):
    add_pr_curve_tensorboard(i, test_probs, test_label)

现在你会看到一个包含每个类别的精确率-召回率曲线的“PR Curves”标签页。请前往查看;你会发现在某些类别上模型几乎有100%的“曲线下面积”,而在其他一些类别上这个面积较低:

7. 总结

通过详细阅读官方文档,可以进一步理解 TensorBoard 作为深度学习工具,展示模型和训练过程强大功能的细节。它具备丰富的可调节特性,以及图形化界面,真正是深度学习研究和实践中的一把利器。开设这个专栏的目的,也是想为初学者提供一个更直观的平台,帮助他们理解那些看似复杂难懂的概念。好了,今天的分享就到这里。如果你对这个话题感兴趣,并希望了解更多,请随时留言催更。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐