Xception网络结构

活动地址:CSDN21天学习挑战赛

1、从 Inception 到 Xception

Xception 是 Google 在 2017 年出品的轻量级神经网络,它与 GoogLeNet 中的 Inception 相似,可以认为是 Inception 的一种极端情况。同时,它与 MobileNet 的思想一致,即推动 Depthwise Conv + Pointwise Conv 的使用。另外,在 Xception 中,类似于 ResNet,一些 residual connects 被应用了进来。
最终模型在ImageNet等数据集上都取得了相比Inception v3与Resnet-152更好的结果。当然其模型大小与计算效率相对Inception v3也取得了较大提高。

Figure1是一个典型的 Inception 模块。

我们知道,卷积层的功能是同时学习跨通道相关性和空间相关性,而 Inception 的思想是尝试将这两个相关性的学习分割开来,即:

  • 先用 1x1 conv来着重学习各通道之间的关联,
  • 再用 3x3/5x5 conv (两个 3x3 conv 即为 5x5 conv)来学习其不同维度上的单个通道内在空间上的关联(也会学到部分各通道之间的关联)。

考虑一个 Inception 模块的简化版本,只使用一种规格的卷积(例如 3×3 conv),并且不含平均池化。如Figure2所示

在这里插入图片描述
而本质上上图2中表示的简化版Inception模块又可被表示为下图3中的形式。可以看出实质上它等价于先使用一个1x1 conv来学习input feature maps之上channels间特征的关联关系,然后再将1x1 conv输出的feature maps进行分割,分别交由下面的若干个3x3 conv来处理其内的空间上元素的关联关系。如下图所示

等价的简化版Inception模块
更进一步,何不做事做绝将每个channel上的空间关联分别使用一个相应的conv 3x3来单独处理呢。如此就得到了下图4中所示的Separable conv。

在这里插入图片描述

2、MobileNet vs Xception

MobileNet 和 Xception 之间有相同的地方也有不同的地方,主要体现在:

  • 相同:都由 channel-wise 空间卷积和 1x1 卷积两个操作组成。

  • 不同:

    • 操作顺序不同:MobileNet 先进行 channel-wise 空间卷积,然后使用 1x1 卷积进行融合;Xception 先进行 1x1 卷积,然后进行 channel-wise 空间卷积。
    • 非线性激励函数:MobileNet 中的两个操作之间添加了 ReLU 非线性激励;而为了保证数据不被破坏,Xception 中的两个操作之间没有激励函数。

上述两个不同点中,第一个并不是太重要,因为这些操作会被堆叠起来,从而改变一下结构中的模块划分顺序,两者基本上就等价了。而对于第二点就比较重要了。

3、Xception架构

Xception 结构由 36 个卷积层组成网络的特征提取基础。这些卷积层被分成 14 个模块,除最后一个外,模块间有线性残差连接。

另外,Xception 引入了 Entry/Middle/Exit 三个 flow,每个 flow 内部使用不同的重复模块,Entry flow主要是用来不断下采样,减小空间维度;中间则是不断学习关联关系,优化特征;最终则是汇总、整理特征,用于交由全连接层来进行表达。

在这里插入图片描述

4、实验结果

下表为Xception与其它模型在Imagenet上分类精度的结果比较。

在这里插入图片描述

然后下表则为Xception与Inception v3在模型参数大小与计算速度上的比较。

在这里插入图片描述

5、深度可分离卷积

深度可分离卷积其实是一种可分解卷积操作(factorized convolutions)。其可以分解为两个更小的操作:depthwise convolution 和 pointwise convolution。

在这里插入图片描述

(1)标准卷积

在这里插入图片描述
输入一个 12 ∗ 12 ∗ 3 12*12*3 12123的一个输入特征图,经过 5 ∗ 5 ∗ 3 5*5*3 553的卷积核得到一个 8 ∗ 8 ∗ 1 8*8*1 881的输出特征图。如果此时有256个卷积核,将得到 8 ∗ 8 ∗ 256 8*8*256 88256的输出特征图。

(2)深度卷积

在这里插入图片描述
与标准卷积网络不一样的是,这里会将卷积核拆分为单通道形式,在不改变输入特征图像的深度情况下,对每一通道进行卷积操作,这样就得到了和输入特征图像通道数一致的输出特征图。如上图,输入 12 ∗ 12 ∗ 3 12*12*3 12123的特征图,经过 5 ∗ 5 ∗ 1 ∗ 3 5*5*1*3 5513的深度卷积之后,得到 8 ∗ 8 ∗ 3 8*8*3 883的输出特征图。输入和输出的维度是不变的3,这样就有一个问题,通道数太少,特征图的维度太少,能获得足够的有效信息吗?

(3)逐点卷积

逐点卷积就是 1 ∗ 1 1*1 11卷积,主要作用就是对特征图进行升维和降维,如下图:

在这里插入图片描述
在深度卷积的过程中,我们得到了 8 ∗ 8 ∗ 3 8*8*3 883的输出特征图,我们用256个 1 ∗ 1 ∗ 3 1*1*3 113的卷积核对输入特征图进行卷积操作,输出的特征图和标准的卷积操作一样都是 8 ∗ 8 ∗ 256 8*8*256 88256了。

标准卷积与深度可分离卷积的过程对比如下:

在这里插入图片描述

(4)为什么用深度可分离卷积?

深度可分离卷积可以实现更少的参数,更少的运算量。

6、SeparableConv2D() vs DepthwiseConv2D()

首先,我们要引入 Tensorflow2.0 中函数 tf.keras.layers.SeparableConv2D(filters, kernel_size, strides=(1, 1), padding=‘valid’) 用于构建 Separable conv。在实现 MobileNet 网络时,我们使用的函数是 tf.keras.layers.DepthwiseConv2D(kernel_size, strides=(1, 1), padding=‘valid’),这两个函数之间是包含与被包含的关系。

简单来说,SeparableConv2D() 是 DepthwiseConv2D() 的升级版。由上面的介绍我们可以知道,深度可分离卷积分为两步:

  • 第一步:depthwise convolution 是在每个通道上独自的进行空间卷积,图a
  • 第二步:pointwise convolution 是利用 1x1 卷积核组合前面 depthwise convolution 得到的特征,图b

在这里插入图片描述
而 DepthwiseConv2D() 实现了第一步, SeparableConv2D() 直接实现了两步。故 SeparableConv2D() 与 DepthwiseConv2D() 相比,需要输入的参数多了一个 pointwise convolution 时的滤波器数量。

7、代码实现

代码原地址:https://blog.csdn.net/qq_36758914/article/details/106899041

import tensorflow as tf

class conv_block(tf.keras.Model):
    def __init__(self, filters, kernel_size = (3,3), strides=(1,1)):
        super().__init__()
        self.listLayers = []
        self.listLayers.append(tf.keras.layers.Conv2D(filters=filters,
                                                      kernel_size=kernel_size,
                                                      strides=strides,     
                                                      padding='SAME'))
        self.listLayers.append(tf.keras.layers.BatchNormalization())
        self.listLayers.append(tf.keras.layers.Activation('relu'))
        
    def call(self, x):
        for layer in self.listLayers.layers:
            x = layer(x)
        return x

class separable_conv_block(tf.keras.Model):
    def __init__(self, filters):
        super().__init__()
        self.listLayers = []
        self.listLayers.append(tf.keras.layers.Activation('relu'))
        self.listLayers.append(tf.keras.layers.SeparableConv2D(filters,
                                                               kernel_size=(3,3),
                                                               strides=(1,1),
                                                               padding='SAME'))
        self.listLayers.append(tf.keras.layers.BatchNormalization())
        
    def call(self, x):
        for layer in self.listLayers.layers:
            x = layer(x)
        return x

def entry_flow(inputs):
    x = conv_block(filters=32, strides=(2, 2))(inputs)
    x = conv_block(filters=64)(x)
    previous_block_activation = x
    for size in [128, 256, 728]:
        x = separable_conv_block(size)(x)
        x = separable_conv_block(size)(x)
        x = tf.keras.layers.MaxPooling2D(3, strides=2, padding='SAME')(x)
        residual = tf.keras.layers.Conv2D(filters=size,
                                          kernel_size=1,
                                          strides=2,     
                                          padding='SAME')(previous_block_activation)
        x = tf.keras.layers.add([x, residual])
        previous_block_activation = x
        
    return x

def middle_flow(x, num_blocks=8):
    previous_block_activation = x
    
    for _ in range(num_blocks):
        for _ in range(3):
            x = separable_conv_block(728)(x)
            
        x = tf.keras.layers.add([x, previous_block_activation])
        previous_block_activation = x
            
    return x

def exit_flow(x, num_classes=1000):
    previous_block_activation = x
    
    for size in [728, 1024]:
        x = separable_conv_block(size)(x)
    
    x = tf.keras.layers.MaxPooling2D(3, strides=2, padding='SAME')(x)
    
    residual = tf.keras.layers.Conv2D(filters=1024,
                                      kernel_size=1,
                                      strides=2,     
                                      padding='SAME')(previous_block_activation)
    x = tf.keras.layers.add([x, residual])
    
    for size in [1536, 2048]:
        x = tf.keras.layers.SeparableConv2D(size,
                                            kernel_size=(3,3),
                                            strides=(1,1),
                                            padding='SAME')(x)
        x = tf.keras.layers.BatchNormalization()(x)
        x = tf.keras.layers.Activation('relu')(x)
        
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    x = tf.keras.layers.Dense(num_classes, activation='softmax')(x)
    
    return x

def xception(inputs):
    x = entry_flow(inputs)
    x = middle_flow(x, num_blocks=8)
    x = exit_flow(x, num_classes=1000)
    
    return x

inputs = np.zeros((1, 299, 299, 3), dtype=np.float32)
outputs = xception(inputs)
outputs.shape

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值