当前位置:   article > 正文

GoogLeNet网络详解与模型搭建_googlenet模型

googlenet模型

1 模型介绍

GoogLeNet是2014年Christian Szegedy等人在2014年大规模视觉挑战赛(ILSVRC-2014)上使用的一种全新卷积神经网络结构,并以6.65%的错误率力压VGGNet等模型取得了ILSVRC-2014在分类任务上的冠军,于2015年在CVPR发表了论文《Going Deeper with Convolutions》。在这之前的AlexNet、VGG等结构都是通过增大网络的深度(层数)来获得更好的训练效果,但层数的增加会带来很多负作用,比如overfitting、梯度消失、梯度爆炸等,GoogLeNet则做了更加大胆的网络结构尝试,Inception的提出则从另一种角度来提升训练结果:能更高效的利用计算资源,在相同的计算量下能提取到更多的特征,从而提升训练结果,采用了Inception结构的GoogLeNet深度只有22层,其参数约为AlexNet的1/12,是同时期VGGNet的1/3。

GoogLeNet是谷歌(Google)提出的深度网络结构,为什么不叫“GoogleNet”,而叫“GoogLeNet”,是为了向经典模型“LeNet”致敬

2 GoogLeNet详解

下面给出了GoogLeNet架构的缩略图,更详细以及带标注的图放在文末。相比于以前的卷积神经网络结构,除了在深度上进行了延伸,还对网络的宽度进行了扩展,整个网络由许多块状子网络的堆叠而成,这个子网络即Inception模块

在这里插入图片描述

首先说说该模型的亮点:

  • 采用了模块化的设计(stem, stacked inception module, axuiliary function和classifier),方便层的添加与修改。
    • Stem部分:论文指出Inception module要在网络中间使用的效果比较好,因此网络前半部分依旧使用传统的卷积层代替
    • 辅助函数(Axuiliary Function):从信息流动的角度看梯度消失,因为是梯度信息在BP过程中能量衰减,无法到达浅层区域,因此在中间开个口子,加个辅助损失函数直接为浅层
    • Classifier部分:从VGGNet以及NIN的论文中可知,fc层具有大量层数,因此用average pooling替代fc,减少参数数量防止过拟合。在softmax前的fc之间加入dropout,p=0.7,进一步防止过拟合。
  • 使用1x1的卷积核进行降维以及映射处理 (虽然VGG网络中也有,但该论文介绍的更详细)。
  • 引入了Inception结构(融合不同尺度的特征信息)。
  • 丢弃全连接层,使用平均池化(average pooling)层,大大减少模型参数。
  • 为了避免梯度消失,网络额外增加了2个辅助的softmax用于向前传导梯度(辅助分类器)。辅助分类器是将中间某一层的输出用作分类,并按一个较小的权重(0.3)加到最终分类结果中,这样相当于做了模型融合,同时给网络增加了反向传播的梯度信号,也提供了额外的正则化,对于整个网络的训练很有裨益。而在实际测试的时候,这两个额外的softmax会被去掉。

Inception模块

GoogLeNet中使用的Inception模块被命名为Inception v1,实际上在2014-2016年间,Google团队不断地对GoogLeNet进行改进的过程中形成了Inception v1-v4和Xception结构,具体有关于inception结构的详细介绍,可以参考博主的另一篇博文详解Inception结构:从Inception v1到Xception

在这里插入图片描述

左图是GoogleNet作者设计的初始inception结构(native inception),其想法是用多个不同类型的卷积核( 1 × 1 1\times1 1×1 3 × 3 3\times3 3×3 5 × 5 5\times5 5×5 3 × 3 P o o l 3\times3Pool 3×3Pool)堆叠在一起(卷积、池化后的尺寸相同,将通道相加)代替一个3x3的小卷积核,好处是可以使提取出来的特征具有多样化,并且特征之间的co-relationship不会很大,最后用把feature map都concatenate起来使网络做得很宽,然后堆叠Inception Module将网络变深。但仅仅简单这么做会使一层的计算量爆炸式增长

native inception中所有的卷积核都在上一层的所有输出上来做,而那个5x5的卷积核所需的计算量就太大了,造成了特征图的厚度很大,为了避免这种情况,在3x3前、5x5前、max pooling后分别加上了1x1的卷积核,以起到了降低特征图厚度的作用,这也就形成了Inception v1的网络结构(右图)。

