深度学习模型之CNN(十九)EfficientNetV2网络详解

前言

本节课讲的是EfficientNet网络的升级版EfficientNetV2,这篇论文发表在2021年的CVPR,原论文:EfficientNetV2: Smaller Models and Faster Training

下图为EfficientNetV2网络和时下流行网络的性能对比,其中紫色的线对应着EfficientNetV2的S、M和L三个不同大小模型的准确率和训练速度,红色的线对应着EfficientNetV2首先在EfficientNet21K上的预训练,之后再ImageNet上进行迁移学习

EfficientNetV2网络和时下流行网络的性能对比

网络创新点

  • 引入Fused-MBConv模块;
  • 引入渐进式学习策略(学习更快)

在EfficientNetV1中作者关注的是准确率,参数数量以及FLOPS(理论计算量小不代表推理速度快),在EfficientNetV2中作者进一步关注模型的训练速度。

EfficientNetV2与EfficientNetV1性能对比

EfficientNetV1中存在的问题

1、训练图像的尺寸很大时,训练速度非常慢

针对这个问题一个比较好想到的办法就是降低训练图像的尺寸,之前也有一些文章这么干过。降低训练图像的尺寸不仅能够加快训练速度,还能使用更大的batch_size。

EfficientNetV1存在问题-图片尺寸

2、在网络浅层中使用Depthwise convolutions速度会很慢

无法充分利用现有的一些加速器(虽然理论上计算量很小,但实际使用起来并没有想象中那么快)。故引入Fused-MBConv结构。

下图左边分别为MBConv和Fused-MBConv结构,下图右展示了不使用Fused-MBConv结构、仅将Stage2~4中的MBConv替换为Fused-MBConv结构、仅将Stage2~6中的MBConv替换为Fused-MBConv结构和将Stage2~8的MBConv替换为Fused-MBConv结构。

在前二者替换效果来看,训练速度和准确率都有提高,但全部替换的形式导致准确率和训练速度都下降,由此可见,并不能无脑的将MBConv转化为Fused-MBConv

Fused-MBConv结构

3、同等的放大每个stage是次优的

在EfficientNetV1中每个stage的深度和宽度都是同等放大的。但每个stage对网络的训练速度以及参数数量的贡献并不相同,所以直接使用同等缩放的策略并不合理。在这篇文章中,作者采用了非均匀的缩放策略来缩放模型

Model input_size width_coefficient depth_coefficient dropout_connect_rate dropout_rate
EfficientNetB0 224x224 1.0 1.0 0.2 0.2
EfficientNetB1 240x240 1.0 1.1 0.2 0.2
EfficientNetB2 260x260 1.1 1.2 0.2 0.3
EfficientNetB3 300x300 1.2 1.4 0.2 0.3
EfficientNetB4 380x380 1.4 1.8 0.2 0.4
EfficientNetB5 456x456 1.6 2.2 0.2 0.4
EfficientNetB6 528x528 1.8 2.6 0.2 0.5
EfficientNetB7 600x600 2.0 3.1 0.2 0.5

EfficientNetV2中做出的贡献

在之前的一些研究中,主要关注的是准确率以及参数数量(注意,参数数量少并不代表推理速度更快)。但在近些年的研究中,开始关注网络的训练速度以及推理速度(可能是准确率刷不动了)。

  • 引入新的网络(EfficientNetV2),该网络在训练速度以及参数数量上都优于先前的一些网络;
  • 提出了改进的渐进学习方法,该方法会根据训练图像的尺寸动态调节正则方法(提升训练速度、准确率),其中方法有Dropout、Rand Augment、Mixup;
  • 通过实验与先前的一些网络相比,训练速度提升11倍,参数数量减少为1/6.8

EfficientNetV2训练参数和准确率对比

EfficientNetV2网络框架

注意,在源码中Stage6的输出Channels是等于256并不是表格中的272,Stage7的输出Channels是1280并不是表格中的1792,后续论文的版本会修正过来。

相比与EfficientNetV1,主要有以下不同:

  • 除了使用MBConv模块,还使用Fused-MBConv模块(主要是在网络浅层中使用);
  • 会使用较小的expansion ratioMBConv中第一个expand conv1x1或者Fused-MBConv中第一个expand conv3x3)比如4,在EfficientNetV1中基本都是6. 这样的好处是能够减少内存访问开销;
  • 偏向使用更小的kernel_size(3x3),在EfficientNetV1中使用了很多5x5的kernel_size。通过下表可以看到使用的kernel_size全是3x3的,由于3x3的感受野是要比5x5小的,所以需要堆叠更多的层结构以增加感受野;
  • 移除了EfficientNetV1中最后一个步距为1的stage (V1中的stage8)

