【Datawhale学习】Pytorch可视化

        对于我们刚刚接触神经网络的初学者来说,它是非常神奇的一个存在:我们将特征进行输入,数据流经网络,进行一番操作后就输出了结果。所谓训练神经网络的过程,就是通过梯度下降不断修改网络内部的参数,从而使得输出结果越来越精确。在写好的程序中,构建神经网络模型完成后就把它封装起来了,并且前向传播(forward)、反向传播(backward)都是自动计算的。因此,神经网络对于使用者而言,更像是一个“黑箱”,我们不知道数据输入黑箱后发生了什么,只知道这个黑箱可以给出我们想要的结果。

        在炼丹的学习过程中,很容易就变成调包侠,所以要自己多动手去搭建一些网络模型——就相当于把黑箱给拆开来,去了解每一部分的原理;那么Pytorch可视化,相当于把一个连续的训练过程一步步拆解开来,去了解数据每一步的变化,这有助于我们去调试模型、优化超参数。

        首先来看一看经典卷积网络中卷积核的可视化。

【Datawhale学习】Pytorch可视化_第1张图片

        一个卷积神经网络显著的结构特征就是随着卷积的深入,通道数增加。在一个卷积层中,为了实现通道增加,就需要多个卷积核进行配合(后一层网络的通道数=前一层卷积的卷积核个数)。个人是这样理解的:通过添加多个卷积核,可以在每个卷积核上有不同的参数设置,这样就可以从不同特征维度对图像的特征进行提取了。

        例如在前面的FashionMNST分类模型中,模型基本结构如下:

【Datawhale学习】Pytorch可视化_第2张图片

【Datawhale学习】Pytorch可视化_第3张图片 

直接print(model)就可以看到模型的基本结构:三层的卷积以及两个全连接 

当然看这种基本的网络结构还可以同torchinfo这个包,可以得到更细致的节点信息:

from torchinfo import summary
summary(model,(1,1,28,28))#图片数量1,通道数1,图片尺寸28*28

 可以说是很详细了【Datawhale学习】Pytorch可视化_第4张图片

         以上操作相当于是把黑箱给拆开观察其内部结构。针对具体结构的某一部分(比如卷积核),同样可以通过可视化的方法来看一看卷积核具体长啥样(conv部分的第一次卷积):

print(model.conv[0])

        

         接下来的操作,目的是把卷积核的权重提取出来做可视化。可以使用detach()这个函数来实现:【Datawhale学习】Pytorch可视化_第5张图片 

         这就通过可视化的方式把32个卷积核进行了输出,其中蓝色代表负数,红色是正数,白色为0,颜色越深则相应的绝对值越大。具体实现代码如下:

model = Net().to(device)
conv1 = model.conv[0]
kernel_set = conv1.weight.detach()#将卷积核的权重提取出来
num = len(conv1.weight.detach())
print(kernel_set)
plt.figure(figsize=(20, 17))
for i in range(0,num):
    i_kernel = kernel_set[i]
    filer = i_kernel[0]
    plt.subplot(6, 6, i+1)
    plt.axis('off')
    plt.imshow(filer[ :, :].detach(),cmap='bwr')
plt.show()

         以上就是通过detach()函数对卷积核的权重进行了输出。此外还可有Hook方法(钩子),来实现每一层卷积(或者其他操作)之后特征图像的输出。
        首先定义一个Hook类,这个类的作用就好像在神经网络中间放置一个钩子一样,将流经钩子前后的特征图称为输入、输出特征,并作为初始化参数输入该类。具体代码如下:

class Hook(object):
    def __init__(self):
        self.module_name = []
        self.features_in_hook = []
        self.features_out_hook = []

    def __call__(self, module, fea_in, fea_out):
        print("hooker working", self)
        self.module_name.append(module.__class__)
        self.features_in_hook.append(fea_in)
        self.features_out_hook.append(fea_out)
        return None

        在pytorch的nn.Module中有register_forward_hook()这个函数,将上面的Hook类实例化后可以通过这个函数输入到模型中去(可以选择把钩子放在模型的特定层中):

hh = Hook() #实例化Hook类
model.conv[3].register_forward_hook(hh)#将Hook输入到模型中的conv[3]层

 特征图像的输出由以下代码来实现:

(选取数据集中特定的一个图片为例,由于train_data中的数据由特征和label两部分组成,要单独把特征部分选取出来作为inputs,inputs = train_data[3][0];且torch中的特征输入是4维的,我们取出的是3维数据,因此要额外进行一次unsqueeze操作)

# forward_model(model,False)
inputs = train_data[3][0]
inputs = inputs.unsqueeze(0)
model.eval()
_ = model(inputs)
print(hh.module_name)
print((hh.features_in_hook[0][0].shape))
print((hh.features_out_hook[0].shape))

