Pytorch总结十二之 深度神经网络模型:NIN、GoogLeNet

Pytorch总结十二之 深度神经网络模型:NIN、GooLeNet、ResNet、DenseNet

引言:
上篇文章介绍的LeNet、AlexNetVGG在设计上的共同之处是:先以由卷积层构成的模块充分抽取空间特征,再以由全连接层构成的模块来输出分类结果。其中,AlexNetVGGLeNet的改进主要在于如何对这两个模块加宽(增加通道数)和加深。

1.NIN块

  • NIN块提出了另外一个思路,即串联多个由卷积层 和全连接层构成的小网络来构建一个深层网络。

1.1 NIN块

  • 卷积层的输入和输出通常是四维数组(样本、通道、高、宽),而全连接层的输入和输出则通常是二维数组(样本、特征)。如果想在全连接层上再接上卷积层,则需要将全连接层的输出变换为四维。形如(多输入通道和多输出通道)里介绍的1x1卷积层,它可以看成全连接层,其中空间维度(高和宽)上的每个元素相当于样本,通道相当于特征。因此,NiN使用1x1卷积层来替代全连接层,从而是空间信息能够自然传递到后边的层中去,下图对比了NiNAlexNetVGG等网络再结构上的主要区别。
    在这里插入图片描述
  • NiN块是NiN中的基础块,它有一个卷积层加两个充当全连接层的1x1卷积层串联而成。其中第一个卷积层的超参数可以自行设置,而第二个和第三个卷积层的超参数一般是固定的。
import time
import torch
from torch import nn,optim

import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')

def nin_block(in_channels, out_channels, kernel_size, stride,padding):
    blk=nn.Sequential(nn.Conv2d(in_channels,out_channels,kernel_size,stride,padding),  #卷积层,超参数可设
                      nn.ReLU(),
                      nn.Conv2d(out_channels,out_channels,kernel_size=1),  #充当全连接层的卷积层1
                      nn.ReLU(),
                      nn.Conv2d(out_channels,out_channels,kernel_size=1),  #充当全连接层的卷积层2
                      nn.ReLU())
    return blk

1.2 NiN模型

  • NiN是在AlexNet问世不久后提出的。它们的卷积层设定有类似之处。NiN使⽤卷积窗⼝形状分别为11x115x53x3 的卷积层,相应的输出通道数也与AlexNet中的⼀致。每个NiN块后接⼀个步幅为2、窗⼝形状为 3x3 的最⼤池化层。
  • 除使⽤NiN块以外,NiN还有⼀个设计与AlexNet显著不同:NiN去掉了AlexNet最后的3个全连接层,取⽽代之地,NiN使⽤了输出通道数等于标签类别数的NiN块,然后使⽤全局平均池化层对每个通道中所有元素求平均并直接⽤于分类。这⾥的全局平均池化层即窗⼝形状等于输⼊空间维形状的平均池化层。NiN的这个设计的好处是可以显著减⼩模型参数尺⼨,从⽽缓解过拟合。然⽽,该设计有时会造成获得有效模型的训练时间的增加
# 已保存在d2lzh_pytorch
class GlobalAvgPool2d(nn.Module):
    ##全局平均池化层可通过将池化窗口设置成输入的高和宽实现
    def __init__(self):
        super(GlobalAvgPool2d,self).__init__()
    def forward(self,x):
        return F.avg_pool2d(x,kernel_size=x.size()[2:])  #import torch.nn.functional as F

net=nn.Sequential(
    nin_block(in_channels=1,out_channels=96,kernel_size=11,stride=4,padding=0),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nin_block(96,256,kernel_size=5,stride=1,padding=2),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nin_block(256,384,kernel_size=3,stride=1,padding=1),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nn.Dropout(0.5),
    #标签类别数是10
    nin_block(384,10,kernel_size=3,stride=1,padding=1),
    GlobalAvgPool2d(),
    #将四维的输出转为二维的输出,其形状为(批量大小,10)
    d2l.FlattenLayer())

#构建一个样本查看每一层的输出形状
X=torch.rand((1,1,224,224))
for name,blk in net.named_children():
    X=blk(X)
    print(name,'output shape:',X.shape)

output:
在这里插入图片描述

1.3 获取数据和训练模型

依然使⽤Fashion-MNIST数据集来训练模型。NiN的训练与AlexNetVGG的类似,但这⾥使⽤的
学习率更⼤。

batch_size=128
# 如出现“out of memory”的报错信息,可减⼩batch_size或resize
train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size,resize=224)
lr,num_epochs=0.002,5
optimizer=torch.optim.Adam(net.parameters(),lr=lr)
d2l.train_ch5(net,train_iter,test_iter,batch_size,optimizer,device,num_epochs)

output:
在这里插入图片描述