假设input feature map的size为 28 × 28 × 256 28\times28\times256 28×28×256,output feature map的size为 28 × 28 × 480 28\times28\times480 28×28×480,则native Inception Module的计算量有854M。计算过程如下

在这里插入图片描述

从上图可以看出,计算量主要来自高维卷积核的卷积操作,因而在每一个卷积前先使用 1 × 1 1\times1 1×1卷积核将输入图片的feature map维度先降低,进行信息压缩,在使用3x3卷积核进行特征提取运算,相同情况下,Inception v1的计算量仅为358M。

在这里插入图片描述

Inception结构总共有4个分支,输入的feature map并行的通过这四个分支得到四个输出,然后在在将这四个输出在深度维度(channel维度)进行拼接(concate)得到我们的最终输出(注意,为了让四个分支的输出能够在深度方向进行拼接,必须保证四个分支输出的特征矩阵高度和宽度都相同),因此inception结构的参数为:

  • branch1: C o n v 1 × 1 Conv 1\times1 Conv1×1, stride=1
  • branch2: C o n v 3 × 3 Conv 3\times3 Conv3×3, stride=1, padding=1
  • branch3: C o n v 5 × 5 Conv 5\times5 Conv5×5, stride=1, padding=2
  • branch4: M a x P o o l 3 × 3 MaxPool 3\times3 MaxPool3×3, stride=1, padding=1

GoogLeNet中使用了9个Inception v1 module,分别被命名为inception(3a)、inception(3b)、inception(4a)、inception(4b)、inception(4c)、inception(4d)、inception(4e)、inception(5a)、inception(5b)。

辅助分类器

GoogLeNet网络结构中有深层和浅层2个分类器,两个辅助分类器结构是一模一样的,其组成如下图所示,这两个辅助分类器的输入分别来自Inception(4a)和Inception(4d)。
在这里插入图片描述

辅助分类器的第一层是一个平均池化下采样层,池化核大小为5x5,stride=3;第二层是卷积层,卷积核大小为1x1,stride=1,卷积核个数是128;第三层是全连接层,节点个数是1024;第四层是全连接层,节点个数是1000(对应分类的类别个数)。

在模型训练时的损失函数按照: L o s s = L 0 + 0.3 ∗ L 1 + 0.3 ∗ L 2 Loss=L_0+0.3*L_1+0.3*L_2 Loss=L0+0.3L1+0.3L2 L 0 L_0 L0是最后的分类损失。在测试阶段则去掉辅助分类器,只记最终的分类损失。

3 GoogLeNet网络结构

在这里插入图片描述

每个卷积层的卷积核个数如何确定呢,下面是原论文中给出的参数列表,对于我们搭建的Inception模块,所需要使用到参数有#1x1, #3x3reduce, #3x3, #5x5reduce, #5x5, poolproj,这6个参数,分别对应着所使用的卷积核个数。
在这里插入图片描述注:上表中的“#3x3 reduce”,“#5x5 reduce”表示在3x3,5x5卷积操作之前使用了1x1卷积的数量。

4 Pytorch模型搭建代码

根据GoogLeNet网络结构图和配置表格,利用Pytorch可以搭建模型代码。
注:本代码参考了Pytorch官方实现的GooLeNet,其实现中:由于LRN层对训练结果影响不大,故代码中去除了LRN层;为了方便修改输出分类类别及迁移学习,softmax层前依然采用了全连接层。

import torch
import torch.nn as nn
import torch.nn.functional as F


