前言
这是卷积神经网络的第十七篇文章,Google 2017年发表的MobileNet V1,其核心思想就是提出了深度可分离卷积来代替标准卷积,将标准卷积分成Depthwise+Pointwise两部分,来构建轻量级的深层神经网络,这一网络即使现在也是非常常用的。论文原文地址和代码实现见附录。
网络结构
在本节中,论文首先描述MobileNet V1的核心层----深度可分离卷积层。然后介绍MobileNet V1网络结构,并以两个模型缩小参数(Width Multiplier
和Resolution Multiplier
)作为总结。
深度可分离卷积
MobileNet V1模型基于深度可分离卷积,它是factorized convolutions
的一种,而factorized convolutions
将标准化卷积分解为深度卷积和
1
×
1
1\times 1
1×1卷积(pointwise convolution
)。对于MobileNet V1,深度卷积将单个滤波器应用到每一个输入通道。然后,点卷积用
1
×
1
1\times 1
1×1卷积来组合深度卷积的输出。我们知道,标准卷积是直接将输入通过卷积的方式组合成一组新的输出。而深度可分离卷积则将其分成两层,一层用于卷积,一层用于组合。这种分解过程能极大减少计算量和模型大小。Figure 2展示了如何将一个标准卷积分解为深度卷积和
1
×
1
1\times 1
1×1的点卷积。
标准卷积使用一个和输入数据通道数相同的卷积核执行逐个通道卷积后求和获得一个通道的输出,计算量为
M
×
D
k
×
D
k
M\times D_k \times D_k
M×Dk×Dk,其中
M
M
M代表输入的通道数,
D
k
D_k
Dk为卷积核的宽和高,一个卷积核处理输入数据时的计算量为:
D
k
×
D
k
×
M
×
D
F
×
D
F
D_k \times D_k \times M \times D_F \times D_F
Dk×Dk×M×DF×DF
其中
D
F
D_F
DF为输入的宽和高,如果在某一个卷积层使用
N
N
N个卷积核,那么这一卷积层的计算量为:
D
k
×
D
k
×
N
×
M
×
D
F
×
D
F
D_k \times D_k \times N \times M \times D_F \times D_F
Dk×Dk×N×M×DF×DF
深度可分离卷积首先使用一组通道数为 1 1 1的卷积核,每次只处理一个输入通道,并且这一组卷积核的个数是和输入通道数相同的。执行完上面的深度卷积后,再使用通道数为输入数据通道数大小的的 1 × 1 1\times 1 1×1卷积来组合之前输出的特征图,将最终输出通道数变为一个指定的数量。
从理论上来看,一组和输入通道数相同的2D卷积核(通道数为
1
1
1,即深度卷积或者说分组卷积)的运算量为:
D
k
×
D
k
×
M
×
D
F
×
D
F
D_k\times D_k\times M \times D_F \times D_F
Dk×Dk×M×DF×DF
而3D(标准卷积)的
1
×
1
1\times 1
1×1卷积核的运算量为:
N
×
M
×
D
F
×
D
F
N\times M \times D_F \times D_F
N×M×DF×DF
因此这种组合方式的计算量为:
D k × D k × M × D F × D F + N × M × D F × D F D_k \times D_k \times M \times D_F \times D_F + N \times M \times D_F \times D_F Dk×Dk×M×DF×DF+N×M×DF×DF
因此,深度可分离卷积相比于标准卷积计算量的比例为:
D
k
×
D
k
×
M
×
D
F
×
D
F
+
N
×
M
×
D
F
×
D
F
D
k
×
D
k
×
M
×
D
F
×
D
F
=
\frac{D_k \times D_k \times M \times D_F \times D_F + N \times M \times D_F \times D_F}{D_k\times D_k\times M \times D_F \times D_F}=
Dk×Dk×M×DF×DFDk×Dk×M×DF×DF+N×M×DF×DF=
1
N
+
1
D
k
2
\frac{1}{N}+\frac{1}{D_k^2}
N1+Dk21
然后举个例子,给定三个通道的 224 × 224 224 \times 224 224×224的图像,VGG16网络的第 3 3 3个卷积层 c o n v 2 1 conv2_1 conv21输入的是尺寸为 112 × 112 112\times 112 112×112的特征图,通道数为 64 64 64,卷积核尺寸为 3 × 3 3\times 3 3×3,卷积核个数为 128 128 128,标准卷积计算量按照上面的公式计算就是:
3 × 3 × 128 × 64 × 112 × 112 = 924844032 3\times 3\times 128\times 64\times 112\times 112=924844032 3×3×128×64×112×112=924844032
如果采用深度可分离卷积,计算量为:
3
×
×
64
×
112
×
112
+
128
×
64
×
112
×
112
=
109985792
3\times \times 64\times 112\times 112 + 128\times 64\times 112\times 112=109985792
3××64×112×112+128×64×112×112=109985792
这两者计算量的比值为: 109985792 924844032 = 0.1189 \frac{109985792}{924844032}=0.1189 924844032109985792=0.1189
可以看到将一个标准卷积换成深度可分离卷积之后模型的计算量减少了 9 9 9倍。
MobileNet V1 网络结构
MobileNet网络结构是以深度可分离卷积为基础单元建立的,其中第一层是标准卷积。MobileNet V1的完整网络结构定义在Table1中。所有卷积层后面都接了BN层和ReLU非线性激活层,但最后的全连接层例外,它没有非线性激活函数,是直接馈送到Softmax
层进行分类。在Figure 3还比较了标准卷积和深度可分离卷积(都跟着BN层和ReLU层)的区别。深度可分离卷积和第一个标准卷积均可以处理下采样问题,并且在最后一个全连接层前面还接了一个全局平均池化层,将特征图的长宽变成
1
×
1
1\times 1
1×1。如果将Depthwise
卷积层和Pointwise
卷积层算成不同层的话,MobileNet V1一共有28
层。
当然,仅仅通过减少网络乘加操作对于网络加速是不够的,确保这些操作能够有效实现也很重要。例如,非结构化的稀疏矩阵操作通常不比密集矩阵运算快,除非是非常稀疏的矩阵。而MobileNet V1的模型结构几乎将全部的计算复杂度放到了
1
×
1
1\times 1
1×1卷积中。这可以通过高度优化的通用矩阵乘法(GEMM)来实现。通常标准卷积如
3
×
3
3\times 3
3×3卷积,
5
×
5
5\times 5
5×5卷积由GEMM算法实现,但需要使用im2col
在内存中对数据进行重排,以将其映射到GEMM可以使用的方式。这个方法在流行的Caffe
深度学习框架中正在使用。
1
×
1
1\times 1
1×1的卷积不需要在内存中重排就可以直接被GEMM(最优化的数值线性代数算法之一)实现。MobileNet V1的
1
×
1
1\times 1
1×1卷积占了总计算量的95%
,并且也占了75%
的参数(见Table 2)。而几乎所有的额外参数都在全连接层。
模型瘦身因子
虽然MobileNet V1网络结构已经很小并且高效,但特定场景或应用程序中可能需要更小更快的模型。为了构建这些较小且计算量较少的模型,论文引入了一个非常简单的参数
α
\alpha
α,称为Width multiplier
。这个参数的作用是在每层均匀地缩减网络宽度。对于一个给定的卷积层和
α
\alpha
α,输入通道的数量从
M
M
M变成
α
M
\alpha M
αM,输出通道的数量从
N
N
N变成
α
N
\alpha N
αN。深度可分离卷积的计算复杂度变成:
其中
α
∈
(
0
,
1
]
\alpha \in (0, 1]
α∈(0,1],通常设为
1
,
0.75
,
0.5
和
0.25
1,0.75,0.5和0.25
1,0.75,0.5和0.25。
α
=
1
\alpha = 1
α=1表示基准MobileNet V1,而
α
<
1
\alpha < 1
α<1则表示瘦身的MobileNet V1。
α
\alpha
α有减少计算复杂度和参数数量(大概
α
2
\alpha ^ 2
α2倍)的作用。并且Width Multiplier
参数可以应用于任何模型结构,以定义一个具有可接受准确率,速度和大小的新模型。
分辨率缩减因子
这里要介绍的第二个超参数是分辨率缩减因子
ρ
\rho
ρ,又叫Resolution multiplier
。我们将其应用于输入图像,并且每个层的特征图分辨率随之被减去相同的
ρ
\rho
ρ倍。实际操作的时候,我们通过输入分辨率隐式的设置
ρ
\rho
ρ。
ρ
=
1
\rho=1
ρ=1是最基础的MobileNet V1模型,而
ρ
<
1
\rho < 1
ρ<1示瘦身的MobileNet V1。Resolution multiplier
同样可以减少
ρ
2
\rho^2
ρ2的计算复杂度。
我们以MobileNet V1中的一个典型的卷积层为例,看看深度可分离卷积,Width Multiplier
和Resolution Multiplier
具体降低了多少计算复杂度和参数量。Table 3展示了将上述Trick应用于该层,得到的计算复杂度(FLPOs)和参数数量(Params)对比结果。
实验结果
Table4-7展示了上面介绍的一系列参数和深度可分离卷积在ImageNet上的精度以及模型的参数量/计算量 的对比结果。
下面的Figure 4展示了在ImageNet,
16
16
16个模型的准确率(
α
∈
(
1
,
0.75
,
0.5
,
0.25
)
\alpha \in(1,0.75,0.5,0.25)
α∈(1,0.75,0.5,0.25),
ρ
∈
(
224
,
192
,
160
,
128
)
\rho \in (224,192,160,128)
ρ∈(224,192,160,128))和计算复杂度的关系。结果近似为log
关系,并在
α
=
0.25
α=0.25
α=0.25有一个显著降低。
Figure 5展示了在ImageNet上,
16
16
16个模型的准确率(
α
∈
(
1
,
0.75
,
0.5
,
0.25
)
\alpha \in(1,0.75,0.5,0.25)
α∈(1,0.75,0.5,0.25),
ρ
∈
(
224
,
192
,
160
,
128
)
\rho \in (224,192,160,128)
ρ∈(224,192,160,128))和参数数量的关系。不同颜色代表不同的分辨率。参数数量不会因输入分辨率不同而不同。
Table 8比较了基准的的MobileNet V1和的GoogleNet以及VGG16的一些指标。可以看到MobileNet V1和VGG16准确率相近,但模型大小小了32倍,计算量也小了
27
27
27倍。MobileNet V1比GoogleNet更加精确,并且模型大小更小,计算复杂度也少了
2.5
2.5
2.5倍。
Table 9比较了瘦身后的(Width multiplier
α
=
0.5
\alpha = 0.5
α=0.5 ,输入分辨率
160
×
160
160\times 160
160×160)的MobileNet V1。瘦身后的MobileNet V1准确率比AlexNet好大约4%
,但模型大小小了45
倍,计算复杂度也小了
9.4
9.4
9.4倍。在模型尺寸差不多的情况下,它比SqueezeNet
有更高的准确率,而计算复杂度降低了
22
22
22倍。
MobileNet V1还可以作为Backbone网络应用在现代目标检测系统中。根据2016 COCO冠军的工作,论文报告了针对COCO数据集使用MobileNet V1做Backbone进行目标检测的结果。从Table 13中可以看到,使用相同Faster-RCNN和SSD框架,MobileNet V1和VGG,InceptionV2分别做检测模型的Backbone进行了比较。其中,SSD使用了 300 × 300 300 \times 300 300×300的输入分辨率(SSD300),而Faster-RCNN使用了 600 600 600和 300 300 300的分辨率(Faster-RCNN300,Faster-RCNN600)。这个Faster-RCNN模型每张图考虑 300 300 300个候选框。模型通过COCO数据集上训练(排除8000张minival图),然后在minival测试集上测试。对于这两个框架,MobileNet V1可以实现与其他Backbone网络的相似结果,但计算复杂度和模型大小都小了非常多。Figure6展示了一个使用SSD做模板检测的可视化例子。
FaceNet模型是最先进的人脸识别模型。它基于TripletLoss构建人脸特征。为了构建一个移动端的FaceNet模型,论文使用Distillation
方法去训练MobileNet FaceNet模型,实验结果可以在Table 14中看到。
结论
这篇论文提出了一种基于深度可分离卷积的新型模型架构,称为MobileNet V1。接着讨论了一些可以得到高效模型的重要设计方法并展示了如何通过使用Width Multiplier
和Resolution Multiplier
来构建更小更快的MobileNet V1模型,以合理的精度损失来减少模型尺寸和提高模型运行速度。最后,将不同的MobileNet V1结构与SOTA的网络模型进行比较,展示了尺寸,速度和精度方面的差异。
Pytorch代码实现
import torch
import torch.nn as nn
import torch.nn.functional as F
class Block(nn.Module):
'''Depthwise conv + Pointwise conv'''
def __init__(self, in_planes, out_planes, stride=1):
super(Block, self).__init__()
self.conv1 = nn.Conv2d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=in_planes, bias=False)
self.bn1 = nn.BatchNorm2d(in_planes)
self.conv2 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False)
self.bn2 = nn.BatchNorm2d(out_planes)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
return out
class MobileNet(nn.Module):
# (128,2) means conv planes=128, conv stride=2, by default conv stride=1
cfg = [64, (128,2), 128, (256,2), 256, (512,2), 512, 512, 512, 512, 512, (1024,2), 1024]
def __init__(self, num_classes=10):
super(MobileNet, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(32)
self.layers = self._make_layers(in_planes=32)
self.linear = nn.Linear(1024, num_classes)
def _make_layers(self, in_planes):
layers = []
for x in self.cfg:
out_planes = x if isinstance(x, int) else x[0]
stride = 1 if isinstance(x, int) else x[1]
layers.append(Block(in_planes, out_planes, stride))
in_planes = out_planes
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layers(out)
out = F.avg_pool2d(out, 2)
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
附录
- 论文原文:https://arxiv.org/pdf/1704.04861.pdf
- 参考:https://baijiahao.baidu.com/s?id=1566004753349359&wfr=spider&for=pc
- 代码实现:https://github.com/kuangliu/pytorch-cifar
推荐阅读
欢迎关注GiantPandaCV, 在这里你将看到独家的深度学习分享,坚持原创,每天分享我们学习到的新鲜知识。( • ̀ω•́ )✧
有对文章相关的问题,或者想要加入交流群,欢迎添加BBuf微信: