经典卷积神经网络模型 – InceptionNet

  • Post author:
  • Post category:其他




卷积神经网络经典模型 – InceptionNet



综述与模型创新突破点

LeNet神经网络问世,标志着卷积神经网络的发展迈入一个新的里程碑。后续提出AlexNet,VggNet方法都是通过堆叠卷积-池化层的方法来提高模型预测的准确度,然而LeNet,AlexNet,VggNet模型的共同点除了是通过增大模型精度(以牺牲算力为代价来提高模型精度),同时在卷积层的角度,我们发现卷积核的尺寸是单一的,这不利于宏观细节特征相兼顾。因此我们提出InceptionNet网络结构,

通过设置多种卷积核尺寸来兼顾宏观细节特征的提取;


【创新点】:

  • 引入Inceotion结构,

    旨在融合不同尺度的特征信息
  • 使用

    1



    ×

    \times






    ×





    1

    的卷积核进行降维以及映射处理

  • 添加两个辅助分类器帮助训练,

    确保了即便是隐藏单元和中间层也参与了特征计算,在Inception网络中起到一种调整的效果并且能防止网络发生过拟合
  • 丢弃全连接层,使用平均池化层,以

    大大减少模型参数



模型结构



Inception模块


【设计思想】

  • 在论文中我们提出了Inception模块,其目的就是通过设置不同尺寸的卷积核提取从宏观到微观的特征,融合不同尺度的特征信息,如下两幅图是提出的两种Inception模块内部的两种基本结构,其两者的本质区别在于是否需要经过一个

    1



    ×

    \times






    ×





    1

    卷积,

    使用该卷积的目的是降维,以减少模型参数,降低模型的复杂度,防止过拟合

    ;

  • 将输入的原始数据放在不同的提取特征通道得到不同的特征层

    (注意:每个分支所得的特征矩阵高和宽必须相同)

    ,随后将特征图分别进行拼接

    在这里插入图片描述
  • 采用不同的提取特征通道站在模型训练的角度来看,有利于实现并行计算,有利于模型训练加速



GoogLeNet – InceptionNet模型的具体实现

GoogLeNet模型就是由若干个Inception模块堆叠构成的,但是值得注意的是,如图所示模型多出两个分支这两个分支就是

辅助分类器

,引入辅助分类器的方法目的就是调整效果防止网络发生过拟合

在这里插入图片描述



模型复现

我们基于Tensorflow2.0复现一个简单的InceptionNet网络来加深对这一个神经网络模型的工作原理的理解

在这里插入图片描述



复现构成Inception模块的子模块

## Inception模块由若不同尺寸的卷积核构成
class ConvBNRelu(Model):
    def __init__(self,channels,kernel_size=3,strides=1,padding='same'):
        super(ConvBNRelu, self).__init__()
        self.model = tf.keras.models.Sequential([
            Conv2D(channels,kernel_size,strides=strides,padding=padding),
            BatchNormalization(),
            Activation('relu')
        ])

    def call(self,x):
        x = self.model(x)
        return x



复现Inception模块

## 设置不同的卷积核(不同提取特征的分支),得到Inception模块
class InceptionBlock(Model):
    def __init__(self,channels,strides=1):
        super(InceptionBlock, self).__init__()
        self.channels = channels
        self.strides = strides

        ## 分支1
        self.c1 = ConvBNRelu(channels,kernel_size=1,strides=strides)

        ## 分支2
        self.c2_1 = ConvBNRelu(channels,kernel_size=1,strides=strides)
        self.c2_2 = ConvBNRelu(channels,kernel_size=3,strides=1)

        ## 分支3
        self.c3_1 = ConvBNRelu(channels,kernel_size=1,strides=strides)
        self.c3_2 = ConvBNRelu(channels,kernel_size=5,strides=1)

        ## 分支4
        self.p4_1 = MaxPool2D(3,strides=1,padding='same')
        self.c4_2 = ConvBNRelu(channels,kernel_size=1,strides=strides)

    def call(self,x):
        x1 = self.c1(x)
        x2_1 = self.c2_1(x)
        x2_2 = self.c2_2(x2_1)
        x3_1 = self.c3_1(x)
        x3_2 = self.c3_2(x3_1)
        x4_1 = self.p4_1(x)
        x4_2 = self.c4_2(x4_1)

        # 将四个分支按照深度的顺序拼接起来
        x = tf.concat([x1,x2_2,x3_2,x4_2],axis=3)
        return x



搭建InceptionNet模型

## 将模型封装在一个类中
class InceptionNet(Model):
    def __init__(self,num_blocks,num_classes,init_channels=16,**kwargs):
        super(InceptionNet, self).__init__(**kwargs)
        self.in_channels = init_channels
        self.out_channels = init_channels
        self.num_blocks = num_blocks
        self.init_channels = init_channels

		## 由若干个Inception模块堆叠构成的
        self.c1 = ConvBNRelu(init_channels)
        self.blocks = tf.keras.models.Sequential()
        for block_id in range(num_blocks):
            for layer_id in range(2):
                if layer_id == 0:
                    block = InceptionBlock(self.out_channels, strides=2)
                else:
                    block = InceptionBlock(self.out_channels, strides=1)
                self.blocks.add(block)
            # 经过一个Inception Block 输出通道数会翻4倍
            self.out_channels *= 2 # 出于计算效率每次乘2,即在二进制中向左移一位
        self.p1 = GlobalMaxPool2D()
        self.f1 = Dense(num_classes,activation='softmax')


    def call(self,x):
        x = self.c1(x)
        x = self.blocks(x)
        x = self.p1(x)
        y = self.f1(x)
        return y



版权声明:本文为weixin_44944722原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。