PyTorch深度学习实战(13)——可视化神经网络中间层输出

news/2024/12/23 4:15:35/

PyTorch深度学习实战(13)——可视化神经网络中间层输出

    • 0. 前言
    • 1. 可视化特征学习的结果
    • 2. 可视化第一个卷积层的输出
    • 3. 可视化不同网络层的特征图
    • 小结
    • 系列链接

0. 前言

随着深度学习的快速发展,神经网络已成为解决各种复杂任务的重要工具。然而,神经网络的黑盒特性使得我们对其内部运作过程和学到的表示仍然不够了解。为了更好地理解神经网络的工作原理,研究者们提出了各种可视化方法来探索网络中间层的输出。特征学习是神经网络最关键的一项任务之一,神经网络通过逐层的变换和学习,能够从原始数据中提取出高级、抽象的特征表示,这些特征表示能够捕捉到数据中的重要信息。然而,这些中间层的输出对于人类来说是难以理解的,因为它们是高维、抽象的向量。
通过可视化特征学习的结果,我们可以以直观的方式观察网络在处理数据时发生的变化,利用可视化方法能够探索中间层的输出,理解网络如何对输入数据进行编码和转换。我们可以通过观察特征图、梯度分布、降维可视化等手段来揭示网络中学到的有用模式、边缘检测、颜色分布等。在本节中,我们将探索神经网络究竟学到了什么,使用卷积神经网络 (Convolutional Neural Networks, CNN) 对包含 XO 图像的数据集进行分类,并检查网络层输出了解激活结果。

1. 可视化特征学习的结果

可视化特征学习的结果具有多方面的应用。首先,可视化可以帮助我们评估和调整神经网络的设计,通过观察特征图和梯度分布,可以判断网络是否学到了有效的特征表示,从而优化网络结构和参数设置;其次,可视化还可以帮助我们解释网络的预测结果,通过观察中间层的输出,我们可以了解网络对不同类别或输入样本的响应模式,解释其预测的依据;最后,通过观察网络学到的特征表示,可以借鉴其中的思想,设计更好的手工特征或特征提取算法。

(1) 为了可视化特征学习的结果,我们将使用包含 XO 图像的数据集,相关数据集可以在 gitcode 链接中下载,下载完成后进行解压,解压完成后,可以看到文件夹中的图像如下:

图像文件列表
图像的类别可以从图像的名称中获得,其中图像名称的第一个字符指定图像所属的类别。

(2) 导入所需库:

import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
from torch.optim import SGD, Adam
device = 'cuda' if torch.cuda.is_available() else 'cpu'
import numpy as np, cv2
import matplotlib.pyplot as plt
from glob import glob
from imgaug import augmenters as iaa

(3) 定义获取数据的类,确保图像形状已调整为 28 x 28,并且目标类别转换为数值形式。

定义图像增强方法,将图像形状调整为 28 x 28

tfm = iaa.Sequential(iaa.Resize(28))

定义一个将文件夹路径作为输入的类,并在 __init__ 方法中遍历该路径中的文件:

class XO(Dataset):def __init__(self, folder):self.files = glob(folder)

定义 __len__ 方法,返回数据集的长度:

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

定义 __getitem__ 方法获取索引,返回该索引处存在的文件,读取图像文件并对图像执行增强。在这里并未使用 collate_fn,因为小数据集不会显着影响训练时间:

    def __getitem__(self, ix):f = self.files[ix]im = tfm.augment_image(cv2.imread(f)[:,:,0])

在图像形状(每个图像的形状为 28 x 28 )前创建通道尺寸:

        im = im[None]

根据文件名中的字符 “/” 和 “@” 之间的字符确定每个图像的类别:

        cl = f.split('/')[-1].split('@')[0] == 'x'

最后,返回图像及其对应的类别:

        return torch.tensor(1 - im/255).to(device).float(), torch.tensor([cl]).float().to(device)

(4) 显示图像样本,通过上述定义的类提取图像及其对应的类:

data = XO('images/*')

根据获得的数据集绘制图像样本:

