ZFNet 详细解读


❤ 文章有点长,可以收藏一下慢慢看哦 ~

提出本模型的论文:

Visualizing and Understanding Convolutional Networks


一、写在前面的话


1. 为什么神经网络能够表现这么好?

仅靠一个理论上的自学习和一个理论上的多参数和一个理论上的非线性分类是无法令人们满意的。神经网络的黑盒子特性也让充满好奇心的机器学习学者们拼命想解开神经网络的秘密。在解密神经网络的庞大队伍中的重要分支是想要利用可视化等技术来对神经网络一探究竟,其中 Z 和 F 二人 开篇性的找到了将卷积核可视化的一种可行的方式。中南大学赵颖老师带领的可解释机器学习项目也加入其中,从此……


2. ZFNet 仅仅是在 AlexNet 上做了一些调参:

改变了 AlexNet 的第一层,即将滤波器的大小 11x11 变成 7x7,并且将步长 4 变成了 2(就这么一丁丁操作就改变了整个卷积神经网络的性能,并在2013年ImageNet大赛中夺冠)

========== 对比图:上为 AlexNet,下为 ZFNet ==========
在这里插入图片描述
3. ZFNet 凭什么将 11 改成 7,将步长减 2,是盲目调参吗?

显然发了这篇论文肯定是有自己的一个调参方法——根据可视化结果判断模型的效果,并可以确定一定的优化调参方向

========== 对比图:==========

在这里插入图片描述

4. 当然这篇论文还根据遮挡图像的局部对分类结果的影响来探讨了对分类任务而言到底哪部分输入信息更重要



二、ZFNet 中做出的改进,以及 Z 和 F 在神经网络可视化方面的独到见解


首先肯定是看看可视化神经网络的方式

(1)可视化什么东西: 卷积层提取出来的特征图像。
(2)如何可视化卷积核提取出来的特征图像?

复习 :

每一层的基本操作是 : 卷积 -> relu -> 池化 -> 归一化

卷积操作:
在这里插入图片描述池化操作:在这里插入图片描述
思考: 看到这里每一层的基本操作之后,如果我们想要可视化一下输出的图片是不是挺好?那能直接转变成像素图片进行可视化吗?或者是进行一些什么样的操作?

Z和F的思考: 截取最终模型的池化层的输出,然后反池化,“反relu”,反卷积,将得到的图片进行展示。 如下图:
在这里插入图片描述
问题1:是对任意一个输入的图片的任意一层的任意一个卷积核的卷积出来的结果进行该操作然后展示吗?

显然不是,因为一张狗的图片对于一个可以提取人脸信息的卷积核是提取不到有用的信息的。那么在选用图片的时候选择一个能让该卷积核激活最大化的图片作为输入就可以了。( 激活最大化可以理解为:这张图片里面的某一特征正好可以被该卷积核完美的提取出来。官方解释论文还未阅读 )


问题2:为什么不从截取归一化的操作进行反归一化然后反池化……

在已经训练出来神经网络模型之后,通过输入图片一层一层的正向传播最终的到输出,而归一化的主要作用是限制每一层的输出范围,提取特征的任务主要交给卷积、激活 和 池化操作。况且归一化也确实不好进行逆向计算。


问题3:如何反池化? 如何“反relu” ? 如何反卷积(转置卷积)?

这两个人开创性的想到:

① 反池化: 利用一组转换变量switch在每个池化区域记录最大值的位置来确定最有效的特征值的位置。那么就可以将池化后的输出Pooled Map 按照 switch 记录的位置填回去喽。

在这里插入图片描述

② “反relu”: 根据 y 求 x 喽 ,又因为 y = max(x) ,经过pooling后大概率是正数 ,那么就 x = y 呗,如果 y = 0 的话信息已经丢掉了也没法弥补x= 0。(我的理解 这里论文没有讲述)

③ 反卷积: 大数学论证,(由卷积之后获得的特征值获得原图像大小的数据分布)简单的说就是存在一种技术让下图中的 5×5 的像素经过卷积之后得到的 3×3 输出,经过一系列变换 ,再变回 5×5 的像素排列(尽管会有部分数据损失,但是也没办法,此技术不为论文重点,作者也是直接用的该技术)。

========== 例子1 ==========
卷积:
在这里插入图片描述
反卷积:
在这里插入图片描述
反卷积中的 padding stride 卷积核大小的确定:

P反 = 卷积核大小 - P正 - 1,图中为(3-0-1=2)
Stride反 = stride的倒数, 那么就用填充 0 来表示倒数卷积核大小与正卷积核大小相同

========== 例子2 ==========
卷积:
在这里插入图片描述
反卷积:
在这里插入图片描述
Padding = 3 -1 - 1 = 1
Stride = 1/2 相当于填充一个白格子



三、结果

根据该方法可以在一定程度上可视化得到卷积层中每个卷积核提取出来的特征。

在这里插入图片描述
发现1:由上图可以看到:
① 第二层应对角落和其他边缘或者颜色的结合;
② 第三层有更加复杂的不变性,捕捉到了相似的纹理;
③ 第四层显示了特定类间显著的差异性;
④ 第五层显示了有显著构成变化的整个物体。


发现2:模型的底层在少数几个 epoches 就能收敛聚集,然而上层在一个相当多的 epoches (40-50) 之后才能有所变化,这显示了让模型完全训练到完全收敛的必要性

可以由下图看到颜色对比度都逐步增强:
在这里插入图片描述

发现3: 特征不变性:一般来说,小的变化对于模型的第一层都有非常大的影响,但对于最高层的影响却几乎没有。对于图像的平移、尺度、旋转的变化来说,网络的输出对于平移和尺度变化都是稳定的,但却不具有旋转不变性,除非目标图像时旋转对称的。

下图为分别对平移,尺度,旋转做的分析图:
在这里插入图片描述
分析: 上图按行顺序分别为对5类图像进行不同程度的垂直方向上的平移、尺度变换、旋转对输出结果影响的分析图。按列顺序分别为原始变换图像,第一层中原始图片和变换后的图片的欧氏距离,第7层中原始图片和变换后的图片的欧氏距离,变换后图片被正确分类的概率图。


思考:到现在,我们一直存在一个疑问:
模型可以根据一个图片提取出来不计其数的特征,尤其是卷积操作还是平移着一部分一部分地尝试提取特征的,哪个特征最有效? 一张狗的图片中顶多一部分是狗对吧,不可能贡献一般大的。那么图片中的哪个部分贡献最大?



四、其他一

该论文还描述了通过 <遮挡实验> 来确定一张图片中最重要的部分是哪( 通过哪一部分得出的相应结果 )。

1、方法: 用一个灰色小方块来挡住图像中的目标,然后再观测输出的分类情况来分析。

2、结果:

在这里插入图片描述
上图为对三个不同的测试图片中的不同位置用灰色小方块进行掩盖后,观测分类输出的改变情况。

第一列(a)为原始测试图片;
第二列(b)为某个区域被灰色小方块掩盖后的网络结构中第五层的特征图;
第三列(c)为将第五层的特征图投影到输入图像的可视化图像;
第四列(d)为正确分类概率的图;
第五列(e)为最有可能的标签。

第一行表明最强烈的特征表现在狗狗的面部区域。

上述结果表明,如果图像中的目标被遮挡,那么被正确分类的概率会显著降低,这表明这种可视化与激发特征图的图像结构式真正对应上的。即大概能知道位置。

思考:如何利用可视化来优化参数?



五、其他二

通过判断提取出来的特征是否有效,失效在什么地方?来决定自己的参数优化趋势。

在这里插入图片描述

发现: 原有的 AlexNet 中提取出来的特征因为 卷积核太大,池化部分步长设置过大 导致是 有一部分失效的所以减小第一层卷积核大小11->7 池化步长4->2



六、启发

盲目调参费时费力,充分挖掘神经网络可视化的方法有助于我们进一步地打开神经网络这个黑匣子。
  • 11
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很抱歉,我不能提供完整的 PyTorch ZFNet 代码。但是我可以为您提供一些指导,帮助您实现该模型。 ZFNet 是一种卷积神经网络,最初用于 ImageNet 图像分类任务。它是 AlexNet 的一个变体,在论文 "Visualizing and Understanding Convolutional Networks" 中被提出。 如果您想实现 ZFNet 模型,您可以利用 PyTorch 的神经网络模块,自定义网络结构。您需要定义一个继承自 `nn.Module` 的类,并在该类中实现网络的前向传播(forward)方法。在前向传播方法中,您可以使用 PyTorch 中的神经网络层(如卷积层、池化层等)构建网络结构。 示例代码如下: ```python import torch.nn as nn class ZFNet(nn.Module): def __init__(self): super(ZFNet, self).__init__() # 在这里定义网络的层 self.conv1 = nn.Conv2d(3, 96, kernel_size=7, stride=2) self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2) self.conv2 = nn.Conv2d(96, 256, kernel_size=5, padding=2) self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2) self.conv3 = nn.Conv2d(256, 384, kernel_size=3, padding=1) self.conv4 = nn.Conv2d(384, 384, kernel_size=3, padding=1) self.conv5 = nn.Conv2d(384, 256, kernel_size=3, padding=1) self.pool5 = nn.MaxPool2d(kernel_size=3, stride=2) self.fc6 = n ### 回答2: PyTorch是一个广泛使用的深度学习框架,而ZFNet是一种经典的卷积神经网络架构。下面是一个对PyTorch中ZFNet的简要代码解释: 首先,我们需要导入PyTorch库和ZFNet相关的模块和函数: ```python import torch import torch.nn as nn import torch.optim as optim ``` 接下来,我们定义一个继承自`nn.Module`的ZFNet类,这个类包含了ZFNet网络的结构: ```python class ZFNet(nn.Module): def __init__(self, num_classes=1000): super(ZFNet, self).__init__() self.features = nn.Sequential( # 定义ZFNet的特征提取层 nn.Conv2d(3, 96, 7, 2, 1), nn.ReLU(inplace=True), nn.MaxPool2d(3, 2, 1), nn.Conv2d(96, 256, 5, 2, 1), nn.ReLU(inplace=True), nn.MaxPool2d(3, 2, 1), nn.Conv2d(256, 384, 3, 1, 1), nn.ReLU(inplace=True), nn.Conv2d(384, 384, 3, 1, 1), nn.ReLU(inplace=True), nn.Conv2d(384, 256, 3, 1, 1), nn.ReLU(inplace=True), nn.MaxPool2d(3, 2, 1) ) self.classifier = nn.Sequential( nn.Linear(256 * 6 * 6, 4096), nn.ReLU(inplace=True), nn.Dropout(), nn.Linear(4096, 4096), nn.ReLU(inplace=True), nn.Dropout(), nn.Linear(4096, num_classes) ) def forward(self, x): x = self.features(x) x = x.view(x.size(0), -1) x = self.classifier(x) return x ``` 在这段代码中,我们首先定义了网络的特征提取层,然后定义了网络的分类器部分。在forward函数中,我们将输入的数据传递给特征提取层和分类器,最终返回分类的结果。 接下来,我们可以实例化一个ZFNet的对象并进行训练和测试: ```python # 创建一个ZFNet的实例 model = ZFNet(num_classes=10) # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 进行训练和测试 for epoch in range(num_epochs): running_loss = 0.0 for i, data in enumerate(train_loader): inputs, labels = data optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 100 == 99: # 每100个batch打印一次loss print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100)) running_loss = 0.0 # 在测试集上进行测试 correct = 0 total = 0 with torch.no_grad(): for data in test_loader: images, labels = data outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy on test images: %.2f %%' % (100 * correct / total)) ``` 在这段代码中,我们首先创建了一个ZFNet的实例,然后定义了损失函数和优化器。接着,我们进行了训练和测试的循环,每个epoch都计算了训练集上的损失并更新了网络的参数。最后,在测试集上计算了分类的准确率。 以上是使用PyTorch实现ZFNet的基本代码解释,通过训练和测试可以让ZFNet网络适应特定的数据集,并得到相应的分类结果。 ### 回答3: Pytorch是一个非常流行的深度学习框架,而ZFNet是一个基于卷积神经网络(Convolutional Neural Network,CNN)的经典模型。 ZFNet是由Matthew Zeiler和Rob Fergus于2013年提出的,在当时是ImageNet图像分类挑战比赛上取得了很好的成绩。该模型主要由五个卷积层和三个全连接层组成。 使用Pytorch实现ZFNet的代码可以分为以下几个步骤: 1. 导入所需的Pytorch库和模块,如torch、torchvision等。 2. 定义ZFNet的网络结构。可以使用torch.nn模块来创建卷积层、全连接层等各种网络层。根据ZFNet的结构,可以定义五个卷积层和三个全连接层,并使用激活函数(如ReLU)来增加网络的非线性能力。 3. 定义模型的前向传播函数。在这个函数中,将输入数据通过各层进行计算,并返回输出。 4. 定义损失函数和优化器。根据具体的任务,选择适用的损失函数(如交叉熵损失函数)和优化器(如随机梯度下降优化器)。 5. 准备训练数据。通常,可以使用torchvision库中的数据集(如MNIST、CIFAR-10等)来获取训练数据。可以使用torchvision.transforms库来对数据进行预处理,如调整大小、标准化等。 6. 开始训练。将训练数据输入网络,通过前向传播计算输出,再将输出与真实标签进行比较,计算损失并进行反向传播更新模型参数。 7. 进行模型测试。使用测试数据对训练好的模型进行测试,输出测试结果。 以上是使用Pytorch实现ZFNet的大致步骤,具体代码可以参考Pytorch官方文档或各种开源代码库中的实现。实际实现中,还可以根据具体需求进行调整和优化,如增加正则化、数据增强等操作,以提高模型的性能和泛化能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值