Stride就是步距,注意每个Stage中会重复堆叠Operator模块多次,只有第一个Opertator模块的步距是按照表格中Stride来设置的,其他的默认都是1。 #Channels表示该Stage输出的特征矩阵的Channels,#Layers表示该Stage重复堆叠Operator的次数。

EfficientNetV2-S模型框架

Fused-MBConv模块

通过上表可以看到EfficientNetV2-S分为Stage0到Stage7(EfficientNetV1中是Stage1到Stage9)。Operator表示在当前Stage中使用的模块:

  • Conv3x3就是普通的3x3卷积 + 激活函数(SiLU)+ BN;

  • 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)也可以理解为减少了网络的深度。

Fused-MBConv模块

EfficientNet网络中的Dropout

EfficientNet网络中的Dropout与前期所有网络结构的Dropout不全一样,例如原始的Dropout参数丢弃比例为0.2,但EfficientNet中给出Dropout = 0.2的参数表示该网络在0~0.2的丢弃比例下逐渐失活。引用论文为:Deep Networks with Stochastic Depth

下图可以理解为正向传播过程中将输入的特征矩阵经历了一个又一个block,每一个block都可以认为是一个残差结构。例如主分支通过$f$函数进行输出,shortcut直接从输入引到输出,在此过程中,会以一定的概率来对主分支进行丢弃(直接放弃整个主分支,相当于直接将上一层的输出引入到下一层的输入,相当于没有这一层)。即Stochastic Depth(随机深度,指的是网络的depth,因为会随机丢弃任意一层block)。

下图中表示存活概率从1.0至0.5,一个渐变的过程。但在EfficientNetV2中采用drop_prob是0~0.2的丢弃比例(提升训练速度,小幅提升准确率)。

注意:这里的dropout层仅指Fused-MBConv模块以及MBConv模块中的dropout层,不包括最后全连接前的dropout层

正向传播过程-Dropout

MBConv模块

MBConv模块和EfficientNetV1中是一样的,其中模块名称后跟的4、6表示expansion ratio,SE0.25表示使用了SE模块,0.25表示SE模块中第一个全连接层的节点个数是输入该MBConv模块特征矩阵channels的1/4

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

MBConv模块

EfficientNetV2-S的详细参数

首先在官方的源码中有个baseline config注意这个不是V2-S的配置,在efficientnetv2 -> effnetv2_configs.py文件中 。

  • r代表当前Stage中Operator重复堆叠的次数;
  • k代表kernel_size;
  • s代表步距stride;
  • e代表expansion ratio;
  • i代表input channels;
  • o代表output channels;
  • c代表conv_type,1代表Fused-MBConv,0代表MBConv(默认为MBConv);
  • se代表使用SE模块,以及se_ratio
1
2
3
4
5
6
7
8
9
#################### EfficientNet V2 configs ####################
v2_base_block = [ # The baseline config for v2 models.
'r1_k3_s1_e1_i32_o16_c1',
'r2_k3_s2_e4_i16_o32_c1',
'r2_k3_s2_e4_i32_o48_c1',
'r3_k3_s2_e4_i48_o96_se0.25',
'r5_k3_s1_e6_i96_o112_se0.25',
'r8_k3_s2_e6_i112_o192_se0.25',
]

EfficientNetV2-S的配置是在baseline的基础上采用了width倍率因子1.4, depth倍率因子1.8得到的(这两个倍率因子是EfficientNetV1-B4中采用的)。

注意:只针对存在MBConv模块或Fused-MBConv模块的Stage,例如r2_k3_s1_e1_i24_o24_c1对应Stage1,Operator重复堆叠2次,kernel_size等于3,stride等于1,expansion等于1,input_channels等于24,output_channels等于24,conv_type为Fused-MBConv

1
2
3
4
5
6
7
8
v2_s_block = [  # about base * (width1.4, depth1.8)
'r2_k3_s1_e1_i24_o24_c1',
'r4_k3_s2_e4_i24_o48_c1',
'r4_k3_s2_e4_i48_o64_c1',
'r6_k3_s2_e4_i64_o128_se0.25',
'r9_k3_s1_e6_i128_o160_se0.25',
'r15_k3_s2_e6_i160_o256_se0.25',
]

EfficientNetV2-S模型框架

EfficientNetV2-M的详细参数

EfficientNetV2-M的配置是在baseline的基础上采用了width倍率因子1.6, depth倍率因子2.2得到的(这两个倍率因子是EfficientNetV1-B5中采用的)。

1
2
3
4
5
6
7
8
9
v2_m_block = [  # about base * (width1.6, depth2.2)
'r3_k3_s1_e1_i24_o24_c1',
'r5_k3_s2_e4_i24_o48_c1',
'r5_k3_s2_e4_i48_o80_c1',
'r7_k3_s2_e4_i80_o160_se0.25',
'r14_k3_s1_e6_i160_o176_se0.25',
'r18_k3_s2_e6_i176_o304_se0.25',
'r5_k3_s1_e6_i304_o512_se0.25',
]

