Pytorch 含并行连结的网络 GoogLeNet
0. 环境介绍
环境使用 Kaggle 里免费建立的 Notebook
教程使用李沐老师的 动手学深度学习 网站和 视频讲解
小技巧:当遇到函数看不懂的时候可以按 Shift+Tab 查看函数详解。
1. GoogLeNet
1.1 简介
GoogLeNet 吸收了 NiN 中串联网络的思想,并在此基础上做了改进。 这篇论文的一个重点是解决了什么样大小的卷积核最合适的问题。GoogLeNet 的一个观点是,有时使用不同大小的卷积核组合是有利的。
GoogLeNet 论文地址:https://arxiv.org/abs/1409.4842
1.2 GoogLeNet 结构
在GoogLeNet 中,基本的卷积块被称为 Inception 块(Inception block)。这很可能得名于电影《盗梦空间》(Inception),因为电影中的一句话“我们需要走得更深”(“We need to go deeper”)。
Inception 块(小学生才做选择,我全都要):
- 4 个路径从不同层面抽取信息,然后在输出通道维合并
- 使用不同窗口大小的卷积层
-
1
×
1
1 \times 1
1×1 的卷积块只抽取通道信息,不抽取空间信息
- 每条路上通道数可能不同
Inception 块跟单
3
×
3
3 \times 3
3×3 和
5
×
5
5 \times 5
5×5 的卷积层相比有更少的参数个数和设计复杂度。 注:问为什么这么设计?调出来的!😂
GoogLeNet 简化版本:
5
5
5 段,
9
9
9 个 Inception 块。 段
1
,
2
1,2
1,2:
- 更小的宽口
- 更多的通道
段
3
3
3: 段
4
,
5
4,5
4,5:
1.3 Inception 后续变种
Inception-BN(V2):使用了 batch normalization Inception-V3:修改了 Inception 块
- 替换
5
×
5
5 \times 5
5×5 为多个
3
×
3
3 \times 3
3×3 卷积层
- 替换
5
×
5
5 \times 5
5×5 为
1
×
7
1 \times 7
1×7 和
7
×
1
7 \times 1
7×1 卷积层
- 替换
5
×
5
5 \times 5
5×5 为
1
×
3
1 \times 3
1×3 和
3
×
1
3 \times 1
3×1 卷积层
- 更深
Inception-V4:使用残差连接
2. 代码实现
2.1 Inception 块
!pip install -U d2l
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)
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(self.p4_1(x)))
return torch.cat((p1, p2, p3, p4), dim=1)
2.2 GoogLeNet 网络结构
第一个模块使用
64
64
64 个通道、
7
×
7
7 \times 7
7×7 卷积层:
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))
第二个模块使用两个卷积层:第一个卷积层是
64
64
64 个通道、
1
×
1
1 \times 1
1×1 卷积层;第二个卷积层使用将通道数量增加三倍的
3
×
3
3 \times 3
3×3 卷积层:
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))
第三个模块串联两个完整的 Inception 块, 第一个Inception 块的输出通道数为
64
+
128
+
32
+
32
=
256
64+128+32+32=256
64+128+32+32=256,四个路径之间的输出通道数量比为
64
:
128
:
32
:
32
=
2
:
4
:
1
:
1
64:128:32:32=2:4:1:1
64:128:32:32=2:4:1:1,第二个 Inception 块的输出通道数增加到
128
+
192
+
96
+
64
=
480
128+192+96+64=480
128+192+96+64=480,四个路径之间的输出通道数量比为
128
:
192
:
96
:
64
=
4
:
6
:
3
:
2
128:192:96:64 = 4:6:3:2
128:192:96:64=4:6:3:2:
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))
第四模块更加复杂, 它串联了
5
5
5 个 Inception 块,其输出通道数分别是:
-
192
+
208
+
48
+
64
=
512
192+208+48+64=512
192+208+48+64=512
-
160
+
224
+
64
+
64
=
512
160+224+64+64=512
160+224+64+64=512
-
128
+
256
+
64
+
64
=
512
128+256+64+64=512
128+256+64+64=512
-
112
+
288
+
64
+
64
=
528
112+288+64+64=528
112+288+64+64=528
-
256
+
320
+
128
+
128
=
832
256+320+128+128=832
256+320+128+128=832
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))
第五模块包含输出通道数为
256
+
320
+
128
+
128
=
832
256+320+128+128=832
256+320+128+128=832 和
384
+
384
+
128
+
128
=
1024
384+384+128+128=1024
384+384+128+128=1024 的两个 Inception 块,后面紧跟输出层,该模块同 NiN 一样使用全局平均池化层,将每个通道的高和宽变成
1
1
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))
GoogLeNet 模型的计算复杂,而且不如 VGG 那样便于修改通道数。 为了使 Fashion-MNIST 上的训练短小精悍,我们将输入的高和宽从
224
224
224 降到
96
96
96,这简化了计算。下面演示各个模块输出的形状变化:
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 训练
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())
|