PyTorch入门(七)TensorBoard入门

PyTorch模型的可视化工具:

  • Visdom
  • TensorBoard
  • Pytorchviz
  • Netron

TensorBoard简介

TensorBoard是TensorFlow自带的一个强大的可视化工具,也是一个Web应用程序套件,可以记录训练过程的数字、图像等内容,以方便研究人员观察神经网络训练过程。

对于PyTorch等其它深度学习框架来说,目前还没有功能像TensorBoard一样全面的类似工具,一些已有的工具功能也有限,或使用起来比较困难。

TensorBoard提供的机器学习实验所需的可视化功能和工具如下:

  • 跟踪和可视化损失及准确率等指标

  • 可视化模型图

  • 查看权重、偏差或其它张量随时间变化的直方图

  • 将嵌入向量投影到较低维度空间

  • 显示图片、文字和音频数据

  • 剖析TensorFlow程序

    如果要使用TensorBoard,首先需要安装tensorflow, tensorboard, tensorboardX, 代码如下:

1
2
3
pip3 install tensorflow
pip3 install tensorboard
pip3 install tensorboardX

其中,tensorboardX这个工具可使得TensorFlow外的其它深度学习框架也可以使用TensorBoard的便捷功能。

TensorBoard目前支持7种可视化,包括Scalars, Images, Audio, Graphs, Distributions, Histograms, Embeddings, 主要功能如下:

  • Scalars: 展示训练过程中的准确率、损失值、权重/偏差的变化情况

  • Images: 展示训练过程中记录的图像

  • Audio: 展示训练过程中记录的音频

  • Graphs: 展示模型的数据流图,以及训练在各个设备上消耗的内存和时间

  • Distributions: 展示训练过程中记录的数据的分布图

  • Histograms: 展示训练过程中记录的数据的柱状图

  • Embeddings: 展示词向量的投影分布

    启动TensorBoard:

1
tonsorboard --logdir=./run/

TensorBoard基础操作

  1. 可视化数值

使用add_scalar方法来记录数字常量,一般使用add_scalar方法来记录训练过程中的loss, arrcuracy, learning rate等数值的变化,直观地监控训练过程。

示例代码:

1
2
3
4
5
6
7
# import modules
from tensorboardX import SummaryWriter
# add_scalar
writer = SummaryWriter('run/scalar')

for i in range(10):
writer.add_scalar('指数', 3**i, global_step=i)
在tensorboard中查看scalar
  1. 可视化图片

使用add_images方法来记录图像数据,一般会使用add_images来实时观察模型的生成效果,或者可视化分割、目标检测的结果,帮助调试模型。

示例代码:

1
2
3
4
5
6
7
8
9
10
# add_images
import cv2

writer = SummaryWriter('run/image')

for i in range(1, 4):
writer.add_images('',
cv2.cvtColor(cv2.imread('./image/image{}.png'.format(i)), cv2.COLOR_BGR2RGB),
global_step=i,
dataformats='HWC')

在tensorboard中查看图片
  1. 可视化统计图

使用add_histogram方法来记录一组数据的直方图。可以通过观察数据、训练参数、特征的直方图了解到它们大致的分布情况,辅助神经网络的训练过程。

示例代码:

1
2
3
4
5
6
7
# add_histogram
import numpy as np

writer = SummaryWriter('run/histogram')
writer.add_histogram('正态分布中心化', np.random.normal(0, 1, 1000), global_step=1)
writer.add_histogram('正态分布中心化', np.random.normal(0, 2, 1000), global_step=50)
writer.add_histogram('正态分布中心化', np.random.normal(0, 3, 1000), global_step=100)

在TensorBoard可视化界面中,我们会发现DISTRIBUTIONS和HISTOGRAMS两栏,它们都是用来观察数据分布的。在HISTOGRAMS中,同一数据不同步数的直方图可以上下错位排布(OFFSET)也可以重叠排布(OVERLAY)。

