EffiecientNetV2架构复现--CVPR2021

简介: 这篇文章介绍了EfficientNetV2,与以前的模型相比,它具有更快的训练速度和更好的参数效率。为了开发这些模型,我们结合使用训练感知神经架构搜索和缩放,共同优化训练速度和参数效率。这些模型是从富含新操作(如 Fused-MBConv)的搜索空间中搜索的。我们的实验表明,EfficientNetV2 模型的训练速度比最先进的模型快得多,同时体积缩小了 6.8 倍。
参考论文:EfficientNetV2: Smaller Models and Faster Training

Mingxing Tan, Quoc V. Le

1、EfficientNetV2简介

image-20220902212349012

  图 1. ImageNet ILSVRC2012 top-1 准确度与训练时间和参数的关系——标记为 21k 的模型在 ImageNet21k 上进行了预训练,而其他模型则直接在 ImageNet ILSVRC2012 上进行了训练。训练时间是用 32 个 TPU 核心测量的。所有 EfficientNetV2 模型都经过渐进式学习的训练。我们的 EfficientNetV2 的训练速度比其他方法快 5 到 11 倍,同时使用的参数最多可减少 6.8 倍。

  这篇文章介绍了EfficientNetV2,与以前的模型相比,它具有更快的训练速度和更好的参数效率。为了开发这些模型,我们结合使用训练感知神经架构搜索和缩放,共同优化训练速度和参数效率。这些模型是从富含新操作(如 Fused-MBConv)的搜索空间中搜索的。我们的实验表明,EfficientNetV2 模型的训练速度比最先进的模型快得多,同时体积缩小了 6.8 倍。

  通过渐进式学习,我们的 EfficientNetV2 在 ImageNet 和 CIFAR/Cars/Flowers 数据集上显着优于以前的模型。通过在相同的 ImageNet21k 上进行预训练,我们的 EfficientNetV2 在 ImageNet ILSVRC2012 上实现了 87.3% 的 top-1 准确度,比最近的 ViT 提高了 2.0%,同时使用相同的计算资源训练速度提高了 5x-11x。

2、EfficientNet存在的问题

  • 非常大的图像尺寸的训练速度很慢。
  • Depthwise Convolution在浅层网络中速度很慢。
  • 同等地放大每个stage是次优的。

3、渐进式学习的改进方法

  在早期训练阶段,我们训练图像尺寸小、正则化弱(例如dropout和数据增强)的网络,然后逐渐增加图像尺寸并添加更强的正则化。基于渐进式调整大小,但通过动态调整正则化,我们的方法可以加快训练速度而不会导致准确率的下降。

4、EfficientNetV2与EfficientNet的区别

  • EfficientNetV2在浅层网络中广泛使用MBConv和Fused-MBConv。
  • EfficientNetV2更偏向MBConv的较小的expansion rario,因为较小的expansion ratio往往具有较小的内存访问开销。
  • EfficientNetV2更偏向更小的3*3卷积核,但它增加了更多的层来补偿由于小卷积核导致的感受野减少。
  • EfficientNetV2完全移除了原始EfficientNet中的最后一个stride-1阶段,这可能是由于其较大的参数大小和内存访问开销。

5、MBConv和Fused-MBConv

image-20220902220415261

  将 MBConv (Sandler et al., 2018; Tan & Le, 2019a) 中的 depthwise conv3x3 和 expansion conv1x1 替换为单个常规 conv3x3,如图 2 所示。

image-20220903085817604

  表 3. 用 Fused-MBConv 替换 MBConv。 No fused表示所有stage都使用MBConv,Fused stage1-3表示在stage {2,3,4}中用Fused-MBConv替换MBConv。

6、EfficientNetV2架构

  这部分参考:https://blog.csdn.net/qq_37541097/article/details/116933569

image-20220903085956200

  表 4. EfficientNetV2-S 架构——MBConv 和 FusedMBConv 块在图 2 中进行了描述。

