【监控模块可视化】

4 篇文章 0 订阅
1 篇文章 0 订阅


一、TensorflowX

PyTorch 自身的可视化功能没有 TensorFlow 的 tensorboard 那么优秀,所以 PyTorch
通常是借助 tensorboard(是借助,非直接使用)进行可视化,目前流行的有如下两种方
法,本文仅介绍第二种——TensorBoardX。

1. 构建 Logger 类
Logger 类中“包”了 tf.summary.FileWriter ,截至目前(2018.10.17),只有三种操作,分别是 scalar_summary(), image_summary(), histo_summary()
优点:轻便,可满足大部分需求
Logger 类参考 github:https://github.com/yunjey/PyTorchtutorial/tree/master/tutorials/04-utils/tensorboard

2. 借助 TensorBoardX 包
TensorBoardX 包的功能就比较全,截至目前(2018.10.17),支持除 tensorboard beholder 之外的所有 tensorboard 的记录类型。
github:https://github.com/lanpa/tensorboardX
API 文档:https://tensorboard/PyTorch.readthedocs.io/en/latest/tutorial_zh.html#

3.安装时小插曲:
一开始按照 github 上的方法安装:pip install git+https://github.com/lanpa/tensorboardX
会报错:from .proto importevent_pb2 ImportError: cannot import name event_pb2查看本地 在文件夹 proto/下确实没有 event_pb2,但是在 github 上是存在的。

最后通过 pip uninstall tensorboardX,使用 pip install tensorboard 安装成功。
tensorboardX 最早叫 tensorboard,但此名易引起混淆,之后改为 tensorboardX,which
stands for tensorboard for X。

代码实现:
tensorboardX ᨀ供 13 个函数,可以记录标量、图像、语音、文字等等,功能十分丰富。
本节将对这些函数进行介绍,所用代码为 tensorboardX 的官方 demo.py,放在/Code/4_viewer/1_tensorboardX_demo.py,请先运行该文件,并且打开 terminal,进入
相应的虚拟环境(如果有),进入到/Result/文件夹,
执行:tensorboard --logdir=runs
然后到浏览器中打开:localhost:6006

可以看到显示界面如下:
在这里插入图片描述

完成以上步骤,就可以一一来学习 tensorbaordX 各功能函数啦。

1.1、Loss 和 Accuracy 曲线(add_scalar)

add_scalar()
add_scalar(tag, scalar_value, global_step=None, walltime=None)

功能:
在一个图表中记录一个标量的变化,常用于 Loss 和 Accuracy 曲线的记录。

参数:
tag(string)- 该图的标签,类似于 polt.title。
scalar_value(float or string/blobname)- 用于存储的值,曲线图的 y 坐标
global_step(int)- 曲线图的 x 坐标
walltime(float)- 为 event 文件的文件名设置时间,默认为 time.time()
运行 demo 中的:用 github 首页 demo 运行这一行:writer.add_scalar('data/scalar1', dummy_s1[0], n_iter)

可以得到下图:
在这里插入图片描述

1.2、trainLoss 和 validLoss 的比较(add_scalars)

** add_scalars()**
add_scalars(main_tag, tag_scalar_dict, global_step=None, walltime=None)
功能:
在一个图表中记录多个标量的变化,常用于对比,如 trainLoss 和 validLoss 的比较等。

参数:
main_tag(string)- 该图的标签。
tag_scalar_dict(dict)- key 是变量的 tag,value 是变量的值。
global_step(int)- 曲线图的 x 坐标
walltime(float)- 为 event 文件的文件名设置时间,默认为 time.time()

运行 demo 中的:

writer.add_scalars('data/scalar_group', {'xsinx': n_iter * np.sin(n_iter),
'xcosx': n_iter * np.cos(n_iter),
'arctanx': np.arctan(n_iter)}, n_iter)

可以得到下图:
在这里插入图片描述

1.3、绘制直方图(add_histogram)

add_histogram()
add_histogram(tag, values, global_step=None, bins='tensorflow', walltime=None)

功能:
绘制直方图和多分位数折线图,常用于监测权值及梯度的分布变化情况,便于诊断网
络更新方向是否正确。

