前言
在深度学习的研究中,神经网络有时候不光要求网络的精度,在调整模型或者精简模型的时候,需要知道模型的运算量有多大。一般来说模型的参数量能够大致反映模型量级,但是运算量才是我们真正需要知道的。本系列文章旨在介绍神经网络中各种网络层的运算量计算方法,以及代码实现自动计算网络模型运算量。
神经网络的计算量主要体现在卷积层、全连接层,其它的激活层、BatchNorm层、池化层等占比重较少。
运算量计算方法
以下计算方法均针对batch size为1的情况,若batch size不为1,则乘上batch size即可。
1、卷积层
一般来说,采用了滑动窗口实现的卷积操作忽略了非线性计算开销,其FLOPs计算公式为:
F
L
O
P
s
=
H
o
u
t
W
o
u
t
(
C
i
n
(
2
K
2
−
1
)
/
g
+
1
)
C
o
u
t
FLOPs = H_{out} W_{out} ( C_{in} (2K^2-1)/g + 1)C_{out}
FLOPs=HoutWout(Cin(2K2−1)/g+1)Cout
其中:
H
o
u
t
、
W
o
u
t
H_{out}、W_{out}
Hout、Wout分别代表卷积层输出的高和宽,
C
i
n
C_{in}
Cin为输入的通道数,
K
K
K为卷积核size,
C
o
u
t
C_{out}
Cout为输出的通道数,
g
g
g为组卷积中的分组数groups,
+
1
+1
+1是针对有bias的卷积。
具体解释:
- 对于输出tensor中的每一个数值,都对应着一次卷积操作,而这一次卷积操作是卷积核对于输入所有通道的一个局部区域进行的计算,也就是 ( 2 K 2 − 1 ) ∗ C i n (2K^2-1)*C_{in} (2K2−1)∗Cin的运算量(加法比乘法少1次)
- 输出一共 H o u t ∗ W o u t ∗ C o u t H_{out}*W_{out}*C_{out} Hout∗Wout∗Cout个值,所以运算量为 H o u t ∗ W o u t ∗ C o u t ∗ ( 2 K 2 − 1 ) ∗ C i n H_{out}*W_{out}*C_{out}*(2K^2-1)*C_{in} Hout∗Wout∗Cout∗(2K2−1)∗Cin
- 对于组卷积,输入按通道分为 g g g组,针对每一组进行卷积操作,每组得到的结果组成最终输出,所以卷积操作运算量为: ( H o u t ∗ W o u t ∗ C o u t g ∗ ( 2 K 2 − 1 ) ∗ C i n g ) ∗ g = H o u t W o u t C o u t C i n ( 2 K 2 − 1 ) / g (H_{out}*W_{out}*\frac{C_{out}}{g}*(2K^2-1)*\frac{C_{in}}{g})*g =H_{out} W_{out} C_{out} C_{in}(2K^2-1)/g (Hout∗Wout∗gCout∗(2K2−1)∗gCin)∗g=HoutWoutCoutCin(2K2−1)/g
- 如果有bias的话,卷积操作过后,每个数值还需要加上bias,即 H o u t ∗ W o u t ∗ C o u t H_{out}*W_{out}*C_{out} Hout∗Wout∗Cout次加法运算
2、全连接层
全连接层FLOPs计算公式为:
F
L
O
P
s
=
(
(
2
C
i
n
−
1
)
+
1
)
C
o
u
t
=
2
C
i
n
C
o
u
t
FLOPs = ((2C_{in}-1) +1)C_{out} = 2C_{in}C_{out}
FLOPs=((2Cin−1)+1)Cout=2CinCout
其中
C
i
n
、
C
o
u
t
C_{in}、C_{out}
Cin、Cout分别为全连接层的输入和输出维数,
+
1
+1
+1针对bias。
这个应该很好理解,全连接层可以看作是向量和矩阵的乘法,同样是加法比乘法少1次。
3、池化层
池化层可以分为全局池化和一般池化两种,其FLOPs计算公式可以写为:
F
L
O
P
s
=
{
C
i
n
H
i
n
W
i
n
全局池化
C
o
u
t
H
o
u
t
W
o
u
t
K
2
一般池化
FLOPs = \begin{cases} C_{in}H_{in}W_{in} & \text{全局池化} \\[2ex] C_{out}H_{out}W_{out}K^2 & \text{一般池化} \end{cases}
FLOPs=⎩⎨⎧CinHinWinCoutHoutWoutK2全局池化一般池化
其中每个变量代表的含义与上述卷积层计算方法中一样。
具体解释:
- 全局池化:针对输入所有值进行一次池化操作,不论是max、sum还是avg,都只需要对每个值算一次。
- 一般池化:对于输出的每一个值,都是一次池化操作,每次池化操作的计算量就是池化过滤器参数的数量。
4、激活层
以使用最多的ReLU为例:
R
e
L
U
(
x
)
=
{
0
,
x
<
0
x
,
x
>
=
0
ReLU(x) = \begin{cases} 0, & \text{$x<0$} \\[2ex] x, & \text{$x>=0$} \end{cases}
ReLU(x)=⎩⎨⎧0,x,x<0x>=0
可见,激活层的运算量只跟输入大小有关,少数激活函数的运算量与输入为线性关系,基本上可以认为等于输入的大小。
5、BN层
对于BN来说,一般标配是conv + bn + relu,在上线使用过程中,一般会将bn和卷积层合并,相当于没了bn这一层,变成 conv +relu,所以bn其实不用考虑。
到此,已经介绍了神经网络中主要运算量的计算方式,后续将会根据上述计算方法实现对神经网络运算量的自动计算。
部分不常用的网络层没有介绍计算方法,之后有时间会加上,以上为个人理解,欢迎批评指正。