03 Googlenet(2014)

目录

一、理论知识

1. Inception块​​​​​​​

2. 辅助输出

 3. 丢弃全连接层

4. 网络结构和参数配置

二、代码复现

1. 网络搭建部分代码

2. train部分


 仅是个人笔记,代码学习资源来源B站博主霹雳吧啦Wz的个人空间_哔哩哔哩_bilibili

一、理论知识

1. Inception块

该结构融合不同尺度的特征信息,各并行分支得到的结果在channel方向进行拼接

Inception块中1*1的作用————降维(减小通道数)

进行降维后再卷积可大大减小参数量

假设输入:512*8*8,走第二条路径的输出是64*8*8

  • 直接使用3*3卷积参数量:

        512*64*3*3                                                =  294  912

  • 先使用1*1降维到24,再使用3*3升维到64:

        512*24*1*1             +        24*64*3*3         =   26  112

2. 辅助输出

除最后一层的输出结果,中间节点的分类效果也有可能是很好的,所以GoogLeNet将中间某一层的输出作为分类,并以一个较小的权重(0.3)加到最终的分类结果中。有2个辅助分类节点。

  

 

 3. 丢弃全连接层

在网络的最终输出部分使用平均池化层+一个全连接

代替之前网络的两个全连接(大大减少模型参数)

为什么最后一个还是全连接??

个人理解。。。绝大多数网络最后一个都是全连接

 

4. 网络结构和参数配置

图中每个Inception的结构都是一样的,只是参数不同

参数配置如下图: 

  •  特征图大小只在输入图像第一个卷积操作和maxpool发生变化
  • Inception不改变特征图大小,只改变通道数

特征图变化说明:

inception(3a)为例:输入28*28*192-------------输出28*28*256

depth==2:因为每个inception块是两层深度

inception(3a)并行分支1:28*28*192-----(1*1卷积降维操作)------28*28*64

inception(3a)并行分支2:28*28*192-----(1*1卷积降维操作,表中3*3reduce)------28*28*96

                                              28*28*96-----(3*3卷积升维操作,表中3*3)------28*28*128

                                                因为要保证特征图不变,所以3*3卷积要padding 1

inception(3a)并行分支3:28*28*192-----(1*1卷积降维操作,表中5*5reduce)------28*28*16

                                              28*28*16-----(5*5卷积升维操作,表中5*5)------28*28*32

                                                因为要保证特征图不变,所以5*5卷积要padding 2

inception(3a)并行分支4:28*28*192-----(池化+1*1卷积,表中poolproj)------28*28*32

最终输出-------通道方向拼接:

28*28*(64+128+32+32)=====28*28*256

二、代码复现

1. 网络搭建部分代码

inception实现

class Inception(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
                    #      对应      参数      配置     表        中      的    参数
        super(Inception, self).__init__()
        self.branch1 = ...
        self.branch2 = ...
        self.branch3 = ...
        self.branch4 = ...
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

inception第四个分支

self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),  
            # pad保证输出大小等于输入大小
            BasicConv2d(in_channels, pool_proj, kernel_size=1)  
            # BasicConv2d是自定义的, 一个卷积 + 一个relu
            )

四个分支拼接

branch1 = self.branch1(x)
branch2 = self.branch2(x)
branch3 = self.branch3(x)
branch4 = self.branch4(x)

outputs = [branch1, branch2, branch3, branch4]
return torch.cat(outputs, 1)

torch.cat():   

torch.cat((A,B),dim)时,除拼接维数dim数值可以不同其余维数数值需相同结果维度数不变

outputs中每个元素B C H W         按照维度C(索引为1)方向拼接

eg:  A (2,3,4)   B(4,3,4)

dim只能为0------------------------拼接结果shape(6,3,4)

torch.stack():

torch.cat((A,B),dim)时,A和B完全一样的shape,dim是新增的维度;结果会增加维度

eg: A B都是(3,3) 

dim = 0-------------拼接结果shape(2,3,3)

dim = 1-------------拼接结果shape(3,2,3)

dim = -1-------------拼接结果shape(3,3,2)

辅助分类器使用:只在训练部分且使用辅助分类器才会被激活

class GoogLeNet(nn.Module):
    def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):  #  是否要aux_logits两个辅助输出
                                                                      
        super(GoogLeNet, self).__init__()
        ......
        if self.aux_logits:
            self.aux1 = InceptionAux(512, num_classes)
            self.aux2 = InceptionAux(528, num_classes)
        ......

    def forward(self, x):
        ......

        if self.training and self.aux_logits:    # eval model lose this layer
            aux1 = self.aux1(x)

        ......

        if self.training and self.aux_logits:    # eval model lose this layer
            aux2 = self.aux2(x)

        ......

        if self.training and self.aux_logits:   # eval model lose this layer
            return x, aux2, aux1
        return x

2. train部分

流程同01  02 一样:

如果使用辅助分类器,如下:

#使用两个辅助分类器的话,损失计算如下

for epoch in range(epochs):
    net.train() # train
    ......
    for step, data in enumerate(train_bar):
        ......
        logits, aux_logits2, aux_logits1 = net(images.to(device))
        loss0 = loss_function(logits, labels.to(device))
        loss1 = loss_function(aux_logits1, labels.to(device))
        loss2 = loss_function(aux_logits2, labels.to(device))
        loss = loss0 + loss1 * 0.3 + loss2 * 0.3     #辅助输出时,3个损失按比例计算总损失
        loss.backward()


    net.eval()
    with torch.no_grad():
        ......
        for val_data in val_bar:
            ......
            logits = net(images.to(device))
            # 因为是val,此时没有辅助器输出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值