第五节:基于Pytorch的相关可视化
在Pytorch发布后,网络及训练过程的可视化工具也相应的被开发出来来帮助用户监督所建立的模型的结构和训练过程
本章将讲解HiddenLayer库,HiddenLayer库是一个非常简单、已与扩展、可用于可视化深度学习训练过程及网络结构的、可以喝Jupyter Notebook完美兼容的库
HiddenLayer开发的初衷是对于小型的项目,没有必要使用TensorBoard这类复杂的高级工具来进行检测,所以HiddenLayer是一个轻量化、小型的可视化工具,除了对Pytorch的支持外,HiddenLayer还支持Keras、TensorFlow等高级工具
此外我们还会介绍PyTorchViz这个用于创建PyTorch执行图和跟踪的可视化图库,这个库非常的请谅解,我们可以使用其提供的make_dot函数来可视化网络结构
tensorboardX是PyTorch用于链接TensorFlow的tensorboard可视化工具的库,通过tensorboardX我们可以使用深度学习可视化神奇tensorboard来监督、查看PyTorch的深度学习网络的训练过程,并且tensorboardX库非常容易使用
最后我们会介绍Visdom库,Visdom库是FB为PyTorch专门开发的一款可视化工具,该库使用时较为灵活,可用于创建、组织和共享实时丰富数据的可视化图像,可以直接对Tensor进行操作,也可以直接操作Numpy库,能够胜任大部分数据可视化任务
通常来说我们以文本的形式来展示网络的结构是非常不利于理解的,因为层与层之间之间会具有联系,例如ResNet
想要直观的了解网络的结构需要求我们以图片的形式进行展示
此外我们能够直观的检测网络训练过程将会帮助我们更好的进行训练
因此我们接下来可视化分为两部分:可视化网络以及可视化训练过程
准备网络和数据
我们首先定义一个简单的CNN来对手写数字进行分类,我们接下来的可视化都将基于这个网络来进行
首先导包
import torch
import torch.nn as nn
import torchvision
import torchvision.utils as vutils
from torch.optim import SGD
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
import numpy as np
接下来导入数据集并进行加载
train_data=torchvision.datasets.MNIST(root='./data/MNIST/',
train=True,
transform=torchvision.transforms.ToTensor(),
download=False)
train_loader=Data.DataLoader(dataset=train_data,shuffle=True,num_workers=2,batch_size=128)
test_data=torchvision.datasets.MNIST(root='./data/MNIST/',train=False,transform=torchvision.transforms.ToTensor(),
download=False)
test_data_X=test_data.data.type(torch.FloatTensor) / 255.0
test_data_X=torch.unsqueeze(input=test_data_X,dim=1)
test_data_y=test_data.targets
for step,(batch_x,batch_y) in enumerate(train_loader):
if step>0:
break
print(len(train_loader))
print(batch_x.shape)
print(batch_y.shape)
print(test_data_X.shape)
print(test_data_y.shape)
>>>
469
torch.Size([128, 1, 28, 28])
torch.Size([128])
torch.Size([10000, 1, 28, 28])
torch.Size([10000])
接下来定义并实例化一个网络
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet,self).__init__()
self.conv1=nn.Sequential(\
nn.Conv2d(in_channels=1,out_channels=16,kernel_size=(3,3),stride=1,padding=1),
nn.ReLU(),
nn.AvgPool2d(kernel_size=(2,2),stride=2))
self.conv2=nn.Sequential(\
nn.Conv2d(in_channels=16,out_channels=32,kernel_size=(3,3),stride=1,padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=(2,2),stride=2))
self.fc=nn.Sequential(\
nn.Linear(in_features=32*7*7,out_features=128),
nn.ReLU(),
nn.Linear(in_features=128,out_features=64),
nn.ReLU())
self.predict=nn.Linear(in_features=64,out_features=10)
def forward(self,x):
x=self.conv1(x)
x=self.conv2(x)
x=self.fc(x)
output=self.predict(x)
return x
MyConvNet=ConvNet()
print(MyConvNet)
>>>
ConvNet(
(conv1): Sequential(
(0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): AvgPool2d(kernel_size=(2, 2), stride=2, padding=0)
)
(conv2): Sequential(
(0): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): MaxPool2d(kernel_size=(2, 2), stride=2, padding=0, dilation=1, ceil_mode=False)
)
(fc): Sequential(
(0): Linear(in_features=1568, out_features=128, bias=True)
(1): ReLU()
(2): Linear(in_features=128, out_features=64, bias=True)
(3): ReLU()
)
(predict): Linear(in_features=64, out_features=10, bias=True)
)
可视化网络
HiddenLayer可视化网络
接下里我们使用HiddenLayer库来可视化网络
HiddenLayer库中包含一个build_graph函数可以方便的帮助我们进行可视化
HiddenLayer可视化的原理就是跟踪一个输入,然后绘制出该输入经过的结构,即网络的结构
import hiddenlayer as hl
MyConvNet_graph=hl.build_graph(MyConvNet,torch.zeros(size=[1,1,28,28]))
MyConvNet_graph.theme=hl.graph.THEMES['blue'].copy()
MyConvNet_graph.save(path='./MyConvNetArchitect.png',format='png')
上面的程序中我们使用了Hidden、
Layer的build_graph函数,为其传入需要绘制结构的网络,接下来我们又传入了一个可用于网络计算的输入x来进行追踪
我们又设置需要绘制的图像的主题为蓝色
最后我们使用svae函数来保存绘制的图像,需要为save函数传入保存的路径和格式即可
得到的结构图如下
PyTorchViz可视化
除了使用HiddenLayer来可视化网络以外,我们还可以使用PyTorchViz来可视化网络
PyTorchViz主要使用make_dot函数来进行绘制,其原理和HiddenLayer相同,都是对输入进行追踪
不过不同的是HiddenLayer对输入的追踪集成到了函数内部,PyTorchViz则需要通过PyTorch来进行追踪
from torchviz import make_dot
x=torch.randn(size=(1,1,28,28)).requires_grad_(True)
y=MyConvNet(x)
MyConvNetVis=make_dot(y,params=dict(list(MyConvNet.named_parameters())+[('x',x)]))
MyConvNetVis.format='png'
MyConvNetVis.directory='./'
MyConvNetVis.view()
这里我们首先通过PyTorch初始化了一个输入x,并指明需要计算其梯度,接下来进行计算得到其输出
然后我们使用make_dot类进行绘图,我们需要指定输出和网络中所有的参数来帮助我们绘图,即这列的list(MyConvNet.named_parameters())+['x',x]
我们指定format属性来设置格式,指定directory来指定路径,最后使用view来显示图像
得到的效果如下
可视化训练过程
网络结构可视化主要帮助我们理解搭建的网络或者搭建网络时存在的错误
而对训练过程进行可视化则是为了监督训练过程、增加对网络的理解等等
接下来我们将使用tensorboardX和HiddenLayer库来可视化我们的训练过程
tensorboardX可视化训练过程
tensorboard是TensorFlow的可视化工具,能够帮助我们对TensorFlow搭建出的网络进行较好的可视化
而PyTorch本身却并没有自带的可视化工具,因此tensorboardX是基于tensorboard为PyTorch开发的,调用tensorboard的库
tensorboard可视化训练过程的基本思想是创建一个编写器来对每一次训练进行记录(Summary),我们每次向其中添加本次训练的参数即可
tensorboardX常用的库
tensorboardX库中常用的功能和调用方式如下
函数 | 功能 | 用法 |
---|---|---|
SummaryWrite | 创建编写器,保存日志 | writer=SummaryWrite() |
weiter.add_scalar() | 添加标量 | writer.add_scalar(‘myscalar’,value,iteration) |
writer.add_image() | 添加图像 | writer.add_image(‘imresult’,x,iteration) |
writer.addhistogram() | 添加直方图 | writer.add_histogram(‘hist’,array,iteration) |
writer.add_graph() | 添加网络结构 | writer.add_graph(model,input_to_model=None) |
writer.add_audio() | 添加音频 | writer.add_audio(tag,audio,iteration,sample_true) |
writer.add_text() | 添加文本 | weiter.add_text(tag,text_string,global_step=None) |
上面列举了常用的可视化方法,如在图像中添加标量,文本,音频,直方图等等
下面将使用上面的网络来进行可视化
tensorboardX进行可视化
from tensorboardX import SummaryWriter
SummaryWriter=SummaryWriter(log_dir='./log')
optimizer=torch.optim.Adam(params=MyConvNet.parameters(),lr=0.0003,betas=(0.9,0.999),eps=1e-8)
lossFunc=torch.nn.CrossEntropyLoss()
train_loss=0
print_step=100
for epoch in range(5):
for step,(batch_x,batch_y) in enumerate(train_loader):
output=MyConvNet(batch_x)
loss=lossFunc(output,batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss+=loss
niter=epoch*len(train_loader)+step+1
if niter % print_step ==0:
SummaryWriter.add_scalar('train loss',train_loss.item()/niter,global_step=niter)
output=MyConvNet(test_data_X)
_,pre_lab=torch.max(output,1)
acc=accuracy_score(test_data_y,pre_lab)
SummaryWriter.add_scalar('test acc',acc.item(),niter)
batch_x_image=vutils.make_grid(batch_x,nrow=12)
SummaryWriter.add_image('train image sample',batch_x_image,niter)
for name,param in MyConvNet.named_parameters():
SummaryWriter.add_histogram(name,param.data)
if niter%500==0:
print('in training')
这里我们在训练的过程中每100轮训练进行一次记录,我们使用add_scalar记录需要保存的常数及其tag/标签
对于add_scalar函数,我们使用的时候需要指定保存的tag,保存的对象的值和当前值在全局的训练轮次
最后我们对数字进行了可视化,首先使用torchvision.utils中的make_grid生成了数字的网格图
然后使用add_image方法将图像添加到我们的编写器中
查看tensorboardX可视化的结果
接下来我们就需要查看可视化的结果,上面我们将日志保存到了log文件夹下
我们生成的是tensorboardX得到的编写器是一个需要使用tensorboard来读取的文件
我们在命令行中输入如下语句,就会得到一个保存了所有内容的本地网页
(pytorchlearning) jack@jack-Alienware-Area-51m:~/PytorchLearning$ tensorboard --logdir='./log/'
>>>
TensorBoard 1.13.1 at http://jack-Alienware-Area-51m:6006 (Press CTRL+C to quit)
打开这个网页其中就是我们可视化的结果
scalar栏目下就是我们记录的所有的常量
虽然tensorboard可视化训练过程的效果很强大,但是对于小型的网络进行可视化就很繁琐了
HiddenLayer可视化训练过程
下面我们将使用HiddenLayer库进行可视化,通常对于小型网络的可视化我们都是使用HiddenLayer进行的
使用HiddenLayer进行可视化实际上和使用tensorboard进行可视化的过程都是大同小异的
%%time
import hiddenlayer as hl
import time
MyConvNet=ConvNet()
optimzer=torch.optim.Adam(MyConvNet.parameters(),lr=0.0003)
lossFunc=nn.CrossEntropyLoss()
history1=hl.History()
canvas1=hl.Canvas()
print_step=100
for epoch in range(5):
for step,(batch_x,batch_y) in enumerate(train_loader):
output=MyConvNet(batch_x)
loss=lossFunc(output,batch_y)
optimzer.zero_grad()
loss.backward()
optimzer.step()
if step%100==0:
output=MyConvNet(test_data_X)
_,pre_lab=torch.max(output,1)
acc=accuracy_score(test_data_y,pre_lab)
history1.log((epoch,step),train_loss=loss,test_acc=acc,hidden_weight=MyConvNet.fc[2].weight)
with canvas1:
canvas1.draw_plot(history1['train_loss'])
canvas1.draw_plot(history1['test_acc'])
canvas1.draw_image(history1['hidden_weight'])
>>>
CPU times: user 4min 2s, sys: 20 s, total: 4min 22s
Wall time: 34.4 s
我们训练的过程都是大同小异的,不同之处在于我们一开始初始化了HiddenLayer的History对象来记录训练数据,以及初始化了Canvas对象来进行绘图,我们在训练的时候调用history对象的log方法来保存日志,最后调用canvas的draw_plot方法来绘制图像
Visdom可视化
Visdom是FB专门为PyTorch开发的可视化工具,这个库非常灵活,可以用于创建、组织和共享实时丰富数据的可视化
Visdom的可视化是基于网页实现的,能够让用户以多种方式来进行交互的可视化,从而对数据的可视化理解更加直观
可视化对象支持PyTorch中的tensor和Numpy的数组
Visdom中常用的可视化结构函数和功能如下
可视化函数 | 功能 |
---|---|
vis.image | 可视化图像 |
vis.images | 可视化一个batch的图像或一个图像列表 |
vis.text | 可视化文本 |
vis.video | 播放视频 |
vis.audio | 播放音频 |
vis.matplot | 可视化matplot中的图像 |
vis.scatter | 可视化散点图 |
vis.line | 可视化线图 |
vis.stem | 可视化茎叶图 |
vis.heatmap | 可视化热力图 |
vis.bar | 可视化条形图 |
vis.histogram | 可视化直方图 |
vis.boxplot | 可视化箱型图 |
vis.surf | 可视化曲面图 |
vis.contour | 可视化等高线图 |
vis.quiver | 可视化箭头图 |
vis.mesh | 可视化网格图 |
我们下面将使用鸢尾花数据集来进行讲解
我们首先导包
from visdom import Visdom
from sklearn.datasets import load_iris
此外,我们首先需要在命令行执行如下命令来开启visdom后台服务器,来实现实时绘图,绘图期间需要保持该命令行终端开启
我们执行命令后就会得到一个本地的地址,例如我这里是http://localhost:8097
,我们在浏览器中打开就可以看到实时绘制的图像
python -m visdom.server
>>>
(pytorchlearning) jack@jack-Alienware-Area-51m:~$ python -m visdom.server
/home/jack/anaconda3/envs/pytorchlearning/lib/python3.7/site-packages/visdom/server.py:39: DeprecationWarning: zmq.eventloop.ioloop is deprecated in pyzmq 17. pyzmq now works with default tornado and asyncio eventloops.
ioloop.install() # Needs to happen before any tornado imports!
It's Alive!
INFO:root:Application Started
You can navigate to http://localhost:8097
绘制二维/三维散点图
我们使用Visdom绘图的时候首先需要实例化一个Visdom对象,接下来调用Visdom对象的scatter方法来进行绘图
需要注意的是,scatter方法能够自动的识别我们传入的是二维数据还是三维数据,从而自动的生成二维点与三维点
vis = Visdom()
vis.scatter(iris_x[:,0:2],Y=iris_y+1,win='windows1',env='main')
vis.scatter(iris_x[:,0:3],Y=iris_y+1,win='3D 散点图',env='main',opts=dict(markersize=4,xlabel='特征1',ylabe='特征2'))
需要注意的是,Visdom将一个网页分为了多个windows和environment,我们在绘制图像的时候还需要指定对应的窗口和环境,此外我们在绘制三维散点图的时候指定opts参数来设定点的格式
执行后打开网页就会看到实时渲染的图片,我们能够看到上方的选项卡中有环境的选择,而我们的两个图片都在各自的窗口中,我们使用鼠标就可以进行交互
绘制折线图
下面我们将绘制折线图,绘制折线图主要使用的是visdom对象的line方法
Sigmoid=nn.Sigmoid()
ReLu=nn.ReLU()
Tanh=nn.Tanh()
x=torch.linspace(start=-6,end=6,steps=100).view((-1,1))
plot_y=torch.cat((Sigmoid(x),ReLu(x),Tanh(x)),dim=1)
plot_x=torch.cat((x,x,x),dim=1)
vis.line(Y=plot_y,X=plot_x,win="line plot",env='new',
opts=dict(dash=np.array(['soild','dash','dashshot']),
legend=['Sigmoid','ReLu','Tanh']))
这里我们指定图像绘制在新环境new中,同事指定线条样式和图例
绘制茎叶图
下面我们使用visdom对象的stem方法绘制一个茎叶图
x=torch.linspace(-6,6,300).view((-1,1))
y1=torch.sin(x)
y2=torch.cos(x)
plot_x=torch.cat((x,x),dim=1)
plot_y=torch.cat((y1,y2),dim=1)
vis.stem(X=plot_y,Y=plot_x,win='stem plot',env='stem',
opts=dict(legend=['sin','cos'],title='茎叶图'))
需要注意的是吗,这里有一个坑就是我们的stem函数中X是我们要绘制的图像的输入,我们需要输入plot_y,Y则是我们需要输入的ploy_x
0可视化的结果如下
绘制热力图
我们调用heatmap来绘制热力图
iris_corr=torch.from_numpy(np.corrcoef(iris_x,rowvar=False))
vis.heatmap(X=iris_corr,win='new',env='heatmap',
opts=dict(rownames=['x1','x2','x3','x4'],
columnnames=['x1','x2','x3','x4'],
title='热力图'))
我们首先使用numpy的corrcoef函数来求出输入的相关系数矩阵,接下来使用heatmap来进行可视化
可视化图像
我们下面分别使用image和images两个接口来可视化单张图像和一个batch的图像
vis.image(batch_x[0,:,:,:],win='one image',env='Image',
opts=dict(title='单张图像'))
vis.images(batch_x,win='many images',env='Image',
nrow=16,opts=dict(title='多张图像'))
可视化结果如下
可视化文本
我们使用text接口来可视化文本
texts='A flexible tool for creating, organizaing, and sharing visualization of live, rich data. Supports Torch and Numpu'
vis.text(texts,win='texts plot',env='Text',opts=dict(title='可视化文本'))