- yolov5转tensorrt出现以下问题,是不是很头大,于是我对比了模型的前处理和后处理,断定我写的c++实现是没有问题的,后续通过分析onnx的结果,发现onnx是没有问题的,起初trt警告cuda版本有点不兼容,我还以为是这样的,后续我解决了这个cuda版本问题后发现,还有这样错误的结果。那么我断定,onnx2trt出错了。但是在转换过程中没有报错。
Polygraphy
废话不多少直接上神器
- 安装Polygraphy
pip install colored polygraphy --extra-index-url https://pypi.ngc.nvidia.com
- 逐层对比模型精度
polygraphy run yolov5.onnx \
--trt --onnxrt \
--trt-outputs mark all \
--onnx-outputs mark all \
--atol 1e-3 --rtol 1e-3 \
--fail-fast \
--val-range [0,1]
这里对相关参数简单介绍下
(1)–trt : 采用TensorRT进行推理
(2)–onnxrt: 采用ONNX runtime进行推理
(3)–xx-outputs mark all: 将模型中的每个节点设置为输出节点,方便逐层比较,但在有些情况下由于Trt会进行算子融合可能会触发异常,导致比较失败
(4)–atol, --rtol: 绝对精度和相对精度,默认值为1e-5,因为YOLOv5把Decode部分也包含进去,所以存在稍微大些的误差也是可以容忍的,所以此处我设置为1e-3
(5)–fail-fast:在第一次出现精度异常的节点时退出比较过程,方便查看首次出现异常的节点
(4)–val-range:设置输入的数据值范围,当然你也可以设置自定义的输入数据集
等待一会儿,果然存在精度异常的节点,
(85.1, 94.2) | 11 |
[I] Error Metrics: 361
[I] Minimum Required Tolerance: elemwise error | [abs=65.657] OR [rel=0.69697] (requirements may be lower if both abs/rel tolerances are set)
[I] Absolute Difference | Stats: mean=12.918, std-dev=11.35, var=128.83, median=13.033, min=0 at (0, 0, 0, 5, 0), max=65.657 at (0, 2, 2, 6, 0), avg-magnitude=12.918
[I] ---- Histogram ----
Bin Range | Num Elems | Visualization
(0 , 6.57) | 13417 | ########################################
(6.57, 13.1) | 5898 | #################
(13.1, 19.7) | 7588 | ######################
(19.7, 26.3) | 6630 | ###################
(26.3, 32.8) | 3228 | #########
(32.8, 39.4) | 1173 | ###
(39.4, 46 ) | 335 |
(46 , 52.5) | 78 |
(52.5, 59.1) | 41 |
(59.1, 65.7) | 12 |
[I] Relative Difference | Stats: mean=0.34557, std-dev=0.26458, var=0.070005, median=0.40489, min=0 at (0, 0, 0, 5, 0), max=0.69697 at (0, 2, 78, 70, 0), avg-magnitude=0.34557
从这里可以看出来361编号的操作是有问题的,误差很大,最大能达到65.657。
查看TensorRT发布文档看看能不能找到说明。
发现原来在TensorRT7中elementwise算子在一个输入为常量并且维度大于4时,会融合成scale算子,导致其不支持基于元素的操作(出现异常,也不报错…脑裂)。跑了下TensorRT8,一切岁月静好,文档果然没骗我~~
tensorrt7
tensorrt8
- 安装onnx-graphsurgeon
pip install onnx_graphsurgeon --index-url https://pypi.ngc.nvidia.com
针对TensorRT7中提到的,我们可以在contant和elementwise之间追加一个identity来防止算子融合带来的精度问题。代码展示如下:
from collections import OrderedDict
import numpy as np
import onnx
import onnx_graphsurgeon as gs
# 导入onnx模型
graph = gs.import_onnx(onnx.load("yolov5s.onnx"))
# 注册Identity层
@gs.Graph.register()
def identity(self, a):
return self.layer(op="Identity", inputs=[a], outputs=["identity_out_gs"])[0]
# 需要修改的Mul层名称
mul_names = ["Mul_261", "Mul_227", "Mul_295"]
mul_nodes = [node for node in graph.nodes if node.name in mul_names]
for mul in mul_nodes:
identity = graph.identity(mul.inputs[1])
mul.inputs[1] = identity
# 保存新模型
onnx.save(gs.export_onnx(graph), "yolov5s_new.onnx")
```![在这里插入图片描述](https://img-blog.csdnimg.cn/af957b4ab8b04e778684e6b7f7cde633.jpeg)