【李沐 | 动手学深度学习】13 含并行连结的网络(GoogLeNet)

目录

前言

[1 GoogLeNet详解](#1 GoogLeNet详解)

[1.1 核心思想:Inception块](#1.1 核心思想:Inception块)

为什么需要Inception?

1×1卷积的神奇作用(降维)

[1.2 Google 架构](#1.2 Google 架构)

[第1部分:前导网络(Stage 1 & Stage 2)](#第1部分:前导网络(Stage 1 & Stage 2))

第2部分:核心Inception模块组

[2-1:Stage 3 :(2个Inception)](#2-1:Stage 3 :(2个Inception))

[2-2:Stage 4(4个Inception):](#2-2:Stage 4(4个Inception):)

[2-3:Stage5 (5个Inception)](#2-3:Stage5 (5个Inception))

[第3部分:辅助分类器(Auxiliary Classifier)](#第3部分:辅助分类器(Auxiliary Classifier))

第4部分:输出层

[1.3 Inception 后续变种](#1.3 Inception 后续变种)

[Inception V3了解](#Inception V3了解)

[2 代码实现](#2 代码实现)

[2.1 Inception 块](#2.1 Inception 块)

[2.2 GoogLeNet架构实现](#2.2 GoogLeNet架构实现)

[2.3 训练](#2.3 训练)

总结


NiN没有被大量使用,而GoogLeNet如今还是使用较为广泛的,它是第一个几乎快要做到100层的卷积神经网络,它把 'L'大写实际上是为了致敬 "LeNet",在学习过NiN之后再来理解GoogLeNet要方便一些。

前言

相信大家都会感到很疑惑,为什么某个网络中卷积层的卷积核要设置为3×3 ,或者要设置为 5×5 ......,什么才是最好的卷积层超参数选择呢?

1 GoogLeNet详解

GoogLeNet(又称 Inception-v1)由 Google 团队在 2014 年提出,并夺得了当年 ImageNet 大赛(ILSVRC 2014)的冠军。它的出现标志着深度学习模型从单纯的"加深"转向了对"结构效能"的深度优化。

1.1 核心思想:Inception块

传统的卷积神经网络(如 AlexNet、VGG)通过简单地堆叠卷积层来提高表达能力,但这会导致参数量巨大且容易过拟合。GoogLeNet 的核心创新是 Inception 模块

Inception 模块的设计初衷是利用不同尺度的卷积核来捕捉不同大小的特征。与其纠结增肌网络的深度(层数)还是宽度(通道数),不如"全都要"。

为什么需要Inception?

在图像中,重要的特征可能具有不同的尺度(比如一张照片里,狗的脸可能占据很大空间,也可能只是远处的一个小点)

  • 1×1卷积:捕捉局部微小特征;
  • 3×3和5×5:捕捉更大范围的空间特征;
  • 池化层(Pooling):保留主要的结构信息

1×1卷积的神奇作用(降维)

直接并联这些卷积层会导致计算量爆炸。GoogleLeNet的聪明之处在于,在3×3和5×5卷积之前加了一个 1×1 卷积层,主要作用如下:

  • *++降维:++*减少输入特征图的通道数,从而大幅减少参数量和计算量;
  • *++增加非线性:++*通过1×1卷积后的ReLU激活函数,增加网络的表达能力。

下图:4个路径从不同层面抽取信息,然后在输出通道维度合并,即输入到输出的宽高是不变的,变化的是通道数。

  • 下图中,白色部分1×1卷积用来变换通道数,而蓝色部分用来抽取信息(其中蓝色1×1Conv用来抽取通道信息,其他用来抽取空间信息)
  • 跟单3×3或单5×5卷积层相比,Inception块有更少的参数个数和计算复杂度。

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | |


1.2 Google 架构

总体来说,由9个Inception块 组成,每个Inception 模块内有4个并行的分支,在中间层有两个辅助分类之路,用于解决深层网络的梯度消失问题。(右图为原始论文的架构)。

|----------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| | |

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| #### 第1部分:前导网络(Stage 1 & Stage 2) Input:224×3×3 Conv1:7×7Conv(64),大卷积核快速扩大感受野,捕捉图像的轮廓,输出尺寸112×112×64 MaxPool1:降采样,输出56×56×64 Conv2:1×1Conv(64)-降维→56×56×64,3×3Conv(192)-提取更复杂特征,输出56×56×192 MaxPool2:降采样,输出28×28×192 | |
| #### 第2部分:核心Inception模块组 ##### 2-1:Stage 3 :(2个Inception) 3a: Input:28×28×192 Branch1:28×28×64; Branch2:1×1Conv(96)-降维→28×28×96,经3×3Conv(128)-提取更复杂特征,输出28×28×128 Branch3:1×1Conv(16)-降维→28×28×16,经5×5Conv(32)-提取更复杂特征,输出28×28×32 Branch4:3×3MaxPool-降采样→28×28×192,经1×1Conv(32)降维,输出28×28×32 通过Concat(通道拼接):输出28×28×256 3b:同理,最终输出28×28×480 经过MaxPool3:输出14×14×480 | |
| ##### 2-2:Stage 4(4个Inception): 4a:输入14×14×480,输出14×14×512 辅助分类器1 4b:输出14×14×512 4c:输出14×14×512 4d:输出14×14×528 辅助分类器2 4d:输出14×14×832 经过MaxPool4:输出7×7×832 ##### 2-3:Stage5 (5个Inception) 5a:输入7×7×832,输出7×7×832 5b:输出7×7×1024 | |


第3部分:辅助分类器(Auxiliary Classifier)

为了防止在训练22层深的网络时梯度无法回传,GoogLeNet分别在Inception 4a和 4d 之后拉出了两个"小弟",用于解决训练时候的梯度消失问题及加快训练

**结构:**AvgPool(K=5×5,stride=3)---1×1卷积(128个通道,用于降维并增加非线性)--- FC(1024个神经元)---Dropout(70%的舍弃率)---Softmax(输出1000类分类结果)。

**训练权重:**在计算总Loss时,两个辅助分类器的Loss权重为0.3。

第4部分:输出层

这是网络收官的地方 GooLeNet 的关键创新之一。

Inception 5b 输出:7×7×1024

Global Average Pooling(GAP):关键!直接计算每一层7×7的平均值。

  • 输出:1×1×1024
  • 意义:彻底摆脱了 AlexNet 那种动辄数千万参数的全连接层,使参数量暴跌。

Dropout(40%):在全连接层之前的最后一次防止过拟合。

**Fully Connected(Linear):**1024映射到1000。

**Sotmax:**得到最终分类概率。

1.3 Inception 后续变种

a) Inception-BN(v2)-使用batch normalization(后面介绍)

b) Inception-V3-修改了Inception块

  • 替换5x5为多个3x3卷积层
  • 替换5x5为1x7和7x1卷积层
  • 替换3x3为1x3和3x1卷积层
  • 更深

c) Inception-V4-使用残差连接(后面介绍)

Inception V3了解

对Inception模块中的卷积核设置进行更改。

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | | |

Inception相较于Vgg来说内存更高,训练更慢,但是效果还是好很多的,现在仍然常被使用。


2 代码实现

2.1 Inception 块

(如沐神所说"没什么好讲的"(不是),结合原始架构设计理解吧~~~~~~~~)

python 复制代码
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l


class Inception(nn.Module):
    # c1--c4是每条路径的输出通道数
    def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):
        super(Inception, self).__init__(**kwargs)
        # 线路1,单1x1卷积层
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        # 线路2,1x1卷积层后接3x3卷积层
        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)
        # 线路3,1x1卷积层后接5x5卷积层
        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)
        # 线路4,3x3最大汇聚层后接1x1卷积层
        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(self.p4_1(x)))
        # 在通道维度上连结输出
        return torch.cat((p1, p2, p3, p4), dim=1)

2.2 GoogLeNet架构实现

因为我们用的数据集是FashionMnist,分类数为10,所以在最后加了一个Linear(1024,10)

python 复制代码
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))

来简单查看下网络形状:

为了使Fashiont-MNIST上的训练短小精悍,我们将输入的宽高降到96×96.

python 复制代码
X = torch.rand(size=(1, 1, 96, 96))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__,'output shape:\t', X.shape)

//输出:

2.3 训练

python 复制代码
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())

//输出:

总结

Inception块用4条有不同超参数的卷积层和池化层的路来抽取不同的信息。它的一个主要优点是模型参数小,计算复杂度低。

GoogleNet使用了9个Inception块,是第一个达到上百层的网络,后续有一系列改进。(V3、V4还是被持续使用,问题是结构很复杂 )

GoogLeNet 的出现标志着深度学习从*++"一味追求深度++* "转向了*++"追求结构效率++*"。它的设计哲学影响了后续的 Inception v2/v3/v4 以及更先进的网络架构。

相关推荐
马士兵教育1 小时前
AI方向的就业工作岗位?
人工智能
2601_950760791 小时前
TNF-α信号通路与自身免疫性疾病研究进展
人工智能·机器学习·蛋白
高洁012 小时前
AI模型部署进阶:Docker容器化部署AI项目
人工智能·python·深度学习·数据挖掘·知识图谱
NOCSAH2 小时前
统好AI:数智化转型的核心支撑路径
大数据·人工智能·产品运营
FlyIer5562 小时前
软件“日抛”需加限定词:给人用的可抛,给流程与Agent用的不可抛
大数据·人工智能
ZhengEnCi2 小时前
01d-前馈神经网络
人工智能·深度学习·神经网络
znhb992 小时前
技术详解 | 脱硫脱硝氨逃逸AI精准控制系统的核心运行逻辑
人工智能·机器学习
hughnz2 小时前
SLB AI项目2026:能源科技主导地位的蓝图
大数据·人工智能
老陈跨境记2 小时前
电商出海效率革命:萤火AI批量图片翻译的技术原理与实战测评
人工智能·ai