pytorch visualizer 深度神经网络可视化工具

1. visdom

visdom使用时需要打开server,可以通过在cmd中输入:

python -m visdom.server
#或者
py -m visdom.server

另外,也可以在cmd中直接输入:visdom

同时还要在浏览器中输入网址:

http://localhost:8097

可以把打开网站操作通过程序实现:

import webbrowser
webbrowser.open('http://localhost:8097/')

下面结合https://github.com/fossasia/visdom/blob/master/example/demo.py官方给出的例子对常用操作逐一进行拆解,以方便使用。
其主要支持的api如下:

  • viz.image:图片
  • viz.images:图像列表
  • viz.text:任意的HTML
  • viz.video:视频
  • viz.svg: SVG对象
  • viz.save:序列化状态服务器端
  • viz.scatter:2D或3D散点图
  • viz.line:线
  • viz.updateTrace:更新现有的线/散点图
  • viz.stem: stem图
  • viz.heatmap:热力图
  • viz.bar: 条形图
  • viz.histogram:直方图
  • viz.boxplot:盒子
  • viz.surf: 曲面图
  • viz.contour:等高线图
  • viz.quiver:二维矢量场图
  • viz.mesh:网格图

不常用操作(如stem、bar等)这里不进行描述。

1.1 通用操作

1.1.1 创建/关闭窗口、查询窗口状态

以viz.text为例:

from visdom import Visdom
viz = Visdom()
textwindow = viz.text('Hello World!')
# close text window:
viz.close(win=textwindow)

查询该text窗口是否关闭:

viz.win_exists(textwindow)

1.1.2 更新窗口 update_window_opts

见如下官方程序,可对某窗口win下的opts,layout属性进行更新。

具体例子可见 1.4.5 修改viz.scatter窗口的属性。

def update_window_opts(self, win, opts, env=None):
    """
    This function allows pushing new options to an existing plot window
    without updating the content
    """
    data_to_send = {
    
    
        'win': win,
        'eid': env,
        'layout': _opts2layout(opts),
        'opts': opts,
    }
    return self._send(data_to_send, endpoint='update')

1.1.3 不同的update模式

update模式 效果
append 在指定win中添加
remove 在指定win中移除指定name的对象(如line)
from visdom import Visdom
import numpy as np
viz = Visdom()
# line plots
# line updates
win = viz.line(
    X=np.column_stack((np.arange(0, 10), np.arange(0, 10))),
    Y=np.column_stack((np.linspace(5, 10, 10),
                       np.linspace(5, 10, 10) + 5)),
)

viz.line(
    X=np.arange(1, 10),
    Y=np.arange(11, 20),
    win=win,
    name='delete this',
    update='append'
)

viz.line(X=None, Y=None, win=win, name='delete this', update='remove')

在这里插入图片描述

1.2 viz.image/images

1.2.1 在窗口显示一张图片

from visdom import Visdom
import numpy as np
viz = Visdom()

# image demo save as jpg
viz.image(
    np.random.rand(3, 256, 256),
    opts=dict(title='Random image as jpg!', caption='How random as jpg.', jpgquality=50),
)

在这里插入图片描述

1.2.2 多张图片在同一窗口(可查看历史图片)

from visdom import Visdom
import numpy as np
viz = Visdom()
# image history demo
viz.image(
    np.random.rand(3, 256, 256),
    win='image_history',
    opts=dict(caption='First random', store_history=True, title='Pick your random!'),
)
viz.image(
    np.random.rand(3, 256, 256),
    win='image_history',
    opts=dict(caption='Second random!', store_history=True),
)

效果如下(两张图是在同一个窗口,可通过工具条进行选择):
在这里插入图片描述

1.2.3 多张图片阵列显示在同一窗口

