用tensorflow计算模型复杂度(浮点计算量,FLOPs)
1.应用模型说明
首先明确模型的计算量一般是衡量冻结模型(.pd)的,.ckpt在权重和偏置按照高斯分布初始化时一般计算量要大于冻结模型,所以我们要首生成模型的冻结文件(.pd)。
2.计算复杂度衡量标准
FLOPS:注意全大写,是floating point operations per second的缩写,意指每秒浮点运算次数,理解为计算速度。是一个衡量硬件性能的指标。
FLOPs:注意s小写,是floating point operations的缩写(s表复数),意指浮点运算数,理解为计算量。可以用来衡量算法/模型的复杂度。
MACCs:是multiply-accumulate operations),也叫MAdds,意指乘-加操作(点积运算),理解为计算量,也叫MAdds, 大约是 FLOPs 的一半。
1TFLOPs=1e3 GFLOPs=1e6 MFLOPs=1e9 BFLOPs=1e12
3.计算原理
3.1.1 全连接层
全连接层的计算
y = matmul(x, W) + b
权重 WW 是一个 I×JI×J 矩阵,输入 xx 是 II 维实值向量,bb 是 JJ 维偏置。输出 yy 也是 JJ 维实值向量。FC 层的 MACCs 也不难计算。
上文例子是向量与向量的点积,FC 是向量与矩阵的点积,每一组点积发生在输入 xx 同权重 WW 某一列之间,计有 II MACCs,一共要计算 JJ 组点积,所以 FC 层的 MACCs 总计 I×JI×J,跟权重的尺寸一致。
偏置项 bb 对 MACCs 的影响可以忽略不计。而上面也提到 MACCs 中加法比乘法少一次, bb 刚好补上了这个缺。
所以,对 II 的输入、权重为 I×JI×J 的权重矩阵和 JJ 的输出,MACCs 为 I×JI×J ,FLOPS 为 (2I−1)×J(2I−1)×J 。
举例:
一个全连接层,输入 100 维,输出 300 维,MACCs 有 300×100=30,000300×100=30,000 。不过,如果一个全连接层紧接着卷积层,输入可能没有指定长度 II 但有 feature map 的尺寸比如(512, 7, 7)。在 Keras 里就需要写一行 Flatten 把它展平,这样此时的 II 就是 512×7×7512×7×7 了。
3.1.2 激活函数
FC 完了接下来通常有个激活函数,ReLU 或者 Sigmoid。激活函数的计算没有点积,所以只用 FLOPS 衡量。
对输出为 JJ 的 FC 层,ReLU 有 JJ FLOPS:
y = max(x, 0)
相比之下 Sigmoid 就复杂很多。
y = 1/(1+exp(-x))
我们把加减乘除、指数、平方根等等运算都算作一次 FLOPS,这里有除法、加法、指数和减法四种运算,所以 FLOPS 就是 J×4J×4 。
相对于全连接的矩阵运算,激活函数的计算量通常忽略不计(博主注:不一定,看情况)。
3.1.3 卷积层
卷积层要单独算而不是用全连接层的结论,是因为输入至少是三维的:H×W×CH×W×C 。对于这样的卷积层,MACCs 有:
K×K×Cin×Hout×Wout×CoutK×K×Cin×Hout×Wout×Cout
解释一下:
- 输出的 feature map 里每个通道上有 Hout×WoutHout×Wout 个元素,
- 权重以 K×KK×K 大小的窗口,在所有的 CinCin 个通道上做点积,
- 共有 CoutCout 个卷积核,上述操作重复了 CoutCout 次
同样,这里也忽略了偏置和激活函数。不应该忽略的是 stride(步长)、dilation factors(漏孔/膨胀卷积)、padding(填充),这就是为什么直接从输出尺寸 Hout×WoutHout×Wout 开始算的原因——都已经考虑在内了。
举例:
3×33×3 卷积,128 个 filer,输入的 feature map 是 112×112×64112×112×64 ,stride=1
,padding=same
,MACCs 有:
3×3×64×112×112×128=924,844,0323×3×64×112×112×128=924,844,032
接近十亿的乘-加操作。
3.1.4 Batch Normalization
计算公式:
z = gamma * (y - mean) / sqrt(variance + epsilon) + beta
首先以输入为卷积层的情况为例。
每个通道上都存在一组 mean
、beta
、gamma
、variance
,CC 个通道就有 C×4C×4 个可学习的参数。而且 BN 是作用在每一个元素上的,这样看来,造成的 FLOPS 应该不少。
但有趣的是,在 BN 直接连接卷积层的情况下,即 Conv-BN-ReLU 时,通过一组推导,可以将 BN 的计算整合到卷积层当中(注意这是 inference 的情况,跟训练阶段差别很大),从而消去的 BN 层造成的 FLOPS。如果是 Conv-ReLU-BN 的结构这一套就行不通了。
( BN 层的计算结合到 Conv 层中去,BN 层的 FLOPS 消失了,Conv 层需要乘一个常系数)
即从结果上来说,在 inference 时模型中的 BN 层实际被消去了。
3.1.5 其他层
像 Pooling 层虽然确实很关键,但没有用到点积运算,所以 MACCs 不能很好地衡量这部分计算消耗。如果用 FLOPS,可以取 feature map 的尺寸然后乘一个常系数。
如 maxpooling 层,stride=2
、filter_sz=2
(即输出保持相同尺寸),112 x 112 x 128
的feature map,FLOPS 就是 112 x 112 x 128 = 1,605,632
。相对卷积层和全连接层的运算,这个计算量比较小,所以也可以忽略不计。
RNN 这里不做讨论。简单来说,以 LSTM 为例,计算主要是两个大的矩阵乘法,sigmoid,tanh 和一些元素级的操作。可以看成两个全连接层的运算,所以 MACCs 主要取决于输入、输出和隐状态向量的尺寸。点积运算还是占了大头。
4.tensorflow实现:
#coding=utf-8
'''
模型复杂度测试
替换模型位置即可。
'''
from tensorflow.python.framework import graph_util
import tensorflow as tf
from tensorflow.contrib.layers import flatten
def stats_graph(graph):
flops = tf.profiler.profile(graph, options=tf.profiler.ProfileOptionBuilder.float_operation())
params = tf.profiler.profile(graph, options=tf.profiler.ProfileOptionBuilder.trainable_variables_parameter())
print('GFLOPs: {}; Trainable params: {}'.format(flops.total_float_ops/1000000000.0, params.total_parameters))
def load_pb(pb):
with tf.gfile.GFile(pb, "rb") as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
with tf.Graph().as_default() as graph:
tf.import_graph_def(graph_def, name='')
return graph
with tf.Graph().as_default() as graph:
#模型开始处××××××××××××××××××××××××××××
# ***** (1) Create Graph *****
#模型结束××××××××××××××××××××××××××××
print('stats before freezing')
stats_graph(graph)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# ***** (2) freeze graph *****
output_graph = graph_util.convert_variables_to_constants(sess, graph.as_graph_def(), ['output'])
with tf.gfile.GFile('graph.pb', "wb") as f:
f.write(output_graph.SerializeToString())
# ***** (3) Load frozen graph *****
with tf.Graph().as_default() as graph:
graph = load_pb('./graph.pb')
print('stats after freezing')
stats_graph(graph)
结果:
/home/wds/anaconda3/envs/py27/bin/python /home/wds/project/CNN-test/TSIGN/FLOPs_Compute.py
stats before freezing
Parsing Inputs...
=========================Options=============================
======================End of Report==========================
MFLOPs: 1.438484; Trainable params: 61666
=========================Options=============================
==================Model Analysis Report======================
node name | # parameters
_TFProfRoot (--/0 params)
======================End of Report==========================
MFLOPs: 1.198208; Trainable params: 0
Process finished with exit code 0
注意:
1.计算时需替换模型实现部分:
2.placeholder 不要出现None, 要给一个具体值不然结果计算不对。
参考:
https://blog.csdn.net/leayc/article/details/81001801
更多推荐
所有评论(0)