深度学习-----------------------含并行连结的网络GoogLeNet

含并行连结的网络GoogLeNet

在这里插入图片描述

在这里插入图片描述




最好的卷积层超参数

在这里插入图片描述

为什么选择1×1,3×3,5×5,Max pooling,AvgPool的卷积层?通道数该怎么选?到底用什么样的卷积比较好?
∴引出inception块,inception不用对此进行选择,是都要。(即:在inception块中抽取的有不同的通道,不同的通道有不同的设计,把想要试的东西都放在里面)




inception块

    Inception块是指通过并行使用不同尺寸的卷积核池化操作来捕捉输入数据的不同尺度信息。这种设计允许模型在同一层内并行地处理数据,通过多个分支(或称为’通道’)来提取特征,每个分支可能包含不同数量的卷积层、不同大小的卷积核以及可能的池化层。

    通过将所有这些设计元素(即不同的卷积核大小、卷积层数量、池化操作等)组合在一个Inception块中,模型能够学习并融合来自多个尺度和视角的特征表示。这种‘想要试的东西都放在里面’的策略,实际上是一种高效的特征提取方法,它允许模型自动学习哪些特征对于特定任务是最有用的。


4个路径从不同层面抽取信息,然后在输出通道维合并
在这里插入图片描述




在这里插入图片描述

input(即:输入)被复制了四份,从四条路径走。

pad1:输入和输出高宽一样

pad2:输入和输出等观

这四条路的输出没有改变高宽(跟输入等同高宽),变的是通道数。将这四个在输出通道上进行合并,并不是图片放在一起。


在这里插入图片描述
①白色的卷积用来改变通道数,蓝色的卷积用来抽取信息。
②最左边一条1X1卷积是用来抽取通道信息,其他的3X3卷积用来抽取空间信息。

③输出相同的通道数,5X5比3X3的卷积层参数个数多,3X3比1X1卷积层的参数个数多。

在这里插入图片描述





inception 结构

    传统的CNN结构如AlexNet、VggNet(下图)都是串联的结构,即将一系列的卷积层和池化层进行串联得到的结构。
在这里插入图片描述




inception原始结构

    GoogLeNet 提出了一种并联结构,下图是论文中提出的inception原始结构,将特征矩阵同时输入到多个分支进行处理,并将输出的特征矩阵按深度进行拼接,得到最终输出。

    inception的作用 增加网络深度和宽度的同时减少参数。
在这里插入图片描述

注意:每个分支所得特征矩阵的宽必须相同(通过调整stride和padding),以保证输出特征能在深度上进行拼接




inception + 降维

    在inception的基础上,还可以加上降维功能的结构,如下图所示,在原始 inception 结构的基础上,在分支2,3,4上加入了卷积核大小为1x1的卷积层,目的是为了降维(减小深度),减少模型训练参数减少计算量

在这里插入图片描述

1×1卷积核的降维功能

    同样是对一个深度为512的特征矩阵使用64个大小为5x5的卷积核进行卷积,不使用1x1卷积核进行降维的 话一共需要819200个参数,如果使用1x1卷积核进行降维一共需要50688个参数,明显少了很多。

在这里插入图片描述

注:CNN参数个数 = 卷积核尺寸×卷积核深度 × 卷积核组数 = 卷积核尺寸 × 输入特征矩阵深度 × 输出特征矩阵深度




GoogLeNet

在这里插入图片描述


段1&2

更小的窗口,更多的通道

在这里插入图片描述
大小从224×224➡112×112
公式:(输入大小-卷积核大小+填充*2)// 步幅 +1 = 输出大小
实际计算过程:((224-7)+ 3 * 2 )// 2 + 1= 112
其实可以看stride一般来说数值为多少,就是减多少倍。




段3

在这里插入图片描述



段4&5

在这里插入图片描述




Inception 的各种变种

在这里插入图片描述


Inception V3块,段3

在这里插入图片描述

Inception V3块,段4

在这里插入图片描述

Inception V3块,段5

在这里插入图片描述


在这里插入图片描述




总结

Inception块用4条有不同超参数的卷积层和池化层的路来抽取不同的信息。
    它的一个主要优点是模型参数小,计算复杂度低。
GoogleNet使用了9个Inception块,是第一个达到上百层的网络。
    后续有一系列改进。




Inception块代码

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): # c1为第一条路的输出通道数、c2为第二条路的输出通道数     
        super(Inception, self).__init__(**kwargs) # python中*vars代表解包元组,**vars代表解包字典,通过这种语法可以传递不定参数。**kwage是将除了前面显式列出的参数外的其他参数, 以dict结构进行接收.                                                    
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        # 定义第二个卷积层p2_1,它将输入通道数减少到c2[0],卷积核大小为1x1。这是第二条路径的第一步。
        self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
        # 定义第二个卷积层p2_2,它接收p2_1的输出作为输入,输出通道数为c2[1],卷积核大小为3x3,且使用padding=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))  # 第一条路的输出
        # 计算第二条路径的输出,首先通过1x1卷积减少通道数,然后通过3x3卷积,最后应用ReLU激活函数。
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        # 计算第四条路径的输出,首先进行最大池化,然后通过1x1卷积,最后应用ReLU激活函数。
        p4 = F.relu(self.p4_2(self.p4_1(x)))
        return torch.cat((p1, p2, p3, p4), dim=1) # 批量大小的dim为0,通道数的dim为1,以通道数维度进行合并
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.MaxPool2d(kernel_size=3,stride=2,padding=1))