from visdom import Visdom
import numpy as np
viz = Visdom()
# grid of images
```python
viz.images(
    np.random.randn(20, 3, 64, 64),
    opts=dict(title='Random images', caption='How random.')
)

在这里插入图片描述
正如官方程序中所述,Given a 4D tensor of shape (B x C x H x W), or a list of images all of the same size, makes a grid of images of size (B / nrow, nrow)。

def images(self, tensor, nrow=8, padding=2,
               win=None, env=None, opts=None):

默认一行显示8张图片。

1.3 viz.line

1.3.1 简单的画线

from visdom import Visdom
import numpy as np
viz = Visdom()
# line plots
viz.line(Y=np.random.rand(10), opts=dict(showlegend=True))

Y = np.linspace(-5, 5, 100)
viz.line(
    Y=np.column_stack((Y * Y, np.sqrt(Y + 5))),
    X=np.column_stack((Y, Y)),
    opts=dict(markers=False),
)

两条line分别显示如下:
在这里插入图片描述

1.3.2 不同dash类型的画线

from visdom import Visdom
import numpy as np
viz = Visdom()

win = viz.line(
    X=np.column_stack((
        np.arange(0, 10),
        np.arange(0, 10),
        np.arange(0, 10),
    )),
    Y=np.column_stack((
        np.linspace(5, 10, 10),
        np.linspace(5, 10, 10) + 5,
        np.linspace(5, 10, 10) + 10,
    )),
    opts={
    
    
        'dash': np.array(['solid', 'dash', 'dashdot']),
        'linecolor': np.array([
            [0, 191, 255],
            [0, 191, 255],
            [255, 0, 0],
        ]),
        'title': 'Different line dash types'
    }
)

viz.line(
    X=np.arange(0, 10),
    Y=np.linspace(5, 10, 10) + 15,
    win=win,
    name='4',
    update='insert',
    opts={
    
    
        'linecolor': np.array([
            [255, 0, 0],
        ]),
        'dash': np.array(['dot']),
    }
)

在这里插入图片描述

1.4 散点图 scatter

1.4.1 二维散点图(自定义标签颜色)

from visdom import Visdom
import numpy as np
viz = Visdom()

# 2D scatter plot with custom colors per label:
viz.scatter(
    X=np.random.rand(255, 2),
    Y=(np.random.randn(255) > 0) + 1,
    #Y=(np.random.rand(255) + 1.5).astype(int),
    opts=dict(
        markersize=10,
        markercolor=np.floor(np.random.random((2, 3)) * 255),
        markerborderwidth=0,
    ),
)

在这里插入图片描述

1.4.2 二维散点图(自定义文本标签)

from visdom import Visdom
import numpy as np
viz = Visdom()

# 2D scatter plot with text labels:

viz.scatter(
    X=np.random.rand(6, 2),
    Y=[1] * 2 + [2] * 3 + [3] * 1,
    opts=dict(
        xtickmin=0,
        xtickmax=1,
        legend=['A', 'B', 'C'],
        textlabels=['Label %d' % (i + 1) for i in range(6)]
    )
)

在这里插入图片描述

1.4.3 二维散点图(update)

from visdom import Visdom
import numpy as np
viz = Visdom()

# scatter plot example with various type of updates
colors = np.random.randint(0, 255, (2, 3,))
win = viz.scatter(
    X=np.random.rand(5, 2),
    Y=(np.random.rand(5) + 1.5).astype(int),
    opts=dict(
        markersize=10,
        markercolor=colors,
        legend=['1', '2']
    ),
)

viz.scatter(
    X=np.random.rand(3),
    Y=np.random.rand(3),
    opts=dict(
        markersize=10,
        markercolor=colors[0].reshape(-1, 3),

    ),
    name='3',
    update='append',
    win=win)

依次执行两段scatter,viz画面从左图更新到右图。
在这里插入图片描述

1.4.4 三维散点图

from visdom import Visdom
import numpy as np
viz = Visdom()
Y = np.random.rand(100)

# 3d scatterplot with custom labels and ranges
viz.scatter(
    X=np.random.rand(100, 3),
    Y=(Y + 1.5).astype(int),
    opts=dict(
        legend=['Men', 'Women'],
        markersize=5,
        xtickmin=0,
        xtickmax=2,
        xlabel='Arbitrary',
        xtickvals=[0, 0.75, 1.6, 2],
        ytickmin=0,
        ytickmax=2,
        ytickstep=0.5,
        ztickmin=0,
        ztickmax=1,
        ztickstep=0.5,
    )
)

在这里插入图片描述

1.4.5 修改viz.scatter窗口的属性

from visdom import Visdom
import numpy as np
viz = Visdom()

# scatter plots
Y = np.random.rand(100)
old_scatter = viz.scatter(
    X=np.random.rand(100, 2),
    Y=(Y[Y > 0] + 1.5).astype(int),
    opts=dict(
        legend=['Didnt', 'Update'],
        xtickmin=-50,
        xtickmax=50,
        xtickstep=0.5,
        ytickmin=-50,
        ytickmax=50,
        ytickstep=0.5,
        markersymbol='cross-thin-open',
    ),
)

viz.update_window_opts(
    win=old_scatter,
    opts=dict(
        legend=['Apples', 'Pears'],
        xtickmin=0,
        xtickmax=1,
        xtickstep=0.5,
        ytickmin=0,
        ytickmax=1,
        ytickstep=0.5,
        markersymbol='cross-thin-open',
    ),
)

可以看出old_scatter窗口由于坐标轴的原因,显示极不合理,通过修改部分属性,update_window_opts后显示就完美了。
在这里插入图片描述

1.5 热力图 heatmap

from visdom import Visdom
import numpy as np
viz = Visdom()
# heatmap
viz.heatmap(
    X=np.outer(np.arange(1, 6), np.arange(1, 11)),
    opts=dict(
        columnnames=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
        rownames=['y1', 'y2', 'y3', 'y4', 'y5'],
        colormap='Electric',
    )
)

在这里插入图片描述

1.6 等高线图 contour

from visdom import Visdom
import numpy as np
viz = Visdom()
# contour
x = np.tile(np.arange(1, 101), (100, 1))
y = x.transpose()
X = np.exp((((x - 50) ** 2) + ((y - 50) ** 2)) / -(20.0 ** 2))
viz.contour(X=X, opts=dict(colormap='Viridis'))

在这里插入图片描述

1.7 viz.matplot

from visdom import Visdom
viz = Visdom()

try:
    import matplotlib.pyplot as plt
    plt.plot([1, 23, 2, 4])
    plt.ylabel('some numbers')
    viz.matplot(plt)
except BaseException as err:
    print('Skipped matplotlib example')
    print('Error message: ', err)

在这里插入图片描述

2. hiddenlayer

安装(pip3 install graphviz)及使用过程都比较简单。

需要说明的是hiddenlayer主要使用了graphviz进行绘图,因此使用hiddenlayer ,必须首先安装graphviz。

具体方法可参考python graphviz安装

参考文献3中给出的模型为例,介绍下其使用过程,同时也方便比较hiddenlayer与Netron模型可视化的结果。

import torch
import torch.nn as nn
import torch.nn.functional as F

import hiddenlayer as hl 

 
class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.block1 = nn.Sequential(
            nn.Conv2d(64, 64, 3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 32, 1, bias=False),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.Conv2d(32, 64, 3, padding=1, bias=False),
            nn.BatchNorm2d(64)
        )
 
        self.conv1 = nn.Conv2d(3, 64, 3, padding=1, bias=False)
        self.output = nn.Sequential(
            nn.Conv2d(64, 1, 3, padding=1, bias=True),
            nn.Sigmoid()
        )
 
    def forward(self, x):
        x = self.conv1(x)
        identity = x
        x = F.relu(self.block1(x) + identity)
        x = self.output(x)
        return x

if __name__ == '__main__':
    d = torch.rand(1, 3, 416, 416)
    m = model()
    o = m(d)
    hl.build_graph(m, d)

产生的可视化结果:
在这里插入图片描述
需注意的是,在非jupyter_notebook环境下,需要使用命令行执行:

hl.build_graph(m, d)

在这里插入图片描述
另外,有几点修改可供参考:

  • 图片显示hiddenlayer源程序是从左到右,如果要设置模型显示的方向,,可以修改python安装目录\Lib\site-packages\hiddenlayer\graph.py文件class Graph()中的rankdir变量:
# rankdir="TD" 可视化自上而下top-down打印
# rankdir="LR" 可视化从左到右left-right打印
  • 可以对主题THEMES进行修改,包里是给了两个: “basic”,“blue”
  • 另外,在class Graph()中对两个_repr_svg_子函数进行修改,可将模型自动保存成pdf,并自动打开。
    def _repr_svg_(self):
        """Allows Jupyter notebook to render the graph automatically."""
        self.save("D:\\modle.gv")
        return self.build_dot()._repr_svg_()
    
    def save(self, path, format="pdf"):
        # TODO: assert on acceptable format values
        dot = self.build_dot()
        dot.format = format
        directory, file_name = os.path.split(path)
        # Remove extension from file name. dot.render() adds it.
        file_name = file_name.replace("." + format, "")
        dot.render(file_name, directory=directory, view=True, cleanup=True,)

3.Netron

这里是拷贝了参考文献3 知乎 https://zhuanlan.zhihu.com/p/197737707的内容:

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.onnx

import netron

class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.block1 = nn.Sequential(
            nn.Conv2d(64, 64, 3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 32, 1, bias=False),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.Conv2d(32, 64, 3, padding=1, bias=False),
            nn.BatchNorm2d(64)
        )
 
        self.conv1 = nn.Conv2d(3, 64, 3, padding=1, bias=False)
        self.output = nn.Sequential(
            nn.Conv2d(64, 1, 3, padding=1, bias=True),
            nn.Sigmoid()
        )
 
    def forward(self, x):
        x = self.conv1(x)
        identity = x
        x = F.relu(self.block1(x) + identity)
        x = self.output(x)
        return x

if __name__ == '__main__':
    d = torch.rand(1, 3, 416, 416)
    m = model()
    o = m(d)
    onnx_path = "D:\\onnx_model_name.onnx"
    torch.onnx.export(m, d, onnx_path)
    netron.start(onnx_path)

结果会在网页http://localhost:8080/显示:
在这里插入图片描述

通过比较我们可以发现Netron是完全逐模块可视化的,而hiddenlayer内部则集成了子模块折叠的方法。

另外,Netron是从input开始可视化直至output,而hiddenlayer则默认从第一个模块开始。

如果提示下述错误:

RuntimeError: Unsupported: ONNX export of index_put in opset 11. Please try opset version 11.

可通过设置opset_version进行修改:

torch.onnx.export(model, d, onnx_path,opset_version=11)

参考

[1] https://github.com/waleedka/hiddenlayer/blob/master/demos/pytorch_graph.ipynb
[2] https://github.com/lutzroeder/Netron
[3] https://zhuanlan.zhihu.com/p/197737707
[4] https://ptorch.com/news/77.html

猜你喜欢

转载自blog.csdn.net/WANGWUSHAN/article/details/110450275
今日推荐