参数:
tag(string)- 该图的标签,类似于 polt.title。
values(torch.Tensor, numpy.array or string/blobname)- 用于绘制直方图的值
global_step(int)- 曲线图的 y 坐标
bins(string)- 决定如何取 bins,默认为‘tensorflow’,可选:’auto’, ‘fd’等
walltime(float)- 为 event 文件的文件名设置时间,默认为 time.time()

运行 demo 中的:

for name, param in resnet18.named_parameters():
writer.add_histogram(name, param.clone().cpu().data.numpy(), n_iter)

可以得到以下两种图分别在 HISTOGRAMS 和 DISTRIBUTIONS 里面:
在这里插入图片描述
x 轴即变量大小,y 轴为 gloabl_step。377 表示卷积层 conv1 的权值中有 377 个
weight 的大小是在 0.036 这个区间。
在这里插入图片描述
x 轴为 gloabl_step,由于这里没有训练,所以随着 x 轴的增加,曲线是平直的。看 y
轴,从上到下,共计 9 条曲线(若有训练,会是曲线,现在是直线),分别对应[maximum,
93%, 84%, 69%, 50%, 31%, 16%, 7%, minimum]分位数。

1.4、绘制图片(add_image)

** add_image()**
add_image(tag, img_tensor, global_step=None, walltime=None)

功能:
绘制图片,可用于检查模型的输入,监测 feature map 的变化,或是观察 weight。

参数:
tag(string)- 该图的标签,类似于 polt.title。
img_tensor(torch.Tensor,numpy.array, or string/blobname)- 需要可视化的图片数据, shape = [C,H,W]。
global_step(int)- x 坐标。
walltime(float)- 为 event 文件的文件名设置时间,默认为 time.time()。

通常会借助 torchvision.utils.make_grid() 将一组图片绘制到一个窗口
补充 torchvision.utils.make_grid()
torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, ra
nge=None, scale_each=False, pad_value=0)

功能:
将一组图片拼接成一张图片,便于可视化。

参数:
tensor(Tensor or list)- 需可视化的数据,shape:(B x C x H x W) ,B 表示 batch 数,即几张图片
nrow(int)- 一行显示几张图,默认值为 8。
padding(int)- 每张图片之间的间隔,默认值为 2。
normalize(bool)- 是否进行归一化至(0,1)。
range(tuple)- 设置归一化的 min 和 max,若不设置,默认从 tensor 中找 min 和 max。
scale_each(bool)- 每张图片是否单独进行归一化,还是 min 和 max 的一个选择。
pad_value(float)- 填充部分的像素值,默认为 0,即黑色。

运行 demo 代码:

import torchvision.utils as vutils
dummy_img = torch.rand(32, 3, 64, 64) # (B x C x H x W)
if n_iter % 10 == 0:
x = vutils.make_grid(dummy_img, normalize=True, scale_each=True)
writer.add_image('Image', x, n_iter) # x.size= (3, 266, 530) (C*H*W)

可得到下图:
在这里插入图片描述

1.5、网络结构拓扑图(add_graph)

add_graph()
add_graph(model, input_to_model=None, verbose=False, **kwargs)

功能:
绘制网络结构拓扑图。

参数:
model(torch.nn.Module)- 模型实例
inpjt_to_model(torch.autograd.Variable)- 模型的输入数据,可以生成一个随机数,只
要 shape 符合要求即可

运行以下代码:

import torchvision.models as models
resnet18 = models.resnet18(False)
dummy_input = torch.rand(6, 3, 224, 224)
writer.add_graph(resnet18, dummy_input)

可在 GRAPHS 中看到 Resnet18 的网络拓扑

在这里插入图片描述

1.6、高维空间展示数据(add_embedding)

** add_embedding()**
add_embedding(mat, metadata=None, label_img=None, global_step=None, tag='de fault', metadata_header=None)

功能:
在三维空间或二维空间展示数据分布,可选 T-SNE、PCA 和 CUSTOM 方法。
参数:

mat(torch.Tensor or numpy.array)- 需要绘制的数据,一个样本必须是一个向量形式。
shape = (N,D),N 是样本数,D 是特征维数。
metadata(list)- 数据的标签,是一个 list,长度为 N。
label_img(torch.Tensor)- 空间中展示的图片,shape = (N,C,H,W)。
global_step(int)- Global step value to record,不理解这里有何用处呢?知道的朋友补充一下吧。
tag(string)- 标签

