PyTorch2ONNX2TensorRT 踩坑日志

PyTorch2ONNX2TensorRT 踩坑日志

从“用PyTorch写的网络,通过ONNX,使用TensorRT序列化,最终完成模型加速”的全流程踩坑日志。


2019/12/07 初版
2019/12/17 更新AdaptivePooling, 找BUG思路
2019/12/27 添加AdaptivePooling示例
2020/01/01 添加VGG16示例链接


实验环境

ONNX可以不用安装,对ONNX2TRT没有影响,推荐使用anaconda管理包。

  • Ubuntu 16.04
  • RTX2080TI, Driver Version: 410.79
  • CUDA 10.0
  • cudnn 7.6.3 (经测低版本如7.5.0无影响)
  • pycuda 2019.1.2
  • pytorch 1.3.1
  • torchvision 0.4.2
  • tensorrt 6.0.1.5
  • python 3.6.9
    • 经测ONNX无法使用,建议使用python 3.7.x
    • onnx 1.6.0
    • protobuf 3.9.2 (需要降级到3.9.x,不然onnx会报libprotobuf.so.20的错)

1. RuntimeError: ONNX export failed: Couldn’t export operator aten::upsample_bilinear2d

无法解决,ONNX2TensorRT报错,待TensorRT后续版本支持,见后文替代方法#4

近似地,应该与警告信息 UserWarning: ONNX export failed on upsample_bilinear2d because align_corners == True not supported 相关联。

原因

转换ONNX使用的版本较低,PyTorch.ONNX不支持。另外,参考源码, torch.onnx.export 默认使用 opset_version=9

解决办法

警告信息已经完整说明,ONNX's Upsample/Resize operator did not match Pytorch's Interpolation until opset 11.,因此将ONNX的导出代码中规定其版本,具体如下:

import torch
torch.onnx.export(model, ..., opset_version=11)

较完整报错信息

输出的个人信息就被我隐去了,也为了报错、警告的简洁,所以这里叫做“较完整”,此说明后续不再赘述。

UserWarning: You are trying to export the model with onnx:Upsample for ONNX opset version 9. This operator might cause results to not match the expected results by PyTorch.
ONNX's Upsample/Resize operator did not match Pytorch's Interpolation until opset 11. Attributes to determine how to transform the input were added in onnx:Resize in opset 11 to support Pytorch's behavior (like coordinate_transformation_mode and nearest_mode).
We recommend using opset 11 and above for models using this operator. 

UserWarning: ONNX export failed on upsample_bilinear2d because align_corners == True not supported

RuntimeError: ONNX export failed: Couldn't export operator aten::upsample_bilinear2d

2. RuntimeError: ONNX export failed: Couldn’t export operator aten::adaptive_avg_pool2d

无法解决,ONNX2TensorRT报错,待TensorRT后续版本支持,见后文替代方法#5

类似错误 aten::adaptive_avg_pool*donnx#63, pytorch#14395, discuss.pytorch#30204

原因

因为PyTorch的网络中用了 torch.nn.AdaptiveAvgPool2d ,个人感觉,ONNX没有 avg_pool2d 操作,见ONNX Operator,所以PyTorch.ONNX就会报错 aten::adaptive_avg_pool2d 无法转换。

解决办法

参考pytorch#14395添加额外Option,如下:

import torch
torch.onnx.export(model, ..., operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK)

该方法只是阻止ONNX替换PyTorch的OP、而是使用ATen的OP替换,PyTorch2ONNX能通,但ONNX2TRT却不能通,原因是ONNX phaser识别不到非ONNX的OP。

较完整报错信息

UserWarning: ONNX export failed on adaptive_avg_pool2d because output size that are not factor of input size not supported

RuntimeError: ONNX export failed: Couldn't export operator aten::adaptive_avg_pool2d

3. Error: In node 2 (importGather): UNSUPPORTED_NODE: Assertion failed: !(data->getType() == nvinfer1::DataType::kINT32 && nbDims == 1) && “Cannot perform gather on a shape tensor!”

原因

"Cannot perform gather on a shape tensor!",网络内部使用x_size = x.size()[1:]等类似操作,TensorRT在trace的时候,会被解析成一个shape layer的输出,获得一个shape tensor,用Netron工具可视化就可以发现,对应的node 2实际上是个Constant node,与预期不符。

解决办法

不使用该操作,另一种解法来自onnx-tensorrt#192

x_size = torch.tensor(x.shape)[1:]

4. Error: In node 1 (importUpsample): UNSUPPORTED_NODE: Assertion failed: (nbDims >= 1) && (nbDims <= 3)

使用Netron工具可视化模型,找到对应的node 1,就可以发现对应的是F.interpolate(x, size=(128, 128), mode='bilinear', align_corners=False)操作。

原因

目前ONNX2TRT的转换过程中,貌似不支持F.interpolatebilinear模式,只支持linearnearest

解决办法

将所有的bilinear模式替换为nearest模式。


5. 使用AvgPooling替换AdaptivePooling

针对2. RuntimeError: ONNX export failed: Couldn't export operator aten::adaptive_avg_pool2d问题,使用AvgPooling替换AdaptivePooling。因为ONNX支持AvgPooling,PyTorch2ONNX、ONNX2TRT流程能够跑通。

原因

目前PyTorch2ONNX流程中,ONNX并不支持AdaptivePooling操作,该操作仅存于PyTorch中。

解决方法

参考[开发技巧]·AdaptivePooling与Max/AvgPooling相互转换一文、PyTorch官方文档可知,AdaptivePooling可通过输入大小input_size自适应控制输出大小output_size,而一般的AvgPooling/MaxPooling则是通过kernel_sizestridepadding来计算output_size,公式如下:

o u t p u t _ s i z e = c e i l ( ( i n p u t _ s i z e + 2 ∗ p a d d i n g − k e r n e l _ s i z e ) / s t r i d e ) + 1 \mathbf{output\_size} = ceil(( \mathbf{input\_size} + 2 * \mathbf{padding} - \mathbf{kernel\_size}) / \mathbf{stride})+1 output_size=ceil((input_size+2paddingkernel_size)/stride)+1

因此通过input_sizeoutput_size反推kernel_sizestridepadding,参考官方源码padding设为0,那么可推出去kernel_sizestride

s t r i d e = f l o o r ( i n p u t _ s i z e / o u t p u t _ s i z e ) \mathbf{stride} = floor(\mathbf{input\_size} / \mathbf{output\_size}) stride=floor(input_size/output_size)
k e r n e l _ s i z e = i n p u t _ s i z e − ( o u t p u t _ s i z e − 1 ) ∗ s t r i d e \mathbf{kernel\_size} = \mathbf{input\_size}- (\mathbf{output\_size}-1) * \mathbf{stride} kernel_size=input_size(output_size1)stride

示例

