问题的开始
前些天尝试使用TensorRT转换一个模型,模型用TensorFlow训练,包含LSTM Transform CNN,是一个典型的时序结构模型,包含编码解码结构,暂称为debug.onnx
吧。
这个debug.onnx
使用tf2onnx导出,导出后tf2onnx会自动对这个onnx做一些优化,例如常量折叠、算子融合等等一些常规操作,一般来说这些操作不会影响网络结构(也会出现影响的情况!之后老潘会说),而且有助于模型的优化。
然后导出来之后使用onnxruntime
简单测试一下导出模型是否正确,是否与TensorFlow的结果一致。如果正确我们再进行下一步。
模型体积不大(30MB左右),但是op节点很多,以至于使用Netron打开前会提示:
嗯?不就是个警告么,Yes就行!
然后等了N久(1个小时),还是提示打不开,节点太多了-_-||。
实在打不开咋办,可以通过onnx的python接口读取onnx模型,然后将网络结构打印出来:
代码语言:javascript复制import onnx
model_path = '/home/oldpan/code/models/debug.onnx'
onnx_model = onnx.load(model_path)
onnx.checker.check_model(onnx_model)
f = open('net.txt', 'w ')
print(onnx_model, file=f)
f.close()
这里将onnx_model
的网络结构输出为net.txt
,好了,打开这个txt慢慢看吧。
(fun) oldpan@oldpan-fun:~/code$ cat net.txt | head
ir_version: 4
producer_name: "tf2onnx"
producer_version: "1.6"
graph {
node {
input: "input.1"
input: "conv1.weight"
output: "340"
op_type: "Conv"
attribute {
老潘这里提一句,并不是模型体积大才代表模型复杂,模型大小只会影响这个模型占用内存或者显存的量,整个模型执行的时间与其中的OP计算量也是有关系的。
PS:还是检测类的模型好一些,没有错综复杂的op,全是卷积一块一块的,简单粗暴。
FP32与FP16的代沟
好了!有这个debug.onnx
模型,使用TensorRT自带的trtexec
转换一下吧。
这里使用的TensorRT是最新的TensorRT-7.2.3.4
版本,使用的显卡为RTX2080Ti
。
转换过程中没有任何问题,除了是有一些int64
截断和Type
的警告,但是一般来说这种警告对结果是没有影响的(如果有有影响的例子,请告诉我~):
转化好之后,简单测试下FP32的结果是正确的,看起来不错,对比了下FP32与TensorFlow原生推理的精度,精度相差不多(万分之一的差距),还是有使用价值的。
简单测一下速度,嗯…相较TensorFlow原来差不多500Q的速度,FP32也才550Q,提升10%不到啊。
还咩有具体看每个层的耗时,老潘初步推断,整个模型中的op比较多也比较复杂,不是那种像VGG、unet这个一大块一大块卷积相连的,更多的是一些细小的op,TensorRT优化起来作用并不大。怎么形容,一个resnet50转化为onnx的node节点数也就150左右,而我们的这个debug.onnx
模型足足有3000多个node节点,转化为TensorRT格式的时候使用trt_network->getNbLayers();
看了下,debug.trt
足足有9000多个节点。
哦mygodholyshit。
好了不纠结那么多,能转过来就好。
让我试试FP16的速度咋样吧,嗯,1000Q,差不多500q的两倍,还是有收益的。以上实验是在RTX2080TI
上做的,20系列有FP16计算单元,所以模型转化为FP16是有速度收益的。如果我们用的是1080TI
,那么模型转化为FP16只有模型体积的缩小,模型运行速度并不会提升,反而会有下降。
测试一下FP16的结果
铺垫那么多…FP16的提速固然是可喜的,但是结果完全不对。
输出的置信度和标签完全不对。
正确的结果[24,23,4,5,2],[1.000,0.99,0.99,0.99,1.0]
。
错误的结果[5,5,5,5,7],[0.768,0.65,0.5,0.5,0.3]
。
肿么办,肿么办,完全没有头绪,感觉FP16和FP32的代沟还挺大的。
想要弄清楚原因,首先要明白什么是FP16
。
关于FP16
FP16之所以计算那么快,最重要的原因是因为FP16只占两个字节,相比FP32所占的内存更小,实现的指令也比FP32更快。有专门FP16计算单元的显卡,相比FP32,FP16的flops往往都很高。
比如RTX2080TI。Peak Fp16 Tflops
为26.9
而Peak Fp32 Tflops
为13.4
,几乎两倍多的差距。很显然使用FP16不论是推理还是训练都会比FP32快一些。
不过FP16快是快,但因为指数位和尾数位都比FP32要小,其动态范围和精度也大大减小了,如果一个数超出了FP16的动态范围,那么显然是会溢出的。
寻找结果错误原因
由上所述,问题的原因应该比较明了了,大概率是模型中某一层的计算FP16因为动态范围和精度不够,导致某个op节点的计算值溢出了。然后牵一发而动全身,整个模型后面的所有层都崩了。
而这种情况最直接最简单寻找问题op的方法就是逐层打印输出观察,然后从输入到输出每一层对比输出结果观察哪一层出问题了。
但是对于转化后的TensorRT模型我们并不能做到这一点,呃