以下代码,展示 Mnist 中的 100 张图片

dataset = datasets.MNIST('mnist', train=False, download=True)
images = dataset.test_data[:100].float()
label = dataset.test_labels[:100]
features = images.view(100, 784)
writer.add_embedding(features, metadata=label, label_img=images.unsqueeze(1))

在这里插入图片描述

1.7、记录文字(add_text)

add_text()
add_text(tag, text_string, global_step=None, walltime=None)

**功能:
**记录文字

1.8、记录video(add_video)

add_video()
add_video(tag, vid_tensor, global_step=None, fps=4, walltime=None)

功能:
记录 video

1.9、图片到图像(add_figure)

** add_figure()**
add_figure(tag, figure, global_step=None, close=True, walltime=None)

功能:
添加 matplotlib 图片到图像中

1.10、绘制 (Boxadd_image_with_boxes)

** add_image_with_boxes()**
add_image_with_boxes(tag, img_tensor, box_tensor, global_step=None, walltime =None, **kwargs)

功能:
图像中绘制 Box,目标检测中会用到

1.11、PR 曲线(add_pr_curve)

** add_pr_curve()**
add_pr_curve(tag, labels, predictions, global_step=None, num_thresholds=127, w eights=None, walltime=None)

功能:
绘制 PR 曲线

1.12、原始数据上绘制 PR 曲线(add_pr_curve_raw)

add_pr_curve_raw()
add_pr_curve_raw(tag, true_positive_counts, false_positive_counts, true_negati ve_counts, false_negative_counts, precision, recall, global_step=None, num_thres holds=127, weights=None, walltime=None)

功能:
从原始数据上绘制 PR 曲线

1.13、保存scalars 信息到 json 文件(export_scalars_to_json)

export_scalars_to_json()
export_scalars_to_json(path)

功能:
将 scalars 信息保存到 json 文件,便于后期使用

二、卷积核可视化

神经网络中最重要的就是权值,而人们对神经网络理解有限,所以我们需要通过尽可能了解权值来帮助诊断网络的训练情况。除了查看权值分布图和多折线分位图,还可以对卷积核权值进行可视化,来辅助我们分析网络。本小节就介绍卷积核权值可视化原理和方法。在 2012 年 AlexNet 的论文中,展示了一副卷积核权值可视化的图片,如下图所示。

在这里插入图片描述
文中将第一个卷积层的卷积核权值进行可视化,发现有趣的现象。第一个 GPU 中的卷积核呈现初边缘的特性,第二个 GPU 中的卷积核呈现色彩的特性。

对卷积核权值进行可视化,在一定程度上帮助我们诊断网络的训练好坏,因此对卷积核权值的可视化十分有必要。可视化原理很简单,对单个卷积核进行“归一化”至 0~255,然后将其展现出来即可,这一系列操作可以借助 TensorboardX 的 add_image 来实现。先看两张可视化效果图,接着简单介绍卷积核卷积过程,最后讲解代码。

下图以卷积层 conv1 为例,conv1.weight.shape() = [6,3,5,5],输入通道数为 3,卷积核个数为 6,则 feature map 数为 6,卷积核大小为 5*5。

图 1,绘制全部卷积核
在这里插入图片描述

图 2,以 feature map 为单位,借助 step 参数区分 feature map,将卷积核分开绘制
在这里插入图片描述
好奇的朋友就会问了,为什么要将每一行显示 3 个卷积核呢?因为每 3 个卷积核共同作用去决定了一张 feature map,因此将其放在一起观察。具体卷积核过程可以观察下图,输入通道数为 4,输出通道数为 2,卷积层卷积核有 8 个 2*2的卷积核。

第一个输出特征图的值,是由 4 个卷积核分别对 4 个输入通道进行卷积并求和,加上bias 项,再通过激活函数,才得到最终 feature map 上的值。所以可以将卷积层的卷积核按照 feature map 输出数进行划分,一个 feature map 对应的卷积核数为输入通道数。

