Pytorch学习笔记:TensorBoard

简介: Pytorch学习笔记:TensorBoard

前言


作者是老朋友奥辰,本文是他Pytorch系列学习笔记之一,如果大家感兴趣,我再邀请他写个完整、系统的Pytorch教程

  • 1 开启TensorBoard的WEB应用
  • 2 SummaryWriter类
  • 3 写入数据
  • 3.1 标量数据
  • 3.2 图像数据
  • 3.3 模型结构
  • 4 总结

训练模型过程中,经常需要追踪一些性能指标的变化情况,以便了解模型的实时动态,例如:回归任务中的MSE、分类任务中的Accuracy、生成对抗网络中的图片、网络模型结构可视化…… 除了追踪外,我们还希望能够将这些指标以动态图表的形式可视化显示出来。

TensorFlow的附加工具Tensorboard就完美的提供了这些功能。不过现在经过Pytorch团队的努力,TensorBoard已经集成到了Pytorch中,只要安装有pytorch也可以直接使用TensorBoard。

Tensorboard同时提供了后端数据记录功能和前端数据可视化功能。通过后端数据记录功能,我们可以将需要追踪的性能指标写入到指定文件;通过前端数据可视化功能,我们可是实时查看当前训练情况。

在接下来的文章中,将对TensorBoard的使用方法进行介绍,如果你还没有安装,可以通过一下命令进行安装。注意,虽然torch集成有TensorBoard,但是并不完整,需要使用下面命令完整安装后,才能开启TensorBoard的WEB应用。


pip install tensorboard


正文


1 开启TensorBoard的WEB应用


在通过上述命令完成tensorboard的安装后,即可在命令行调用tensorboard进行启动。如下所示:


tensorboard --logdir=./run


运行后输出如下:

2345.pngimage.png

logdir参数的作用是指定读取记录数据的目录,如果该目录内又多个记录文件,也会在页面中列表显示。另外从输出结果中,tensorboard默认从6006端口启动,当然也可以通过port参数指定端口,如下所示,我们指定从8088端口启动:

tensorboard --logdir=./run --port 8088

在浏览器地址栏,我们输入对应地址,打开页面如下:

4323.pngimage.png

现在之所以提示这些信息,是因为我们还没有记录过任何数据。


2 SummaryWriter类


SummaryWriter是tensorboard中专门用来记录数据的类,只有通过SummaryWriter记录好的数据,才能在前端页面中展示。SummaryWriter类实例化时,主要参数如下:

  • log_dir (str):指定了数据保存的文件夹的位置,如果该文件夹不存在则会创建一个出来。如果没有指定的话,默认的保存的文件夹是./runs/现在的时间_主机名,例如:Dec16_21-13-54_DESKTOP-E782FS1,因此每次运行之后都会创建一个新的文件夹。
  • comment (string):给默认的log_dir添加的后缀,如果我们已经指定了log_dir具体的值,那么这个参数就不会有任何的效果
  • purge_step (int):TensorBoard在记录数据的时候有可能会崩溃,例如在某一个epoch中,进行到第𝑇+𝑋�+�个step的时候由于各种原因(内存溢出)导致崩溃,那么当服务重启之后,就会从𝑇�个step重新开始将数据写入文件,而中间的𝑋�,即purge_step指定的step内的数据都被被丢弃。
  • max_queue (int):在记录数据的时候,在内存中开的队列的长度,当队列慢了之后就会把数据写入磁盘(文件)中。
  • flush_secs (int):以秒为单位的写入磁盘的间隔,默认是120秒,即两分钟。
  • filename_suffix (string):添加到log_dir中每个文件的后缀.

In [4]:

import torch
from torch.utils.tensorboard import SummaryWriter

In [4]:

# 使用默认参数创建summary writer,程序将会自动生成文件名
writer = SummaryWriter()
# 生成的文件路径为: runs/Dec16_21-13-54_DESKTOP-E782FS1/
# 创建summary writer时,指定文件路径
writer = SummaryWriter("my_experiment")
# 生成的文件路径为: my_experiment
# 创建summary writer时,使用comment作为后缀
writer = SummaryWriter(comment="LR_0.1_BATCH_16")
# folder location: runs/Dec16_21-19-59_DESKTOP-E782FS1LR_0.1_BATCH_16/


