tensorboardx可视化_Pytorch训练可视化(TensorboardX)

今天我们来聊一聊数据可视化的问题,在这里向大家推荐一个大佬开源的教程,我的可视化的工作也是从他的教程借鉴而来的,再次表达感谢!

开源教程地址:tensor-yu/PyTorch_Tutorial​github.com

Pytorch框架也有自己的可视化软件--Visdom,但是我用着不太习惯,感觉它的API也不太方便,参数设置过于复杂,而且可视化的功能性并不是太强,所以有人就写个库用来将Pytorch中的参数放到tensorboard上面进行可视化,十分方便!

tensorboadX

这相当于一个辅助工具,可以把Pytorch中的参数传递到Tensorboad上面,那么如何进行安装呢?分为三个步骤:pip install tensorboardX

pip install tensorboard

pip install tensorflow

注意numpy的版本要对应,否则会报错,如果不匹配,那就进行更新或者新建虚拟环境了!

网络训练(Cifar10)

首先,我使用了非官方的代码对Cifar10进行训练,类似于ResNet, 由于Cifar10中的图片尺寸都很小,大约32x32,所以我们对传统的resnet进行了修改,其网络结构如下:

参考于官方的ResNet18并做如下修改:由于像素太小,修改第一个卷积核步长为1,不进行下采样

修改通道,让通道变小些

删除layer4,不用再继续降采样了

# ResNet

class ResNet(nn.Module):

def __init__(self, block, layers, num_classes = 10): # block为残差模块

super(ResNet, self).__init__()

self.in_channels = 16

self.conv = conv3x3(3, 16)

self.bn = nn.BatchNorm2d(16)

self.relu = nn.ReLU(inplace=True)

self.layer1 = self.make_layer(block, 16, layers[0], 1) # 残差模块

self.layer2 = self.make_layer(block, 32, layers[1], 2)

self.layer3 = self.make_layer(block, 64, layers[2], 2)

self.avg_pool = nn.AvgPool2d(8) # kernel_size = 8

self.fc = nn.Linear(64, num_classes)

def make_layer(self, block, out_channels, blocks, stride=1):

downsample = None

if (stride != 1) or (self.in_channels != out_channels):

downsample = nn.Sequential(

conv3x3(self.in_channels, out_channels, stride=stride),

nn.BatchNorm2d(out_channels)

)

layers = []

layers.append(block(self.in_channels, out_channels, stride, downsample))

self.in_channels = out_channels

for i in range(1, blocks):

layers.append(block(out_channels, out_channels))

return nn.Sequential(*layers) # 把一个列表变成一个层的函数

def forward(self, x):

out = self.conv(x)

out = self.bn(out)

out = self.relu(out)

out = self.layer1(out)

out = self.layer2(out)

out = self.layer3(out)

out = self.avg_pool(out)

out = out.reshape(out.size(0), -1) # 1*1*64 features

out = self.fc(out)

return out

然后进行模型训练,训练代码如下:

# 训练整个网络

total_step = len(train_loader)

curr_lr = learning_rate

for epoch in range(num_epoches):

for i, (images, labels) in enumerate(train_loader):

images = images.to(device)

labels = labels.to(device)

# 正向传播

outputs = model(images)

loss = criterion(outputs, labels)

# 反向传播

optimizer.zero_grad()

loss.backward()

optimizer.step()

if (i+1) % 100 == 0:

print(f'Epoch {epoch+1}/{num_epoches}, Step {i+1}/{total_step}, {loss.item()}') # 不要忘了item()

torch.save(model.state_dict(), 'ResnetCifar10.pt')

最后我们得到模型,精度为:0.78左右,没有一直进行训练,这无所谓!!

TensorBoardX可视化

参考了一开始的那个代码库,我把可视化的工作分成了如下几个:卷积核的可视化

feature map的可视化

模型图的可视化

标量变化情况的可视化

权重的直方图的可视化(可以知道权重的数据分布)

那么,话不多说,我们来看看如何进行可视化的把,首先把tensorboardX导入并进行初始化:

from tensorboardX import SummaryWriter

# 定义Summary_Writer

writer = SummaryWriter('./Result') # 数据存放在这个文件夹