class GoogLeNet(nn.Module):
    def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):
        super().__init__()
        self.aux_logits = aux_logits

        self.conv1 = BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3)
        self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True)
        self.conv2 = BasicConv2d(64, 64, kernel_size=1)
        self.conv3 = BasicConv2d(64, 192, kernel_size=3, padding=1)
        self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True)

        self.inception3a = Inception(192, 64, 96, 128, 16, 32, 32) 
        self.inception3b = Inception(256, 128, 128, 192, 32, 96, 64)
        self.pool3 = nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True)

        self.inception4a = Inception(480, 192, 96, 208, 16, 48, 64) 
        self.inception4b = Inception(512, 160, 112, 224, 24, 64, 64)
        self.inception4c = Inception(512, 128, 128, 256, 24, 64, 64)
        self.inception4d = Inception(512, 112, 144, 288, 32, 64, 64) 
        self.inception4e = Inception(528, 256, 160, 320, 32, 128, 128)
        self.pool4 = nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True) 

        self.inception5a = Inception(832, 256, 160, 320, 32, 128, 128)
        self.inception5b = Inception(832, 384, 192, 384, 48, 128, 128)

        if aux_logits:
            self.aux1 = InceptionAux(512, num_classes)
            self.aux2 = InceptionAux(528, num_classes)

        self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))
        self.dropout = nn.Dropout(p=0.2)
        self.fc = nn.Linear(in_features=1024, out_features=num_classes)

        if init_weights:
            self._init_weights()

    def forward(self, x):
        x = self.conv1(x)  # [None, 3, 224, 224] -> [None, 64, 112, 112]
        x = self.pool1(x)  # [None, 64, 112, 112] -> [None, 64, 56, 56]
        x = self.conv2(x)
        x = self.conv3(x)  # [None, 64, 112, 112] -> [None, 192, 56, 56]
        x = self.pool2(x)  # [None, 192, 56, 56] -> [None, 192, 28, 28]

        x = self.inception3a(x) # [None, 192, 28, 28] -> [None, 256, 28, 28]
        x = self.inception3b(x)  # [None, 256, 28, 28] -> [None, 480, 28, 28]
        x = self.pool3(x)  # [None, 480, 28, 28] -> [None, 480, 14, 14]
        x = self.inception4a(x) # [None, 480, 14, 14] -> [None, 512, 14, 14]
        if self.training and self.aux_logits:  # eval mode discards this layer
            aux1 = self.aux1(x)

        x = self.inception4b(x)
        x = self.inception4c(x)
        x = self.inception4d(x) # [None, 512, 14, 14] -> [None, 528, 14, 14]
        if self.training and self.aux_logits:
            aux2 = self.aux2(x)

        x = self.inception4e(x)  # [None, 528, 14, 14] -> [None, 832, 14, 14]
        x = self.pool4(x) # [None, 832, 14, 14] -> [None, 832, 7, 7]
        x = self.inception5a(x)
        x = self.inception5b(x)  # [None, 832, 7, 7] -> [None, 1024, 7, 7]

        x = self.avgpool(x)
        x = torch.flatten(x, start_dim=1)
        x = self.dropout(x)
        x = self.fc(x)
        if self.training and self.aux_logits:
            return x, aux2, aux1
        return x

    def _init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_uniform_(m.weight, mode='fan_out', nonlinearity='leaky_relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.weight, 0.01)
                nn.init.constant_(m.bias, 0)


class BasicConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, **kwargs):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)
        self.bn = nn.BatchNorm2d(num_features=out_channels, eps=0.001)

    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        return F.relu(x, inplace=True)


class Inception(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
        super().__init__()

        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)

        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)
        )

        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch5x5red, kernel_size=1),
            BasicConv2d(ch5x5red, ch5x5, kernel_size=5, padding=2)
        )

        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )

    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, dim=1)


class InceptionAux(nn.Module):
    def __init__(self, in_channels, num_classes):
        super().__init__()
        # self.avgpool = nn.AvgPool2d(kernel_size=5, stride=3)
        self.avgpool = nn.AdaptiveAvgPool2d(output_size=(4, 4))
        self.conv = BasicConv2d(in_channels, 128, kernel_size=1)  # output size [batch, 128, 4, 4]

        self.fc1 = nn.Linear(2048, 1024)
        self.fc2 = nn.Linear(1024, num_classes)

    def forward(self, x):
        # aux1: N x 512 x 14 x 14, aux2: N x 528 x 14 x 14
        x = self.avgpool(x)
        # aux1: N x 512 x 4 x 4, aux2: N x 528 x 4 x 4
        x = self.conv(x)
        # N x 128 x 4 x 4
        x = torch.flatten(x, start_dim=1)
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.fc1(x)
        x = F.relu(x, inplace=True)
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.fc2(x)
        return x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/338800
推荐阅读
相关标签
  

闽ICP备14008679号