6.1 Fused-MBConv

  Fused-MBConv模块名称后跟的1,4表示expansion ratio,k3x3表示kenel_size为3x3,下面是我参考大佬的结构图,注意当expansion ratio等于1时是没有expand conv的,还有这里是没有使用到SE结构的(原论文图中有SE)。

  注意当stride=1且输入输出Channels相等时才有shortcut连接

  还需要注意的是,当有shortcut连接时才有Dropout层,而且这里的Dropout层是Stochastic Depth,即会随机丢掉整个block的主分支(只剩捷径分支,相当于直接跳过了这个block)也可以理解为减少了网络的深度。具体可参考Deep Networks with Stochastic Depth这篇文章。

Fused-MBConv

6.2 MBConv模块

  MBConv模块**和EfficientNetV1中是一样的,其中模块名称后跟的4,6表示expansion ratio,

  SE0.25表示使用了SE模块,0.25表示SE模块中第一个全连接层的节点个数是输入该MBConv模块特征矩阵channels的 $\frac{1}{4}$ ,下面是大佬重绘的MBConv模块结构图。

  注意当stride=1且输入输出Channels相等时才有shortcut连接。同样这里的Dropout层是Stochastic Depth。
MBConv

   Stride就是步距,注意每个Stage中会重复堆叠Operator模块多次,只有第一个Opertator模块的步距是按照表格中Stride来设置的,其他的默认都是1。

  #Channels表示该Stage输出的特征矩阵的Channels,

  #Layers表示该Stage重复堆叠Operator的次数。

7、代码复现

这部分参考:

https://github.com/keras-team/keras/blob/07e13740fd181fc3ddec7d9a594d8a08666645f6/keras/applications/efficientnet_v2.py#L816

https://blog.csdn.net/dgvv4/article/details/123598847

https://blog.csdn.net/qq_37541097/article/details/116933569

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model, layers

7.1 swish激活函数

def swish(x):
    x = x *layers.Activation('sigmoid')(x)
    return x

7.2 标准卷积模块

def conv_block(inputs, filters, kernel_size, stride, activation=True):
    # 卷积+BN+激活
    x = layers.Conv2D(filters=filters,
                      kernel_size=kernel_size,
                      strides=stride,
                      padding='same',
                      use_bias=False)(inputs)

    x = layers.BatchNormalization()(x)

    if activation:  # 如果activation==True就使用激活函数
        x = swish(x)

    return x

7.3 SENet注意力机制模块

def se_block(inputs, in_channel, ratio=0.25):
    '''
    inputs: 深度卷积层的输出特征图
    input_channel: MBConv模块的输入特征图的通道数
    ratio: 第一个全连接层的通道数下降为MBConv输入特征图的几倍
    '''
    squeeze = int(in_channel * ratio)  # 第一个FC降低通道数个数
    excitation = inputs.shape[-1]  # 第二个FC上升通道数个数

    # 全局平均池化 [h,w,c]==>[None,c]
    x = layers.GlobalAveragePooling2D()(inputs)

    # [None,c]==>[1,1,c]
    x = layers.Reshape(target_shape=(1, 1, x.shape[-1]))(x)

    # [1,1,c]==>[1,1,c/4]
    x = layers.Conv2D(filters=squeeze,  # 通道数下降1/4
                      kernel_size=(1, 1),
                      strides=1,
                      padding='same')(x)

    x = swish(x)  # swish激活

    # [1,1,c/4]==>[1,1,c]
    x = layers.Conv2D(filters=excitation,  # 通道数上升至原来
                      kernel_size=(1, 1),
                      strides=1,
                      padding='same')(x)

    x = tf.nn.sigmoid(x)  # sigmoid激活,权重归一化

    # [h,w,c] * [1,1,c] ==> [h,w,c]
    outputs = layers.multiply([inputs, x])

    return outputs

