前言
本文记录了深度学习模型中卷积操作的感受野和特征图尺寸计算方式,以及如何分析网络的参数量和计算量,用来评价一个模型的规模大小与推理速度。
正文
一、感受野计算
感受野就是卷积神经网络每一层输出的特征图feature map
上的像素点在原始图像上映射的区域大小。对于只有一层卷积或池化的网络来说,感受野就是feature map
上的一个像素点对应于输入图像上的映射区域的大小,可以发现,感受野大小就是卷积核的大小。
-
对于多层网络,一种方法是由深到浅的计算,才能得到深层网络对应于最外层网络的映射区域大小,计算公式如下:
\(\text {for l in (layers from deep to light):} \\ RF_l = (RF_{l-1}-1)*s_l + k_l\)
其中$RF_l$是第l层感受野大小,$s_l$是第l层的步长,$k_l$是第l层的卷积核尺寸(设长宽一致)。 -
另一种角度是直接从外层向里计算:
\[RF_l = RF_{l-1} + ((k_l - 1) * \prod_{i=1}^{l-1}s_i)\] -
具体计算过程
为了计算每层的感受野,需要条件是上一层感受野大小$r$,输出特征上两个相邻特征点的距离$j$和特征点中心坐标$start$,某个特征点感受野的中心坐标就等于这个特征点的中心坐标。当采用的卷积核大小为$k$,padding
大小为$p$,步长为$s$,输出特征图的感受野可以按照如下步骤计算:
\( j_{out} =j_{in} * s
\)
输出特征图的特征点间隔$j_{out}$,等于上一层的间隔值$j_{in}$乘以卷积的步长$s$,所以间隔值将是按照步长呈指数级增长。
\[r_{out} =r_{in}+(k-1) * j_{in}\]输出特征图的感受野大小$r_{out}$,等于上一层感受野大小加上$ (k-1)*j_{in}$ ,所以感受野是呈指数级增加。
\[start_{out} =start_{in}+\left(\frac{k-1}{2}-p\right) * j_{in}\]输出特征图的感受野中心坐标即对应特征点的中心坐标$start_{out}$,等于第一层的中心坐标$start_{in}$加上$ \frac{(k-1)}{2}-p$,再乘以前一层的间隔距离以得到实际距离。
padding
并不影响感受野,但影响感受野中心坐标,步长$s$影响下层感受野,卷积核尺寸$k$影响该层的感受野大小和中心坐标。
![]() |
---|
via 感受野计算示意图 |
二、特征图尺寸计算
定义如下相关参数符号:
符号 | 含义 |
---|---|
$K_{in}$ | 输入特征图尺寸,设长宽一致 |
$K_{out}$ | 输出特征图尺寸,设长宽一致 |
$f$ | 卷积核尺寸,设长宽一致 |
$C_{in}$ | 卷积核通道数,即输入特征通道数 |
$C_{out}$ | 卷积核个数,即输出特征通道数 |
$s$ | 卷积核的移动步长 |
$p$ | 对输入特征的填充行数或列数 |
对于不同的神经网络有不同的尺寸计算方式:
注:$[.]$表示向下取整
-
卷积
-
在Pytorch中需要人为设定
\[K_{out}=[\frac{K_{in}-f+2p}{s}]+1\]padding
,计算公式如下所示; -
在Tensorflow中有
\[K_{out}=[\frac{K_{in}}{s}]+1\]VALID
和SAME
两种填充模式,SAME
模式自动补零,公式如下:SAME
模式虽然是自动补零,但仍然应该了解底层是如何补零的,这里根据卷积核尺寸分成两种情况:-
卷积核尺寸为奇数时
\[p=\frac{f-1}{2}\] -
卷积核尺寸为偶数时
\[p=f-1\]
\[K_{out}=[\frac{K_{in}-f}{s}]+1\]VALID
模式不补零,计算方式如下: -
有关pytorch与tensorflow关于补零位置的区别可见Pytorch与Tensorflow模型相互转换时,各种算子的差异
-
-
池化
相关于无填充的卷积,计算公式为:
\[K_{out}=[\frac{K_{in}-f}{s}]+1\] -
全连接
全连接层的输入输出特征是向量,对于图片特征图而言,输入特征向量长度是$K_{in}K_{in}C_{in}$,输出特征向量长度与全连接层的输出神经元数量$K_{out}K_{out}C_{out}$相同,对于图像任务来说,通常只用在模型输出
softmax
时使用。 -
空洞卷积
空洞卷积是在普通卷积核上增加了间隔,用参数$r$表示膨胀系数
\[K_r=K+(K-1)*(r-1)=(K-1)*r+1\]dilation
,普通卷积就是$r=1$的空洞卷积,所以在计算空洞卷积的相关参数时需要先将卷积核尺寸等效处理,即: -
反卷积
计算公式
一般卷积是输出特征尺寸保持不变或减小的过程,而反卷积相反,是让输出特征尺寸增加的过程,本质上是先间隔填充零,扩大输入特征尺寸后再做步长为1的一般卷积的过程。
将一般卷积的计算公式中的输入输出特征尺寸调换后,可变形为如下形式:
\[K_{out}=(K_{i}-1)*s+f-2*p\]但是要注意,由于一般卷积中有出现余数的情况,所以在上式中要加上余数:
\[K_{out}=(K_{i}-1)*s+f-2*p+余数\]在Pytorch中,这个余数就是
\[K_{out}=(K_{i}-1)*s+f-2*p+output\_padding\]output_padding
,代入上式可得计算公式:为了更深刻理解Pytorch内部是如何操作的,需要先了解
padding
和output_padding
的官方定义:
![]() |
---|
via Pytorch官方文档 |
底层分析
前面说到反卷积的本质上是先间隔填充零,扩大输入特征尺寸后再做步长为1的一般卷积的过程,所以padding
是对输入特征做间隔填充零的操作的过程,而这里一般卷积根据Tensorflow和Pytorch分为两种情况:
1、Pytorch中,一般卷积指的是不补零的卷积,类似Tensorflow中的VALID
模式
所以output_padding
是在做完卷积后在输出特征后面补零的行列数
2、Tensorflow中,一般卷积也有两种模式SAME
和VALID
,对于VALID
与Pytorch操作相同,做完卷积后在输出特征后面补零;对于SAME
则卷积需要补零,做完卷积后在输出特征后面截断多余的行列数。
![]() |
---|
via 反卷积过程示意图 |
三、复杂度分析
终端设备上运行深度学习算法需要考虑内存和算力的需求,因此需要进行模型复杂度分析,涉及到模型计算量(时间/计算复杂度)和模型参数量(空间复杂度)分析。
1、参数量计算
定义如下相关参数符号:
符号 | 含义 |
---|---|
$f$ | 卷积核尺寸,设长宽一致 |
$C_{in}$ | 卷积核通道数,即输入特征通道数 |
$C_{out}$ | 卷积核个数,即输出特征通道数 |
$F_1$ | 前一层全连接层神经元数量 |
$F_2$ | 当前层全连接层神经元数量 |
$P_w$ | 当前层的weights参数量 |
$P_b$ | 当前层的biases参数量 |
$P$ | 当前层总参数量 |
网络层的参数量包括权重
weight
和偏差bias
两种
-
卷积
所有类型的卷积的参数均来自卷积核的大小:
\[\begin{align} &P_w=f^2*C_{in}*C_{out} \\ &P_b=C_{out} \\ &P=P_w+P_b \end{align}\] -
可分离卷积
可分离卷积由逐通道卷积Depthwise Conv
和逐点卷积Pointwise Conv
组成,Depthwise Conv
是分组卷积的特例,即分组数与输入特征通道数相同,每组卷积核数为1。分组卷积的结构详见文章,
Depthwise Conv
作用:减小参数量与计算量
Pointwise Conv
作用:改变输出通道数;融合通道特征
可分离卷积过程就是用$C_{in}$组$fxfx1$尺寸的卷积核对输入特征做分组卷积,再用$1x1xC_{out}$尺寸的卷积核做步长为1的卷积。因此参数量为:
\[\begin{align} &P_{w}=f^2*1*C_{in}+1*1*C_{in}*C_{out} \\ &P_{b}=C_{in}+C_{out} \\ &P=P_w+P_b \end{align}\]- 全连接
全连接层参数量是前一层与当前层的神经元的大小乘积,神经元的大小由输入输出特征的长度决定(如果全连接层跟卷积相连,则需要将卷积输出特征展开为向量形式,向量长度由特征的尺寸决定):
\[\begin{align} &P_w=F_1*F_2 \\ &P_b=F_2 \\ &P=P_w+P_b \end{align}\]-
其他网络
参数量指的就是需要迭代更新数值的一类参数,比如
BN
层有四个更新参数:平移因子、缩放因子、均值和方差。不论什么网络层,本质一样,计算不复杂。
2、计算量分析
一些专业名称:
FLOPs
:floating point operations
,浮点运算次数,即计算量,可以用来衡量算法模型时间复杂度。
FLOPS
:Floating-point Operations Per Second
,每秒所执行的浮点运算次数,即硬件的计算速度,是一个衡量硬件性能的指标。
MACCs
:multiply-accumulate operations
,乘-加操作次数,是将一次乘法与一次加法和为一个操作。
沿用上面的符号定义
-
全连接
全连接层前一层输出神经元数量与当前层神经元数量是全部参与计算的,所以构成一个$F_1xF_2$的
\[\begin{align} &FLOPs_w=(F_2+(F_2-1)) \\ &FLOPs_b=F_1+F_2 \\ &FLOPs=FLOPs_w+FLOPs_b=2*F_1*F_2 \end{align}\]weight
矩阵和$F_2$维的bias
向量,weight
矩阵与输入特征做点积,再与bias
向量做加法,总的运算次数为: -
卷积
卷积层的输出特征图中每一个特征点都是由一组卷积核在输入特征图中做一次卷积运算得到的,以二维卷积为例,每组卷积核也是由一组
weight
和bias
组成的。每个输出特征点需要的运算次数为:
\[\begin{align} &FLOPs_w=f^2*C_{in}+(f^2-1)*C_{in}+C_{in}-1=2*f^2*C_{in}-1 \\ &FLOPs_b=1 \end{align}\]总共有${K_{out}}^2*C_{out}$个特征点,因此总共运算次数为:
\[\begin{align} FLOPs&=(FLOPs_w+FLOPs_b)*{K_{out}}^2*C_{out} \\ &=2*f^2*C_{in}*{K_{out}}^2*C_{out} \end{align}\] -
可分离卷积
根据上面参数量计算的分析结果,需要注意
Depthwise Conv
过程中每组卷积核只与每组输入特征做运算,而不是与所有输入特征做运算,Pointwise Conv
和一般卷积无异,因此可分两部分对可分离卷积的运算次数作分析:
\[\begin{align} &FLOPs_w=f^2*1+(f^2-1)*1 \\ &FLOPs_b=1 \\ &FLOPs_{dw}=(FLOPs_w+FLOPs_b)*({K_{out}}^2*C_{in})=2*f^2*{K_{out}}^2*C_{in} \end{align}\]Depthwise Conv
:
\[FLOPs_{pw}=2*1*1*C_{in}*{K_{out}}^2*C_{out}\]Pointwise Conv
:总运算次数为:
\[FLOPs=FLOPs_{dw}+FLOPs_{pw}=(2*f^2+C_{out})*{K_{out}}^2*C_{in}\]比较卷积与可分离卷积的计算量,可以发现可分离卷积的计算量比卷积的小了很多倍,提高了运算速度。
-
激活函数
激活函数与正则化函数的运算没有点积运算,它们的计算量就是对每个输入值的数学运算的次数,比如
ReLU
激活函数对每个输入标量只有一次运算,Sigmoid
激活函数有四次运算,等等。 -
池化
求得输出特征图的每个特征点的运算次数,就能得到池化的运算次数,但是池化有很多类型,滤波器的尺寸也不同,得到的池化运算次数就不同,比如$2x2$均值池化,每个特征点需要运算4次:三次加法和一次乘法(除法就是乘法);最大池化则是比较三次大小即可,最后再乘以输出特征大小$K^2*C_{out}$。
-
其他网络
统计乘法与加法的总数就是总的
FLOPs
。
四、模型推理速度分析
相同FLOPs
的两个模型其运行速度是会相差很多的:
-
计算平台不同
硬件对算法模型的影响。
-
MAC(
Memory Access Cost
)相同
FLOPs
的算法对内存的访问情况不同,对速度也是有影响的。 -
并行度
相同
FLOPs
的网络结构可能存在不一样的运行方式,有的是串行网络,有的是多路并行。
最后
参考文章:
当padding为“SAME” 和 “VALID”卷积层输出尺寸的大小的计算
CNN 模型所需的计算力(flops)和参数(parameters)数量是怎么计算的? - DengBoCong的回答
CNN 模型所需的计算力(flops)和参数(parameters)数量是怎么计算的? - AI生成创作的回答
声明
本文仅作为个人学习记录。