课程学习 CV 北京邮电大学 鲁鹏(笔记五:CV经典网络讲解 之 GoogLeNet)

GoogLeNet

GoogLeNet论文:Going Deeper with Convolutions
在这里插入图片描述

Inception单元结构

Inception 最初版本的主要思想是利用不同大小的卷积核实现不同尺度的感知,网络结构图如下:
在这里插入图片描述
Inception Module基本组成结构有四个成分。11卷积,33卷积,55卷积,33最大池化。最后对四个成分运算结果进行通道上组合,这就是Naive Inception的核心思想:利用不同大小的卷积核实现不同尺度的感知,最后进行融合,可以得到图像更好的表征

但是Naive Inception有两个问题:
(1)所有卷积层直接和前一层输入的数据对接,所以卷积层中的计算量会很大;
(2)在这个单元中使用的最大池化层保留了输入数据的特征图的深度,所以在最后进行合并时,总的输出的特征图的深度只会增加,这样增加了该单元之后的网络结构的计算量。
于是人们就要想办法减少参数量来减少计算量,在受到了模型 “Network in Network”的启发,开发出了在GoogleNet模型中使用的Inception单元(Inception V1),这种方法可以看做是一个额外的1*1卷积层再加上一个ReLU层。如下所示:
在这里插入图片描述
这里使用1x1 卷积核主要目的是进行压缩降维,减少参数量,从而让网络更深、更宽,更好的提取特征,这种思想也称为Pointwise Conv,简称PW。

因为1×1卷积层的加入,总的卷积参数数量已经大大低于之前的Naive Inception单元,而且因为在最大池化层之前也加入了1×1的卷积层,所以最终输出的特征图的深度也降低了,这样也降低了该单元之后的网络结构的计算量。
GoogLeNet层数更深,参数更少,计算效率更高、非线性表达能力更强。

GoogleNet网络结构(Inception V1)的网络结构:
由于全连接网络参数多,计算量大,容易过拟合,所以GoogLeNet没有采用VGG、LeNet、AlexNet三层全连接结构,直接在Inception模块之后使用Average Pool和Dropout方法,不仅起到降维作用,还在一定程度上防止过拟合。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
GoogLeNet每层使用的激活函数为ReLU激活函数。

辅助分类器
根据实验数据,发现神经网络的中间层也具有很强的识别能力,为了利用中间层抽象的特征,在某些中间层中添加含有多层的分类器。下图红色边框内部是GoogLeNet添加的辅助分类器。GoogLeNet中共增加了两个辅助的softmax分支,作用有两点,一是为了避免梯度消失,用于向前传导梯度。二是将中间某一层输出用作分类,起到模型融合作用
在这里插入图片描述

GoogLeNet其他版本

该部分内容参考自CSDN博主「雷恩Layne」的原创文章。

Inception V2

  1. 学习VGGNet的特点,用两个33卷积代替55卷积,可以降低参数量。
  2. 提出BN算法Batch Normalization。BN算法是一个正则化方法,可以提高大网络的收敛速度。就是每一batch的输入分布标准化处理,使得规范化为N(0,1)的高斯分布,收敛速度大大提高。(详情可看博客:批量归一化Batch Normalization详解
    在这里插入图片描述

Inception V3

学习Factorization into small convolutions的思想,在Inception V2的基础上,将一个二维卷积拆分成两个较小卷积。这样做的好处是降低参数量。

通过这种非对称的卷积拆分比对称的拆分为几个相同的小卷积效果更好,可以处理更多,更丰富的空间特征。下图是Inception V3的网络结构:
在这里插入图片描述
————————————————
再次声明:以上内容总结自博主「雷恩Layne」的文章

Inception的实现

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)
        # 线路21 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)
        # 线路31 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)
        # 线路43 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)  # 在通道维上连结输出

GlobalAvgPool2d

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:])

FlattenLayer

class FlattenLayer(torch.nn.Module):
    def __init__(self):
        super(FlattenLayer, self).__init__()

    def forward(self, x):  # x shape: (batch, *, *, ...)
        return x.view(x.shape[0], -1)

GoogLeNet的实现

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

        self.b1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
                                nn.ReLU(),
                                nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

        self.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))

        self.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))

        self.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))

        self.b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),
                                Inception(832, 384, (192, 384), (48, 128), 128),
                                GlobalAvgPool2d())
        self.output = nn.Sequential(FlattenLayer(),
                                    nn.Dropout(p=0.4),
                                    nn.Linear(1024, 1000))

        def forward(self, x):
            x = self.b1(x)
            x = self.b2(x)
            x = self.b3(x)
            x = self.b4(x)
            x = self.b5(x)
            x = self.output(x)
            return x

猜你喜欢

转载自blog.csdn.net/weixin_44845357/article/details/120704605
今日推荐