Part 6 GoogLeNet
约 1001 字大约 3 分钟
2025-08-05
GoogLeNet 重点解决了多大的卷积核最合适这个问题,它使用不同大小的卷积核的组合。
1 Inception 块和 GoogLeNet 模型
在 GoogLeNet 中基本的卷积块称为 Inception 块。
如图所示,一个 Inception 块由四条并行路径组成。前三条路径分别使用大小为 1、3、5 的卷积核从不同的空间大小中提取信息,中间两条在输入时使用 1×1 卷积以降低模型复杂性,最后一条使用 3×3 池化层,然后使用 1×1 卷积改变通道数。最后将每条线路的输出合并,构成 Inception 块的总输出。
在 Inception 块中,其超参数为每层输出通道数。
GoogLeNet 一共使用 9 个 Inception 块和若干单独的卷积层、池化层堆叠。
我们可以将其划分为 5 个模块:
- 第一模块,64 通道的 7×7 卷积层和 3×3 的最大池化层;
- 第二模块,64 通道的 1×1 卷积层、192 通道的 3×3 卷积层和 3×3 的最大池化层;
- 第三模块,两个 Inception 块,通道数量分别为 64+128+32+32 和 128+192+96+64,然后是一个 3×3 的最大池化层;
- 第四模块,五个 Inception 块,通道数量分别为 192+208+48+64、160+224+64+64、128+256+64+64、112+288+64+64 和 256+320+128+128,然后是一个 3×3 的最大池化层;
- 第五模块,两个 Inception 块,通道数量分别为 256+320+128+128 和 384+384+128+128,然后是一个 3×3 的最大池化层。
2 GoogLeNet 模型的 PyTorch 实现
我们先来实现 Inception 块。
from torch import nn
from torch.nn import functional as F
class Inception(nn.Module):
# c1 c2 c3 c4 是对应路径的输出通道数
def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):
super(Inception, self).__init__(**kwargs)
# 线路 1,单 1×1 卷积层
self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
# 线路 2,1×1 卷积层后接 3×3 卷积层
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,1×1 卷积层后接 5×5 卷积层
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,3×3 最大池化层后接 1×1 卷积层
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)
然后我们按模块实现 GoogLeNet 模型。
第一模块,64 通道的 7×7 卷积层和 3×3 的最大池化层:
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 通道的 1×1 卷积层、192 通道的 3×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 块,通道数量分别为 64+128+32+32 和 128+192+96+64,然后是一个 3×3 的最大池化层:
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),
)
第四模块,五个 Inception 块,通道数量分别为 192+208+48+64、160+224+64+64、128+256+64+64、112+288+64+64 和 256+320+128+128,然后是一个 3×3 的最大池化层:
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),
)
第五模块,两个 Inception 块,通道数量分别为 256+320+128+128 和 384+384+128+128,然后是一个 3×3 的最大池化层:
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 模型,我们将 MNIST 数据集尺寸缩放到 96。
trans = [transforms.Resize((96, 96)), transforms.ToTensor()]
学习率调整为 0.1,训练流程和之前一致。