在这里插入图片描述
代码实现:

  • 运行 /Code/4_viewer/2_visual_weights.py
  • 运行代码后,在文件夹/Result/下获得文件夹visual_weights,然后打开terminal,进入相应的虚拟环境(如果有),进入目录/Result/
  • 执行:tensorboard --logdir=visual_weights
  • 进入浏览器,打开网页:http://localhost:6006/

三、特征图可视化

有时候我们会好奇,原始图像经过网络的操作之后,会是什么样。本小节就介绍如何可视化经网络操作后的图像(feature maps)。

基本思路:

  1. 获取图片,将其转换成模型输入前的数据格式,即一系列 transform,
  2. 获取模型各层操作,手动的执行每一层操作,拿到所需的 feature maps,
  3. 借助 tensorboardX 进行绘制。

Tips:
此处获取模型各层操作是__init__()中定义的操作,然而模型真实运行采用的是forward(), 所以需要人工比对两者差异。本例的差异是,init()中缺少激活函数relu。

先看看图,下图为 conv1 层输出的 feature maps, 左图为未经过 relu 激活函数,右图为经过 relu 之后的 feature maps。
在这里插入图片描述
原始图片(已经 resize 至 32*32)
在这里插入图片描述
代码实现:

  • 运行 /Code/4_viewer/3_visual_featuremaps.py
  • 运行代码后,在文件夹/Result/下获得文件夹visual_featuremaps,然后打开terminal,进入相应的虚拟环境(如果有),进入目录/Result/
  • 执行:tensorboard --logdir=visual_featuremaps
  • 进入浏览器,打开网页:http://localhost:6006/

文末探讨:

  1. 经过 relu 激活函数之后,明显很多像素点变黑,即像素值变为 0 了? 这样是否会影响
    网络性能?
  2. 除了 net._modules.items()这种方法之外,是否还有更简便的方法来获取 feature
    maps 吗? 因此上述方法感觉不太“智能”,需要人工比对 forward()中的操作和
    init()中的操作,比较容易出差错,如果有更好的方法,欢迎分享。

四、梯度及权值分布可视化

在网络训练过程中,我们常常会遇到梯度消失、梯度爆炸等问题,我们可以通过记录每个epoch 的梯度的值来监测梯度的情况,还可以记录权值,分析权值更新的方向是否符合规律。
本小节就介绍如何记录梯度及权值,并进行可视化。

代码实现:

  • 运行 /Code/4_viewer/4_hist_grad_weight.py
  • 运行代码后,在文件夹/Result/下获得文件夹 hist_grad_weight,然后打开terminal,进入相应的虚拟环境(如果有),进入目录/Result/
  • 执行:tensorboard --logdir=hist_grad_weight
  • 进入浏览器,打开网页:http://localhost:6006/

记录梯度和权值主要是以下三行代码:

# 每个 epoch,记录梯度,权值
for name, layer in net.named_parameters():
writer.add_histogram(name + '_grad', layer.grad.cpu().data.numpy(), epoch)
writer.add_histogram(name + '_data', layer.cpu().data.numpy(), epoch)

4.1、权值监控

  1. 权值 weights 的监控
    经过 100 个 epoch 的训练,来看看第一个卷积层的权值分布的变化。x 轴即变量大小,y 轴为 gloabl_step。

图 1 x=0.306, y=0, 数值显示为 0.00,表示第 0 个 epoch 时,权值为 0.306 的个数为0.00。
在这里插入图片描述
图 2, x=0.306, y=85, 数值显示为 5.71,表示第 85 个 epoch 时,权值在 0.306 区间的有 5.71 个。
在这里插入图片描述
这里暂时还不明白为什么会是小数,知道的朋友可以补充一下。通过 HISTOGRAMS 可以看到第一个卷积层的权值随着训练的不断的“扩散”,扩大,一开始是个比较标准的高斯分布,并且最大值不会超过 0.3。而到了后期,权值会发散到 0.6+,这个问题也是需要关注的,若权值太大容易导致过拟合。因为模型的输出值会被该特征所主导,从而引起过拟合现象,这个可以通过权值衰减(weight_decay)来缓解。

4.2、偏置监控

通常会监控输出层的 bias 的大小,若有特别大,或者特别小的 bias,那么某一类别的召回率可能会很低,可以通过观察输出层的 bias 来诊断是否在这一环节出问题。从图上可以看到,一开始 10 个类别的 bias 都比较小,随着训练的进行,每个类别都有了自己的固定的 bias 大小。