在tensorboard中查看柱状图
  1. 可视化模型图

使用add_graph方法来可视化一个神经网络。该方法可以将神经网络模型可视化,显示模型中的操作和网络层。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.nn as nn
import torch.nn.functional as F

dummy_input = (torch.zeros(1, 3),)
writer = SummaryWriter('run/graph')

# simple MLP model
class LinearInLinear(nn.Module):
def __init__(self):
super(LinearInLinear, self).__init__()
self.l = nn.Linear(3, 5)

def forward(self, x):
return self.l(x)

writer.add_graph(LinearInLinear(), dummy_input)
在tensorboard中查看模型图
  1. 可视化向量

使用add_embedding方法可以在二维或三维空间可视化Embedding向量。add_embedding方法是一个很实用的方法,不仅可以将高维特征使用PCA, T-SNE等方法降维至二维平面或三维空间,还可以观察每一个数据点在降维前的特征空间的K近邻情况。

下面的例子中我们取MNIST训练集中的前30个数据,将图像展开成一维向量作为Embedding,使用TensorBoardX进行可视化。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
# add_embedding
import torchvision

writer = SummaryWriter('run/vector')
mnist = torchvision.datasets.MNIST('./', download=False)
writer.add_embedding(mnist.data.reshape((-1, 28*28))[:30, :],
metadata=mnist.targets[:30],
label_img = mnist.data[:30, :, :].reshape((-1, 1, 28, 28)).float()/255,
global_step=0
)

可以发现,虽然还没有做任何特征提取工作,但MNIST数据已经呈现出聚类的效果,相同数字之间距离更近一些。

在tensorboard中查看向量嵌入

