【论文笔记】轻量级网络MobileNet
【深度学习】总目录
MobileNet V1:《MobileNets: Efficient Convolutional Neural Networks for MobileVision Applications》
MobileNet V2:《MobileNetV2: Inverted Residuals and Linear Bottlenecks》
MobileNet V3:《Searching for MobileNetV3》
1 MobileNet V1
Motivation
自从2012年,AlexNet在ImageNet大赛上以远超第二名的成绩夺冠,卷积神经网络在计算机视觉中变得无处不在。为了实现更高的精度,网络越来越深越来越复杂。然而,这些提高准确性的进步并不一定会使网络在大小和速度方面更加高效。在机器人、自动驾驶汽车和增强现实等许多现实世界应用中,识别任务需要在计算有限的平台上及时执行。因此需要构建非常小、低延迟的模型,该模型可以轻松地与移动和嵌入式视觉应用的设计要求相匹配。
亮点:Depthwise Separable Convolution 深度可分离卷积
可分离卷积主要有两种类型:空间可分离卷积和深度可分离卷积。空间可分离就是将一个大的卷积核变成两个小的卷积核,比如将一个3×3的核分成一个3×1和一个1×3的核;而深度可分离卷积(depthwise separable convolution)将普通卷积拆分成为一个深度卷积和一个逐点卷积。标准卷积
- 卷积核参数量:DK×DK×M×N
- 计算量(只计算乘法):DK×DK×M×N×DF×DF
深度可分离卷积
深度可分离卷积分为两部分:Depthwise卷积和Pointwise卷积。
- Depthwise卷积对每个输入通道单独使用一个卷积核处理,计算量:DK×DK×M×DF×DF
- Pointwise卷积的卷积核尺寸为1 × 1,用于将depthwise卷积的输出组合起来,计算量:1×1×M×N×DF×DF
- 总计算量:(DK×DK+N)×M×DF×DF
标准卷积与深度可分离卷积的比较
通常MobileNet会使用卷积核为3 × 3的深度可分离卷积,上面这个式子的结果就接近于1/9,大约可以比普通卷积减少了8到9倍的计算量。
实验可以看出使用深度可分离卷积与标准卷积,参数和计算量能下降为后者的九分之一到八分之一左右,但是准确率只有下降极小的1%。
DW卷积的代码实现
3x3 Depthwise Conv —> BN —> ReLU —> 1x1 Conv —> BN —> ReLU
class Block(nn.Module): '''Depthwise conv + Pointwise conv''' def __init__(self,input_channels,output_channels,stride=1): super(Block,self).__init__() self.conv1 = nn.Conv2d\ (input_channels,input_channels,kernel_size=3,stride=stride, padding=1,groups=input_channels,bias=False) self.bn1 = nn.BatchNorm2d(input_channels) self.conv2=nn.Conv2d(input_channels,output_channels,kernel_size=1, stride=1,padding=0,bias=False) self.bn2=nn.BatchNorm2d(output_channels) def forward(self,x): x = F.relu(self.bn1(self.conv1(x))) x = F.relu(self.bn2(self.conv2(x))) return x
Depthwise conv不改变通道数,因此outout_channels = input_channels。相当于对卷积的通道数进行分组,然后对每组的特征图分别进行卷积,是组卷积(group convolution)的一种扩展,每组只有一个特征图。
2 MobileNet V2
MobileNet v2网络是由google团队在2018年提出的,相比MobileNet V1网络,准确率更高,模型更小。
MobieNet V2的Top1准确率为72.0,相对于MobieNet V1准确率为70.6,准确率提升了。同时,参数、运算量和运算时间都明显比MobieNet V1更加优秀。在cpu上MobieNet V2 运算时间只有75ms,基本上可以实现在移动设备上实时推理效果。当卷积核倍率因子为1.4时,MobieNet V2 TOP1准确率达到74.7,比论文中其他网络的准确率高。
亮点1: Linear Bottlenecks 线性瓶颈
假设2维空间有一组由n个点组成的螺旋线数据,经过随机矩阵T映射到m维,并进行ReLU运算,再通过T的逆矩阵将m维矩阵映射回2维空间。从下图可以看出,当映射维度dim=2,3时,数据坍塌;当dim>15时,数据基本被保存。虽然这不是严格的数学证明,但是至少说明:channel少的feature map不应后接ReLU,否则会破坏feature map。
发现当维度n很小的时候,后面接ReLU非线性变换的话会导致很多信息的丢失。为了减少信息丢失,本文提出了linear bottleneck,就是在bottlenck(瓶颈层,也就是1 × 1卷积)的最后输出不接非线性激活层,只做linear操作,如下图所示。
右图和左图在堆叠的时候是等效的,(线性激活后)pw升维-relu6-dw-relu6-pw降维-线性激活。
亮点2: Inverted residuals 反向残差
Inverted residuals借鉴resnet,都采用1x1卷积→3x3卷积→1x1卷积的模式,同时输入输出shortcut连接。不同的是,mobilenet的3x3卷积是dw卷积,且进入block后会先将特征维数放大,然后再压缩回去,呈现梭子的外形,而传统残差设计是沙漏形。
如下图所示,经过1x1卷积后,维度是之前的t倍。1x1卷积和dw卷积后都接relu,最后的1x1降维时,接线性变换。
bottleneck代码实现
class Bottleneck(nn.Module): def __init__(self,input_channels,output_channels,stride=1,expansion_ratio=1): super(Bottleneck,self).__init__() self.stride = stride temp_channels = input_channels*expansion_ratio self.conv1 = nn.Conv2d(input_channels,temp_channels,kernel_size=1,bias=False) self.bn1 = nn.BatchNorm2d(temp_channels) self.conv2 = nn.Conv2d(temp_channels,temp_channels,padding=1,groups=temp_channels,kernel_size=3,stride=stride,bias=False) self.conv3 = nn.Conv2d(temp_channels,output_channels,kernel_size=1,bias=False) self.bn2 = nn.BatchNorm2d(output_channels) if(stride==1 and input_channels!=output_channels): self.shortcut = nn.Conv2d(in_channels=input_channels,out_channels=output_channels,kernel_size=1) if(stride==1 and input_channels==output_channels): self.shortcut = nn.Sequential() def forward(self,x): output = F.relu(self.bn1(self.conv1(x))) output = F.relu(self.bn1(self.conv2(output))) output = self.bn2(self.conv3(output)) if(self.stride==1): output = output+self.shortcut(x) return output
Tips:
1. 在stride=2时,不需要将输入与输出shortcut
2. 输入和输出的维数不同,无法直接相加,可以用1×1卷积改变输入通道的维数。(在resnet中比较过三种短接方法)
3. 上面每一个卷积后都要加BN层(BN层添加在激活函数前,对输入激活函数的输入进行归一化。这样解决了输入数据发生偏移和增大的影响。
4. 关于nn.Conv2d的bias参数是false还是true?(卷积之后,如果要接BN操作,最好是不设置偏置,因为不起作用,而且占显卡内存。
3 MobileNet V3
MobileNetV3 参数是由NAS(network architecture search)搜索获取的,又继承的V1和V2的一些实用成果,并引人SE通道注意力机制,可谓集大成者。
- 可以看出V3版本的Large 1.0(V3-Large 1.0)的Top-1是75.2,对于V2 1.0 它的Top-1是72,相当于提升了3.2%
- 在推理速度方面也有一定的提升,(V3-Large 1.0)在P-1手机上推理时间为51ms,而V2是64ms,很明显V3比V2,不仅准确率更高了,而且速度更快了
- V3-Small版本的Top-1是 67.4,而V2 0.35 (0.35表示卷积核的倍率因子)的Top-1只有60.8,准确率提升了6.6%
亮点1:加入SE模块
《常用的注意力机制模块(SE、CBAM)》SE模块通过显式地建模通道之间的相互依赖关系,自适应地重新校准通道式的特征响应。具体来说,就是通过学习的方式来自动获取每个通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征。因为SE结构会消耗一定的时间,所以作者在含有SE的结构中,将expansion layer的channel变为原来的1/4,这样既提高了精度,同时还没有增加时间消耗。
亮点2:重新设计激活函数
在 MobileNetV2 都是使用 ReLU6 激活函数。现在比较常用的是 swish 激活函数,即 x 乘上 sigmoid 激活函数。使用 swish 激活函数确实能够提高网络的准确率,但是也有一些问题(1)计算复杂(2)对量化过程非常不友好,特别是对于移动端的设备,为了加速一般都会进行量化操作。为此,作者提出了一个叫做 h-swish 的激活函数。
- 用ReLU6(x+3)/6替换了sigmoid:1)ReLU6可以在任何软硬件平台进行计算,2)量化的时候,它消除了由于近似sigmoid的不同实现而导致的潜在数值精度损失。3)在实践中,h-swish可以作为一个分段函数来实现,以减少内存访问的数量,从而大幅降低延迟成本。
- 只在下半部分使用h-swish:随着我们深入网络,应用非线性的成本会降低,因为每层激活内存通常在分辨率下降时减半。我们发现swish的大部分好处都是通过在更深层次上使用它们来实现的。因此,在我们的架构中,我们只在下半部分使用h-swish。
亮点3:重新设计耗时层结构
(1)第一层卷积层,用了32个3x3的卷积核来做最初的边缘检测。我们改为用16个,并且用hard swish的非线性层。作者通过实验发现,这样做其实准确率并没有改变,但是参数量小了,节省大概 2ms的时间。
(2)V2最后用了1x1的卷积来增加维度(320->1280),为了拥有更多的特征,但是会导致额外的延迟。将其放在avg pooling的后面,首先利用avg pooling将特征图大小由7x7降到了1x1,然后再利用1x1提高维度,这样就减少了7x7=49倍的计算量。删掉3x3以及1x1后,精度并没有得到损失,但是速度快了很多,节省了 7ms 的推理时间,7ms占据了全部推理时间的 11%。
MobileNetV3 Definitions
MobileNetV3被定义为两种型号:MobileNetV3-large和MobileNetV3-small,分别针对高资源和低资源用例。这些模型是通过应用支持平台的NAS和NetAdapt进行网络搜索并结合本节中定义的网络改进而创建的。
参考