通过配置文件可知Stage0的卷积核个数是24(i24

EfficientNetV2-L的详细参数

EfficientNetV2-L的配置是在baseline的基础上采用了width倍率因子2.0, depth倍率因子3.1得到的(这两个倍率因子是EfficientNetV1-B7中采用的)。

1
2
3
4
5
6
7
8
9
v2_l_block = [  # about base * (width2.0, depth3.1)
'r4_k3_s1_e1_i32_o32_c1',
'r7_k3_s2_e4_i32_o64_c1',
'r7_k3_s2_e4_i64_o96_c1',
'r10_k3_s2_e4_i96_o192_se0.25',
'r19_k3_s1_e6_i192_o224_se0.25',
'r25_k3_s2_e6_i224_o384_se0.25',
'r7_k3_s1_e6_i384_o640_se0.25',
]

通过配置文件可知Stage0的卷积核个数是32(i32

注意:EfficientNetV2-M和EfficientNetV2-L比EfficientNetV2-S多出一个Stage

EfficientNetV2其他训练参数

下面是源码中给出的关于efficientnetv2-sefficientnetv2-mefficientnetv2-l三个参数的配置信息。

其中的v2_s_blockv2_m_block以及v2_l_block就是上面刚刚讲到过的网络配置参数,剩下就关注下train_size, eval_size, dropout, randaug, mixup, aug即可。比如efficientnetv2-strain_size=300(注意实际训练中train_size是会变化的,但最大不超过300,但在验证过程中默认为384x383,后面讲Progressive Learning中会细讲),eval_size=384dropout=0.2(指的是最后一个Stage中池化层和全连接层之间的一个dropout_rate),randaug=10,mixup=0,aug='randaug'(针对迁移学习策略所使用到的参数)

1
2
3
4
5
6
7
8
efficientnetv2_params = {
# (block, width, depth, train_size, eval_size, dropout, randaug, mixup, aug)
'efficientnetv2-s': # 83.9% @ 22M
(v2_s_block, 1.0, 1.0, 300, 384, 0.2, 10, 0, 'randaug'),
'efficientnetv2-m': # 85.2% @ 54M
(v2_m_block, 1.0, 1.0, 384, 480, 0.3, 15, 0.2, 'randaug'),
'efficientnetv2-l': # 85.7% @ 120M
(v2_l_block, 1.0, 1.0, 384, 480, 0.4, 20, 0.5, 'randaug'),

Progressive Learning渐进学习策略

训练图像的尺寸对训练模型的效率有很大的影响。所以在之前的一些工作中很多人尝试使用动态的图像尺寸(比如一开始用很小的图像尺寸,后面再增大)来加速网络的训练,但通常会导致Accuracy降低。为什么会出现这种情况呢?

作者提出了一个猜想:Accuracy的降低是不平衡的正则化unbalanced regularization导致的。在训练不同尺寸的图像时,应该使用动态的正则方法(之前都是使用固定的正则方法)。

为了验证这个猜想,作者接着做了一些实验。训练模型过程中尝试使用不同的图像尺寸以及不同强度的数据增强data augmentations。当训练的图片尺寸较小时,使用较弱的数据增强augmentation能够达到更好的结果;当训练的图像尺寸较大时,使用更强的数据增强能够达到更好的接果。如下表所示,当Size=128RandAug magnitude=5时效果最好;当Size=300RandAug magnitude=15时效果最好:

![Progressive Learning渐进学习策略效果](EfficientNetV2网络详解/Progressive Learning渐进学习策略效果.png)

基于以上实验,作者就提出了渐进式训练策略Progressive Learning。如下图所示,在训练早期使用较小的训练尺寸以及较弱的正则方法weak regularization,这样网络能够快速的学习到一些简单的表达能力。接着逐渐提升图像尺寸,同时增强正则方法adding stronger regularization。这里所说的regularization包括dropout rateRandAugment magnitude以及mixup ratio

![Progressive Learning渐进学习策略实验](EfficientNetV2网络详解/Progressive Learning渐进学习策略实验.png)

下表给出了EfficientNetV2(S,M,L)三个模型的渐进学习策略参数:

EfficientNetV2三个模型的渐进学习策略参数

通过对比可以看出使用渐进式学习策略确实能够有效提升训练速度。为了进一步验证渐进式学习策略的有效性,作者还在Resnet以及EfficientNetV1上进行了测试,如下表所示,使用了渐进式学习策略后确实能够有效提升训练速度并且能够小幅提升Accuracy。

渐进学习策略在各网络模型中的性能对比