第一个任务:标量可视化与权重直方图add_scalar(tag, scalar_value, global_step=None, walltime=None) 记录标量的变化

add_scalars(main_tag, tag_scalar_dict, global_step=None, walltime=None) 记录多个标量的值

add_histogram(tag, values, global_step=None, bins='tensorflow', walltime=None) 绘制直方图

这个权重直方图,非常的关键,特别是寻找模型训练loss出现的各种问题,由于我是在测试时画的图,所以只有一幅,但一般我们在训练阶段使用,用于寻找模型的问题~~

任务代码:

# 显示每个layer的权重

loss = 10 # 第0层

for i, (name, param) in enumerate(model.named_parameters()):

if 'bn' not in name:

writer.add_histogram(name, param, 0)

writer.add_scalar('loss', loss, i)

loss = loss*0.5

效果图:

第二个任务:feature map的可视化和卷积核的可视化add_image(tag, img_tensor, global_step=None, walltime=None) 绘制图片

torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, ra nge=None, scale_each=False, pad_value=0) 制作网格用于显示

我们把每个卷积层后的feature map来进行可视化,一共有4个卷积层,所以我们将要绘制4个阶段的feature map,由于我们的模型已经训练好了,所以可视化出来的feature map可以很好地解释我们训练出来的模型到底是什么,每个卷积层提取的特征是什么?当然只能部分解释~

任务代码:

img_grid = vutils.make_grid(x, normalize=True, scale_each=True, nrow=2)

# 绘制原始图像

writer.add_image('raw img', img_grid, global_step=666) # j 表示feature map数

print(x.size())

model.eval()

for name, layer in model._modules.items():

# 为fc层预处理x

x = x.view(x.size(0), -1) if "fc" in name else x

print(x.size())

x = layer(x)

print(f'{name}')

# 第一个卷积没有进行relu,要记得加上

x = F.relu(x) if 'conv' in name else x

if 'layer' in name or 'conv' in name:

x1 = x.transpose(0, 1) # C,B, H, W ---> B,C, H, W

img_grid = vutils.make_grid(x1, normalize=True, scale_each=True, nrow=4) # normalize进行归一化处理

writer.add_image(f'{name}_feature_maps', img_grid, global_step=0)

效果图:Cifar 10里面的一张原图conv/layer 1/layer 2/layer 3四个层提取的feature map

我们可以清晰的看到,在网络的底层,我们可以看到船的边缘特征,由于归一化了,所以色彩特征不太明显,当然也可以不进行归一化,但是layer3这种高层特征我们就能以解释了,它代表更多的是语义特征,很抽象!

卷积核的可视化与这个类似,其实质也就是权重可视化,代码如下:

# 可视化卷积核

# in model.named_parameters():

if 'conv' in name and 'weight' in name:

in_channels = param.size()[1]

out_channels = param.size()[0] # 输出通道,表示卷积核的个数

k_w, k_h = param.size()[3], param.size()[2] # 卷积核的尺寸

kernel_all = param.view(-1, 1, k_w, k_h) # 每个通道的卷积核

kernel_grid = vutils.make_grid(kernel_all, normalize=True, scale_each=True, nrow=in_channels)

writer.add_image(f'{name}_all', kernel_grid, global_step=0)

效果图(conv层和layer 1层):

第三个任务:feature map的可视化和卷积核的可视化

这个不知道为什么,我使用Pytorch自带的网络模型就没有问题,但是自己定义的网络进行模型图显示就会出现一些莫名其妙的问题,有大佬知道的请告知原因啊!这里面我们使用ResNet18的结构来进行可视化,很简单,就一句话:

model = torchvision.models.resnet18(False)

writer.add_graph(model, torch.rand([1,3,224,224])) # 自己定义的网络有时显示错误

效果图:

总结

再次感谢首页大佬的开源教程,让我学到了很多,通过这些可视化的工作,我们就可以更好的去观测和调控这个模型的训练过程了!这个工程代码过段时间整理后也会和其他的一同开源~供大家参考学习~模型图可视化的问题,有人知道还请告知啊!

你可能感兴趣的:(tensorboardx可视化)