out1 = hh.features_out_hook[0]

total_ft = out1.shape[1]
first_item = out1[0].cpu().clone()

plt.figure(figsize=(20, 17))

for ftidx in range(total_ft):
    if ftidx > 99:
        break
    ft = first_item[ftidx]
    plt.subplot(10, 10, ftidx + 1)

    plt.axis('off')
    # plt.imshow(ft[ :, :].detach(),cmap='gray')
    plt.imshow(ft[:, :].detach())
plt.show()

        在本例中,第一次卷积后的特征图像如下所示:

【Datawhale学习】Pytorch可视化_第6张图片 

        第二次卷积后的特征图像相较第一次发生了较大变化:

【Datawhale学习】Pytorch可视化_第7张图片 

        以上就是训练过程中每一层神经网络输出的特征图可视化的过程,可以看出每一层神经网络对特征的选取以及具体怎样对输入数据进行了操作,还是挺有趣的~

        整个过程包括前面模型构建的代码如下:

import torchvision.models as models
from torchinfo import summary

import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as opt
from torch.utils.data import Dataset,DataLoader
import matplotlib.pyplot as plt
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

batch_size = 256
num_workers = 4
lr = 1e-4
epoch = 20

class MyDataset(Dataset):
    def __init__(self,df,transform=None):
        self.df = df
        self.transform = transform
        self.image = df.iloc[:,1:].values.astype(np.uint8)
        self.labels = df.iloc[:,0].values

    def __len__(self):
        return len(self.image)

    def __getitem__(self, index):
        image = self.image[index].reshape(1,28,28)
        label = int(self.labels[index])
        if self.transform is not None:
            image = self.transform(image)#如果定义了transform操作,可对输入图像进行旋转、缩放等操作
        else:
            image = torch.tensor(image/255.,dtype=torch.float)
        label = torch.tensor(label,dtype=torch.long)#转化为tensor进行输入
        return image,label

train_dataflow = pd.read_csv('D:\王宇航\pythonProject\局部变量和全局变量\算法练习\机器学习练习\hashion-mnist_train.csv')
test_dataflow = pd.read_csv('D:\王宇航\pythonProject\局部变量和全局变量\算法练习\机器学习练习\hashion-mnist_test.csv')
train_data = MyDataset(train_dataflow)
test_data = MyDataset(test_dataflow)
print(train_data[0][0])
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False)


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv=nn.Sequential(nn.Conv2d(1,32,5),
                                nn.ReLU(),
                                nn.MaxPool2d(2,stride=2),
                                #nn.Dropout(0.3),
                                nn.Conv2d(32,64,5),
                                nn.ReLU(),
                                nn.MaxPool2d(2,stride=2))
                               # nn.Dropout(0.3))
        self.fc = nn.Sequential(nn.Linear(64*4*4,512),nn.ReLU(),nn.Linear(512,10))

    def forward(self,x):
        x = self.conv(x)
        x = x.view(-1,64*4*4)
        x = self.fc(x)
        return x

model = Net().to(device)

criterion = nn.CrossEntropyLoss()
optimizer = opt.Adam(model.parameters(),lr) #定义好损失函数和优化器

def train(epoch):
    for i in range(epoch):
        model.train()
        train_loss = 0
        for data,label in train_loader:
            data,label = data.to(device),label.to(device)
            output = model(data)
            loss = criterion(output,label)
            loss.backward()
            optimizer.step()
            train_loss += loss.item()*len(data)
        train_loss /= len(train_loader.dataset)

        print('epoch:{},train_loss:{}'.format(i,train_loss))

train(5)


class Hook(object):
    def __init__(self):
        self.module_name = []
        self.features_in_hook = []
        self.features_out_hook = []

    def __call__(self, module, fea_in, fea_out):
        print("hooker working", self)
        self.module_name.append(module.__class__)
        self.features_in_hook.append(fea_in)
        self.features_out_hook.append(fea_out)
        return None



hh = Hook()
model.conv[3].register_forward_hook(hh)

# forward_model(model,False)
inputs = train_data[3][0]
inputs = inputs.unsqueeze(0)
model.eval()
_ = model(inputs)
print(hh.module_name)
print((hh.features_in_hook[0][0].shape))
print((hh.features_out_hook[0].shape))

out1 = hh.features_out_hook[0]

total_ft = out1.shape[1]
first_item = out1[0].cpu().clone()

plt.figure(figsize=(20, 17))

for ftidx in range(total_ft):
    if ftidx > 99:
        break
    ft = first_item[ftidx]
    plt.subplot(10, 10, ftidx + 1)

    plt.axis('off')
    # plt.imshow(ft[ :, :].detach(),cmap='gray')
    plt.imshow(ft[:, :].detach())
plt.show()

 

你可能感兴趣的:(pytorch,深度学习)