当前位置: 首页 > news >正文

[动手学习深度学习] 27.含并行连结的网络 GoogLeNet/Inception v3

NiN现在没有被使用,但是GoogleNet现在还是广泛被使用的(在2014年的ImageNet图像识别挑战赛中大放异彩)
这是第一个卷积神经网络可以做到快100层的网络(不是有100层深,但是里面卷积层的个数超过了100)
注意GoogLeNet中的L是大写的,这里其实和LeNet没关系,Google做的然后名字只是致敬了一下

  • GoogLeNet的设计收到了NiN很大的影响,吸收了NiN中串联网络的思想,并在此基础上进行了改进
    在这里插入图片描述

Inception块

看完前面LeNet、AlexNet、VGG等就会存在一个问题:如何选择这个卷积层,用33、55还是用块?通道数怎么选?
在这里插入图片描述

设计结构

GoogLeNet中最重要的是Inception块,这个块要的设计是:“小学生才做选择,我全都要”
在这里插入图片描述
Inception块中抽取了不同的通道,不同的通道有不同的设计(如上图所示,Inception块通过四条路经从不同层面抽取信息,然后在输出通道维度合并)

  • 之前输入后就一条网络就下去了,但是这里不一样

    • 第1条:直接用一个1*1的卷积层,然后输出到后面
    • 第2条:先输入1*1的conv中对通道作变换(不改变高宽),然后输入3*3的conv且pad=1,使输入和输出的高宽是一样的
    • 第3条:先输入1*1的conv中对通道作变换(不改变高宽),然后通过5*5的卷积层来处理些空间信息,pad=2输入和输出等宽
    • 第4条:3*3的MaxPool,pad=1,再在后面加1*1的conv

    所有这四条路的输出都没改变高宽,最后一起作Concatenate操作,在输出的通道数上作合并(高宽一样,但是通道数很多)(这不是将4张图片放在一起形成一张更大的图片,而是在输出的通道数上作合并,最终的输出和输入等宽等高,但是通道数更多)

通道数变化

在这里插入图片描述
假设输入通道数是192,宽高是128*128

  1. 直接把通道数压缩到了64
  2. 压缩到了96,因为想降低后面3*3的卷积的输入数,以此来降低模型复杂度(一般可以认为是需要学习的参数个数)
  3. 5*5的卷积开销更大,所以这一层降的更多
  4. MaxPool不会改变通道数,这一层在后面卷积层直接降到了32
  • 白色的卷积用来改变通道数,蓝色的卷积用来抽取信息
  • 最左边一条1*1卷积用来抽取通道信息,其他的3*3卷积用来抽取空间信息,maxpool也是抽取空间信息(提高鲁棒性)

最后的通道数相当于是64+128+32+32=256,即相当于从192到了256

  • 如果不用Inception块,一样做到从192到256的改变,可能需要学习更多的参数
    在这里插入图片描述
    所以和3*3、5*5的卷积层相比,Inception块有更少的参数个数和计算复杂度

GoogLeNet架构

有5段,用了9个Inception块
在这里插入图片描述
其实就是一大堆inception块,就和VGG没有什么区别了
(这里将高宽减半的块称为一个stage)
最底下是输入,是一个7*7的卷积,Stage2也没有到Inception,用的是1*1的卷积来降低通道数
stage3:用2个Inception block(不改变高宽只改变通道数),再用MaxPooling stride=2来降低高宽
后面其实也是一样的过程

  • 和NiN的联系
    1. 可以发现GoogLeNet也是用了大量1*1的卷积(当全连接在用)
    2. 也是用了全局平均池化层
      差异点:没有设计Inception block使最后一个层输出个数=类别个数,所以这里拿到输出后再通过全连接层映射到目标输出类别

stage设计

stage 1&2

在这里插入图片描述

stage 3

在这里插入图片描述
通道数:192->256->480
两个Inception块 通道数分配是不一样的
第一个:输出256,分配了1/2给第二个,1/4给第一个,剩下的1/4平分给3和4
第二个:输出480,但第二个没有拿到1/2,第三个增幅是比较大的

  • 这里的分配没有太多规律(有可能当时作者是搜索出来的结果,这就导致形状会比较奇怪,这也就导致论文很难复现)

stage 4&5

在这里插入图片描述

  • 1*7卷积层是看行空间信息,列信息不看
  • 7*1卷积层是看列空间信息,行信息不看

Inception后续变种

上面介绍的是V1,V1版本机会没有被用过,后面V3、V4用的多一点

  • Inception-BN(V2)
    在V1的基础上加了batch normalization(后面介绍)

  • Inception-V3:修改了Inception块

    • 替换5*5为多个3*3卷积层
    • 替换5*5为1*7和7*1卷积层
    • 替换3*3为1*3和3*1卷积层
    • 更深
  • Inception-V4:使用残差连接(后面介绍)

  • 如v3对比:右边是原始,左边是V3
    请添加图片描述
    请添加图片描述
    请添加图片描述

  • 内存消耗
    在这里插入图片描述

总结

  • Inception块用4条不同超参数的卷积层和池化层的路来抽取不同信息
    • 主要优点:模型参数小,计算复杂度低
  • GoogLeNet用了9个Inception块,是第一个达到上百层的网络
    • 后续有一系列改进

代码

手动实现

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

class Inception(nn.Module):
    def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):  
        super(Inception, self).__init__(**kwargs)
        # c就是channel,c1就是第一个path的输出通道数,c2就是第二个path的输出通道数,因为有2个网络,所以这是个元组
        # 下面p就是path,即p2_2就是path2的第二个;
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
        self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
        self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)
        self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
        self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
        self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)

    def forward(self, x):
        p1 = F.relu(self.p1_1(x))   # 第一条路的输出
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        p4 = F.relu(self.p4_2(F.relu(self.p4_1(x))))
        return torch.cat((p1, p2, p3, p4), dim=1) # 在通道维上连结输出,批量大小的dim=0,通道的dim=1以通道数维度进行合并
    

# 实现stage
# 参数对着图抄
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1), nn.ReLU(), 
                   nn.Conv2d(64, 192, kernel_size=3, padding=1), nn.ReLU(), 
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32), 
                   Inception(256, 128, (128, 192), (32, 96), 64), 
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),
                   Inception(512, 160, (112, 224), (24, 64), 64),
                   Inception(512, 128, (128, 256), (24, 64), 64),
                   Inception(512, 112, (144, 288), (32, 64), 64),
                   Inception(528, 256, (160, 320), (32, 128), 128),
                    nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),
                   Inception(832, 384, (192, 384), (48, 128), 128),
                     nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten())

net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))

写起来比较麻烦,但是结构还是简单的

  • 在实际的项目当中,我们往往预先只知道的是输入数据和输出数据的大小,而不知道核与步长的大小。
  • 我们可以手动计算核的大小和步长的值。而自适应(Adaptive)能让我们从这样的计算当中解脱出来,只要我们给定输入数据和输出数据的大小,自适应算法能够自动帮助我们计算核的大小和每次移动的步长。
  • 相当于我们对核说,我已经给你输入和输出的数据了,你自己适应去吧。你要长多大,你每次要走多远,都由你自己决定,总之最后你的输出符合我的要求就行了。
  • 比如我们给定输入数据的尺寸是9,输出数据的尺寸是3,那么自适应算法就能自动帮我们计算出,核的大小是3,每次移动的步长也是3,然后依据这些数据,帮我们创建好池化层。

应用实例

# googlenet开销比较大,想要快速跑完,这里用了比较小的数据,将高宽从224降低到96
X = torch.randn(size=(1,1,96,96))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__, 'output shape:\t', X.shape)

在这里插入图片描述

训练

lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

在这里插入图片描述

从数据来看,这里减小了数据量(大概9倍),但是开销却和AlexNet持平了,所以GoogLeNet大概要比AlexNet贵9倍

相关文章:

  • RabbitMQ八股文
  • 【Java导出word】使用poi-tl轻松实现Java导出数据到Word文档
  • PyTorch中Batch Normalization1d的实现与手动验证
  • 动态代理示例解析
  • Docker DockerFile和Django最佳实践
  • 0321美团实习面试——技能大致内容
  • JVM的一些知识
  • 如何在项目中有效管理设计模式的复杂性
  • 达梦数据库主备切换技术解析与实践指南
  • 《数字图像处理》第三章3.3直方图处理学习笔记
  • Java面试第十一山!《SpringCloud框架》
  • ArcGIS10. 8简介与安装,附下载地址
  • nginx 日志切割
  • Docker进阶篇1:什么是Docker数据卷?为什么需要Docker数据卷?Docker数据卷3种类型介绍
  • 5、MySQL为什么使用 B+树 来作索引【高频】
  • 【机器学习chp14 — 2】生成式模型—变分自编码器VAE(超详细分析,易于理解,推导严谨,一文就够了)
  • 从零开始实现 C++ TinyWebServer 缓冲区 Buffer类详解
  • 【万字总结】前端全方位性能优化指南(一)——Brotli压缩、CDN智能分发、资源合并
  • 界面控件DevExpress WinForms v25.1预览 - 提升.NET开发效率
  • Spark 中agg的用法
  • 读科学发展的壮丽史诗,也读普通人的传奇
  • 从息屏24小时到息屏1小时,姚明在深圳开启落地试点
  • 中国海外宏洋集团:一季度经营溢利同比降48.6%,密切关注行业收并购机会等
  • 中国专家组赴缅开展地震灾害评估工作
  • 温氏股份一季度归母净利润20.01亿元,同比扭亏为盈
  • 中国和阿塞拜疆签署互免签证协定