网络结构的日益复杂使得我们在设计和调试算法的时候越来越难直接通过代码来确定神经网络的内部结构、输入输出以及参数等信息。因此,我们需要借助图形化的交互工具来辅助我们完成神经网络结构设计和神经网络训练调试。
在Tensorflow中,我们可以使用tensorflow.summary来记录网络结构,并通过Tensorboard对网络结构进行显示,通过可视化地查看网络结构辅助我们对神经网络各个部分的结构进行了解,并对训练中的各种训练数据进行动态记录和显示,以辅助算法调试。此外,Tensorflow中name_scope
和variable_scope
的概念可以帮助我们对代码进行结构化的分割和显示,从而是我们能够通过图形化交互更清晰地分析代码所实现出的网络结构。
在Pytorch中,尚没有一种便利的工具来对模型结构进行可视化,作为折衷,人们开发了torchinfo工具包 (由torchsummary和torchsummaryX重构出的库),通过文本输出描述的形式对网络模型结构进行描述。(尽管有了这样的工具,但是感觉还是没有Tensorflow+Tensorboard的组合更好用。不过,现在Pytorch也可以结合TensorboardX使用,通过添加图的方式,采用与Tensorboard类似的方式,也可以对其网络结构进行可视化显示,训练过程也同样可以通过中间结果绘制进行监控。)
考虑如下模型(Pytorch实现)
import torch.nn as nn
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3)
self.pool = nn.MaxPool2d(kernel_size = 2,stride = 2)
self.conv2 = nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5)
self.flatten = nn.Flatten()
self.linear1 = nn.Linear(20736,32)
self.relu = nn.ReLU()
self.linear2 = nn.Linear(32,1)
self.sigmoid = nn.Sigmoid()
def forward(self,x):
x = self.conv1(x)
x = self.pool(x)
x = self.conv2(x)
x = self.pool(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
y = self.sigmoid(x)
return y
model = Net()
print(model)
通过print函数调用,可以打印出其网络结构如下
Net(
(conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1))
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=20736, out_features=32, bias=True)
(relu): ReLU()
(linear2): Linear(in_features=32, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
从上述过程我们可以看出,单纯的print函数,只能得出基础构件的信息,既不能显示出每一层的shape,也不能显示对应参数量的大小。那么,采用其他方式是否可以获取到更多信息呢?
仍然使用上述模型,采用torchinfo对网络结构进行描述
from torchinfo import summary
summary(model, (1, 3, 84, 84)) # 1:batch_size 3:图片的通道数 224: 图片的高宽
则可得到如下结果
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Net -- --
├─Conv2d: 1-1 [1, 32, 82, 82] 896
├─MaxPool2d: 1-2 [1, 32, 41, 41] --
├─Conv2d: 1-3 [1, 64, 37, 37] 51,264
├─MaxPool2d: 1-4 [1, 64, 18, 18] --
├─Flatten: 1-5 [1, 20736] --
├─Linear: 1-6 [1, 32] 663,584
├─ReLU: 1-7 [1, 32] --
├─Linear: 1-8 [1, 1] 33
├─Sigmoid: 1-9 [1, 1] --
==========================================================================================
Total params: 715,777
Trainable params: 715,777
Non-trainable params: 0
Total mult-adds (M): 76.87
==========================================================================================
Input size (MB): 0.08
Forward/backward pass size (MB): 2.42
Params size (MB): 2.86
Estimated Total Size (MB): 5.37
==========================================================================================
从该结果,我们可以看到torchinfo提供了更加详细的信息,包括模块信息(每一层的类型、输出shape和参数量)、模型整体的参数量、模型大小、一次前向或者反向传播需要的内存大小,但仍然不是很直观。
仍然使用上述模型,采用TensorboardX对模型的网络结构进行描述
from tensorboardX import SummaryWriter
writer = SummaryWriter('./runs')
# Use TensorboardX to show the model
writer.add_graph(model, input_to_model=torch.rand(1, 3, 84, 84))
writer.close()
然后,通过Tensorboard进行展示,Tensorboard启动命令为
tensorboard --logdir=runs
通过点击显示的链接即可从浏览器访问Tensorboard,看到网络结构
通过点击相应的模块进行展开,可以看到各个模块的参数配置情况(如下图右侧部分给出了线性层linear2
的参数配置情况)。
可以看到,通过图形交互的方式,Tensorboard带给网络模型设计的使用体验。事实上,我们可以将TensorBoard看做一个记录员,它可以记录我们指定的数据,包括模型每一层的feature map,权重,以及训练loss等等。TensorBoard将记录下来的内容保存在一个用户指定的文件夹里,程序不断运行中TensorBoard会不断记录,记录下的内容可以再通过网页的形式加以可视化。
上述三个部分,我们给出了三种网络结构可视化的方式,其中,前两个更为便利,而使用Tensorboard的方式达到的效果则对人更为友好,并且使用过程也并不会增加很多代码量和工作量。因此,我觉得还是采用Tensorboard的方式更好一些。
除去上述对网络模型的可视化外,Tensorboard/TensorboardX(二者实际上差不多,以下不加以区分,均称为Tensorboard)还可以对训练过程中的连续变量、参数分布、图像、声音、文本等多类型数据进行可视化,并通过在页面选择不同类型的数据获得对应数据的可视化显示,如下图右侧所示。
连续变量可视化可通过add_scalar
方法实现对数据的记录,然后通过Tensorboard进行绘制,如
writer = SummaryWriter('./pytorch_tb')
for i in range(500):
x = i
y = x**2
writer.add_scalar("x", x, i) #日志中记录x在第step i 的值
writer.add_scalar("y", y, i) #日志中记录y在第step i 的值
writer.close()
上述可以分别得到一条x
的曲线和一条y
的曲线。如果想要两条曲线绘制在同一张图上,则需要分别建立存放子路径(使用SummaryWriter指定路径即可自动创建,但需要在tensorboard运行目录下),同时在add_scalar中修改曲线的标签使其一致即可
writer1 = SummaryWriter('./pytorch_tb/x')
writer2 = SummaryWriter('./pytorch_tb/y')
for i in range(500):
x = i
y = x*2
writer1.add_scalar("same", x, i) #日志中记录x在第step i 的值
writer2.add_scalar("same", y, i) #日志中记录y在第step i 的值
writer1.close()
writer2.close()
当我们需要对参数(或向量)的变化,或者对其分布进行研究时,可以方便地用TensorBoard来进行可视化,通过add_histogram实现,如
import torch
import numpy as np
# 创建正态分布的张量模拟参数矩阵
def norm(mean, std):
t = std * torch.randn((100, 20)) + mean
return t
writer = SummaryWriter('./pytorch_tb/')
for step, mean in enumerate(range(-10, 10, 1)):
w = norm(mean, 1)
writer.add_histogram("w", w, step)
writer.flush()
writer.close()