7.4 MBConvm模块

def MBConv(x, expansion, kernel_size, stride, out_channel, dropout_rate):
    '''
    expansion: 第一个卷积层特征图通道数上升的倍数
    kernel_size: 深度卷积层的卷积核size
    stride: 深度卷积层的步长
    out_channel: 第二个卷积层下降的通道数
    dropout_rate: Dropout层随机丢弃输出层的概率,直接将输入接到输出
    '''
    # 残差边
    residual = x

    # 输入特征图的通道数
    in_channel = x.shape[-1]

    #  1*1标准卷积升维
    x = conv_block(inputs=x,
                   filters=in_channel * expansion,  # 上升通道数为expansion倍
                   kernel_size=(1, 1),
                   stride=1,
                   activation=True)

    #  3*3深度卷积
    x = layers.DepthwiseConv2D(kernel_size=kernel_size,
                               strides=stride,
                               padding='same',
                               use_bias=False)(x)
    x = layers.BatchNormalization()(x)

    x = swish(x)

    #  SE注意力机制,输入特征图x,和MBConv模块输入图像的通道数
    x = se_block(inputs=x, in_channel=in_channel)

    #  1*1标准卷积降维,使用线性激活
    x = conv_block(inputs=x,
                   filters=out_channel,  # 上升通道数
                   kernel_size=(1, 1),
                   stride=1,
                   activation=False)  # 不使用swish激活

    # ⑥ 只有步长=1且输入等于输出shape,才使用残差连接输入和输出
    if stride == 1 and residual.shape == x.shape:

        # 判断是否进行dropout操作
        if dropout_rate > 0:
            # 参数noise_shape一定的概率将某一层的输出丢弃
            x = layers.Dropout(rate=dropout_rate,  # 丢弃概率
                               noise_shape=(None, 1, 1, 1))

        # 残差连接输入和输出
        x = layers.Add([residual, x])

        return x

    # 如果步长=2,直接输出1*1卷积降维后的结果
    return x

7.5 Fused_MBConv模块

def Fused_MBConv(x, expansion, kernel_size, stride, out_channel, dropout_rate):
    # 残差边
    residual = x

    # 输入特征图的通道数
    in_channel = x.shape[-1]

    #  如果通道扩展倍数expansion==1,就不需要升维
    if expansion != 1:
        # 3*3标准卷积升维
        x = conv_block(inputs=x,
                       filters=in_channel * expansion,  # 通道数上升为原来的expansion倍
                       kernel_size=kernel_size,
                       stride=stride)

    #  判断卷积的类型
    # 如果expansion==1,变成3*3卷积+BN+激活;
    # 如果expansion!=1,变成1*1卷积+BN,步长为1
    x = conv_block(inputs=x,
                   filters=out_channel,  # FusedMBConv模块输出特征图通道数
                   kernel_size=(1, 1) if expansion != 1 else kernel_size,
                   stride=1 if expansion != 1 else stride,
                   activation=False if expansion != 1 else True)

    #  当步长=1且输入输出shape相同时残差连接
    if stride == 1 and residual.shape == x.shape:

        # 判断是否使用Dropout层
        if dropout_rate > 0:
            x = layers.Dropout(rate=dropout_rate,  # 随机丢弃输出层的概率
                               noise_shape=(None, 1, 1, 1))  # 代表不是杀死神经元,是丢弃输出层

        # 残差连接输入和输出
        outputs = layers.Add([residual, x])

        return outputs

    # 若步长等于2,直接输出卷积层输出结果
    return x

7.6 堆叠MBConv和Fused-MBConv

#每个模块重复执行num次
# Fused_MBConv模块
def Fused_stage(x, num, expansion, kernel_size, stride, out_channel, dropout_rate):
    for _ in range(num):
        # 传入参数,反复调用Fused_MBConv模块
        x = Fused_MBConv(x, expansion, kernel_size, stride, out_channel, dropout_rate)

    return x


