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

news2024/11/16 13:43:43

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]             640
         MaxPool2d-2           [-1, 64, 13, 13]               0
              ReLU-3           [-1, 64, 13, 13]               0
            Conv2d-4          [-1, 128, 11, 11]          73,856
         MaxPool2d-5            [-1, 128, 5, 5]               0
              ReLU-6            [-1, 128, 5, 5]               0
           Flatten-7                 [-1, 3200]               0
            Linear-8                  [-1, 256]         819,456
              ReLU-9                  [-1, 256]               0
           Linear-10                    [-1, 1]             257
          Sigmoid-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 = batch
        batch_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.coloradmin.cn/o/921981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day 38 | ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ

518. 零钱兑换 II 这道题就是完全背包问题&#xff0c;因为可以选择的数量是无限的。所以第二层的遍历顺序就是从前往后。 因为是次数问题&#xff0c;递推公式是 的&#xff0c;初值应该设定为dp【0】 1&#xff0c;否则无法进行累加。 func change(amount int, coins []i…

Python编程基础-基本语法II

循环语句 for()语句 可以遍历任何序列的项目&#xff0c;如一个列表、元组或者一个字符串 格式&#xff1a; for 循环索引值 in 序列 循环体 #for循环把字符串中字符遍历出来 for letter in Python:print ( 当前字母 :, letter )#通过索引循环 fruits [banana, apple, m…

百度地图:设置复杂的自定义覆盖物,添加自定义覆盖物ComplexCustomOverlay

// 设置复杂的自定义覆盖物 setComplexCustomOverlay({coordinate,icon 1,label,contentHTML, }) {var mp this.map;let _BMAP this.data.type 3 ? BMapGL : BMap;// 自定义覆盖物----------------------------------------function ComplexCustomOverlay({point,icon,lab…

【全站最全】被苹果、谷歌和Microsoft停产的产品(一)

目录 ​编辑 2025 Skype for Business 2023 Cortana Google Domains Google Optimize Google Universal Analytics YouTube Stories Grasshopper Google Currents (2019) Google Stadia 2022 YouTube Originals Google OnHub Atom Google Surveys Apple Watc…

【3dsmax】练习——制作碗椅

目录 目标 步骤 一、制作主体部分 二、制作靠垫部分 三、制作支架部分 目标 制作如下图所示的碗椅 步骤 一、制作主体部分 1. 首先创建一个球体 2. 转换为可编辑多边形&#xff0c;然后切换到边层级&#xff0c;选中球体上部的所有边&#xff0c;然后删除 3. 通过“壳…

Linux下的系统编程——gdb调试工具

前言&#xff1a; 程序中除了一目了然的Bug之外都需要一定的调试手段来分析到底错在哪。到目前为止我们的调试手段只有一种∶根据程序执行时的出错现象假设错误原因﹐然后在代码中适当的位置插入printf﹐执行程序并分析打印结果﹐如果结果和预期的一样﹐就基本上证明了自己假设…

东风纳米首款车型纳米 01 亮相:纯电小车,固态电池 + 超级快充

根据近期发布的消息&#xff0c;东风纳米品牌推出的首款车型纳米 01 在全新发布会上正式亮相。这款车型采用东风量子架构 3 号平台&#xff0c;被寄予厚望将在国内市场迎来广泛的认可度。 作为一款小型纯电动车&#xff0c;纳米 01注重家庭出游、市区代步、个人通勤、接送孩子、…

“好声音”塌房、星空华文市值暴跌,两个交易日蒸发234亿港元

8月17日&#xff0c;因李玟生前录音事件&#xff0c;再次将《中国好声音》被舆论推至风口浪尖&#xff0c;引发社会对后者的质疑。 次日(8月18日)&#xff0c;《中国好声音》的IP运营商星空华文(06698.HK)股价大跌&#xff0c;其港股收盘股价跌幅达到23.4%&#xff0c;一天内市…

基于逻辑斯蒂回归的肿瘤预测案例

导入包 import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report,roc_a…

