敞开生长之旅!这是我参加「日新计划 12 月更文挑战」的第3天,点击查看活动概况。
- 1、相关作业
- 2、MobileNets 结构
- 3、试验
- 4、定论
- 5、基准模型代码
- 个人思考
- 后续改善-MobileDets
- 参阅资料
文章同步发于 github、博客园 和 知乎。最新版以
github
为主。假如看完文章有所收成,一定要先点赞后收藏。究竟,赠人玫瑰,手有余香。
MobileNet
论文的首要奉献在于提出了一种深度可分离卷积架构(DW+PW 卷积),先经过理论证明这种架构比惯例的卷积核算成本(Mult-Adds
)更小,然后经过分类、检测等多种试验证明模型的有用性。
1、相关作业
规范卷积
一个巨细为 h1w1h_1\times w_1 过滤器(2
维卷积核),沿着 feature map
的左上角移动到右下角,过滤器每移动一次,将过滤器参数矩阵和对应特征图 h1w1c1h_1 \times w_1 \times c_1 巨细的区域内的像素点相乘后累加得到一个值,又由于 feature map
的数量(通道数)为 c1c_1,所以咱们需求一个 shape
为 (c1,h1,w1) (c_1, h_1, w_1) 的滤波器( 3
维卷积核),将每个输入 featue map 对应输出像素点位置核算和的值相加,即得到输出 feature map 对应像素点的值。又由于输出 feature map
的数量为 c2c_2 个,所以需求 c2c_2 个滤波器。规范卷积笼统进程如下图所示。
2D
卷积核算进程动态图如下,经过这张图可以更直观了解卷积核如何执行滑窗操作,又如何相加并输出 c2c_2 个 feature map
,动态图来历 这儿。
分组卷积
Group Convolution
分组卷积,最早见于 AlexNet
。惯例卷积与分组卷积的输入 feature map 与输出 feature map 的衔接方式如下图所示,图片来自CondenseNet。
分组卷积的定义:对输入 feature map
进行分组,然后分组别离进行卷积。假定输入 feature map 的尺度为 HWc1H \times W \times c_{1},输出 feature map 数量为 c2c_2 个,假如将输入 feature map 按通道分为 gg 组,则每组特征图的尺度为 HWc1gH \times W \times \frac{c_1}{g},每组对应的滤波器(卷积核)的 尺度 为 h1w1c1gh_{1} \times w_{1} \times \frac{c_{1}}{g},每组的滤波器数量为 c2g\frac{c_{2}}{g} 个,滤波器总数依然为 c2c_2 个,即分组卷积的卷积核 shape
为 (c2,c1g,h1,w1)(c_2,\frac{c_1}{g}, h_1,w_1)。每组的滤波器只与其同组的输入 map 进行卷积,每组输出特征图尺度为 HWc2gH \times W \times \frac{c_{2}}{g},将 gg 组卷积后的结果进行拼接 (concatenate
) 得到终究的得到终究尺度为 HWc2H \times W \times c_2 的输出特征图,其分组卷积进程如下图所示:
分组卷积的含义:分组卷积是现在网络结构规划的中心,它经过通道之间的稀少衔接(也便是只和同一个组内的特征衔接)来降低核算复杂度。一方面,它答应咱们使用更多的通道数来添加网络容量然后提高准确率,但另一方面跟着通道数的增多也对带来更多的 MACMAC。针对 111 \times 1 的分组卷积,MACMAC 和 FLOPsFLOPs 核算如下:
从以上公式可以得出分组卷积的参数量和核算量是规范卷积的 1g\frac{1}{g} 的定论 ,但其实对分组卷积进程进行深化了解之后也可以直接得出以上定论。
分组卷积的深化了解:关于 111\times 1 卷积,惯例卷积输出的特征图上,每一个像素点是由输入特征图的 c1c_1 个点核算得到,而分组卷积输出的特征图上,每一个像素点是由输入特征图的 c1g \frac{c_1}{g}个点得到(参阅惯例卷积核算进程)。卷积运算进程是线性的,自然,分组卷积的参数量和核算量是规范卷积的 1g\frac{1}{g} 了。
当分组卷积的分组数量 = 输入 feature map 数量 = 输出 feature map 数量,即 g=c1=c2g=c_1=c_2,有 c1c_1 个滤波器,且每个滤波器尺度为 1KK1 \times K \times K 时,Group Convolution 就成了 Depthwise Convolution(DW 卷积),DW
卷积的卷积核权重尺度为 (c1,1,K,K)(c_{1}, 1, K, K)。
惯例卷积的卷积核权重 shape 都为(
C_out, C_in, kernel_height, kernel_width
),分组卷积的卷积核权重shape
为(C_out, C_in/g, kernel_height, kernel_width
),DW
卷积的卷积核权重shape
为(C_in, 1, kernel_height, kernel_width
)。
从 Inception module 到 depthwise separable convolutions
深度可分离卷积(depthwise separable convolutions)的提出最早来历于 Xception
论文,Xception 的论文中说到,关于卷积来说,卷积核可以看做一个三维的滤波器:通道维+空间维(Feature Map 的宽和高),惯例的卷积操作其实便是完成通道相关性和空间相关性的联合映射。Inception 模块的背后存在这样的一种假定:卷积层通道间的相关性和空间相关性是可以退耦合(完全可分)的,将它们分开映射,能到达更好的效果(the fundamental hypothesis behind Inception is that cross-channel correlations and spatial correlations are sufficiently decoupled that it is preferable not to map them jointly.)。
引进深度可分离卷积的 Inception,称之为 Xception,其作为 Inception v3 的改善版,在 ImageNet 和 JFT 数据集上有一定的性能提高,可是参数量和速度并没有太大的变化,由于 Xception 的目的也不在于模型的紧缩。深度可分离卷积的 Inception 模块如图 Figure 4 所示。
Figure 4 中的“极限” Inception 模块与本文的主角-深度可分离卷积模块相似,区别在于:深度可分离卷积先进行 channel-wise
的空间卷积,再进行 111 \times 1 的通道卷积,Figure 4 的 Inception 则相反;
2、MobileNets 结构
2.1,深度可分离卷积
MobileNets
是谷歌 2017 年提出的一种高效的移动端轻量化网络,其间心是深度可分离卷积(depthwise separable convolutions
),深度可分离卷积的中心思想是将一个完好的卷积运算分解为两步进行,别离为 Depthwise Convolution(DW
卷积) 与 Pointwise Convolution(PW
卷积)。深度可分离卷积的核算过程和滤波器尺度如下所示。
Depthwise 卷积
留意本文 DW 和 PW 卷积核算量的核算与论文有所区别,本文的输出 Feature map 巨细是 DGDGD_G \times D_G, 论文公式是DFDFD_F \times D_F。
不同于惯例卷积操作, Depthwise Convolution 的一个卷积核只担任一个通道,一个通道只能被一个卷积核卷积(不同的通道选用不同的卷积核卷积),也便是输入通道、输出通道和分组数相同的特别分组卷积,因而 Depthwise(DW
)卷积不会改动输入特征图的通道数目。深度可分离卷积的 DW
卷积过程如下图:
DW
卷积的核算量 MACC=MDG2DK2MACC = M \times D_{G}^{2} \times D_{K}^{2}
Pointwise 卷积
上述 Depthwise 卷积的问题在于它让每个卷积核独自对一个通道进行核算,可是各个通道的信息没有到达交换,然后在网络后续信息流动中会丢失通道之间的信息,因而论文中就加入了 Pointwise 卷积操作,来进一步交融通道之间的信息。PW 卷积是一种特别的惯例卷积,卷积核的尺度为 111 \times 1。PW
卷积的进程如下图:
假定输入特征图巨细为 DGDGMD_{G} \times D_{G} \times M,输出特征图巨细为 DGDGND_{G} \times D_{G} \times N,则滤波器尺度为 11M1 \times 1 \times M,且一共有 NN 个滤波器。因而可核算得到 PW
卷积的核算量 MACC=NMDG2MACC = N \times M \times D_{G}^{2}。
综上:Depthwise
和 Pointwise
卷积这两部分的核算量相加为 MACC1=MDG2DK2+NMDG2MACC1 = M \times D_{G}^{2} \times D_{K}^{2} + N \times M \times D_{G}^{2},而规范卷积的核算量 MACC2=NDG2DK2MMACC2 = N \times D_{G}^{2} \times D_{K}^{2} \times M。所以深度可分离卷积核算量于规范卷积核算量比值的核算公式如下。
可以看到 Depthwise + Pointwise
卷积的核算量相较于规范卷积近乎削减了 NN 倍,NN 为输出特征图的通道数目,同理参数量也会削减许多。在到达相同目的(即对相邻元素以及通道之间信息进行核算)下, 深度可分离卷积能极大削减卷积核算量,因而大量移动端网络的 backbone
都选用了这种卷积结构,再加上模型蒸馏,剪枝,能让移动端更高效的推理。
深度可分离卷积的详细核算进程可参阅 Depthwise卷积与Pointwise卷积。
2.2、网络结构
333 \times 3 的深度可分离卷积 Block
结构如下图所示:
左面是带 bn
和 relu
的规范卷积层,右边是带 bn 和 relu 的深度可分离卷积层。
333 \times 3 的深度可分离卷积 Block
网络的 pytorch 代码如下:
class MobilnetV1Block(nn.Module):
"""Depthwise conv + Pointwise conv"""
def __init__(self, in_channels, out_channels, stride=1):
super(MobilnetV1Block, self).__init__()
# dw conv kernel shape is (in_channels, 1, ksize, ksize)
self.dw = nn.Conv2d(in_channels, in_channels, kernel_size=3,stride=stride,padding=1, groups=in_channels, bias=False)
self.bn1 = nn.BatchNorm2d(in_channels)
self.pw = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
def forward(self, x):
out1 = F.relu(self.bn1(self.dw(x)))
out2 = F.relu(self.bn2(self.pw(out1)))
return out2
MobileNet v1
的 pytorch
模型导出为 onnx
模型后,深度可分离卷积 block
结构图如下图所示。
仅用 MobileNets 的 Mult-Adds
(乘加操作)次数更少来定义高性能网络是不够的,保证这些操作可以有用实施也很重要。例如非结构化稀少矩阵运算(unstructured sparse matrix operations)一般并不会比密布矩阵运算(dense matrix operations)快,除非是十分高的稀少度。
这句话是不是和
shufflenet v2
的观念共同,即不能仅仅以 FLOPs 核算量来表现网络的运转速度,除非是同一种网络架构。
MobileNet 模型结构将简直所有核算都放入密布的 11 卷积中(dense 1 1 convolutions),卷积核算可以经过高度优化的通用矩阵乘法(GEMM
)函数来完成。 卷积一般由 GEMM
完成,但需求在内存中进行名为 im2col
的初始从头排序,然后才映射到 GEMM
。 caffe 结构便是使用这种办法完成卷积核算。 11
卷积不需求在内存中进行从头排序,可以直接使用 GEMM
(最优化的数值线性代数算法之一)来完成。
如表 2 所示,MobileNet 模型的 1x1
卷积占据了 95%
的核算量和 75%
的参数,剩余的参数简直都在全衔接层中, 3×3 的 DW 卷积核惯例卷积占据了很少的核算量(Mult-Adds)和参数。
2.3、宽度乘系数-更小的模型
虽然基本的 MobileNet
体系结构已经很小且网络推迟 latency
很低,但许多情况下特定用例或应用或许要求模型变得更小,更快。为了构建这些更小且核算成本更低的模型,咱们引进了一个十分简略的参数 \alpha,称为 width 乘数
。宽度乘数 \alpha 的作用是使每一层的网络均匀变薄。关于给定的层和宽度乘数 \alpha,输入通道的数量变为 M\alpha M,而输出通道的数量 NN 变为 N\alpha N。具有宽度乘数 \alpha 的深度可分离卷积(其它参数和上文共同)的核算成本为:
MDG2DK2+NMDG2\alpha M \times D_{G}^{2} \times D_{K}^{2} + \alpha N \times \alpha M \times D_{G}^{2}
其间 ∈(0,1]\alpha \in (0,1],典型值设置为 1、0.75、0.5
和 0.25
。=1\alpha = 1 是基准 MobileNet 模型,<1\alpha < 1 是缩小版的 MobileNets
。宽度乘数的作用是将核算量和参数数量大约削减 2\alpha^2 倍,然后降低了网络核算成本( computational cost of a neural network)。 宽度乘数可以应用于任何模型结构,以定义新的较小模型,且具有合理的准确性、网络推迟 latency
和模型巨细之间的权衡。 它用于定义新的精简结构,需求从头开始进行练习模型。基准 MobileNet
模型的全体结构定义如表 1 所示。
2.4、分辨率乘系数-削减表明
削减模型核算成本的的第二个超参数(hyper-parameter)是分辨率因子 \rho,论文将其应用于输入图画,则网络的每一层 feature map 巨细也要乘以 \rho。实践上,论文经过设置输入分辨率来隐式设置 \rho。
将网络中心层的核算成本表明为具有宽度乘数 \alpha 和分辨率乘数 \rho 的深度可分离卷积的公式如下:
MDG2DK2+NMDG2\alpha M \times \rho D_{G}^{2} \times D_{K}^{2} + \alpha N \times \alpha M \times \rho D_{G}^{2}
其间 ∈(0,1]\rho \in (0,1],一般是隐式设置的,因而网络的输入分辨率为 224、192、160
或 128
。=1\rho = 1 时是基准(baseline
) MobilNet,<1\rho < 1 时缩小版 MobileNets
。分辨率乘数的作用是将核算量削减 2\rho^2。
2.5、模型结构总结
- 整个网络不算平均池化层与
softmax
层,且将DW
卷积和PW
卷积计为独自的一层,则MobileNet
有28
层网络。+ 在整个网络结构中步长为2的卷积较有特色,卷积的一起充当下采样的功用; - 第一层之后的
26
层都为深度可分离卷积的重复卷积操作,分为4
个卷积stage
; - 每一个卷积层(含惯例卷积、深度卷积、逐点卷积)之后都紧跟着批规范化和
ReLU
激活函数; - 最后一层全衔接层不使用激活函数。
3、试验
作者别离进行了 Stanford Dogs dataset 数据集上的细粒度辨认、大规模地理分类、人脸特点分类、COCO 数据集上方针检测的试验,来证明与 Inception V3
、GoogleNet
、VGG16
等 backbone
比较,MobilNets
模型可以在核算量(Mult-Adds
)数 10 被下降的情况下,可是精度却简直不变。
4、定论
论文提出了一种基于深度可分离卷积的新模型架构,称为 MobileNets
。 在相关作业章节中,作者首要调查了一些让模型更有用的重要规划原则,然后,演示了如何经过宽度乘数和分辨率乘数来构建更小,更快的 MobileNet,经过权衡合理的精度以削减模型巨细和推迟。 然后,咱们将不同的 MobileNets
与流行的模型进行了比较,这些模型展示了超卓的尺度,速度和准确性特性。 最后,论文演示了 MobileNet 在应用于各种使命时的有用性。
5、基准模型代码
自己复现的基准 MobileNet v1 代模型 pytorch 代码如下:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models
from torch import flatten
class MobilnetV1Block(nn.Module):
"""Depthwise conv + Pointwise conv"""
def __init__(self, in_channels, out_channels, stride=1):
super(MobilnetV1Block, self).__init__()
# dw conv kernel shape is (in_channels, 1, ksize, ksize)
self.dw = nn.Sequential(
nn.Conv2d(in_channels, 64, kernel_size=3,
stride=stride, padding=1, groups=4, bias=False),
nn.BatchNorm2d(in_channels),
nn.ReLU(inplace=True)
)
# print(self.dw[0].weight.shape) # print dw conv kernel shape
self.pw = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1,
stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True)
)
def forward(self, x):
x = self.dw(x)
x = self.pw(x)
return x
def convbn_relu(in_channels, out_channels, stride=2):
return nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride,
padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True))
class MobileNetV1(nn.Module):
# (32, 64, 1) means MobilnetV1Block in_channnels is 32, out_channels is 64, no change in map size.
stage_cfg = [(32, 64, 1),
(64, 128, 2), (128, 128, 1), # stage1 conv
(128, 256, 2), (256, 256, 1), # stage2 conv
(256, 512, 2), (512, 512, 1), (512, 512, 1), (512, 512, 1), (512, 512, 1), (512, 512, 1), # stage3 conv
(512, 1024, 2), (1024, 1024, 1) # stage4 conv
]
def __init__(self, num_classes=1000):
super(MobileNetV1, self).__init__()
self.first_conv = convbn_relu(3, 32, 2) # Input image size reduced by half
self.stage_layers = self._make_layers(in_channels=32)
self.linear = nn.Linear(1024, num_classes) # 全衔接层
def _make_layers(self, in_channels):
layers = []
for x in self.stage_cfg:
in_channels = x[0]
out_channels = x[1]
stride = x[2]
layers.append(MobilnetV1Block(in_channels, out_channels, stride))
in_channels = out_channels
return nn.Sequential(*layers)
def forward(self, x):
"""Feature map shape(h、w) is 224 -> 112 -> 56 -> 28 -> 14 -> 7 -> 1"""
x = self.first_conv(x)
x = self.stage_layers(x)
x = F.avg_pool2d(x, 7) # x shape is 7*7
x = flatten(x, 1) # x = x.view(x.size(0), -1)
x = self.linear(x)
return x
if __name__ == "__main__":
model = MobileNetV1()
model.eval() # set the model to inference mode
input_data = torch.rand(1, 3, 224, 224)
outputs = model(input_data)
print("Model output size is", outputs.size())
程序运转结果如下:
Model output size is torch.Size([1, 1000])
个人思考
在降低 FLOPs
核算量上,MobileNet
的网络架构规划确实很好,可是 MobileNet
模型在 GPU
、DSP
和 TPU
硬件上却不一定性能好,原因是不同硬件进行运算时的行为不同,然后导致了 FLOPs
少不等于 latency
低的问题。
假如要实践解释 TPU
与 DSP
的运作原理,或许有点费事,可以参阅下图,从结果直观地了解他们行为上的差异。考虑一个简略的 convolution
,在 CPU
上 latency
跟着 input
与 output
的channel
上升正相关的添加。然而在 DSP
上却是阶梯型,甚至在更高的 channel
数下存在特别低latency
的甜美点。
在一定的程度上,网络越深越宽,性能越好。宽度,即通道(channel
)的数量,网络深度,即 layer
的层数,如 resnet18
有 18
个卷积层。留意咱们这儿说的和宽度学习一类的模型没有联系,而是特指深度卷积神经网络的(通道)宽度。
-
网络深度的含义:
CNN
的网络层可以对输入图画数据进行逐层笼统,比如第一层学习到了图画边际特征,第二层学习到了简略形状特征,第三层学习到了方针形状的特征,网络深度添加也提高了模型的笼统才能。 -
网络宽度的含义:网络的宽度(通道数)代表了滤波器(
3
维)的数量,滤波器越多,对方针特征的提取才能越强,即让每一层网络学习到更加丰富的特征,比如不同方向、不同频率的纹理特征等。
后续改善-MobileDets
-
FLOPs
低不等于latency
低,尤其是在有加快功用的硬体 (GPU
、DSP
与TPU
)上不成立。 -
MobileNet conv block
(depthwise separable convolution
)在有加快功用的硬件(专用硬件规划-NPU
芯片)上比较没有用率。 - 低
channel
数的情况下 (如网路的前几层),在有加快功用的硬件使用一般convolution
一般会比separable convolution
有用率。 - 在大多数的硬件上,
channel
数为8
的倍数比较有利核算。 -
DSP
与TPU
上,一般咱们需求运算为uint8
方式,quantization
(低精度量化)是常见的技巧。 -
DSP
与TPU
上,h-Swish
与squeeze-and-excitation
效果不明显 (由于硬体规划与uint8
紧缩的联系)。 -
DSP
与TPU
上,5x5
convolution
比较没功率。
参阅资料
- Group Convolution分组卷积,以及Depthwise Convolution和Global Depthwise Convolution
- 了解分组卷积和深度可分离卷积如何降低参数量
- 深度可分离卷积(Xception 与 MobileNet 的点滴)
- MobileNetV1代码完成
- Depthwise卷积与Pointwise卷积
- 【CNN结构规划】深化了解深度可分离卷积
- FLOPs与模型推理速度
- MobileDets: FLOPs不等于Latency,考量不同硬体的高效架构