tensorboard可视化

1. TensorBoard简介与安装

1.1 安装TensorBoard

TensorBoard:TensorFlow中强大的可视化工具

image-20200806224445463

通过pip install future和pip install tensorboard安装tensorboard

1.2 运行TensorBoard

实验代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
from torch.utils.tensorboard import SummaryWriter


writer = SummaryWriter(comment='test_tensorboard')

for x in range(100):

writer.add_scalar('y=2x', x * 2, x)
writer.add_scalar('y=pow(2, x)', 2 ** x, x)

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

运行后,会在py文件目录中生成一个run文件夹,每一次运行会在run文件夹中生成一个子文件夹。

image-20200807150757596

然后在terminal中cd到run文件夹的根目录,输入tensorboard --logdir=./runs命令,就开始运行tensorboard了,在浏览器中可以查看。

1
2
3
TensorFlow installation not found - running with reduced feature set.
or pass --bind_all
TensorBoard 2.2.1 at http://localhost:6006/ (Press CTRL+C to quit)

1.3 界面TensorBoard

image-20200806225425418

image-20200806225500005

image-20200806225633639

image-20200806230716606

平滑的作用在于,当曲线噪声非常大时,使用平滑我们可以看出大致的趋势(低通滤波?)

2. TensorBoard使用

2.1 SummaryWriter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.tensorboard import SummaryWriter
from tools.common_tools import set_seed

set_seed(1) # 设置随机种子

class SummaryWriter(object):
def __init__(self,
log_dir=None,
comment='',
purge_step=None,
max_queue=10,
flush_secs=120,
filename_suffix=’’)

功能:提供创建event file的高级接口;SummaryWriter类提供了一个高级API,用于在给定目录中创建事件文件,并向其中添加摘要和事件。类异步更新文件内容,这使得训练程序可以调用方法直接从训练循环中向文件中添加数据,而不会减慢训练速度。

主要属性:

  • log_dir:设置event file输出文件夹;默认为runs/CURRENT_DATETIME_HOSTNAME
  • comment:为默认log_dir添加后缀。如果指定了log_dir,则此参数无效。
  • filename_suffix:为log_dir目录中所有event file文件名添加后缀

实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.tensorboard import SummaryWriter
from tools.common_tools import set_seed

set_seed(1) # 设置随机种子

log_dir = "./train_log/test_log_dir"
writer = SummaryWriter()
# writer = SummaryWriter(log_dir=log_dir, comment='_scalars', filename_suffix="12345678")
# writer = SummaryWriter(comment='_scalars', filename_suffix="12345678")

for x in range(100):
writer.add_scalar('y=pow_2_x', 2 ** x, x)

writer.close()
  1. 运行writer = SummaryWriter(),得到输出文件夹如下所示:

    image-20200807163509626

  2. 运行writer = SummaryWriter(log_dir=log_dir, comment='_scalars', filename_suffix="12345678"),得到输出文件夹如下所示:

    image-20200807164947430

  3. 运行riter = SummaryWriter(comment='_scalars', filename_suffix="12345678"),得到输出文件夹如下所示:

    image-20200807165126857

2.1.1 add_scalar

1
2
3
4
add_scalar(tag, 
scalar_value,
global_step=None,
walltime=None)

功能:将标量数据添加到summary(只能记录一条曲线)

  • tag:图像的标签名,图的唯一标识
  • scalar_value:要记录的标量
  • global_step:x轴

实验:

1
2
3
4
5
6
7
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter()
x = range(100)
for i in x:
writer.add_scalar('y=2x', i * 2, i)
writer.close()
image-20200807170106483

2.1.2 add_scalars

1
2
3
4
add_scalars(main_tag, 
tag_scalar_dict,
global_step=None,
walltime=None)

功能:将许多标量数据添加到summary(记录多条曲线)

  • main_tag:该图的标签
  • tag_scalar_dict:key是变量的tag,value是变量的值

实验:

1
2
3
4
5
6
7
8
9
10
11
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter()
r = 5
for i in range(100):
writer.add_scalars('run_14h', {'xsinx':i*np.sin(i/r),
'xcosx':i*np.cos(i/r),
'tanx': np.tan(i/r)}, i)
writer.close()
# This call adds three values to the same scalar plot with the tag
# 'run_14h' in TensorBoard's scalar section.
image-20200807170501816

2.1.3 add_histogram

1
2
3
4
add_histogram(tag, values, 
global_step=None,
bins='tensorflow',
walltime=None)

功能:统计直方图与多分位数折线图

实验:

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
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.tensorboard import SummaryWriter
from tools.common_tools import set_seed

set_seed(1) # 设置随机种子

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)

plt.subplot(121)
plt.hist(data_union, label="union")
plt.legend()
plt.subplot(122)
plt.hist(data_normal, label="normal")
plt.legend()
plt.show()

writer.close()
  1. matplot输出如下所示(得到两个figure):
image-20200807174329774 image-20200807174451131

plt.hist默认bin=10,即把直方图分成10个条。对于左图就是[0, 9], [10, 19], …, [90, 99]统计在一起,显然对于每一个bin都有10个数据落入其中;

以下是bin=20的直方图:

image-20200808190423187 image-20200808190432190

打开tensorboard,可以看到下面的直方图。bins=‘tensorflow’(默认),这个bin的计算方式我目前还没看懂。均匀分布的直方图本来应该是平的,但是出现的高低起伏,说明bin是不等宽的,由内部函数动态分配。

image-20200808190630473 image-20200808190644191

如果我们令bins=10:

1
2
writer.add_histogram('distribution union', data_union, x, bins=10)
writer.add_histogram('distribution normal', data_normal, x, bins=10)

tensorboard得到下面直方图:

image-20200808190847039 image-20200808190900011

我们可以发现,已经和我们用plt.hist输出的直方图很相似了,唯一还搞不懂的就是纵坐标的计算方式(居然是个小数,其次这个直方图还不是条形的,搞不懂)

以下是bins=20的tensorboard直方图,可以看到随着bins的增加,曲线更加圆滑了(还是搞不懂这个图是怎么来的,直方图却不是条状)

image-20200808191546038 image-20200808191559457

下面是bins=100的tensorboard直方图:

image-20200808191854291 image-20200808191930137

通过以上实验可以发现,bins这个参数就调整精细程度,bins愈大得到的直方图越精细。此外随着bins提高,直方图的值(纵坐标)并没有变化,这也说明了tensorboard的直方图不是传统意义的直方图(传统直方图是统计在bin内数据的数量)

另外,我还发现了,无论bins取多少,tensorboard这个直方图总是有30个采样点(下图展示了无论bins取多少,每个采样的位置都是一样的。可以看到第一个是点是-2.94,第二个点是-2.70)。这30个数据点的值加在一起就约等于样本总体的数据量。像下面这个标准正态分布的30个数据点的数加起来约等于1000。所以这个tensorboard的直方图还是有智慧的。我们不能把他看成传统意义的直方图,纵坐标应该看成在这个横坐标左右区域(比如第一个数据点横坐标是-2.94,第二个是-2.70,那么宽度就是0.24,所以第一个数据点纵坐标的意义是:在[-3.06, -2.82])大概有3.33个数据点。

image-20200808193745391 image-20200808193658538 image-20200808193832336 image-20200808193856160

所以我猜测:这个图的计算方法是先按给定的bins参数绘制出直方图,然后对直方图等间隔采样30个数据点,再将这些数据点用折线连接起来。数据点的值做了归一化处理,与bins的数量无关。

2.1.4 实战一: 监控loss, accuracy, weights, gradients

下面是我们之前用Lenet做的的rmb二分类的tensorboard可视化的结果。

实验代码:

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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
from torch.utils.tensorboard import SummaryWriter
import torch.optim as optim
from matplotlib import pyplot as plt
from model.lenet import LeNet
from tools.my_dataset import RMBDataset
from tools.common_tools import set_seed

set_seed() # 设置随机种子
rmb_label = {"1": 0, "100": 1}

# 参数设置
MAX_EPOCH = 10
BATCH_SIZE = 16
LR = 0.01
log_interval = 10
val_interval = 1

# ============================ step 1/5 数据 ============================

split_dir = os.path.join("..", "data", "rmb_split")
train_dir = os.path.join(split_dir, "train")
valid_dir = os.path.join(split_dir, "valid")

norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]

train_transform = transforms.Compose([
transforms.Resize((32, 32)),
transforms.RandomCrop(32, padding=4),
transforms.RandomGrayscale(p=0.8),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std),
])

valid_transform = transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std),
])

# 构建MyDataset实例
train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
valid_data = RMBDataset(data_dir=valid_dir, transform=valid_transform)

# 构建DataLoder
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)

# ============================ step 2/5 模型 ============================

net = LeNet(classes=2)
net.initialize_weights()

# ============================ step 3/5 损失函数 ============================
criterion = nn.CrossEntropyLoss() # 选择损失函数

# ============================ step 4/5 优化器 ============================
optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9) # 选择优化器
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) # 设置学习率下降策略

# ============================ step 5/5 训练 ============================
train_curve = list()
valid_curve = list()

iter_count = 0

# 构建 SummaryWriter
writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")

for epoch in range(MAX_EPOCH):

loss_mean = 0.
correct = 0.
total = 0.

net.train()
for i, data in enumerate(train_loader):

iter_count += 1

# forward
inputs, labels = data
outputs = net(inputs)

# backward
optimizer.zero_grad()
loss = criterion(outputs, labels)
loss.backward()

# update weights
optimizer.step()

# 统计分类情况
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).squeeze().sum().numpy()

# 打印训练信息
loss_mean += loss.item() # tensor转换为float
train_curve.append(loss.item())
if (i+1) % log_interval == 0:
loss_mean = loss_mean / log_interval
print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))
loss_mean = 0.

# 记录数据,保存于event file 每个iteration,记录Loss,Accuracy
writer.add_scalars("Loss", {"Train": loss.item()}, iter_count)
writer.add_scalars("Accuracy", {"Train": correct / total}, iter_count)

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

scheduler.step() # 更新学习率

# validate the model
if (epoch+1) % val_interval == 0:

correct_val = 0.
total_val = 0.
loss_val = 0.
net.eval()
with torch.no_grad():
for j, data in enumerate(valid_loader):
inputs, labels = data
outputs = net(inputs)
loss = criterion(outputs, labels)

_, predicted = torch.max(outputs.data, 1)
total_val += labels.size(0)
correct_val += (predicted == labels).squeeze().sum().numpy()

loss_val += loss.item()

valid_curve.append(loss.item())
print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val, correct / total))

# 记录数据,保存于event file
writer.add_scalars("Loss", {"Valid": np.mean(valid_curve)}, iter_count)
writer.add_scalars("Accuracy", {"Valid": correct / total}, iter_count)

train_x = range(len(train_curve))
train_y = train_curve

train_iters = len(train_loader)
valid_x = np.arange(1, len(valid_curve)+1) * train_iters*val_interval # 由于valid中记录的是epochloss,需要对记录点进行转换到iterations
valid_y = valid_curve

plt.plot(train_x, train_y, label='Train')
plt.plot(valid_x, valid_y, label='Valid')

plt.legend(loc='upper right')
plt.ylabel('loss value')
plt.xlabel('Iteration')
plt.show()
  1. Accuracy

    image-20200808215556184 image-20200808215741753
  2. Loss

    1 image-20200808215811301
  3. conv1.weight_data

    image-20200808220043248

    可以看见,conv1层的参数分布还是不错的,方差保持了一致性

  4. conv1.weight_grad

    image-20200808220124651

    conv1层的梯度也可以,在后面梯度变为0的原因有两种:

    1. 训练完成,loss很小
    2. 发生梯度弥散,梯度没办法方向传播到前面的网络层

    怎么判断呢?可以看loss是否足够低,也可以看最后一层网络层的梯度是否也很小,如果不是说明发生了梯度弥散。

  5. fc3.weight_data

    image-20200808220309895
  6. fc3.weight_grad

    image-20200808220330517

    最后一层网络层随着迭代的次数,梯度也逐渐变小,说明没有发生梯度回传障碍。

2.1.5 add_image

1
2
3
4
5
add_image(tag, 
img_tensor,
global_step=None,
walltime=None,
dataformats='CHW')

功能:记录图像

  • tag:图像的标签名,图的唯一标识
  • img_tensor:图像数据,注意尺度(数据在[0,1]内的自动乘255转为0255的像素值;数据不在[0,1]内的当成0255的像素值)
  • global_step:x轴
  • dataformats:数据形式,CHW,HWC,HW

实验:

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
import os
import torch
import time
import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.utils as vutils
from tools.my_dataset import RMBDataset
from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import DataLoader
from tools.common_tools import set_seed
from model.lenet import LeNet

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()

image-20200808224844182

1
fake_img = torch.randn(3, 512, 512)

数据处于[0, 1]范围内,所以会自动乘255转化为像素值

image-20200808224902181

1
fake_img = torch.ones(3, 512, 512)

数据处于[0, 1]范围内,所以会自动乘255转化为像素值;所以图片变成了像素值全为255的纯白照片。

image-20200808224912239

1
fake_img = torch.ones(3, 512, 512) * 1.1

数据不处于[0, 1]范围内,且全部数据值为1.1,所以显示出来是黑色的照片

image-20200808224922284

1
fake_img = torch.rand(512, 512)

数据处于[0, 1]范围内,所以会自动乘255转化为像素值;但只有一个通道,参数应写为dataformats=“HW”

image-20200808224929722

1
fake_img = torch.rand(512, 512, 3)

数据处于[0, 1]范围内,所以会自动乘255转化为像素值;但通道是HWC,参数应写为dataformats=“HWC”

