Skip to content

Latest commit

 

History

History
103 lines (78 loc) · 7.92 KB

File metadata and controls

103 lines (78 loc) · 7.92 KB

      depthwise separable convolutions in mobilenet

引言

深度学习模型的精简和加速,在实际项目落地的过程中,越来越重要。模型文件的大小,直接决定了运行时内存的消耗,而且越大的模型,往往也意味着部署时更多的运算量(乘加数)和更慢的运行速度,更多的功耗。而深度可分离卷积(Depth-wise separable convolution)把一个传统的卷积层分解为一个深度卷积层(Depth-wise convolution)串联一个点卷积层(Point-wise convolution)的形式,这样的分解在保持输入特征图和输出特征图维度不变的前提下(假设stride=1,加padding),使参数数量个运算量都大大得减少了,而且对模型预测精度牺牲小,可推广性强,易于理解和实现,是深度学习中一个重要而且实用的概念。本文就MobileNet-V1论文中关于深度可分离卷积的讲解来做个笔记。

欢迎探讨,本文持续维护。

1, 深度可分离卷积的结构

因为MobileNet-V1论文中的配图只画出了卷积核,没有体现输入输出的特征图,为了便于理解,我加了特征图重画了一个:

先约定一下,输入特征图维度为1xMxDfxDf(与caffe中Blob排列顺序一致,NxCxHxW),经过卷积层(kernalsize为Dk)后输出特征图维度为1xNxDfxDf(做了卷积核stride=1,加padding,保证输入特征图和输出特征图分辨率不变),在论文原文中,感觉Df和Dg有点乱,这里简化一下。

输入特征图走路径1的话,就是传统卷积形式:一张MxDfxDf的特征图(三维的),经过N个MxDkxDk的(立体)卷积核分别卷积,然后合并之后,生成了一个NxDfxDf的特征图。

输入特征图走路径2的话,就是深度可分离卷积的形式:首先,同样一张MxDfxDf的特征图(三维的),先经过M个1xDkxDk的卷积后,生成了一张MxDfxDf的特征图。这里的卷积过程和传统卷积不一样,需要说明一下,这里有M个深度为1,kernalsize大小为DkxDk的小卷积核,每个小卷积核,只和输入的M通道的特征图的对应通道做卷积,卷积的结果,也放在输出特征图的对应通道上。传统的卷积操作,一组卷积核里面的每一个小卷积,都会和输入特征图每个通道相作用,而这里的卷积操作,一组卷积核里面的每一个小卷积通道数(深度)都是1,小卷积只和输入特征图的某一个通道相作用,故称深度卷积(英文Depth-wise convolution的Depth-wise这个单词可能更好理解,按深度来卷积),后面简称DW卷积;然后,在DW输出的1个MxDfxDf的特征图作为输入,经过N个Mx1x1的卷积核,进行传统的卷积操作之后,生成1个NxDfxDf的特征图,其实这一步就是一个传统的卷积,特殊之处只在于它的卷积核的kernalsize等于一,是一个point,所以把它命名为点卷积(Point-wise convolution),区别于kernalsize不等于一的时候的卷积(那种卷积,是不是应该叫Patch-wise convolution?哈哈),后面称PW卷积,PW卷积的作用是让DW卷积输出的特征图每个通道之间的信息能够互通组合,不损伤特征图的表达能力。

2, 计算量和参数数量分析

MobileNet中使用深度可分离卷积的目的还是在于减少计算量和模型大小,下面是关于深度可分离卷积的计算量分析,以及和传统卷积的对比。

  • 2.1 计算量分析