# MBConv模块
def stage(x, num, expansion, kernel_size, stride, out_channel, dropout_rate):
    for _ in range(num):
        # 反复执行MBConv模块
        x = MBConv(x, expansion, kernel_size, stride, out_channel, dropout_rate)

    return x

7.7 搭建EfficientNetV2-S网络结构

def efficientnetv2(input_shape, classes, dropout_rate):
    # 构造输入层
    inputs = keras.Input(shape=input_shape)

    # 标准卷积层[224,224,3]==>[112,112,24]
    x = conv_block(inputs, filters=24, kernel_size=(3, 3), stride=2)

    # [112,112,24]==>[112,112,24]
    x = Fused_stage(x, num=2, expansion=1, kernel_size=(3, 3),
                    stride=1, out_channel=24, dropout_rate=dropout_rate)

    # [112,112,24]==>[56,56,48]
    x = Fused_stage(x, num=4, expansion=4, kernel_size=(3, 3),
                    stride=2, out_channel=48, dropout_rate=dropout_rate)

    # [56,56,48]==>[32,32,64]
    x = Fused_stage(x, num=4, expansion=4, kernel_size=(3, 3),
                    stride=2, out_channel=64, dropout_rate=dropout_rate)

    # [32,32,64]==>[16,16,128]
    x = stage(x, num=6, expansion=4, kernel_size=(3, 3),
              stride=2, out_channel=128, dropout_rate=dropout_rate)

    # [16,16,128]==>[16,16,160]
    x = stage(x, num=9, expansion=6, kernel_size=(3, 3),
              stride=1, out_channel=160, dropout_rate=dropout_rate)

    # [16,16,160]==>[8,8,256]
    x = stage(x, num=15, expansion=6, kernel_size=(3, 3),
              stride=2, out_channel=256, dropout_rate=dropout_rate)

    # [8,8,256]==>[8,8,1280]
    x = conv_block(x, filters=1280, kernel_size=(1, 1), stride=1)

    # [8,8,1280]==>[None,1280]
    x = layers.GlobalAveragePooling2D()(x)

    # dropout层随机杀死神经元
    if dropout_rate > 0:
        x = layers.Dropout(rate=dropout_rate)

        # [None,1280]==>[None,classes]
    logits = layers.Dense(classes)(x)

    # 构建网络
    model = Model(inputs, logits)

    return model
注意,这里最后的全连接层暂时每加softmax激活函数,根据个人情况吧, compile的时候加也可以。
if __name__ == '__main__':
    model = efficientnetv2(input_shape=[224, 224, 3],classes=1000,dropout_rate=0)
    # 查看模型摘要
    model.summary()

image-20220903094255397

  我去看源码的时候,发现Tensorflow的2.9版本实现了这个模型,所以版本高的可以直接去调用API,用迁移学习的方法训练自己的数据集(自己实现会忽略掉很多细节,这里的代码也是才考别人实现的,源码中那种代码风格感觉不太优雅)。

7.8 模型结构大图(长图)

image-20220903095453586

其中,image-20220903095517412这个地方是swish激活函数的实现。

References

EfficientNetV2: Smaller Models and Faster Training

10.1 EfficientNetV2网络详解

10.3 使用tensorflow搭建EfficientNetV2并基于迁移学习训练

Tensorflow2.9官方源码实现

EfficientNetV2网络详解