add_img显示图片需要用鼠标拖动显示相应step的图像

那有没有方法能一次显示所有图片呢?torchvision.utils.make_grid就是答案。

2.1.6 torchvision.utils.make_grid

1
2
3
4
5
6
7
make_grid(tensor, 
nrow=8,
padding=2,
normalize=False,
range=None,
scale_each=False,
pad_value=0)

功能:制作网格图像

  • tensor:图像数据; 4Dmini-batch的张量(B x C x H x W)或相同大小的图像列表。其中C只能为1(黑白显示)或者3(彩色显示)
  • nrow:网格中每行显示的图像数。最终栅格大小为(B/nrow,nrow);默认值:8。
  • padding:图像间距(像素单位)
  • normalize:是否将像素值标准化;如果为True,则将图像按range指定的最小值和最大值移动到范围(0,1)。默认值:False。
  • range:标准化范围;tuple(min,max),其中min和max是数字,那么这些数字用于规范化图像。默认情况下,min和max输入张量的最小值和最大值。
  • scale_each:是否单张图维度标准化;如果为True,则在一批图像中分别缩放每个图像,而不是在所有图像上缩放(最小值、最大值)。默认值:False。
  • pad_value:padding的像素值

实验:

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
import os
import torch
import time
import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.utils as vutils
from tools.my_dataset import RMBDataset
from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import DataLoader
from tools.common_tools import set_seed
from model.lenet import LeNet

writer = SummaryWriter(comment='test_your_comment', filename_suffix="_test_your_filename_suffix")

split_dir = os.path.join("..", "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)) # batch_size=16,next一次一批16张图片

img_grid = vutils.make_grid(data_batch, nrow=4)
writer.close()

输出图像:

image-20200809155825299

2.1.7 实战二 :卷积核和特征图的可视化

下面是我们对alexnet前两个卷积层的卷积核可视化的结果:

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
import torch.nn as nn
from PIL import Image
import torchvision.transforms as transforms
from torch.utils.tensorboard import SummaryWriter
import torchvision.utils as vutils
from tools.common_tools import set_seed
import torchvision.models as models

set_seed(1) # 设置随机种子


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)

kernel_idx = kernels.view(-1, 1, k_h, k_w) # 3, h, w

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=0)

print("{}_convlayer shape:{}".format(kernel_num, tuple(kernels.shape)))

writer.close()

打印输出:

1
2
0_convlayer shape:(64, 3, 11, 11)
1_convlayer shape:(192, 64, 5, 5)

tensorboard的显示(同一行是一个卷积核的各个通道):

image-20200809175151439

image-20200809175212965

下面是我们对alexnet第一个卷积层的输出特征图可视化的结果

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
import torch.nn as nn
from PIL import Image
import torchvision.transforms as transforms
from torch.utils.tensorboard import SummaryWriter
import torchvision.utils as vutils
from tools.common_tools import set_seed
import torchvision.models as models

set_seed(1) # 设置随机种子


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
convlayer0 = alexnet.features[0] # 得到alexnet的第一个卷积层
fmap_0 = convlayer0(img_tensor) # 得到alexnet的第一个卷积层的输出

# 预处理
b_int, c_int, k_w, k_h = tuple(fmap_0.shape)
fmap_0 = fmap_0.view(-1, 1, k_h, k_w) # 拉平特征图 bchw=(1, 64, 55, 55) --> (64, 1, 55, 55)
fmap_0_grid = vutils.make_grid(fmap_0, normalize=True, scale_each=True, nrow=8)

writer.add_image('feature map in conv0', fmap_0_grid)
writer.close()

tensorboard的显示:

image-20200809181742482

2.1.8 add_graph

1
2
3
add_graph(model, 
input_to_model=None,
verbose=False)

功能:可视化模型计算图

  • model:模型,必须是 nn.Module
  • input_to_model:输出给模型的数据
  • verbose:是否打印计算图结构信息

实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
import torch
import time
import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.utils as vutils
from tools.my_dataset import RMBDataset
from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import DataLoader
from tools.common_tools import set_seed
from model.lenet import LeNet

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()

image-20200809193109809

2.2 torchsummary

1
2
3
4
summary(model, 
input_size,
batch_size=-1,
device="cuda")

功能:查看模型信息,便于调试

  • model:pytorch模型
  • input_size:模型输入size
  • batch_size:batch size(缺省即可)
  • device:“cuda” or “cpu”

通过pip install torchsummary安装torchsummary

device='cuda’有时候会报错

实验:

1
2
3
from torchsummary import summary

print(summary(lenet, (3, 32, 32), device="cpu"))

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
----------------------------------------------------------------
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