在这里插入图片描述

4.3、梯度监控

下图为第一个卷积层权值的梯度变化情况,可以看到,几乎都是服从高斯分布的。倘若前
面几层的梯度非常小,那么就是梯度流通不畅导致的,可以考虑残差结构或者辅助损失层
等 trick 来解决梯度消失。

在这里插入图片描述
文末思考:

  1. 通过观察各层的梯度,权值分布,我们可以针对性的设置学习率,为那些梯度小的层设置更大的学习率,让那些层可以有效的更新,不知道这样是否有用,大家可以尝试。
  2. 对权值特别大的那些层,可以考虑为那一层设置更大的 weight_decay,是否能有效降低该层权值大小呢。
  3. 通过对梯度的观察,可以合理的设置梯度 clip 的值喔。

五、混淆矩阵可视化

在分类任务中,个人十分喜欢混淆矩阵,通过混淆矩阵可以看出模型的偏好,而且对每一个类别的分类情况都了如指掌,为模型的优化ᨀ供很大帮助。本小节就介绍混淆矩阵概念及其可视化。

5.1、混淆矩阵概念

混淆矩阵(Confusion Matrix)常用来观察分类结果,其是一个 N*N 的方阵,N 表示类别数。混淆矩阵的行表示真实类别,列表示预测类别。例如,猫狗的二分类问题,有猫的图像 10 张,狗的图像 30 张,模型对这 40 张图片进行预测,得到的混淆矩阵为
在这里插入图片描述

5.1.1、召回率(针对单独某个类概念)

从第一行中可知道,10 张猫的图像中,7 张预测为猫,3 张预测为狗,猫的召回率(Recall) 为 7/10 = 70%,

从第二行可知道,30 张狗的图像中,8 张预测为猫,22 张预测为狗,狗的召回率(Recall) 为20/30 = 66.7%,

5.1.2、精确度(Precision,也称为精确率,查准率,针对单独某个类概念)

从第一列中可知道,预测为猫的 17 张图像中,有 7 张是真正的猫,猫的精确度(Precision)为 7 / 17 = 41.17%

从第二列中可知道,预测为狗的 23 张图像中,有 20 张是真正的狗,狗的精确度(Precision)为 20 / 23 = 86.96%

5.1.3、准确率(Accuracy,针对整个模型的概念)

模型的准确率(Accuracy)为 7+20 / 40 = 67.5%

可以发现通过混淆矩阵可以清晰的看出网络模型的分类情况,若再结合上颜色可视化,可方便的看出模型的分类偏好。

本小节将介绍,混淆矩阵的统计及其可视化。

5.2、混淆矩阵统计

第一步:创建混淆矩阵
获取类别数,创建 N*N 的零矩阵

conf_mat = np.zeros([cls_num, cls_num])

第二步:获取真实标签和预测标签

labels 为真实标签,通常为一个 batch 的标签
predicted 为预测类别,与 labels 同长度

第三步:依据标签为混淆矩阵计数

for i in range(len(labels)):
	true_i = np.array(labels[i])
	pre_i = np.array(predicted[i])
	conf_mat[true_i, pre_i] += 1.0

5.3、混淆矩阵可视化

可视化还需要各类标签名,存储为一个 list,以 cifar10 为例:
classes = [‘plane’, ‘car’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’]
可视化效果如下图:
在这里插入图片描述
函数位于:/Code/4_viewer/5_Show_ConfMat.py

运行/Code/main_training/main.py 即可在 /Result/your_time/文件夹下面看到混淆矩阵图啦。

本教程是以实际应用、工程开发为目的,着重介绍模型训练过程中遇到的实际问题,实际方法。如下图所示,在机器学习模型开发中,主要涉及三大部分,分别是数据、模型和损失函数及优化器。通过本教程,希望能够给大家带来一个清晰的模型训练结构。当模型训练遇到问题时,首先通过可视化工具对数据、模型、损失等内容进行观察,分析并定位问题出在数据部分?模型部分?还是优化器?只有这样不断的通过可视化诊断你的模型,不断的对症下药,才能训练出一个较满意的模型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【网络星空】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值