Visdom介绍
Visdom是Facebook专为PyTorch开发的实时可视化工具包,其作用相当于TensorFlow中的Tensorboard,灵活高效且界面美观。如果想更多了解关于Visdom的使用可以参考官方文档
安装
打开cmd窗口,输入命令即可
pip install visdom
使用
要是用Visdom,需要在终端先开启监控命令,根据显示在浏览器中输入: http://localhost:8097 。监控命令有两种方式:
'''方式1'''
python -m visdom.server
'''方式2'''
直接visdom
界面
- env被保存在$HOME/.visdom/ 这里,里面保存的是.json文件,如果json被删除,相应保存的env也就没了。
- 浏览器界面中environment右边可以选择env,默认是main,点击哪个env就会显示对应的绘制。
- clear旁边的可以保存env,再右边哪个manage只能保存view,为了将数据保存到本地,只能使用clear旁边将env保存下来
Visdom可视化函数及其参数一览
visdom基本可视化函数
vis.image : #图片
vis.line: #曲线
vis.images : #图片列表
vis.text : #抽象HTML 输出文字
vis.properties : #属性网格
vis.audio : #音频
vis.video : #视频
vis.svg : #SVG对象
vis.matplot : #matplotlib图
vis.save : #序列化状态服务端
上述函数参数
- 注意opt的参数都可以用python字典的格式传入
opts.title : #图标题
win:#窗口名称
opts.width : #图宽
opts.height : #图高
opts.showlegend : #显示图例 (true or false)
opts.xtype : #x轴的类型 ('linear' or 'log')
opts.xlabel : #x轴的标签
opts.xtick : #显示x轴上的刻度 (boolean)
opts.xtickmin : #指定x轴上的第一个刻度 (number)
opts.xtickmax : #指定x轴上的最后一个刻度 (number)
opts.xtickvals : #x轴上刻度的位置(table of numbers)
opts.xticklabels : #在x轴上标记标签 (table of strings)
opts.xtickstep : #x轴上刻度之间的距离 (number)
opts.xtickfont :#x轴标签的字体 (dict of font information)
opts.ytype : #type of y-axis ('linear' or 'log')
opts.ylabel : #label of y-axis
opts.ytick : #show ticks on y-axis (boolean)
opts.ytickmin : #first tick on y-axis (number)
opts.ytickmax : #last tick on y-axis (number)
opts.ytickvals : #locations of ticks on y-axis (table of numbers)
opts.yticklabels : #ticks labels on y-axis (table of strings)
opts.ytickstep : #distances between ticks on y-axis (number)
opts.ytickfont : #font for y-axis labels (dict of font information)
opts.marginleft : #左边框 (in pixels)
opts.marginright :#右边框 (in pixels)
opts.margintop : #上边框 (in pixels)
opts.marginbottom: #下边框 (in pixels)
opts.lagent=['']: #显示图标
应用
实时曲线绘制
- 方法:起始点+数据点更新
from Visdom import Visdom
vis = Visdom() #初始化visdom类。
#括号内可以添加参数,如使用“env = ‘环境名称’ ”对绘图环境进行命名。此时在可视化界面需在上方的 Environment中勾选相应的环境名称才可以将图片显示出来。如果没有env,则默认环境名称为main
'''起点'''
vis.line([0.], #第一个点的Y坐标
[0.], #第一个点的X坐标
win='train loss', #窗口名称
opts=dict(title = 'train_loss',xlabel='episodes',ylabel='loss') #图标题、x轴和Y轴标签
) #设置起点
'''模型数据'''
vis.line([1.],[1.], #下一点的Y坐标及X坐标
win='train loss', ## 窗口名称 与上个窗口同名表示显示在同一个表格里
update='append') # 添加到上一个点的后面
从上图中可以看出:
- 窗口名称显示在图片的右上角;
- 右下角有一个Edit,点击进去,可以对图片类型等进行修改,甚至可以保存数据;
'''多条曲线绘制 实际上就是传入y值时为一个向量'''
vis = Visdom(env='my wind') # 环境名称为'my wind'
#设置起始点
vis.line([[0.0,0.0]], ## Y的起始点
[0.], ## X的起始点
win="test loss", ##窗口名称
opts=dict(title='test_loss') ## 图像标例
)
'''模型数据'''
vis.line([[1.1,1.5]], ## Y的下一个点
[1.], ## X的下一个点
win="test loss", ## 窗口名称
update='append' ## 添加到上一个点后面
实际应用
'''
导入库文件
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from visdom import Visdom
import numpy as np
'''
构建简单的模型:简单线性层+Relu函数的多层感知机
'''
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.model = nn.Sequential(
nn.Linear(784, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 10),
nn.ReLU(inplace=True))
def forward(self, x):
x = self.model(x)
return x
batch_size = 128
earning_rate = 0.01
epochs = 10
train_loader = torch.utils.data.DataLoader(datasets.MNIST(
'D:/data/MNIST', #
train=True,
download=True,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))])),
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(datasets.MNIST(
'D:/Jupyter/工作准备/data/MNIST',
train=False,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))])),
batch_size=batch_size,
shuffle=True)
# 注意此处初始化visdom类
viz = Visdom()
# 绘制起点
viz.line([0.], [0.], win="train loss", opts=dict(title='train_loss'))
device = torch.device('cuda:0')
net = MLP().to(device)
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
criteon = nn.CrossEntropyLoss().to(device)
for epoch in range(epochs):
for batch_idx, (data, target) in enumerate(train_loader):
data = data.view(-1, 28 * 28)
data, target = data.to(device), target.cuda()
logits = net(data)
loss = criteon(logits, target)
optimizer.zero_grad()
loss.backward()
# print(w1.grad.norm(), w2.grad.norm())
optimizer.step()
if batch_idx % 100 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
test_loss = 0
correct = 0
for data, target in test_loader:
data = data.view(-1, 28 * 28)
data, target = data.to(device), target.cuda()
logits = net(data)
test_loss += criteon(logits, target).item()
pred = logits.argmax(dim=1)
correct += pred.eq(target).float().sum().item()
test_loss /= len(test_loader.dataset)
# 绘制epoch以及对应的测试集损失loss
viz.line([test_loss], [epoch], win="train loss", update='append') # win是必须的
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset), correct / len(test_loader.dataset)))
远程显示服务器上的代码结果
- 在xshell上运行visdom
- 通过浏览器访问远程服务器地址的8097(默认)端口来远程观察模型的训练情况
- 假设服务器ip为192.168.1.2
- 在本地浏览器上打开192.168.1.2:8097即可观察到训练结果
暂停与恢复
经常需要的一个功能是,train到一个阶段,暂停/终止训练,下次训练想从断点处继续。因为visdom正常会用新图覆盖原来的图,所以要在这里读取以前的数据,然后接着绘制数据。
第一步需要保存env,env被保存在$HOME/.visdom/ 这里,里面保存的是.json文件,如果json被删除,相应保存的env也就没了。
保存方式如下:
浏览器中的界面,点击manage environment——设置环境名——fork——save(这里的fork意指保存json文件,即保存在$HOME/.visdom/ 这里)。保存完并没有什么提示,而且保存界面不会消失,很奇怪,为了保险反正每次点完等一小会吧。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNH1RBiU-1659922203818)(https://cdn.jsdelivr.net/gh/Yicen-y/Markdown_image@main/images/20210402213453.png)]
保存完之后,从代码中获取visdom保存的数据。使用如下代码可以获得之前保存下来的数据
vis=Visdom(port=8888)
print(vis.get_env_list())
# 可以得到保存的env list
print(vis.win_exists(win="train",env="main1.0"))
# 可以查看指定win是否存在,此处win不是保存的view的名字,可是代码中指定的win
win_data=vis.get_window_data(win="train",env="main1.0")
# 可以获得指定win数据,即我们想恢复的数据,但是要指定号win和env要不然会是None
# 返回的是str数据,但是其实可以使用json来处理,因为数据本来就是储存在json中的
pre_data= json.loads(win_data)
# 这样就将str转换回dict,可以更方便的处理了
xdata=pre_data["content"]["data"][0]["x"]
ydata=pre_data["content"]["data"][0]["y"]
# 得到横纵轴的数据,是float数据的list
- 将旧数据与新数据拼接在一起,共同绘制。。在保存在本地中的json中读取数据继续绘制。
with open("$HOME/.visdom/main1.0.json","r") as f:predata=json.load(f)
# 当然此处地址不能直接写$HOME,应该用正确的地址,剩下的就和上面的处理一样了
- 数据拼接
ydata.append(0.5)
vis.line(
X=np.arange(len(ydata)),
Y=np.array(ydata),
opts=dict(title="try"),
win="little"
)
- 配合matplotlib画折线
import matplotlib.pyplot as plt
from visdom import Visdom
viz=Visdom(port=8888) #(需要先在cmd中激活python -m visdom.server -port 8888)
plt.plot([1,2,3,4]) #默认给定的是y轴数据,x轴会自动从0补全
plt.plot([10,20,30,40]) #默认给定的是y轴数据,x轴会自动从0补全
#plt.axis([0,1,0,3]) 可以使用这个分别指定x轴的起始终止、y轴的起始终止
plt.title("折线")
viz.matplot(plt)
参考
- 轻松学 Pytorch–Visdom 可视化
- Pytorch中文网
- Visdom官方Github指南
- Visdom PyTorch可视化工具(https://www.cnblogs.com/luckforefforts/p/13736158.html#visdom-pytorch可视化工具)https://www.cnblogs.com/luckforefforts/p/13736158.html (这个帖子讲了环境的保存和重新载入)