R, C = 7,7
fig, ax = plt.subplots(R, C, figsize=(5,5))
for label_class, plot_row in enumerate(ax):for plot_cell in plot_row:plot_cell.grid(False); plot_cell.axis('off')ix = np.random.choice(1000)im, label = data[ix]plot_cell.imshow(im[0].cpu(), cmap='gray')
plt.tight_layout()
plt.show()

样本可视化

(5) 定义模型架构、损失函数和优化器:

from torch.optim import SGD, Adam
def get_model():model = nn.Sequential(nn.Conv2d(1, 64, kernel_size=3),nn.MaxPool2d(2),nn.ReLU(),nn.Conv2d(64, 128, kernel_size=3),nn.MaxPool2d(2),nn.ReLU(),nn.Flatten(),nn.Linear(3200, 256),nn.ReLU(),nn.Linear(256, 1),nn.Sigmoid()).to(device)loss_fn = nn.BCELoss()optimizer = Adam(model.parameters(), lr=1e-3)return model, loss_fn, optimizer

由于是二分类问题,此处使用二元交叉熵损失 (nn.BCELoss()),打印模型摘要:

from torchsummary import summary
model, loss_fn, optimizer = get_model()
"""
summary(model, input_size=(1,28,28))
----------------------------------------------------------------Layer (type)               Output Shape         Param #
================================================================Conv2d-1           [-1, 64, 26, 26]             640MaxPool2d-2           [-1, 64, 13, 13]               0ReLU-3           [-1, 64, 13, 13]               0Conv2d-4          [-1, 128, 11, 11]          73,856MaxPool2d-5            [-1, 128, 5, 5]               0ReLU-6            [-1, 128, 5, 5]               0Flatten-7                 [-1, 3200]               0Linear-8                  [-1, 256]         819,456ReLU-9                  [-1, 256]               0Linear-10                    [-1, 1]             257Sigmoid-11                    [-1, 1]               0
================================================================
Total params: 894,209
Trainable params: 894,209
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.69
Params size (MB): 3.41
Estimated Total Size (MB): 4.10
"""
----------------------------------------------------------------

(6) 定义用于批训练的函数,该函数使用图像及其类作为输入,并在对给定的批数据上执行反向传播后返回其损失值和准确率:

def train_batch(x, y, model, optimizer, loss_fn):prediction = model(x)batch_loss = loss_fn(prediction, y)batch_loss.backward()optimizer.step()optimizer.zero_grad()return batch_loss.item()def accuracy(x, y, model):with torch.no_grad():prediction = model(x)max_values, argmaxes = prediction.max(-1)is_correct = argmaxes == y
return is_correct.cpu().numpy().tolist()@torch.no_grad()
def val_loss(x, y, model, loss_fn):prediction = model(x)val_loss = loss_fn(prediction, y)return val_loss.item()

(7) 定义 DataLoader,其中输入是 Dataset 类:

trn_dl = DataLoader(data, batch_size=32, drop_last=True)

(8) 初始化并训练模型:

model, loss_fn, optimizer = get_model()for epoch in range(10):for ix, batch in enumerate(iter(trn_dl)):x, y = batchbatch_loss = train_batch(x, y, model, optimizer, loss_fn)

(9) 获取图像以查看滤波器学习到的图像内容:

im, c = trn_dl.dataset[2]
plt.imshow(im[0].cpu())
plt.show()

查看滤波器学习到的图像内容

2. 可视化第一个卷积层的输出

(1) 将图像通过训练后的模型并获取第一层的输出,将其存储在 intermiddle_output 变量中:

print(list(model.children()))
[Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1)), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), ReLU(), Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1)), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), ReLU(), Flatten(start_dim=1, end_dim=-1), Linear(in_features=3200, out_features=256, bias=True), ReLU(), Linear(in_features=256, out_features=1, bias=True), Sigmoid()]first_layer = nn.Sequential(*list(model.children())[:1])
intermediate_output = first_layer(im[None])[0].detach()