目录
相关文章
|
机器学习/深度学习 编解码 BI
RegNet架构复现--CVPR2020
在这项工作中,我们**提出了一种新的网络设计范式**。我们的目标是帮助促进对网络设计的理解,并发现跨环境通用的设计原则。我们不是专注于设计单个网络实例,而是设计参数化网络群体的网络设计空间。整个过程类似于经典的网络手动设计,但提升到了设计空间级别。使用我们的方法,我们探索了网络设计的结构方面,并**得出了一个由简单、规则的网络组成的低维设计空间,我们称之为** ==RegNet==。
1326 0
RegNet架构复现--CVPR2020
|
机器学习/深度学习 编解码 TensorFlow
MobileNetV3架构解析与代码复现
MobileNet模型基于深度可分离卷积,这是一种分解卷积的形式,将标准卷积分解为深度卷积和`1*1`的点卷积。对于MobileNet,深度卷积将单个滤波器应用于每个输入通道,然后,逐点卷积应用`1*1`卷积将输出与深度卷积相结合。
1058 0
MobileNetV3架构解析与代码复现
|
机器学习/深度学习 编解码 TensorFlow
MnasNet架构解析与复现-神经架构搜索
为移动设备设计卷积神经网络 (CNN) 具有挑战性,因为移动模型需要小而快,但仍要准确。尽管在所有维度上都致力于设计和改进移动 CNN,但当需要考虑如此多的架构可能性时,很难手动平衡这些权衡。在本文中,我们提出了一种**自动移动神经架构搜索 (MNAS) 方法**,该方法明确地将模型延迟纳入主要目标,以便搜索可以识别出在准确性和延迟之间取得良好折衷的模型。与之前的工作不同,延迟是通过另一个通常不准确的代理(例如 FLOPS)来考虑的,我们的方法通过在手机上执行模型来直接测量现实世界的推理延迟。为了进一步在灵活性和搜索空间大小之间取得适当的平衡,我们**提出了一种新颖的分解层次搜索空间,它鼓励整
541 0
MnasNet架构解析与复现-神经架构搜索
|
机器学习/深度学习 TensorFlow 算法框架/工具
GhostNet架构复现--CVPR2020
由于内存和计算资源有限,在嵌入式设备上部署卷积神经网络 (CNN) 很困难。特征图中的冗余是那些成功的 CNN 的一个重要特征,但在神经架构设计中很少被研究。**本文提出了一种新颖的 Ghost 模块,可以从廉价的操作中生成更多的特征图。基于一组内在特征图,我们应用一系列成本低廉的线性变换来生成许多ghost特征图,这些特征图可以充分揭示内在特征的信息。所提出的 Ghost 模块可以作为一个即插即用的组件来升级现有的卷积神经网络。 Ghost 瓶颈旨在堆叠 Ghost 模块,然后可以轻松建立轻量级的 GhostNet。**
224 0
GhostNet架构复现--CVPR2020
|
编解码 数据挖掘 算法框架/工具
ResNet-RS架构复现--CVPR2021
我们的工作重新审视了规范的 ResNet (He et al., 2015),并研究了这三个方面,以试图解开它们。也许令人惊讶的是,我们发现训练和扩展策略可能比架构变化更重要,而且由此产生的 ResNet 与最近最先进的模型相匹配。**我们展示了表现最佳的缩放策略取决于训练方案,并提供了两种新的缩放策略:(1)在可能发生过度拟合的情况下缩放模型深度(否则宽度缩放更可取); (2) 提高图像分辨率的速度比之前推荐的要慢(Tan & Le,2019)**。使用改进的训练和扩展策略,我们设计了一系列 ResNet 架构 **ResNet-RS**,它比 TPU 上的 EfficientNets 快
478 0
ResNet-RS架构复现--CVPR2021
|
8天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
5天前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;
|
7天前
|
缓存 监控 API
探索微服务架构中的API网关模式
随着微服务架构的兴起,API网关成为管理和服务间交互的关键组件。本文通过在线零售公司的案例,探讨了API网关在路由管理、认证授权、限流缓存、日志监控和协议转换等方面的优势,并详细介绍了使用Kong实现API网关的具体步骤。
24 3
|
8天前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
44 4
|
7天前
|
存储 缓存 监控
探索微服务架构中的API网关模式
探索微服务架构中的API网关模式
24 2
下一篇
无影云桌面