文章目录
可视化工具TensorBoard
PyTorch
从 1.2.0 版本开始,正式自带内置的Tensorboard
支持了,可以不再依赖第三方工具来进行可视化Tensorboard
工作原理为:先使用Python
脚本记录可视化数据,然后将数据存入硬盘,最后在终端中运行tensorboard
,并在Web界面进行展示
1、SummaryWriter
类
class SummaryWriter(object):
"""Writes entries directly to event files in the log_dir to be
consumed by TensorBoard.
The `SummaryWriter` class provides a high-level API to create an event file
in a given directory and add summaries and events to it. The class updates the
file contents asynchronously. This allows a training program to call methods
to add data to the file directly from the training loop, without slowing down
training.
"""
def __init__(self, log_dir=None, comment='', purge_step=None, max_queue=10,
flush_secs=120, filename_suffix='')
-
功能:提供创建 event file 的高级接口
-
属性:
-
log_dir
: event file 输出文件夹 -
comment
:不指定log_dir
时,文件夹后缀;(只有在不指定log_dir的情况下有效) -
filename_suffix
: event file 文件名后缀 -
方法:
(1)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 y y坐标global_step(int)
:曲线图的 x x x坐标walltime(float)
:为 event 文件的文件名设置时间,默认为time.time()
- 实例:
max_epoch = 100
writer = SummaryWriter(comment='test_comment', filename_suffix="test_suffix")
for x in range(max_epoch):
writer.add_scalar('y=2x', x * 2, x)
writer.add_scalar('y=pow_2_x', 2 ** x, x)
writer.close()
(2)add_scalars()
add_scalars(main_tag, tag_scalar_dict, global_step=None, walltime=None)
- 功能:在一个图表中记录多个标量的变化,常用于对比,如 trainLoss 和 validLoss 的比较等
- 参数:
main_tag
:该图的标签tag_scalar_dict
:key 是变量的 tag,value 是变量的值- 实例:
max_epoch = 100
writer = SummaryWriter(comment='test_comment', filename_suffix="test_suffix")
for x in range(max_epoch):
writer.add_scalars('data/scalar_group', {"xsinx": x * np.sin(x),
"xcosx": x * np.cos(x)}, x)
writer.close()
(3)add_histgram()
add_histogram(tag, values, global_step=None, bins='tensorflow', walltime=None, max_bins=None)
- 功能:绘制直方图和多分位数折线图,常用于监测权值及梯度的分布变化情况,便于诊断网 络更新方向是否正确
- 参数:
tag(string)
:该图的标签。values(torch.Tensor, numpy.array or string/blobname)
:用于绘制直方图的值global_step(int)
:曲线图的 y 坐标bins(string)
:决定如何取 bins,默认为’tensorflow’,可选:‘auto’,'fd’等walltime(float)
:为event 文件的文件名设置时间,默认为time.time()
- 实例:
writer = SummaryWriter(comment='test_comment', filename_suffix="test_suffix")
for x in range(2):
np.random.seed(x)
data_union = np.arange(100)
data_normal = np.random.normal(size=1000)
writer.add_histogram('distribution union', data_union, x)
writer.add_histogram('distribution normal', data_normal, x)
writer.close()
(4)add_image()
add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW')
- 功能:绘制图片,可用于检查模型的输入,监测 feature map 的变化,或是观察 weight。
- 参数:
tag(string)
:该图的标签img_tensor(torch.Tensor,numpy.array, or string/blobname)
:需要可视化的图片数据global_step(int)
: x x x坐标walltime(float)
:event 文件的文件名设置时间dataformats
:数据形式,可以有:“CHW”,“HWC”,“HW”,C表示通道数,H表示高度,W表示宽度- 实例:
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
# img 1 random
fake_img = torch.randn(3, 512, 512)
writer.add_image("fake_img", fake_img, 1)
time.sleep(1)
# img 2 ones
fake_img = torch.ones(3, 512, 512)
time.sleep(1)
writer.add_image("fake_img", fake_img, 2)
# img 3 1.1
fake_img = torch.ones(3, 512, 512) * 1.1
time.sleep(1)
writer.add_image("fake_img", fake_img, 3)
# img 4 HW
fake_img = torch.rand(512, 512)
writer.add_image("fake_img", fake_img, 4, dataformats="HW")
# img 5 HWC
fake_img = torch.rand(512, 512, 3)
writer.add_image("fake_img", fake_img, 5, dataformats="HWC")
writer.close()
这里需要注意,如果所有的数字均小于等于1,则会自动乘上255;如果有任意一个数大于,则不会乘255;这就是代码中img 2是白色,而img 3是黑色的原因
利用torchvision.utils.make_grid
可以将多张图片放到一张图片中,方便观察:
make_grid(tensor, nrow=8, padding=2,
normalize=False, range=None, scale_each=False, pad_value=0)
- 功能:制作网格图像
- 参数:
tensor
:图像数据,BCH*W形式(B为batch size)nrow
:行数(列数自动计算)padding
:每张图片之间的间隔,默认值为 2normalize
:是否进行归一化至(0,1)range
:标准化范围(设置归一化的 min 和 max,若不设置,默认从 tensor 中找 min 和 max)scale_each
:每张图片是否单独进行归一化,还是 min 和 max 的一个选择pad_value
:填充部分的像素值,默认为 0,即黑色- 实例:
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
split_dir = os.path.join("..", "..", "WeekTwo/lesson_6/data", "rmb_split")
train_dir = os.path.join(split_dir, "train")
# train_dir = "path to your training data"
transform_compose = transforms.Compose([transforms.Resize((32, 64)), transforms.ToTensor()])
train_data = RMBDataset(data_dir=train_dir, transform=transform_compose)
train_loader = DataLoader(dataset=train_data, batch_size=16, shuffle=True)
data_batch, label_batch = next(iter(train_loader))
img_grid = vutils.make_grid(data_batch, nrow=4, normalize=True, scale_each=True)
# img_grid = vutils.make_grid(data_batch, nrow=4, normalize=False, scale_each=False)
writer.add_image("input img", img_grid, 0)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DYDghdqU-1590656711575)(./_image/截屏2020-05-2816.27.03.png?r=40)][center]
(5)add_graph()
add_graph(model, input_to_model=None, verbose=False)
- 功能:可视化模型计算图
- 参数:
model
:模型,必须是nn.Module
input_to_model
:输出给模型的数据verbose
:是否打印计算图结构信息- 实例:
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
# 模型
fake_img = torch.randn(1, 3, 32, 32)
lenet = LeNet(classes=2)
writer.add_graph(lenet, fake_img)
writer.close()
print(summary(lenet, (3, 32, 32), device="cpu"))
# ----------------------------------------------------------------
# Layer (type) Output Shape Param #
# ================================================================
# Conv2d-1 [-1, 6, 28, 28] 456
# Conv2d-2 [-1, 16, 10, 10] 2,416
# Linear-3 [-1, 120] 48,120
# Linear-4 [-1, 84] 10,164
# Linear-5 [-1, 2] 170
# ================================================================
# Total params: 61,326
# Trainable params: 61,326
# Non-trainable params: 0
# ----------------------------------------------------------------
# Input size (MB): 0.01
# Forward/backward pass size (MB): 0.05
# Params size (MB): 0.23
# Estimated Total Size (MB): 0.30
# ----------------------------------------------------------------
# None
2、利用Tensorboard对网络模型的卷积和和特征图进行可视化
- 对卷积和的可视化
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
alexnet = models.alexnet(pretrained=True)
kernel_num = -1
vis_max = 1
for sub_module in alexnet.modules():
if isinstance(sub_module, nn.Conv2d):
kernel_num += 1
if kernel_num > vis_max:
break
kernels = sub_module.weight
c_out, c_int, k_w, k_h = tuple(kernels.shape)
for o_idx in range(c_out):
kernel_idx = kernels[o_idx, :, :, :].unsqueeze(1) # make_grid需要 BCHW,这里拓展C维度
kernel_grid = vutils.make_grid(kernel_idx, normalize=True, scale_each=True, nrow=c_int)
writer.add_image('{}_Convlayer_split_in_channel'.format(kernel_num), kernel_grid, global_step=o_idx)
kernel_all = kernels.view(-1, 3, k_h, k_w) # 3, h, w
kernel_grid = vutils.make_grid(kernel_all, normalize=True, scale_each=True, nrow=8) # c, h, w
writer.add_image('{}_all'.format(kernel_num), kernel_grid, global_step=322)
print("{}_convlayer shape:{}".format(kernel_num, tuple(kernels.shape)))
writer.close()
- 对特征图的可视化
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")
# 数据
path_img = "./lena.png" # your path to image
normMean = [0.49139968, 0.48215827, 0.44653124]
normStd = [0.24703233, 0.24348505, 0.26158768]
norm_transform = transforms.Normalize(normMean, normStd)
img_transforms = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
norm_transform
])
img_pil = Image.open(path_img).convert('RGB')
if img_transforms is not None:
img_tensor = img_transforms(img_pil)
img_tensor.unsqueeze_(0) # chw --> bchw
# 模型
alexnet = models.alexnet(pretrained=True)
# forward
convlayer1 = alexnet.features[0]
fmap_1 = convlayer1(img_tensor)
# 预处理
fmap_1.transpose_(0, 1) # bchw=(1, 64, 55, 55) --> (64, 1, 55, 55)
fmap_1_grid = vutils.make_grid(fmap_1, normalize=True, scale_each=True, nrow=8)
writer.add_image('feature map in conv1', fmap_1_grid, global_step=322)
writer.close()