1.4 小结

  • NiN重复使⽤由卷积层和代替全连接层的 1x1 卷积层构成的NiN块来构建深层⽹络。
  • NiN去除了容易造成过拟合的全连接输出层,⽽是将其替换成输出通道数等于标签类别数的NiN
    块和全局平均池化层。
  • NiN的以上设计思想影响了后⾯⼀系列卷积神经⽹络的设计。

2.含并行连结的网路(GoogLeNet)

在2014年的ImageNet图像识别挑战赛中,⼀个名叫GoogLeNet的⽹络结构⼤放异彩 [1]。它虽然在名字上向LeNet致敬,但在⽹络结构上已经很难看到LeNet的影⼦。GoogLeNet吸收了NiN中⽹络串联⽹络的思想,并在此基础上做了很⼤改进。在随后的⼏年⾥,研究⼈员对GoogLeNet进⾏了数次改进,本节将介绍这个模型系列的第⼀个版本。

2.1 INCEPTION块

  • GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与上⼀
    节介绍的NiN块相⽐,这个基础块在结构上更加复杂,如下图所示:
    在这里插入图片描述
    由上图可以看出,Inception块里有4条并行的线路。前3条线路使用窗口分别为1x1,3x3 和 5x5的卷积层来抽取不同空间尺寸下的信息,其中中间2个线路对输入先做1x1卷积来减少输入通道数,以降低模型复杂度。第四条线路则使用3x3最大池化层,后接1x1卷积层来改变通道数。4条线路都使用了合适的填充来使输入与输出的高和宽移植,最后将每条线路的输出在通道维上连结,并输入接下来的层中去。
    Inception块中可以⾃定义的超参数是每个层的输出通道数,我们以此来控制模型复杂度。
#test_googlenet
import time
import torch
from torch import nn,optim
import torch.nn.functional as F

import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')

class Inception(nn.Module):
    # c1 - c4为每条线路⾥的层的输出通道数
    def __init__(self,in_c,c1,c2,c3,c4):
        super(Inception,self).__init__()
        # 线路1,单1 x 1卷积层
        self.p1_1 = nn.Conv2d(in_c, c1, kernel_size=1)
        # 线路2,1 x 1卷积层后接3 x 3卷积层
        self.p2_1 = nn.Conv2d(in_c, c2[0], kernel_size=1)
        self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3,padding=1)
        # 线路3,1 x 1卷积层后接5 x 5卷积层
        self.p3_1 = nn.Conv2d(in_c, c3[0], kernel_size=1)
        self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5,padding=2)
        # 线路4,3 x 3最⼤池化层后接1 x 1卷积层
        self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1,padding=1)
        self.p4_2 = nn.Conv2d(in_c, 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模型

  • GoogLeNetVGG⼀样,在主体卷积部分中使⽤5个模块(block),每个模块之间使⽤步幅为2的 3x3 最⼤池化层来减⼩输出⾼宽。第⼀模块使⽤⼀个64通道的 7x7 卷积层。
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))
  • 第⼆模块使⽤2个卷积层:⾸先是64通道的 卷积层,然后是将通道增⼤3倍的 3x3 卷积层。它对
    Inception块中的第⼆条线路。
b2=nn.Sequential(nn.Conv2d(64,64,kernel_size=1),
                 nn.Conv2d(64,192,kernel_size=3,padding=1),
                 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),
                   d2l.GlobalAvgPool2d())
net=nn.Sequential(b1,b2,b3,b4,b5,d2l.FlattenLayer(),nn.Linear(1024,10))

GoogLeNet模型的计算复杂,⽽且不如VGG那样便于修改通道数。本节⾥我们将输⼊的⾼和宽从224
降到96来简化计算。下⾯演示各个模块之间的输出的形状变化。

net=nn.Sequential(b1,b2,b3,b4,b5,d2l.FlattenLayer(),nn.Linear(1024,10))
X=torch.rand(1,1,96,96)
for blk in net.children():
    X=blk(X)
    print('output shape:',X.shape)

output:
在这里插入图片描述

2.3 获取数据和训练模型

我们使⽤⾼和宽均为96像素的图像来训练GoogLeNet模型。训练使⽤的图像依然来⾃Fashion-MNIST数据集。

batch_size = 128
# 如出现“out of memory”的报错信息,可减⼩batch_size或resize
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer,
              device, num_epochs)

在这里插入图片描述

2.4 小结

  • Inception块相当于⼀个有4条线路的⼦⽹络。它通过不同窗⼝形状的卷积层和最⼤池化层来并⾏抽取信息,并使⽤ 1x1 卷积层减少通道数从⽽降低模型复杂度。
  • GoogLeNet将多个设计精细的Inception块和其他层串联起来。其中Inception块的通道数分配之⽐是在ImageNet数据集上通过⼤量的实验得来的。
  • GoogLeNet和它的后继者们⼀度是ImageNet上最⾼效的模型之⼀:在类似的测试精度下,它们的计算复杂度往往更低。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月醉窗台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值