b3 = nn.Sequential(Inception(192,64,(96,128),(18,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))

①在实际的项目当中,我们往往预先只知道的是输入数据和输出数据的大小,而不知道核与步长的大小。

②我们可以手动计算核的大小和步长的值。而自适应(Adaptive)能让我们从这样的计算当中解脱出来,只要我们给定输入数据和输出数据的大小,自适应算法能够自动帮助我们计算核的大小和每次移动的步长。

③相当于我们对核说,我已经给你输入和输出的数据了,你自己适应去吧。你要长多大,你每次要走多远,都由你自己决定,总之最后你的输出符合我的要求就行了。

④比如我们给定输入数据的尺寸是9, 输出数据的尺寸是3,那么自适应算法就能自动帮我们计算出,核的大小是3,每次移动的步长也是3,然后依据这些数据,帮我们创建好池化层。


为了使Fashion-MNIST上的训练短小精悍,我们将输入的高和宽从224降到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)

结果:
在这里插入图片描述




训练模型


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())

结果:
在这里插入图片描述


总代码

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):  # c1为第一条路的输出通道数、c2为第二条路的输出通道数
        # python中*vars代表解包元组,**vars代表解包字典,通过这种语法可以传递不定参数。**kwage是将除了前面显式列出的参数外的其他参数, 以dict结构进行接收.
        super(Inception, self).__init__(**kwargs)
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        # 定义第二个卷积层p2_1,它将输入通道数减少到c2[0],卷积核大小为1x1。这是第二条路径的第一步。
        self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
        # 定义第二个卷积层p2_2,它接收p2_1的输出作为输入,输出通道数为c2[1],卷积核大小为3x3,且使用padding=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))  # 第一条路的输出
        # 计算第二条路径的输出,首先通过1x1卷积减少通道数,然后通过3x3卷积,最后应用ReLU激活函数。
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        # 计算第四条路径的输出,首先进行最大池化,然后通过1x1卷积,最后应用ReLU激活函数。
        p4 = F.relu(self.p4_2(self.p4_1(x)))
        return torch.cat((p1, p2, p3, p4), dim=1)  # 批量大小的dim为0,通道数的dim为1,以通道数维度进行合并


# Stage 1
# 经过7×7的卷积层后,输出形状为(96-7+6)//2+1=48,即48×48
# 经过3×3的最大池化层后,(48-3+2)//2+1=24,即24×24
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))
# Stage 2
# 经过1×1的卷积层后,输出形状为(24-1)+1=24
# 经过3×3的卷积后,输出形状为(24-3+2)+1=24
# 经过3×3的最大池化层后,输出形状为(24-3+2)//2+1=12,即12×12
b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1), nn.ReLU(),
                   nn.Conv2d(64, 192, kernel_size=3, padding=1),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
# Stage 3
# 两个Inception块

# 第一块
# 经过第一条路径(1×1的卷积)形状不变,通道数由192变成64(自己设置的)
# 经过第二条路径(1×1的卷积)形状不变,通道数由192变成96,然后经过3×3的卷积,通道数变为128
# 经过第三条路径(1×1的卷积)形状不变,通道数由192变成16,然后经过5×5的卷积,通道数变成32
# 经过第四条路径(3×3的最大池化层)形状由(12-3+2)+1=12,然后经过(1×1的卷积)形状不变,通道数由192变成32
# 然后全部相加64+128+32+32=256个通道

# 第二块
# 经过第一条路径(1×1的卷积)形状不变,通道数由256变成128(自己设置的)
# 经过第二条路径(1×1的卷积)形状不变,通道数由256变成128,然后经过3×3的卷积,通道数变为192
# 经过第三条路径(1×1的卷积)形状不变,通道数由256变成32,然后经过5×5的卷积,通道数变成96
# 经过第四条路径(3×3的最大池化层)形状由(12-3+2)+1=12,然后经过(1×1的卷积)形状不变,通道数由256变成64
# 然后全部相加128+192+96+64=480个通道

# 然后经过3×3的最大池化层,形状由12×12,变成6×6,即:(12-3+2)//2+1=6

b3 = nn.Sequential(Inception(192, 64, (96, 128), (18, 32), 32),
                   Inception(256, 128, (128, 192), (32, 96), 64),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
# Stage 4
# 五个Inception块
# 依次类推经过五个Inception块后的形状为[1,832,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))
# Stage 5
# 两个Inception块
# 经过两个Inception块后的形状为[1,1024,3,3],计算通道数:384+384+128+128=1024
# 然后经过全局平均池化层固定高宽为1×1,所以最后形状为[1,1024,1,1]
# 然后经过展平层,形状就是[1,1024]
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())

# 将五个阶段按照顺序加入容器,最后在加入一个线性层将输出分为10种分类
net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))

lr, num_epochs, batch_size = 0.1, 10, 128
# 128×96×96
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())
d2l.plt.show()


在这里插入图片描述

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值