首先,看传统卷积的计算量: 每个输出特征图上的一个像素点,对应的计算量就是一组NxCxHxW卷积核中的一个卷积核CxHxW中的卷积乘加次数,这个次数是CxHxW(卷积核中每个“像素”对应一次乘加运算),输出特征图上所有像素的个数是输出特征图的体积CxHxW。所以在1.1节图中,传统卷积路径1的计算量为:输出特征图的体积x一组卷积核中的一个卷积核的体积,为DfxDfxNxDkxDkxM。 然后,看深度可分离卷积的计算量: 深度可分离卷积的计算量分为两部分,第一部分是DW卷积的计算量,DW卷积的输出特征图体积是DfxDfxM,特征图中每个像素是由一个1xDkxDk的小卷积核卷积出来的,一个小卷积核做卷积对应的乘加数就是它的体积DkxDk,所以,DW卷积的计算量为DfxDfxMxDkxDk;第二部分,PW卷积输出NxDfxDf个像素的特征图,每个像素点都是一个Mx1x1卷积卷出来的,对于的乘加数为M(也就是小卷积核的体积),所以,PW卷积的计算量为NxDfxDfxM。DW卷积的计算量和PW卷积的计算量之和就是整个深度可分离卷积的计算量DfxDfxMxDkxDk+NxDfxDfxM

所以,深度可分离卷积的计算量比上传统卷积的计算量就为:(DfxDfxMxDkxDk+NxDfxDfxM)/(DfxDfxNxDkxDkxM)=1/N+1/(DkxDk),当N比较大(输出特征图的通道数比较多)的时候约等于1/(DkxDk),也就是说,深度可分离卷积的计算量(与传统卷积的比值)和卷积核的平方成反比。当卷积核kernalsize为3的时候,计算量(理论上)为传统卷积的1/9,当卷积核kernalsize为5的时候,计算量为传统卷积的1/25,是一个很大的进步。

  • 2.2 参数数量分析

传统卷积的参数数量,就是所有小卷积核的体积之和,NxMxDkxDk;深度可分离卷积的参数数量,等于DW卷积的参数数量Mx1xDkxDk和PW卷积的参数数量NxMx1x1之和Mx1xDkxDk+NxMx1x1。 所以,深度可分离卷积的参数数量比传统卷积的参数数量就是: (Mx1xDkxDk+NxMx1x1)/(NxMxDkxDk)=1/N+1/(DkxDk),和2.1节计算量分析一致,进一步的分析也和2.1节一致。

  • 2.3 深度可分离卷积层内部分析

2.1和2.2小节分析的都是深度可分离卷积和传统卷积之间的对比。这节对深度可分离卷积内部的DW卷积和PW卷积之间做做分析。 由于2.1和2.2小节已经分析地很清楚了,这里拿来数据直接用。 PW层计算量比上DW层计算量等于(NxDfxDfxM)/(DfxDfxMxDkxDk)=N/(DkxDk),也就是说,如果Dk等于3时,在输出特征图的通道数N大于9的时候,PW层比DW层更耗计算资源。而这个N,一般确实都很大(比如Dk=3,M=512,N=512,Df=14),所以在深度可分离卷积中,更多的计算(和参数数量)是发生在PW层。 参数数量的分析,也是类似的。

3, Caffe实现

深度可分离卷积里面的PW卷积,就是一个传统的kernalsize=1的卷积,只需把kernalsize参数设置为1就行了;里面的DW卷积,可以用分组卷积里面的group参数设置为1来实现。需要注意的是,为了保证输入特征图和输出特征图的分辨率不变,我们在DW卷积的时候需要设置一下stride=1,pad=1(kernalsize=3时)。

DW层

layer { name: "conv5_1/dw" type: "Convolution" bottom: "conv4_2/sep" top: "conv5_1/dw" param { lr_mult: 1 decay_mult: 1 } convolution_param { num_output: 512 bias_term: false pad: 1 kernel_size: 3 group: 512 engine: CAFFE stride: 1 weight_filler { type: "msra" } } }

PW层

layer { name: "conv5_1/sep" type: "Convolution" bottom: "conv5_1/dw" top: "conv5_1/sep" param { lr_mult: 1 decay_mult: 1 } convolution_param { num_output: 512 bias_term: false pad: 0 kernel_size: 1 stride: 1 weight_filler { type: "msra" } } }

总结

以上差不多就是深度可分离卷积的原理和计算量,参数数量的理论分析了,最后顺带加了用Caffe实现。

参考资料