3 写入数据


SummaryWriter类中定义了各式各样的方法,用于记录不同的数据,这些方法都已“add_”开头,我们先罗列一下这些方法:

In [8]:

writer = SummaryWriter('runs')
for i in SummaryWriter.__dict__.keys():
    if i.startswith("add_"):
        print(i)
add_hparams
add_scalar
add_scalars
add_histogram
add_histogram_raw
add_image
add_images
add_image_with_boxes
add_figure
add_video
add_audio
add_text
add_onnx_graph
add_graph
add_embedding
add_pr_curve
add_pr_curve_raw
add_custom_scalars_multilinechart
add_custom_scalars_marginchart
add_custom_scalars
add_mesh

大体来说,所能记录的数据类型包括标量(scalar)、图像(image)、统计图(diagram)、视频(video)、音频(audio)、文本(text)、Embedding等等。下面我们一次来说说怎么记录这些不同类型的数据。


3.1 标量数据


注意:训练过程中,添加loss等数据是,一定要通过item()方法,转换为标量之后才能添加到tensorboard中。

(1)add_scalar:一图一曲线

  • tag (str):用于给数据进行分类的标签,标签中可以包含父级和子级标签。例如给训练的loss以loss/train的tag,而给验证以loss/val的tag,这样的话,最终的效果就是训练的loss和验证的loss都被分到了loss这个父级标签下。而train和val则是具体用于区分两个参数的标识符(identifier)。此外,只支持二级标签。
  • global_step (int):首先,每个epoch中我们都会更新固定的step。因此,在一个数据被加入的时候,有两种step,第一种step是数据被加入时当前epoch已经进行了多少个step,第二种step是数据被加入时候,累计(包括之前的epoch)已经进行了多少个step。而考虑到我们在绘图的时候往往是需要观察所有的step下的数据的变化,因此global_step指的就是当前数据被加入的时候已经计算了多少个step。计算global_step的步骤很简单,就是𝑔𝑙𝑜𝑏𝑎𝑙_𝑠𝑡𝑒𝑝=𝑒𝑝𝑜𝑐ℎ∗𝑙𝑒𝑛(𝑑𝑎𝑡𝑎𝑙𝑜𝑎𝑑𝑒𝑟)+𝑐𝑢𝑟𝑟𝑒𝑛𝑡_𝑠𝑡𝑒𝑝
  • wlltime (int):从SummaryWriter实例化开始到当前数据被加入时候所经历时间(以秒计算),默认是使用time.time()来自动计算的,当然我们也可以指定这个参数来进行修改。这个参数一般不改

In [42]:

writer = SummaryWriter('runs/add_scalar')
for n_iter in range(100):
    writer.add_scalar('Loss/train', np.random.random(), n_iter)
    writer.add_scalar('Loss/test', np.random.random(), n_iter)
    writer.add_scalar('Accuracy/train', np.random.random(), n_iter)
    writer.add_scalar('Accuracy/test', np.random.random(), n_iter)
writer.close()

4567.png

image.png

使用不同的SummaryWriter实例,相同的tag进行记录数据时,将可以实现在同一张图表中显示多条曲线。注意,实例化SummaryWriter时,必须指定不同的文件夹,否则多个记录数据虽然也是在同一图表中,但是图是混乱的。

In [45]:

# 两个Accuracy写入不同的文件夹中
writer1 = SummaryWriter('runs/add_scalar-1')
writer2 = SummaryWriter('runs/add_scalar-2')
for n_iter in range(100):
    # 使用两个SummaryWriter分别记录LOSS和Accuracy,注意,tag必须一样
    writer1.add_scalar('Loss', np.random.random(), n_iter)
    writer2.add_scalar('Loss', np.random.random(), n_iter)
    writer1.add_scalar('Accuracy', np.random.random(), n_iter)
    writer2.add_scalar('Accuracy', np.random.random(), n_iter)