uniapp - 全平台兼容实现上传图片带进度条功能,用户上传图像到服务器时显示上传进度条效果功能(一键复制源码,开箱即用)

效果图 uniapp小程序/h5网页/app实现上传图片并监听上传进度,显示进度条完整功能示例代码 一键复制,改下样式即可。 全部代码 记得改下样式,或直接

JavaWeb_LeadNews_Day7-ElasticSearch, Mongodb

JavaWeb_LeadNews_Day7-ElasticSearch, Mongodb elasticsearch安装配置 app文章搜索创建索引库app文章搜索思路分析具体实现 新增文章创建索引思路分析具体实现 MongoDB安装配置SpringBoot集成MongoDB app文章搜索记录保存搜索记录思路分析具体实现 查询搜索历史删除搜索历史 搜…

关于java三元组的问题

在改代码的时候&#xff0c;发现一个奇怪的地方&#xff0c;举例如下 Testpublic void buildTest(){TT t new TT();Long time tnull?System.currentTimeMillis():t.getTime();System.out.println("done");}Datapublic static class TT{Long time;}这个地方运行就…

STL---vector

目录 1.vector的介绍及使用 2.vector接口说明及模拟实现 2.1vector定义 2.2vector迭代器的使用 2.3vector容量 2.4vector增删查改 3迭代器失效 4.使用memcpy拷贝 5.模拟实现 1.vector的介绍及使用 vector的文档介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数…

电子病历系统EMR

电子病历系统EMR源码 一体化电子病历系统基于云端SaaS服务的方式&#xff0c;采用B/S&#xff08;Browser/Server&#xff09;架构提供&#xff0c;覆盖了医疗机构电子病历模板制作到管理使用的整个流程。除实现在线制作内容丰富、图文并茂、功能完善的电子病历模板外&#xff…

phpcms重置密码为123456

0b817b72c5e28b61b32ab813fd1ebd7f3vbCrK

实战项目ssm权限系统 3-总结篇,权限模块保护业务模块

一 工程模块介绍 1.1 工程模块关系 在业务微服务模块中引入安全认证模块&#xff0c;起到对业务模块的认证授权保护

ubuntu18.04复现yolo v8环境配置之CUDA与pytorch版本问题以及多CUDA版本安装及切换

最近在复现yolo v8的程序&#xff0c;特记录一下过程 环境&#xff1a;ubuntu18.04ros melodic 小知识&#xff1a;GPU并行计算能力高于CPU—B站UP主说的 Ubuntu可以安装多个版本的CUDA。如果某个程序的Pyorch需要不同版本的CUDA&#xff0c;不必删除之前的CUDA&#xff0c;…

我是如何使用Spring Retry减少1000 行代码

使用 Spring Retry 重构代码的综合指南。 问题介绍 在我的日常工作中&#xff0c;我主要负责开发一个庞大的金融应用程序。当客户发送请求时&#xff0c;我们使用他们的用户 ID 从第三方服务获取他们的帐户信息&#xff0c;保存交易并更新缓存中的详细信息。尽管整个流程看起来…

APT60DQ20BG-ASEMI新能源功率器件APT60DQ20BG

编辑&#xff1a;ll APT60DQ20BG-ASEMI新能源功率器件APT60DQ20BG 型号&#xff1a;APT60DQ20BG 品牌&#xff1a;ASEMI 封装&#xff1a;TO-3P 恢复时间&#xff1a;&#xff1e;50ns 正向电流&#xff1a;60A 反向耐压&#xff1a;200V 芯片个数&#xff1a;2 引脚数…

Springboot+dynamic-datasource+Druid数据库配置加密

Springbootmybatis-plusdynamic-datasourceDruid数据库配置加密 文章目录 0.前言1. 动态添加移除数据源2.基础介绍3. 使用步骤示例简单方式&#xff0c;使用默认的加密1. 使用下面 工具类输出&#xff0c;加密后的密码1. 将上面加密后的密码配置到配置文件中如果使用的默认key…