1、GoogLeNet模型介绍
2014年的ImageNet图像识别挑战赛中,一个名叫GoogLeNet 的网络架构大放异彩。GoogLeNet吸收了NiN中串联网络的思想。解决了选择多大的卷积核才比较合适呢,成年人不需要做选择,我都要。因此GoogLeNet实现了这个需求。
2、Inception快
在GoogLeNet中,我们把每一个卷积块被称为Inception块(Inception block)。这很可能得名于电影《盗梦空间》(Inception),因为电影中的一句话“我们需要走得更深”(“We need to go deeper”)。
该图就是Inception快的基本结构,从图可以看出,我们可以从四条路径去抽取特征信息,最后在concatenation,将各个通道合并在一起。采取不同的卷积核,在不同路径分配不同的通道数目,来实现我们提取特征信息的需求,一般在第二条路径的通道数选择大一点(从左到右)。同时,需要满足四条路径都使用合适的填充来使输入与输出的高和宽一致。
该模块用Inception类实现如下:
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)
后期我们会反复使用Inception块,来实现GoogLeNet模型的搭建
3、GoogLeNet模型
改图就是GoogLeNet 模型的整体架构,GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠而来,Inception块之间的最大汇聚层可降低维度。
如何实现每个模块,只需要按照架构一步步的Sequential起来。
在这为大家讲解一下何为Sequential,它是我们定义的的顺序块,顾名思义,就是按照顺序来实现每一个块,GoogLeNet 模型就是多个Inception块组成。
每一个块用代码分别实现如下:
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))
实现每一个模块之后,同样使用顺序块Sequential连接起来。
最后可以通过简单代码看一下我们输出的现状,方便后面模型的纠正
X = torch.rand(size=(1, 1, 96, 96))
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape:\t', X.shape)
最后使用Fashion-MNIST数据集来训练一下:
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())
模型训练结果如下:
欢迎大家一起讨论学习