writer1.close()
writer2.close()

7656.png

image.png


(2)add_scalars:一图多曲线


一张图显示多条曲线可以更加方便得对比数据,SummaryWriter中提供add_scalars方法实现在一张图中绘制多条曲线。

  • main_tag (str):多条曲线共用的标签
  • tag_scalar_dict (dict):多个需要记录的数据组成的键值对
  • global_step (int):训练的 step
  • walltime (float):从SummaryWriter实例化开始到当前数据被加入时候所经历时间(以秒计算)

In [44]:

writer = SummaryWriter('runs/add_scalar')
for n_iter in range(100):
    writer.add_scalars('Loss', {'test':np.random.random(),'train':np.random.random()}, n_iter)
writer.close()

55434.png

image.png


3.2 图像数据


(1)add_image

  • tag (str):数据标签
  • img_tensor:图像数据,数据类型可以使torch.Tensor, numpy.ndarray, string/blobname
  • global_step (int):训练的step
  • walltime (float):从SummaryWriter实例化开始到当前数据被加入时候所经历时间(以秒计算)
  • dataformats (str):图像数据的格式,可以是CHW, HWC, HW, WH等,默认为CHW,即Channel x Height x Width。通常来说,默认即可,但如果图像tensor不是CHW,就要通过这个参数指定了。

在本地文件夹有images下有多张图片,我们选择一张将其记录到tensorboard中:

In [7]:

from torchvision.io import read_image

In [8]:

# 将图片打开为torch.Tensor类型
img = read_image('images/0975.jpg')

In [9]:

img.shape

Out[9]:

torch.Size([3, 224, 224])

可见,图片为CHW类型。我们将所有图片上传记录:

In [14]:

writer = SummaryWriter('runs/add_image')
path_lst = [os.path.join('images', i) for i in os.listdir('images')]
for i, img in enumerate(path_lst):
    img = read_image(img)
    writer.add_image('img', img, i)
writer.close()

556.gifadd_image.gif

add_image方法一般情况下只能一次插入一张图片。如果要一次性插入多张图片,可以使用 torchvision 中的 make_grid 方法,将多张图片拼合成一张图片后,再调用 add_image 方法。

In [15]:

from torchvision.utils import make_grid

In [18]:

path_lst = [os.path.join('images', i) for i in os.listdir('images')]
img_lst = []
for i, img in enumerate(path_lst):
    img = read_image(img)
    img_lst.append(img)
writer = SummaryWriter('runs/add_image')
img_grid = make_grid(img_lst, nrow=5)
writer.add_image('img_grid', img_grid)
writer.close()

3456.pngimage.png

(2) add_images

add_images是tensorboard中提供直接一次性记录多张图片的方法,此方法参数与add_image基本一致,区别就在于记录的数据是多张图片组成的torch.Tensor或numpy.array, 数据的shape为(N,3,H,W),其中N为图片数量。

In [29]:

path_lst = [os.path.join('images', i) for i in os.listdir('images')]
img_lst = []
for i, img in enumerate(path_lst):
    img = read_image(img)
    img_lst.append(img)
imgs_tensor = torch.stack(img_lst,0)

In [30]:

writer = SummaryWriter('runs/add_image')
writer.add_images('add_images', imgs_tensor)
writer.close()

5567.pngimage.png


3.3 模型结构


使用add_graph方法,可以绘制模型结构:

  • model (torch.nn.Module) :需要绘制的模型
  • input_to_model (torch.Tensor or list of torch.Tensor):传递给模型的一个数据
  • verbose (bool) :是否同时在命令行中绘制

In [54]:

import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable

先定义一个模型:

In [55]:

class Net1(nn.Module):
    def __init__(self):
        super(Net1, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)
        self.bn = nn.BatchNorm2d(20)
    def forward(self, x):
        x = F.max_pool2d(self.conv1(x), 2)
        x = F.relu(x) + F.relu(-x)
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = self.bn(x)
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        x = F.softmax(x, dim=1)
        return x