(2) 绘制 64 个滤波器的输出,itermiddle_output 中每个元素对应于一个滤波器的卷积输出:

n = 8
fig, ax = plt.subplots(n, n, figsize=(10,10))
for ix, axis in enumerate(ax.flat):axis.set_title('Filter: '+str(ix))axis.imshow(intermediate_output[ix].cpu())
plt.tight_layout()
plt.show()

卷积输出
在以上输出中,可以看到一些滤波器(例如滤波器 0467 )学习到了图像中存在的边缘。

(3) 输入多个 O 图像并使用第 4 个滤波器执行卷积观察输出结果。

从数据集中获取多张 O 图像:

x, y = next(iter(trn_dl))
x2 = x[y==0]
print(len(x2))
# 15

调整 x2 形状使其能够作为卷积神经网络的输入,即批大小 x 通道 x 高度 x 宽度

x2 = x2.view(-1,1,28,28)

定义用于存储模型输出的变量:

first_layer = nn.Sequential(*list(model.children())[:1])

提取 O 图像 (x2) 在第一层 (first_layer) 后的输出:

first_layer_output = first_layer(x2).detach()

(4) 绘制图像通过第一层后的输出:

n = 4
fig, ax = plt.subplots(n, n, figsize=(10,10))
for ix, axis in enumerate(ax.flat):if ix < n**2-1:axis.imshow(first_layer_output[ix,4,:,:].cpu())axis.set_title(str(ix))
plt.tight_layout()
plt.show()

滤波器特征提取效果
可以看到,给定滤波器的行为在不同图像之间具有一致性。

3. 可视化不同网络层的特征图

(1) 提取原始 O 图像从输入层到第 2 个卷积层的输出,绘制第 2 层中的滤波器与输入 O 图像进行卷积后的输出。

绘制滤波器与相应图像的卷积输出:

second_layer = nn.Sequential(*list(model.children())[:4])
second_intermediate_output = second_layer(im[None])[0].detach()print(second_intermediate_output.shape)
# torch.Size([128, 11, 11])
n = 11
fig, ax = plt.subplots(n, n, figsize=(10,10))
for ix, axis in enumerate(ax.flat):axis.imshow(second_intermediate_output[ix].cpu())axis.set_title(str(ix))
plt.tight_layout()
plt.show()print(im.shape)
# torch.Size([1, 28, 28])

卷积输出
以第 34 个滤波器的输出为例,当我们通过滤波器 34 传递多个 O 图像时,可以在不同图像之间看到相似的激活:

second_layer = nn.Sequential(*list(model.children())[:4])
second_intermediate_output = second_layer(x2).detach()print(second_intermediate_output.shape)
# torch.Size([15, 128, 11, 11])
n = 4
fig, ax = plt.subplots(n, n, figsize=(10,10))
for ix, axis in enumerate(ax.flat):if ix < n**2-1:axis.imshow(second_intermediate_output[ix,34,:,:].cpu())axis.set_title(str(ix))
plt.tight_layout()
plt.show()print(len(data))
# 2498

请添加图片描述
(2) 绘制全连接层的激活。

首先,获取图像样本:

custom_dl = DataLoader(data, batch_size=2498, drop_last=True)

接下来,从数据集中选择 O 图像,对它们进行整形后,作为输入传递到 CNN 模型中:

x, y = next(iter(custom_dl))
x2 = x[y==0]
print(len(x2))
# 1245
x2 = x2.view(len(x2),1,28,28)

将以上图像通过 CNN 模型,获取全连接层输出:

flatten_layer = nn.Sequential(*list(model.children())[:7])
flatten_layer_output = flatten_layer(x2).detach()print(flatten_layer_output.shape)
# torch.Size([1245, 3200])

绘制全连接层输出:

plt.figure(figsize=(100,10))
plt.imshow(flatten_layer_output.cpu())
plt.show()

全连接层输出
输出的形状为 1245 x 3200,因为数据集中有 1,245O 图像,且在全连接层中的每个图像的输出为 3,200 维。当输入为 O 时,全连接层中大于零的激活值会突出显示,在图像中显示为白色像素。可以看到,模型已经学习了图像中的结构信息,即使属于同一类别的输入图像存在较大的风格差异。