例如,PyTorch网络的某一层含有nn.AdaptiveAvgPool2d(output_size=(14,14)),它的output_size(14, 14),该层的输入特征图大小为10*128*128,那么输出的特征图大小为10*14*14,那么带入公式,可计算出nn.AvgPool2d(kernel_size, stride)stride=(int(128/14), int(128/14)), kernel_size=((128-(14-1)*stride, (128-(14-1)*stride),验证如下:

import torch
from   torch import nn

input = torch.randn(10, 36, 36)
AAVP = nn.AdaptiveAvgPool2d(output_size=(12,12))
AVP  = nn.AvgPool2d(kernel_size=(3,3), stride=(3,3))

output_AAVP = AAVP(input)
output_AVP  = AVP(input)

6. PyTorch2ONNX、ONNX2TRT到底哪里出了问题?

下面是遇到无法解决的问题后该找谁问的一个思路:

PyTorch2ONNX是调用的PyTorch内部的转换脚本,所以PyTorch2ONNX出了问题,需要去PyTorch那里的issue寻找解决办法;ONNX2TRT是使用ONNX自己写的转换脚本onnx-tensorrt,同理如果ONNX2TRT出了问题,就需要到它的那里找解决办法;在产生好TRT模型后,使用TRT模型进行推理出问题了,那就要去TRT那里问了,有GitHub官方论坛可以使用。

那怎么让报错暴露出来呢,下面是一些办法。

解决方法

按下列方法多半能找到问题所在。

1. PyTorch2ONNX
  1. 更新PyTorch到最新版,一般最新版中ONNX的OP支持应该会更多;
  2. 按下列代码将日志等级调到最高,逐一分析。
import torch
torch.onnx.export(..., verbose=True, ...)
2. 检测ONNX模型

下载Netron可视化自己的ONNX模型,分析是否与PyTorch模型一致,或者与自己想造的模型一致。多看看resizeshapepermute操作,ONNX对这些需要对tensor切片的操作不是很支持。

3. ONNX2TRT
  1. 更新onnx-tensorrt库,也就是libnvonnxparser.so。下面贴一段TRT的安装步骤:
    1. 安装TRT.
    2. 编译onnx-tensorrt.
    3. libnvonnxparser.so移到TRT的lib文件夹中.
  2. 按下列代码将日志等级调到最高,逐一分析。
import tensorrt as trt
TRT_LOGGER = trt.Logger(trt.Logger.VERBOSE)

最终解决办法

放弃ONNX2TRT吧,PyTorch与ONNX与TRT的版本难以互相支持,在版本的迭代中任意节点不支持了,整个链路就会断掉,另外TRT是闭源的项目,你完全不知道ONNX2TRT的过程中出了哪些问题,就算有堆栈信息,也不可能根据信息去trace它的错误。所以,直接使用TRT提供的api直接构建网络,是最复杂、也是最简单直接的方法。

Pytorch 2 TRT python API

使用TRT提供的python接口,构建网络,整个流程十分简单,大家可以看看TRT提供的示例<TRT_root>/samples/python/network_api_pytorch_mnist/sample.py,与之对照的是<TRT_root>/samples/python/network_api_pytorch_mnist/model.py

def populate_network(network, weights):
    # Configure the network layers based on the weights provided.
    input_tensor = network.add_input(name=ModelData.INPUT_NAME, dtype=ModelData.DTYPE, shape=ModelData.INPUT_SHAPE)
    """
    TRT python API
    """
    network.mark_output(tensor=fc2.get_output(0))

你只需要把这个populate_network写出来就好了,weights就是网络的权重了,由torch.load()得到,是不是超级简单啊。想使用PyTorch的F.interpolatebilinear模式?TRT提供!下篇日志将会记录“如何使用TRT python API搭建简单的VGG16网络,我再也不想用ONNX2TRT了。

  • 31
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
### 回答1: PyTorch是一个深度学习框架,可以用来构建神经网络模型。ONNX是一种开放的神经网络交换格式,可以将不同框架的模型转换为统一的格式。TensorRT是NVIDIA推出的用于加速深度学习推理的库,可以将模型转换为高效的C++代码。因此,将PyTorch模型转换ONNX格式,再将ONNX模型转换TensorRT格式,可以提高模型的推理速度和效率。 ### 回答2: Pytorch是一款非常流行的深度学习框架之一,而ONNX(Open Neural Network Exchange)则是一种用于在不同深度学习框架之间交换模型的标准格式。TensorRT是基于NVIDIA GPU优化的深度学习推理引擎,可以加速深度学习模型的预测速度。PytorchONNXTensorRT的过程主要包括以下几个步骤: 第一步,使用Pytorch训练好的模型可以通过Pytorch提供的方法将模型转化为ONNX格式的模型。这一步通常需要在节省内存的情况下,对训练的模型进行优化并减少其大小。Pytorch提供了一些方法,例如ONNX的exporter,可以在不损失精度的情况下将Pytorch模型转换ONNX格式。 第二步,将ONNX模型转换TensorRT格式的模型。这一步通常需要使用TensorRT提供的工具将ONNX格式的模型转换为可以使用TensorRT来加速推理的格式。TensorRT可以根据模型的结构,对其进行优化,从而提高推理的速度和效率。 第三步,使用TensorRT引擎来进行推理。在这一步中,可以使用一些Python或C++的API来调用TensorRT引擎,以加速推理的过程。通常情况下,TensorRT会在NVIDIA GPU上运行,以提高推理的速度和效率。 总之,PytorchONNXTensorRT是一种很常见的深度学习模型加速优化的方法。通过这种方法,可以将训练好的模型转化为可以快速进行推理的格式,并提高推理的速度和效率,从而更好的满足实际应用需求。同时也可以加深对于PytorchONNXTensorRT的理解和应用。 ### 回答3: 先介绍一下三个工具的概念。 PyTorch是一个基于Python的库,提供了高度可拓展性和可定制化的机器学习算法、模型和数据处理工具,特别适合用于深度学习。 ONNX(Open Neural Network Exchange)是一个开放的模型表示和转换工具,使得不同框架之间的模型转换和协作更加容易。ONNX 可以将每个框架的模型表示转换为通用表示格式,这样就可以一次性完成跨框架的模型部署。 TensorRT是NVIDIA推出的高性能深度学习推理库,可以对训练好的深度学习模型进行优化,并在NVIDIA GPU上加速模型的推理过程。使用TensorRT能够提升模型的运行速度和效率。 下面是如何将PyTorch模型转为TensorRT模型的过程: 1.将PyTorch模型转化为ONNX格式。有两种方法可以实现这一步骤:一种是使用PyTorch官方提供的torch.onnx.export方法,将PyTorch模型转化为ONNX格式;另一种方法是使用ONNX官方提供的onnx-coreml转换工具。 2.使用TensorRT提供的工具trtexec将ONNX模型转化为TensorRT格式。通过命令行的方式调用trtexec,在将模型转换TensorRT格式的同时,可以指定一些优化参数,比如设置合适的batch size、设置推理设备的数据类型、设置最大批处理等参数,以提高模型的性能和效率。 3.将转换后的模型导入到TensorRT库中,使用C++或Python等支持TensorRT的代码进行推理。 总体而言,PyTorchONNXONNXTensorRT这一过程需要先对PyTorch模型进行格式转化,再将ONNX格式的模型转转化为TensorRT格式,最终通过TensorRT库对模型进行推理。需要注意的是,不同的深度学习模型在转换过程中有着各自的特点和难点,需要根据具体情况进行优化和调整。
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值