最近在用PyTorch训练一个语义分割模型,准备部署到Jetson上,所以想把模型转成TensorRT。但折腾了两天,各种报错快把我整懵了。比如动态shape怎么处理?有些自定义算子(像F.interpolate)在onnx导出时总警告,转trt后直接报错。还有量化精度掉得厉害,int8比fp32掉了5个点,不知道是不是校准集没选好。另外,有没有办法在不重写网络结构的情况下,让TensorRT自动融合一些算子?我看官方文档说支持Layer Fusion,但实际效果好像不明显。希望有经验的大佬能分享下实战经验,特别是从PyTorch->ONNX->TensorRT这条链路下的常见坑和优化技巧,先谢过了!
PyTorch转TensorRT时,有哪些常见的踩坑点和优化思路?
全部回复
共 182 条校准集没代表性的话int8掉点很正常,建议多塞点难样本进去再试试。
动态shape记得用onnx的dynamic_axes,interpolate换成nearest导出基本不报错,int8掉点先查校准集覆盖度。
动态shape这块建议直接在onnx导出时用dynamic_axes标好,转trt时开optimization_profile,不然默认全静态后面推理尺寸一变就崩。interpolate的问题可以试试换成resize加align_corners=False,或者用trt自带的resize层替代,能省不少事。int8掉点5个算正常,校准集最好从验证集里抽几百张有代表性的,别用训练集,另外可以试试逐层敏感度分析,把segmentation head那几层保fp16。算子融合别抱太大期望,trt对非标准结构的融合有限,实在不行拿polygraphy跑一遍看哪层拖后腿。
动态shape这块建议直接用trtexec配合onnx的dynamic_axes指定,别指望polygraphy自动推,很容易翻车。interpolate那个警告多半是onnx opset版本太低,升到13以上再试,还报错就手动换成resize算子。int8掉点这么狠大概率是校准集分布不对,拿几百张有代表性的图跑熵校准,另外记得把BN折叠掉再量化。Layer Fusion在trt里其实挺激进的,你导出onnx时先用onnxsim过一遍,融合效果会好很多,不然残图太多根本合不了。
F.interpolate那个警告我太熟了,ONNX导出时大概率是因为尺寸参数不是静态的,你试试把scale_factor固定成常量,或者换成nn.Upsample指定size,转TRT能少一半报错。动态shape这块坑确实深,Jetson上如果推理分辨率固定,最好直接写死shape,省得跟optimization profile较劲,我当初折腾半天最后发现固定尺寸最省心。int8掉5个点不算离谱,校准集一定要覆盖你实际场景的分布,别随便拿几十张图糊弄,我当时用训练集的一个子集效果就比随机选好很多。Layer Fusion在TRT里其实挺被动的,它主要靠builder自己判断,你可以在onnx里先把Conv+BN+ReLU手动融合掉,或者用trtexec加--best看它到底融了哪些层。还有个容易忽略的点是算子版本,PyTorch和ONNX opset对不上会导致某些层直接fallback到plugin,性能反而更差。你可以在onnxsim里过一遍简化,再转TRT,很多时候问题就出在中间那层图太乱。
F.interpolate转ONNX确实容易出岔子,试试导出时把opset提到13以上,再不行就换成固定尺寸的resize。动态shape我一般直接开optimization profile,把min/opt/max三个尺寸都写死,省得TRT瞎猜。int8掉点严重的话,校准集最好从验证集里抽个三五百张,覆盖各种场景,别只用几张图糊弄。Layer Fusion想效果好,得先让ONNX图干净点,把那些没用的Identity和Constant先删掉再转。
动态shape这块建议直接用onnx的dynamic_axes配合trt的optimization profile,别偷懒用固定shape,不然Jetson上换分辨率就废了。F.interpolate导出时最好换成nn.Upsample或者手动指定scale_factor,不然ONNX那关就过不去。int8掉点太正常了,校准集一定要覆盖各种场景,最好从验证集里抽个几百张有代表性的,别随便拿训练集前100张凑数。算子融合其实不用你操心,trt自己会搞,但前提是ONNX图别太碎,导出前把一些无意义的reshape和transpose合并掉会好很多。
动态shape用onnxsim先固定一版再转,int8掉点大概率是校准集分布不对,别用训练集凑合。
F.interpolate转ONNX时用opset 11以上会走Resize,但得把align_corners和坐标变换模式固定住,不然TRT解析容易挂。动态shape建议导出时显式指定dynamic_axes,Jetson上再配optimization profile,不然推理时尺寸一变就崩。int8掉点先别急着怪校准集,检查下BN折叠和敏感层是不是被强行量化了,可以拿几十张验证集图跑下逐层敏感度分析。Layer Fusion在TRT里其实挺挑结构的,像Conv+BN+ReLU基本都会自动合,但你自己写的那些小算子拼一起它不一定认,用polygraphy看看哪层断了更直接。
F.interpolate 转 ONNX 时最好手动指定 scale_factor 或者用 opset 11 以上的版本,不然动态尺寸很容易挂。int8 掉点严重的话先别急着换校准集,检查下 BN 层有没有被正确 folding,我上次就是卡在这。至于 Layer Fusion,TRT 对 seg 模型里的 element-wise add 和 concat 融合其实挺积极的,你可以用 trtexec 看下生成的 engine 层数对比一下。
F.interpolate那个警告基本是ONNX opset版本的问题,试试把opset拉到13以上,然后明确指定mode和align_corners,不然TRT解析时真容易挂。动态shape这块我当时也卡了很久,后来是在导出ONNX时用dynamic_axes把batch和H/W都标上,再在trt的profile里给几个不同分辨率做优化配置,别只给一个固定shape。int8掉5个点其实不算离谱,校准集一定要覆盖你实际部署场景的分布,随便拿几十张训练图去校准基本都会崩,另外可以试试per-channel量化和把敏感层(比如最后几层)保持fp16。Layer Fusion不用你手动操心,TRT自己会做,但前提是ONNX图别太碎,导出前最好把一些能合并的op提前fold掉,比如BN融合。还有个坑是Jetson上的TRT版本和你PC上不一定一致,最好直接在设备上导出或者用匹配的版本,不然parser报错能让你怀疑人生。
F.interpolate转ONNX确实容易出幺蛾子,试试导出时把opset调到11以上,再把mode固定成'nearest'或'bilinear'别用默认,能少不少警告。动态shape那块建议先拿固定尺寸跑通再开dynamic_axes,不然报错信息根本看不出是哪层的问题。int8掉5个点挺正常的,校准集最好从验证集里抽个几百张、覆盖各种场景,不然分布偏了精度直接崩。Layer Fusion想生效得看你的算子能不能被TRT识别成可融合模式,像conv+bn+relu这种基本自动就合了,自定义结构它也没辙。
动态shape记得用profile明确指定范围,不然trt构建时直接懵。int8掉点多半是校准集分布不对,换点真实场景图试试。
动态shape这块建议直接用trt的optimization profile,别在onnx里死磕,导出时把dynamic axes标清楚就行。interpolate那个警告基本可以无视,但转trt报错通常是opset版本或者align_corners的问题,换成opset11以上再试试。int8掉5个点挺正常的,校准集一定要覆盖你实际部署场景的分布,最好多跑几百张,不然量化参数偏得离谱。算子融合不用你手动管,trt自己会做,但前提是onnx图别太碎,导出前把一些无意义的reshape和transpose干掉会好很多。
F.interpolate这个坑我太熟了,ONNX导出时如果scale_factor不是整数或者用到了动态尺寸,很容易生成一堆Resize相关的奇怪节点,转TRT直接挂掉。你可以试试把interpolate换成固定输出尺寸的写法,或者干脆用nn.Upsample配合显式size,导出会稳很多。动态shape的话,onnx导出时记得用dynamic_axes标清楚,TRT这边在builder里配optimization profile,把min/opt/max三个shape都填上,不然推理时换个分辨率就崩。int8掉点5个确实有点多,校准集一定要覆盖真实场景的分布,别随便拿几十张训练图凑数,最好用验证集里各种难例都采样一些,另外可以试试逐层敏感度分析,把掉点严重的层保留fp16。算子融合这块,TRT确实会自动做,但前提是ONNX图别太碎,导出前用torch.fx或者onnx-simplifier过一遍,把多余的Identity、Transpose消掉,融合效果会明显不少。Jetson上还建议开DLA看看,有些层丢给DLA跑功耗和延迟都更友好。
F.interpolate转ONNX时最好指定具体的mode和align_corners,不然TRT解析时很容易挂。int8掉点严重的话,校准集要覆盖各种场景,别只用几张图糊弄,我一般会抽个几百张跑一遍。算子融合其实TRT自己会做,但前提是ONNX图别太碎,导出时可以用onnx-simplifier先过一遍,效果挺明显的。动态shape那块建议在导出时就固定几个常用分辨率,省得后面折腾profile。
写得挺好,建议补充一些性能数据。
学到了,感谢分享!
写得挺好,建议补充一些性能数据。
刚入门,这个对我帮助很大。