TensorBoard实战

  1. 例子1:在模型训练过程中记录loss和accuracy. Python示例代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    # -*- coding: utf-8 -*-
    import torch
    from numpy import vstack
    from numpy import argmax
    from pandas import read_csv
    from sklearn.preprocessing import LabelEncoder, LabelBinarizer
    from sklearn.metrics import accuracy_score
    from torch.optim import SGD, Adam
    from torch.utils.data import Dataset, DataLoader, random_split
    from torch.nn import Linear, ReLU, Softmax, Module, CrossEntropyLoss
    from torch.nn.init import kaiming_uniform_, xavier_uniform_

    from tensorboardX import SummaryWriter


    # dataset definition
    class CSVDataset(Dataset):
    # load the dataset
    def __init__(self, path):
    # load the csv file as a dataframe
    df = read_csv(path, header=None)
    # store the inputs and outputs
    self.X = df.values[:, :-1]
    self.y = df.values[:, -1]
    # ensure input data is floats
    self.X = self.X.astype('float32')
    # label encode target and ensure the values are floats
    self.y = LabelEncoder().fit_transform(self.y)

    # number of rows in the dataset
    def __len__(self):
    return len(self.X)

    # get a row at an index
    def __getitem__(self, idx):
    return [self.X[idx], self.y[idx]]

    # get indexes for train and test rows
    def get_splits(self, n_test=0.3):
    # determine sizes
    test_size = round(n_test * len(self.X))
    train_size = len(self.X) - test_size
    # calculate the split
    return random_split(self, [train_size, test_size])


    # model definition
    class MLP(Module):
    # define model elements
    def __init__(self, n_inputs):
    super(MLP, self).__init__()
    # input to first hidden layer
    self.hidden1 = Linear(n_inputs, 5)
    kaiming_uniform_(self.hidden1.weight, nonlinearity='relu')
    self.act1 = ReLU()
    # second hidden layer
    self.hidden2 = Linear(5, 6)
    kaiming_uniform_(self.hidden2.weight, nonlinearity='relu')
    self.act2 = ReLU()
    # third hidden layer and output
    self.hidden3 = Linear(6, 3)
    xavier_uniform_(self.hidden3.weight)

    # forward propagate input
    def forward(self, X):
    # input to first hidden layer
    X = self.hidden1(X)
    X = self.act1(X)
    # second hidden layer
    X = self.hidden2(X)
    X = self.act2(X)
    # output layer
    X = self.hidden3(X)
    return X


    class Model(object):
    def __init__(self, file_path, model):
    self.writer = SummaryWriter('./run/mlp_demo')
    # load the dataset
    dataset = CSVDataset(file_path)
    # calculate split
    train, test = dataset.get_splits()
    # prepare data loaders
    self.train_dl = DataLoader(train, batch_size=4, shuffle=True)
    self.test_dl = DataLoader(test, batch_size=1024, shuffle=False)
    # model
    self.model = model

    # train the model
    def train(self):
    criterion = CrossEntropyLoss()
    optimizer = Adam(self.model.parameters())
    # enumerate epochs
    for epoch in range(100):
    init_loss = torch.Tensor([0.0])
    # enumerate mini batches
    for i, (inputs, targets) in enumerate(self.train_dl):
    targets = targets.long()
    # clear the gradients
    optimizer.zero_grad()
    # compute the model output
    yhat = self.model(inputs)
    # calculate loss
    loss = criterion(yhat, targets)
    # credit assignment
    loss.backward()
    print("epoch: {}, batch: {}, loss: {}".format(epoch, i, loss.data))
    init_loss += loss.data
    # update model weights
    optimizer.step()

    self.writer.add_scalar('Loss/Train', init_loss/(i+1), epoch)
    test_accuracy = self.evaluate_model()
    self.writer.add_scalar('Accuracy/Test', test_accuracy, epoch)

    # evaluate the model
    def evaluate_model(self):
    predictions, actuals = [], []
    for i, (inputs, targets) in enumerate(self.test_dl):
    # evaluate the model on the test set
    yhat = self.model(inputs)
    # retrieve numpy array
    yhat = yhat.detach().numpy()
    actual = targets.numpy()
    # convert to class labels
    yhat = argmax(yhat, axis=1)
    # reshape for stacking
    actual = actual.reshape((len(actual), 1))
    yhat = yhat.reshape((len(yhat), 1))
    # store
    predictions.append(yhat)
    actuals.append(actual)
    predictions, actuals = vstack(predictions), vstack(actuals)
    # calculate accuracy
    acc = accuracy_score(actuals, predictions)
    return acc


    if __name__ == '__main__':
    # train the model
    Model('iris.csv', MLP(4)).train()

在训练过程中的训练集的损失值以及验证集的准确率如下:

在tensorboard中查看loss和accuracy
  1. 例子2: 利用TensorBoard查看ONNX文件

需安装Python第三方模块onnx, Python代码如下:

1
2
3
4
5
# -*- coding: utf-8 -*-
from tensorboardX import SummaryWriter

with SummaryWriter('./run/onnx') as w:
w.add_onnx_graph('iris.onnx')

在TensorBoard中选择Graphs, Run选项选择onnx,模型图如下:

在tensorboard中查看onnx模型图

从中可以看出,TensorBoard对于ONNX模型文件支持不是太好,可以尝试使用Netron.

参考文献

  1. tensorboardX Tutorials: https://tensorboardx.readthedocs.io/en/latest/tutorial.html#tutorials
  2. 动手学PyTorch深度学习建模与应用,王国平著
  3. PyTorch 使用 TensorboardX 进行网络可视化:https://www.pytorchtutorial.com/pytorch-tensorboardx/
欢迎关注我的公众号NLP奇幻之旅,原创技术文章第一时间推送。

欢迎关注我的知识星球“自然语言处理奇幻之旅”,笔者正在努力构建自己的技术社区。


PyTorch入门(七)TensorBoard入门
https://percent4.github.io/PyTorch入门(七)TensorBoard入门/
作者
Jclian91
发布于
2023年7月30日
许可协议