基于Pytorch的可视化工具
深度学习网络通常具有很深的层次结构,而且层与层之间通常会有并联、串联等连接方式。当使用PyTorch建立一个深度学习网络并输出文本向读者展示网络的连接方式是非常低效的,所以需要有效的工具将建立的深度学习网络结构有层次化的展示,这就需要使用相关的深度学习网络结构可视化库。
3.1 准备网络
import torch
import torch.nn as nn
import torchvision
import torchvision.utils as vutils
from torch.optim import SGD
import torch.utils.data as Data
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
"""
下面导入手写字体数据,并将数据处理为数据加载器
"""
train_data=torchvision.datasets.MNIST(
root="./Dataset",
train=True,
#将数据转化为torch使用的张量,取值范围为[0-1]
transform=torchvision.transforms.ToTensor(),
download=False#因为数据已经下载过所以这里不在下载
)
#定义一个数据加载器
train_loader=Data.DataLoader(
dataset=train_data,#使用的数据集
batch_size=128,#批处理样本大小
shuffle=True,
num_workers=0
)
#准备需要的测试集
test_data=torchvision.datasets.MNIST(
root="./Dataset",
train=False,
download=False
)
#为数据添加一个通道维度,并且取值范围缩放到[0-1]之间
test_data_x=test_data.data.type(torch.FloatTensor)/255.0
test_data_x=torch.unsqueeze(test_data_x,dim=1)
test_data_y=test_data.targets#测试集的标签
print(test_data_x.shape)
print(test_data_y.shape)
"""
(1)针对训练集使用torchvision.datasets.MNIST()函数导入数据,
并将其像素值转化到0~1之间,然后使用Data.DataLoader()函数定义一个数据加载器,
每个batch包含128张图像。
(2)针对测试数据,同样使用torchvision.datasets.MNIST()函数导入数据,
但不将数据处理为数据加载器,而是将整个测试集作为一个batch,方便计算模型在测试集上的预测精度。
"""
#搭建一个卷积神经网络
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet,self).__init__()
#定义第一个卷积层
self.conv1=nn.Sequential(
nn.Conv2d(
in_channels=1,#输入的feature_map
out_channels=16,#输出的feature_map
kernel_size=3,#卷积核尺寸
stride=1,#卷积核步长
padding=1,#进行填充
),
nn.ReLU(),#激活函数
nn.AvgPool2d(
kernel_size=2,#平均值池化层,使用2*2
stride=2,#池化步长为2
),
)
#定义第二个卷积层
self.conv2=nn.Sequential(
nn.Conv2d(16,32,3,1,1),
nn.ReLU(),#激活函数
nn.MaxPool2d(2,2)#最大值池化
)
#定义全连接层
self.fc=nn.Sequential(
nn.Linear(
in_features=32*7*7,#输入特征
out_features=128#输出特征
),
nn.ReLU(),#激活层
nn.Linear(128,64),#
nn.ReLU()#激活层
)
self.out=nn.Linear(64,10)#最后的分类层
def forward(self,x):
x=self.conv1(x),
x=self.conv2(x),
x=x.view(x.size(0),-1),#展平多维的卷积图层
x=self.fc(x),
output=self.out(x),
return output
myConvNet=ConvNet()
3.2 网络结构的可视化---PytorchViz
从定义网络和网络的输出可以看出,在myConvNet网络结构中,共包含两个使用nn.Sequential()函数连接的卷积层,即conv1和conv2,每个层都包含有卷积层、激活函数层和池化层。在fc层中,包含两个全连接层和激活函数层,out层则由一个全连接层构成。通过文本输出myConvNet网络的网络结构得到上面的输出结果,但这并不容易让读者理解在网络中层与层之间的连接方式,所以需要将PyTorch搭建的深度学习网络进行可视化,通过图像来帮助读者理解网络层与层之间的连接方式。PyTorchViz库是一个可视化网络结构的库,学习和了解这些库对网络结构进行可视化,可以帮助我们查看、理解所搭建深度网络的结构。
from torchviz import make_dot
x=torch.randn(1,1,28,28).requires_grad_(True)
y=myConvNet(x)
#make_dot用来得到网络的可视化图像
myConvNets=make_dot(y,params=dict(list(myConvNet.named_parameters())+[("x",x)]))
myConvNets.format='png'#指定可视化图像的格式
myConvNets.directory="model_graph/"#指定图像保存的文件夹
myConvNets.view()
得到的图像如下图所示:
3.3 训练过程可视化---TensorboardX
网络结构可视化,主要是帮助使用者理解所搭建的网络或检查搭建网络时存在的错误。而网络训练过程的可视化,通常用于监督网络的训练过程或呈现网络的训练效果。tensorboardX是帮助pytorch使用tensornoard工具来可视化的库,在tensorboardX库中,提供了多种向tensorboard中添加事件的函数。
函数 | 功能 | 用法 |
SummaryWriter() | 创建编写器,保存日志 | writer=SummaryWriter() |
writer.add_scalar() | 添加标量 | writer.add_scalar(''myscalar",value,iteration) |
writer.add_image() | 添加图像 | writer.add_image("imresult",x,iteration) |
writer.add_histogram() | 添加直方图 | writer.add_histogram('hist',array,iteration) |
writer.add_graph() | 添加网络结构 | writer.add_graph(model,input_to_model=None) |
writer.add_audio() | 添加音频 | add_audio(tag,audio,iteration,sample_rate) |
writer.add_text() | 添加文本 | writer.add_text(tag,text_string,global_step=None) |
下面针对建立好的MNIST手写字体识别网络,使用rensorboardX库对网络在训练过程中的损失函数的变化情况、精度变化情况、权重分布等内容进行可视化,程序如下:
from tensorboardX import SummaryWriter
SumWriter=SummaryWriter(log_dir='./log')#日志保存路径
#定义优化器
optimizer=torch.optim.Adam(myConvNet.parameters(),lr=0.003)
#定义损失函数
loss_func=nn.CrossEntropyLoss()
train_loss=0
print_step=100#每经过100次迭代,输出损失
#对模型进行迭代训练,对所有的数据训练epoch轮
for epoch in range(5):
#对训练数据的加载器进行迭代计算
for step,(b_x,b_y) in enumerate(train_loader):
#计算每个batch上的损失
output=myConvNet(b_x)
loss=loss_func(output,b_y)#交叉熵损失函数
optimizer.zero_grad()#每个迭代步的梯度初始化为0
loss.backward()#损失后向传播
optimizer.step()#使用梯度进行优化
train_loss=train_loss+loss#计算累加损失
niter=epoch*len(train_loader)+step+1#计算迭代次数
#计算每经过print_step次迭代后的输出
if niter % print_step==0:
#为日志添加训练集损失函数
SumWriter.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)
#为日志添加测试集上的预测精度
SumWriter.add_scalar("test acc",acc.item(),niter)
#为日志中添加训练数据的可视化图像,使用当前batch的图像
#将一个batch的数据进行预处理
b_x_im=vutils.make_grid(b_x,nrow=12)
SumWriter.add_image('train image sample',b_x_im,niter)
#使用直方图可视化网络中参数的分布情况
for name,param in myConvNet.named_parameters():
SumWriter.add_histogram(name,param.data.numpy(),niter)
在网络训练完毕之后,会得到网络的训练过程文件,该文件的可视化结果可以通过tensorboard可视化工具进行查看。
在conda环境下执行>tensorboard --logdir="文件路径"
成功后,tensorboard会返回一个本地网址链接,浏览器打开该链接即可查看到可视化界面
3.5 Visdom可视化
Visdom库中包含多种用于可视化图像的接口
可视化函数 | 功能描述 |
vis.image | 可视化一张图像 |
vis.image | 可视化一个batch的图像,或者一个图像列表 |
vis.text | 可视化文本 |
vis.audio | 用于播放音频 |
vis.matplot | 可视化Matplotlib的图像 |
vis.scatter | 2D或者3D的散点图 |
vis.line | 线图 |
vis.stem | 茎叶图 |
vis.heatmap | 热力图 |
vis.bar | 条形图 |
vis.histogram | 直方图 |
vis.boxplot | 盒形图 |
vis.surf | 曲面图 |
vis.contour | 等高线图 |
vis.quiver | 箭头图 |
vis.video | 播放音频 |
vis.mesh | 网格图 |
下面使用具体的数据集进行可视化图像:
import numpy as np
import torch
from visdom import Visdom
from sklearn.datasets import load_iris
iris_x,iris_y=load_iris(return_X_y=True)
print(iris_x.shape)
print(iris_y.shape)
"""
上面的程序导入了鸢尾花数据集,包含3类数据,150个样本,每个样本包含4个特征。
"""
vis=Visdom()
#2D散点图
vis.scatter(iris_x[:,0:2],Y=iris_y+1,win="windows1",env="main")
#3D散点图
vis.scatter(iris_x[:,0:3],Y=iris_y+1,win="3D 散点图",env="main",opts=dict(markersize=4,xlabel="特征一",ylabel="特征二"))
"""
程序中使用vis = Visdom()初始化一个绘图对象,通过vis.scatter()为对象添加散点图。
在该函数中,如果输入的X为二维则可得到2D散点图,如果输入的X为三维则可得到3D散点图,
其中参数Y用于指定数据的分组情况,参数win指定图像的窗口名称,参数env则指定图像所在的环境。
可以发现两幅图像都在主环境main中。图像的其他设置可使用opts参数通过字典的形式设置。
在上述初始化的可视化图像环境main中,继续添加其他窗口,以绘制不同类型的图像,如添加折线图.
"""
#添加折线图
x=torch.linspace(-6,6,100).view((-1,1))
sigmoid=torch.nn.Sigmoid()
sigmoidy=sigmoid(x)
tanh=torch.nn.Tanh()
tanhy=tanh(x)
relu=torch.nn.ReLU()
reluy=relu(x)
#连接3个张量
ploty=torch.cat((sigmoidy,tanhy,reluy),dim=1)
plotx=torch.cat((x,x,x),dim=1)
vis.line(Y=ploty,X=plotx,win="line plot",env="main",opts=dict(dash=np.array(["solid","dash","dashdot"]),legend=["Sigmoid","Tanh","ReLU"]))
"""
上面的程序中,可视化出了sigmoid、Tanh和ReLU三种激活函数的图像。
在可视化折线时,使用vis.line()函数进行绘图,图像在环境main中,
通过win参数指定窗口名称为line plot,然后通过opts参数为不同的线设置了不同的线型。
"""
#添加茎叶图
x=torch.linspace(-6,6,100).view((-1,1))
y1=torch.sin(x)
y2=torch.cos(x)
#连接2个变量
plotx=torch.cat((y1,y2),dim=1)
ploty=torch.cat((x,x),dim=1)
vis.stem(X=plotx,Y=ploty,win="stem plot",env="main",
#设置图例
opts=dict(legend=["sin","cos"],title="茎叶图") )
"""
上面的程序中,可视化出了正弦和余弦函数的茎叶图。
在可视化时通过vis.stem()函数绘图,图像在环境main中,
通过win参数指定窗口名称为stem plot,然后通过opts参数为图像添加图例和标题。
"""
#添加热力图
iris_corr=torch.from_numpy(np.corrcoef(iris_x,rowvar=False))
vis.heatmap(iris_corr,win="heatmap",env="main",
#设置每个特征的名称
opts=dict(rownames=["x1","x2","x3","x4"],columnnames=["x1","x2","x3","x4"],title="热力图"))
"""
程序中可视化出了鸢尾花数据集中4个特征的相关系数热力图。
在可视化时通过vis.heatmap()函数进行绘图,图像在环境main中,
通过win参数指定窗口名称为heatmap,然后通过opts参数为图像添加X轴的变量名称、Y轴变量名称和标题。
"""
注意:在使用visdom可视化图像之前,应该在命令行激活visdom服务,否则程序报错
python -m visdom.server
命令执行成功后会返回一个本地链接: http://localhost:8097
然后在pycharm中执行代码,再用浏览器打开上述链接即可得到下面的可视化图像: