目录
[1 GoogLeNet详解](#1 GoogLeNet详解)
[1.1 核心思想:Inception块](#1.1 核心思想:Inception块)
[1.2 Google 架构](#1.2 Google 架构)
[第1部分:前导网络(Stage 1 & Stage 2)](#第1部分:前导网络(Stage 1 & Stage 2))
[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))
[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 以及更先进的网络架构。