想提速但TensorRT的FP16不得劲?怎么办?在线支招!

2023-10-19 11:03:23 浏览数 (1)

问题的开始

前些天尝试使用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慢慢看吧。

代码语言:javascript复制
(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往往都很高。

比如RTX2080TIPeak Fp16 Tflops26.9Peak Fp32 Tflops13.4,几乎两倍多的差距。很显然使用FP16不论是推理还是训练都会比FP32快一些。

不过FP16快是快,但因为指数位和尾数位都比FP32要小,其动态范围和精度也大大减小了,如果一个数超出了FP16的动态范围,那么显然是会溢出的

寻找结果错误原因

由上所述,问题的原因应该比较明了了,大概率是模型中某一层的计算FP16因为动态范围和精度不够,导致某个op节点的计算值溢出了。然后牵一发而动全身,整个模型后面的所有层都崩了。

而这种情况最直接最简单寻找问题op的方法就是逐层打印输出观察,然后从输入到输出每一层对比输出结果观察哪一层出问题了。

但是对于转化后的TensorRT模型我们并不能做到这一点,呃

0 人点赞