最近在用PyTorch训练一个语义分割模型,准备部署到Jetson上,所以想把模型转成TensorRT。但折腾了两天,各种报错快把我整懵了。比如动态shape怎么处理?有些自定义算子(像F.interpolate)在onnx导出时总警告,转trt后直接报错。还有量化精度掉得厉害,int8比fp32掉了5个点,不知道是不是校准集没选好。另外,有没有办法在不重写网络结构的情况下,让TensorRT自动融合一些算子?我看官方文档说支持Layer Fusion,但实际效果好像不明显。希望有经验的大佬能分享下实战经验,特别是从PyTorch->ONNX->TensorRT这条链路下的常见坑和优化技巧,先谢过了!
PyTorch转TensorRT时,有哪些常见的踩坑点和优化思路?
全部回复
共 163 条动态shape确实头疼,试试固定输入尺寸或用TRT8的显式batch模式。校准集得选跟实际场景分布一致的,要不精度掉得厉害。
动态shape这块确实头大,我一般用固定batch size和输入尺寸,或者用TensorRT的ShapeTensor配合IF条件处理,能省不少事。F.interpolate在onnx里建议用torch.nn.functional.interpolate的align_corners参数调成False,有时候能绕过警告。int8精度掉5个点的话,校准集最好跟实际场景分布一致,多试几组数据,或者换成逐通道量化试试。Layer Fusion不用太执着,有些手动优化比如把BN和Conv提前融合,效果反而更直接。
动态shape确实头疼,我一般直接固定输入尺寸或者用trt的optimization profile来兜底,但profiles设多了显存也涨得厉害。F.interpolate这个坑我踩过,后来改成用torch.nn.functional.upsample或者自定义plugin才稳下来。int8掉点的话,校准集尽量贴近真实场景分布,别偷懒随机采样,我试过分桶校准能稍微好点。层融合这块别太指望自动,有些trick得手动调网络结构,比如把bn和conv合并后再导出,trt自己反而容易漏掉。
动态shape确实是个大坑,我建议你在onnx导出时把输入尺寸固定下来,或者在trt里用explicit batch模式配合profile处理多尺度,否则后续算子解析很容易崩。F.interpolate那个警告我碰过,可以试试用torch.nn.functional.upsample代替,或者直接写一个简单的resize op注册到onnx里,虽然麻烦但能跑通。int8掉点5个的话,校准集大概率有问题,最好从训练集里挑一批多样性的数据,同时试试trt的熵校准和逐通道量化,有些场景下混合精度(int8+fp16)反而更稳。Layer Fusion这东西别太指望自动,trt对常见conv+bn+relu融合还行,但像分割网络里的跳连和上采样,它经常摆烂,不如手动把一些冗余结构砍掉,或者用onnx-simplifier优化一下图结构再转。另外提一嘴,如果Jetson上跑,记得把显存池和dla核心用上,这些硬件特性对实时性提升挺明显的。
动态shape建议固定输入尺寸,INT8校准集最好覆盖真实场景,层融合可以试试TRT8以后的版本。
动态shape建议用TRT8的显式batch模式,自定义算子可以试试写plugin,校准集最好从训练数据里均匀抽500张以上。
跟你一样,这条链路我踩了得有俩月的坑,动态shape这块建议先别急着上,先在onnx里把input设成固定尺寸跑通再说,后面再慢慢加dynamic axes,不然报错定位成本太高了。F.interpolate这个我一般会用torch.nn.functional.upsample或者自己写一个简单的最近邻上采样替换掉,onnx导出时能省不少警告。int8精度掉5个点其实还算正常,校准集最好选跟实际场景分布一致的图片,数量不用太多,两三百张就够了,关键是要覆盖全类别。算子融合这块吧,trtexec里加个--best或者--fp16有时候能自动触发一些融合,但效果确实看模型,我试过有些层它死活不融,最后还是得手写plugin或者改网络结构。你用的哪个Jetson型号?Orin系列对动态shape支持好一点,Xavier的话建议直接固定尺寸。
动态shape建议固定输入尺寸先跑通,int8校准集选100-500张代表性图片效果会好很多。
动态shape建议用onnx的dynamic_axes参数固定几个常用尺寸,量化校准集得覆盖真实场景分布才行。
动态shape确实是个大坑,建议导出onnx时固定一些维度,或者在trt里用explicit batch模式配合profile来设置范围。F.interpolate这种算子最好用torch.nn.functional.interpolate的重写插件或者直接换成trt支持的resize层,不然容易炸。int8掉点五个点的话,校准集尽量覆盖真实场景分布,而且可以试试开启fp16混合精度,有时候比纯int8省心不少。
动态shape确实是个老大难,我一般会在onnx导出时固定一个batch和输入尺寸,实在要动态就先用torchscript试试。F.interpolate这种算子建议用onnx支持的resize替代,或者在trt里用plugin实现。int8精度下降5个点有点多了,校准集建议选和真实场景分布接近的数据,迭代次数也可以加大。至于layer fusion,trt默认会做一部分,但像一些轻量级网络收益有限,可以试试用trtexec的--best参数自动调优。
同感,最近也在折腾这个链路,动态shape确实是个大坑。我试过用onnx的dynamic_axes参数,但转出来的trt引擎在batch size变化时还是会炸,最后干脆固定输入尺寸,推理时做resize和letterbox填充,虽然多了一步预处理但稳得很。F.interpolate这个问题我遇到过,后来用torch.nn.functional.upsample配合mode='nearest'勉强绕过去了,但如果你需要线性插值的话可能得自己写个plugin。int8掉点5个其实不算太离谱,校准集建议用训练集里分布均匀的几百张图,类别覆盖要全,另外可以试试开启trt的熵校准或者分桶校准,效果比默认的均匀采样好。Layer fusion这块,我观察下来trt对常见的conv+bn+relu融合得不错,但跨层跳跃连接或者多分支结构它就不太积极了,可以试试把网络里多余的identity层删掉,或者把一些add操作合并到前面的卷积里手动fuse。还有个小技巧,导出onnx时设置opset_version=11或13,有些坑在低版本下更常见。
动态shape确实容易踩坑,建议导出onnx时固定一个batch和输入尺寸,后续用TensorRT的优化profile来处理不同尺寸,能省不少麻烦。F.interpolate的话,我一般用torchvision的ops替代或者直接重写一个支持导出的版本,不然onnx那关就过不去。int8校准集建议选跟实际推理场景分布接近的数据,数量不用太多但覆盖要全,不然精度会崩。Layer Fusion其实挺依赖模型结构的,有些简单融合框架会自动做,但复杂场景可以试试手动fuse一些conv+bn+relu,效果更可控。
我最近也在搞这个,动态shape确实头疼,我的做法是固定batch和输入尺寸,或者用trt的profile多设几个范围。F.interpolate的话,可以试试在onnx导出前换成torchvision里的操作,能少很多警告。int8掉点5个其实不算太离谱,校准集尽量跟实际场景分布一致,我一般会多跑几百张图看看。至于算子融合,有时候需要手动调一下trt的builder配置,比如把strict_type_constraints关掉,或者试下精度约束,效果会好一些。
动态shape确实坑多,我一般直接固定输入尺寸或者用trt的profile范围来兜底,不然onnx导出时维度推导很容易炸。F.interpolate这个我试过用onnx的resize算子代替,或者干脆在导出前把上采样写成固定倍数,能少很多警告。int8校准集我建议用真实场景图,数量500张左右比较稳,跑完看看每层的直方图分布,有时候是某些层敏感度太高导致的掉点。至于layer fusion,你可以试试torch-tensorrt或者polygraphy的auto-optimize,有些手动标记的fuse策略比自动融合更靠谱。
动态shape最好转成固定batch再转trt,不然onnx导出那步就够喝一壶的。int8精度掉5个点其实挺正常,换大点校准集试试看。
动态shape确实头疼,我一般用固定batch加padding解决,或者转trt时设置optimization profile。F.interpolate建议用torch.nn.functional.interpolate的onnx导出兼容模式,或者干脆换成固定尺寸的resize算子。int8校准集最好选覆盖真实场景的几百张图,别用训练集随机抽,另外可以试试用trt的calibrator自己写个熵校准。layer fusion你检查下有没有用trt的plugin替换掉不支持的算子,有些自定义层不注册的话确实融合不了。
动态shape建议用trtexec的min/max/opt profile,别指望onnx自动搞定。int8校准用几百张真实场景图比用训练集效果好不少。
动态shape确实头疼,我一般直接固定输入尺寸,或者用trtexec的--minShapes参数分段处理。F.interpolate的问题可以试试在onnx导出时用torch.onnx.export的opset_version=11以上,有些版本能绕过警告。int8掉点5个其实算正常的,校准集最好覆盖真实场景的分布,多跑几轮校准试试。算子融合我感觉跟TensorRT版本关系挺大,8.x以后自动融合效果好了不少,但有些小众算子还是得手动写plugin。
动态shape建议用trt8.x的显式batch模式,f.interpolate换torchvision的roi_align试试能稳很多。