python3环境下pytorch模型转onnx部署tensorRT

转载请注明出处,谢谢!

环境:python3.5.2+pytorch1.2.0+tensorRT7.0.0.11

1、pytorch模型转onnx模型

pytorch自带onnx模型导出,几句话就可以实现很方便

model = model.cuda()
model = model.eval()
dummy_input = torch.randn(1, channel, height, width, device='cuda')
torch.onnx.export(model, dummy_input, onnx_path, verbose=True,input_names=input_names,output_names=output_names)

dummy_input是固定一个网络的输入,对于检测类的全卷积网络,模型的输入是不受限制的,所以这个输入尺寸在转onnx模型时是个可以随便设置的

torch.onnx.export是pytorch自带的onnx模型导出函数,onnx_path是onnx模型导出保存路径,input_names是网络输入名字,output_names是网络输出名字

onnx网络结构op划分比较细,网络结构的可视化可使用netron,使用方法可见官方github https://github.com/lutzroeder/netron

onnx可视化的网络结构看起来比较费劲,可使用一个网络简化模块来简化网络结构,简化后的网络就是我们正常熟悉的那些层了

onnx-simplifier的安装使用见官方github https://github.com/daquexian/onnx-simplifier

另外如果不使用onnx-simplifier,直接将onnx模型导入tensorRT时可能会报错

2、python环境下安装tensorRT

官网下载链接https://developer.nvidia.com/nvidia-tensorrt-download

下载合适自己电脑环境的RT版本,以TensorRT 7.0.0.11为例子

#解压下载好的压缩文件
tar xzvf TensorRT-7.0.0.11.Ubuntu-16.04.x86_64-gnu.cuda-10.0.cudnn7.6.tar.gz
#将lib绝对路径添加到环境变量中,使用vim ~/.bashrc
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/oyrq/TensorRT-7.0.0.11/lib
#安装TensorRT,这里匹配的是python3.5的环境
cd TensorRT-7.0.0.11/python
sudo pip install tensorrt-7.0.0.11-cp35-none-linux_x86_64.whl
#安装graphsurgeon
cd TensorRT-7.0.0.11/graphsurgeon
sudo pip install graphsurgeon-0.4.1-py2.py3-none-any.whl

import tensorrt进行测试就OK了

3、tensorRT导入onnx模型

相关代码可参考nvidia-tensorRT里面的例子,TensorRT-7.0.0.11/samples/python下面有很多种,主要参考yolov3_onnx/onnx_to_tensorrt.py就可以了

4、tensorRT导入onnx模型报错

1)、[TensorRT] ERROR: Parameter check failed at: ../builder/Layers.cpp::ConstantLayer::1585, condition: weights.type == DataType::kFLOAT || weights.type == DataType::kHALF || weights.type == DataType::kINT32

该错误是在TensorRT-5.1.2.2环境下发生的,错误原因是pytorch模型转onnx模型时某些层的数据类型转为了int64,但是该版本的TensorRT还不支持int64

解决办法升级新版本TensorRT5.1.5.0及其以后版本既可以解决

 

2)、[TensorRT] ERROR: ../rtSafe/cuda/caskConvolutionRunner.cpp (290) - Cask Error in checkCaskExecError<false>: 7 (Cask Convolution execution)
[TensorRT] ERROR: FAILED_EXECUTION: std::exception

这个问题很奇怪,刚开始在google上搜一堆,说是什么构建engine和cuda context不在同一个线程里:下面来自github的答案https://github.com/NVIDIA/TensorRT/issues/301

我不知道我遇到的和他说的本质上是不是一样的,貌似是cuda初始化的问题,我在构建tensorRT engine和context之后使用了img = img.cuda()这段代码就报错,如果在构建tensorRT engine和context之前使用,则是没有问题,下面是代码片段:

engine = onnx_build_engine(onnx_file_path)
inputs, outputs, bindings, stream, context = allocate_buffers(engine)
img = img.cuda() #img是用torch的tensor格式
inputs[0].host = img.cpu().numpy()
OutPuts = do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)

上面这样写就报错

img = img.cuda() #img是用torch的tensor格式
engine = onnx_build_engine(onnx_file_path)
inputs, outputs, bindings, stream, context = allocate_buffers(engine)
inputs[0].host = img.cpu().numpy()
OutPuts = do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)

这样写就没有问题,似乎要在构建tensorRT engine之前先使cuda初始化一下

3)、在将onnx模型或caffe模型导入RT时,虽然成功了,但是网络的输出结果始终匹配不上,这种情况发生在有某些多分支输出的网络,也不是所有的多分支输出网络会有这样的问题,作者就正好遇到了,也不知道是不是RT版本的bug还是什么,这里7.0.0.11就有这个问题。

当然这样的问题也是有解决办法的,先分析下出现这样问题的可能情况有哪些:

1)加载的onnx模型(或者其他框架的模型)的权重就不对,结果当然不对;

2)权重都保证正确的情况下,某一层的处理方式和其他框架下的处理方式不同,可以设置不同的超参数;

作者通过查看某些中间层的输出来定位问题出现在那一层,发现问题正好在分支融合那里,是个什么情况呢,我大致描述一下:

加载的是一个目标检测网络,有三个分支输出,FPN那样的结构,4个输出;正常情况下是在RT代码里面设置好输入输出层名即可,但是该模型不行,还需要在设置输出层名的队列里面加上三个融合分支的层名,文字可能说得不容易明白,我给个大致的图,如下:

                                                                          

如果514层属于主干的话,517层就属于分支,则需要将517加入到RT的输出层名队列中,同理,其他三个分支也要这样处理。导致这样的问题不知道是不是RT在序列话网络的时候,没把分支给加进去,还是什么,目前还不清楚,但是这样做很简单,也能愉快的解决问题。

       上面那个图是caffe模型的网络可视化图,作者也尝试了,caffe模型添加中间层名到输出层名队列中,是没有毛病的,但是同样的方式用在onnx模型身上就不行了,通过查看onnx模型结构的时候,发现onnx的输入输出在导出onnx模型的时候就固定了,RT加载onnx模型的时候,就不能像加载caffe模型那样输出中间层的结果,那咋办呢?还是有办法的,就给你想要的那个中间层在导出onnx模型时设置一个输出,还是以上图为例子吧,想要输出517层,就给517层再接一个没有权重的层,比如relu,sigmoid,dropout等,再导出onnx模型。反正中间层的输出咋样也不会影响到结果输出,也不会影响到RT的inference推理速度。

    通过使用上面的操作之后,现在的RT输出结果就完全正确了。说了一大堆,其实就是一句话,在设置RT输出的时候,把分支层名也加进去,只是不使用而已。用了这么久的RT,还是第一次遇到这样奇葩的问题,如果你没遇到就万事大吉,遇到了就试试这样的骚操作吧。。。。

4)、在pytorch测试阶段需正确使用with torch.no_grad()model.eval(),前者可以使测试inference阶段不启用梯度更新,主要是节省显存,不然很容易在批量测试图片时导致显存爆炸;后者是设定网络为测试阶段,并使用网络模型中训练好bn层中的全部权重,不然的话输出结果会有差异。下面是这两句话的官方解释:

Use both. They do different things, and have different scopes.
with torch.no_grad: disables tracking of gradients in autograd.
model.eval(): changes the forward() behaviour of the module it is called upon. eg, it disables dropout and has batch norm use the entire population statistics

评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值