In [59]:

dummy_input = Variable(torch.rand(13, 1, 28, 28))
model = Net1()
with SummaryWriter('runs/Net1') as w:
    w.add_graph(model, (dummy_input, ))

9877.gif

GIF 2022-12-17 星期六 11-16-19.gif


4 总结


除了上述介绍到的方法外,tensorboard还提供许多其他方法,但我认为用到不多,这里就不介绍了,可以参考官方文档。

相关文章
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(五):nn.AdaptiveAvgPool2d()函数详解
PyTorch中的`nn.AdaptiveAvgPool2d()`函数用于实现自适应平均池化,能够将输入特征图调整到指定的输出尺寸,而不需要手动计算池化核大小和步长。
146 1
Pytorch学习笔记(五):nn.AdaptiveAvgPool2d()函数详解
|
2月前
|
算法 PyTorch 算法框架/工具
Pytorch学习笔记(九):Pytorch模型的FLOPs、模型参数量等信息输出(torchstat、thop、ptflops、torchsummary)
本文介绍了如何使用torchstat、thop、ptflops和torchsummary等工具来计算Pytorch模型的FLOPs、模型参数量等信息。
305 2
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(六):view()和nn.Linear()函数详解
这篇博客文章详细介绍了PyTorch中的`view()`和`nn.Linear()`函数,包括它们的语法格式、参数解释和具体代码示例。`view()`函数用于调整张量的形状,而`nn.Linear()`则作为全连接层,用于固定输出通道数。
107 0
Pytorch学习笔记(六):view()和nn.Linear()函数详解
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(四):nn.MaxPool2d()函数详解
这篇博客文章详细介绍了PyTorch中的nn.MaxPool2d()函数,包括其语法格式、参数解释和具体代码示例,旨在指导读者理解和使用这个二维最大池化函数。
157 0
Pytorch学习笔记(四):nn.MaxPool2d()函数详解
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(三):nn.BatchNorm2d()函数详解
本文介绍了PyTorch中的BatchNorm2d模块,它用于卷积层后的数据归一化处理,以稳定网络性能,并讨论了其参数如num_features、eps和momentum,以及affine参数对权重和偏置的影响。
194 0
Pytorch学习笔记(三):nn.BatchNorm2d()函数详解
|
2月前
|
机器学习/深度学习 PyTorch TensorFlow
Pytorch学习笔记(二):nn.Conv2d()函数详解
这篇文章是关于PyTorch中nn.Conv2d函数的详解,包括其函数语法、参数解释、具体代码示例以及与其他维度卷积函数的区别。
208 0
Pytorch学习笔记(二):nn.Conv2d()函数详解
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(七):F.softmax()和F.log_softmax函数详解
本文介绍了PyTorch中的F.softmax()和F.log_softmax()函数的语法、参数和使用示例,解释了它们在进行归一化处理时的作用和区别。
466 1
Pytorch学习笔记(七):F.softmax()和F.log_softmax函数详解
|
2月前
|
机器学习/深度学习 PyTorch 算法框架/工具
Pytorch学习笔记(八):nn.ModuleList和nn.Sequential函数详解
PyTorch中的nn.ModuleList和nn.Sequential函数,包括它们的语法格式、参数解释和具体代码示例,展示了如何使用这些函数来构建和管理神经网络模型。
105 1
|
2月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(一):torch.cat()模块的详解
这篇博客文章详细介绍了Pytorch中的torch.cat()函数,包括其定义、使用方法和实际代码示例,用于将两个或多个张量沿着指定维度进行拼接。
93 0
Pytorch学习笔记(一):torch.cat()模块的详解
|
2月前
|
PyTorch 算法框架/工具 Python
Pytorch学习笔记(十):Torch对张量的计算、Numpy对数组的计算、它们之间的转换
这篇文章是关于PyTorch张量和Numpy数组的计算方法及其相互转换的详细学习笔记。
39 0