小结

在探索神经网络内部的特征学习过程中,可视化起着重要作用。它为我们提供了一种直观、可解释的方式来理解网络的运行机制和学习的特征表示。通过可视化特征学习的结果,可以深入了解神经网络,并为网络的优化、解释和改进提供参考。本节中,通过介绍如何可视化神经网络中间层输出,加深了对网络行为和学到的特征表示的认识。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络
PyTorch深度学习实战(12)——数据增强


http://www.ppmy.cn/news/1057064.html

相关文章

FinalShell报错:Swap file “.docker-compose.yml.swp“ already exists

FinalShell中编辑docker-compose.yml文件&#xff0c;保存时报错&#xff1a;Swap file ".docker-compose.yml.swp" already exists&#xff1b;报错信息截图如下&#xff1a; 问题原因&#xff1a;有人正在编辑docker-compose.yml文件或者上次编辑没有保存&#xff…

Docker 搭建 LNMP + Wordpress(详细步骤)

目录 一、项目模拟 1. 项目环境 2. 服务器环境 3.任务需求 二、Linux 系统基础镜像 三、Nginx 1. 建立工作目录 2. 编写 Dockerfile 脚本 3. 准备 nginx.conf 配置文件 4. 生成镜像 5. 创建自定义网络 6. 启动镜像容器 7. 验证 nginx 四、Mysql 1.…

Mysql 设置表字段自动赋值创建时间,以及自动更新某一个字段的更新时间

使用场景 一般表设计中记录都有创建时间以及更新时间&#xff0c;而 Mysql 也支持了这种通用的设计需求。 即&#xff1a;可以通过默认值来给时间字段自动赋值&#xff0c;在创建时的默认值就是当前时间也就是记录的创建时间。 记录更新&#xff1a;即某一记录更新时我们要更…

0基础学习VR全景平台篇 第88篇:智慧眼-成员管理

一、功能说明 成员管理&#xff0c;是指管理智慧眼项目的成员&#xff0c;拥有相关权限的人可以进行添加成员、分配成员角色、设置成员分类、修改成员以及删除成员五项操作。但是仅限于管理自己的下级成员&#xff0c;上级成员无权管理。 二、前台操作页面 登录智慧眼后台操…

Qt双击某一文件通过自己实现的程序打开,并加载文件显示

双击启动 简述方法一方法二注意 简述 在Windows系统中&#xff0c;双击某类扩展名的文件&#xff0c;通过自己实现的程序打开文件&#xff0c;并正确加载及显示文件。有两种方式可以到达这个目的。 对于系统不知道的扩展名的文件&#xff0c;第一次打开时&#xff0c;需要自行…

前端面试:【TypeScript】静态类型检查与编译时类型检查

TypeScript是一种由Microsoft开发的编程语言&#xff0c;它在JavaScript的基础上添加了强大的静态类型系统。在本文中&#xff0c;我们将深入探讨TypeScript的静态类型检查和编译时类型检查&#xff0c;以及它们如何提高代码的可靠性和可维护性。 1. 静态类型检查&#xff08;S…

ctfshow-web12

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 国际惯例看一下返回包&#xff0c;是不是有注释 然后做一下测试&#xff0c;看是命令执行还是代码执行 通过phpinfo看到可以执行代码 然后尝试执行命令&#xff0c;无法&#xff0c;发现存在disable_function…

完全免费的GPT,最新整理,2023年8月24日,已人工验证,不用注册,不用登录,更不用魔法,点开就能用

完全免费的ChatGPT&#xff0c;最新整理&#xff0c;2023年8月24日&#xff0c;已人工验证&#xff0c; 不用注册&#xff0c;不用登录&#xff0c;更不用魔法&#xff0c;点开就能用&#xff01; 第一个&#xff1a;网址地址统一放在文末啦&#xff01;文末直达 看上图你就能…