前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ONNXRUNTIEM版本升级到1.13的大坑

ONNXRUNTIEM版本升级到1.13的大坑

作者头像
OpenCV学堂
发布2023-01-04 17:15:07
1.8K0
发布2023-01-04 17:15:07
举报

微信公众号:OpenCV学堂

ONNXRUNTIME

一直使用的是ONNXRUNTIME1.7.0版本做推理测试,周末有空就把ONNXRUNTIME版本从1.7.0升级到1.13.1版本了。

升级导致的问题

发现C++部分的代码没有什么变化,有个获取输入输入层名称跟输出层名称的函数没有啦,之前1.7.1对应的获取输入层跟输出层的函数方法如下:

代码语言:javascript
复制
session_.GetInputName(i, allocator);
session_.GetOutputName(i, allocator);

升级到1.13.1版本之后,上面的函数没了,只有下面的函数:

代码语言:javascript
复制
session_.GetInputNameAllocated(i, allocator);
session_.GetOutputNameAllocated(i, allocator);

修改之后,我依然跟之前一样把输入名称跟输出名称保存在两个全局的std::vector里面,然后推理的时候直接作为参数传入,然后我就一直遇到推理错误,一直报input node is empty 或者 invalid input node,程序直接崩溃了。以YOLOv5模型为例,错误信息如下:

错误分析

没升级之前的代码是这样的

代码语言:javascript
复制
std::vectoroutput_bad_names;
for (int i = 0; i < numOutputNodes; i++) {
auto out_name = session_. GetOutputName (i, allocator);
    output_bad_names.push_back(out_name.get());
}

正常工作没错误!升级之后代码是这样的

代码语言:javascript
复制
std::vectoroutput_bad_names;
for (int i = 0; i < numOutputNodes; i++) {
    auto out_name = session_.GetOutputNameAllocated(i, allocator);
    output_bad_names.push_back(out_name.get());
}

然后我在推理之前打印了一下这个output_bad_names这个数组,打印代码如下:

代码语言:javascript
复制
for (auto item : output_bad_names) {
    std::cout << "output node:" << item << std::endl;
}

输出的结果如下:

而且我还注意到并不是每次打印输出的结果并不一致,相当随机。有时候会正确推理一次。多数时候都直接挂了。所以我很怀疑

代码语言:javascript
复制
auto out_name = session_.GetOutputNameAllocated(i, allocator);

获取的AllocatedStringPtr指针是个临时变量,过了for循环之后会随机释放掉,然后我定义了一个全局的变量来测试一下,

代码语言:javascript
复制
const char *ddd = "Hello World";
for (int i = 0; i < numOutputNodes; i++) {
    auto out_name = session_.GetOutputNameAllocated(i, allocator);
    ddd = out_name.get();
    output_bad_names.push_back(out_name.get());
}

运行一下,输出结果如下:

看来GetOutputNameAllocated返回的必须作为全局变量才行,Bug捉到了!

代码修改与测试

解决的方法很简单就是把查询到这些节点名称全部复制一份到一个全局的std::vector对象中去,这样就算返回的临时变量被复写或者或者变化了,不会影响到保存好的全局变量。先初始化一下定义的std::vector的输入与输出节点数组:

代码语言:javascript
复制
size_t numInputNodes = session_.GetInputCount();
size_t numOutputNodes =session_.GetOutputCount();
for (int i = 0; i < numInputNodes; i++) {
    input_node_names.push_back(std::string(""));
}
for (int i = 0; i < numOutputNodes; i++) {
    output_node_names.push_back(std::string(""));
}

然后读取输出节点保存一下:

代码语言:javascript
复制
for (int i = 0; i < numOutputNodes; i++) {
    auto out_name = session_.GetOutputNameAllocated(i, allocator);
    output_node_names[i].append(out_name.get());
}

然后在推理之前创建临时变量就好啦:

代码语言:javascript
复制
const std::arrayinputNames = { input_node_names[0].c_str() };
const std::arrayoutNames = { output_node_names[0].c_str(), output_node_names[1].c_str(), output_node_names[2].c_str(), output_node_names[3].c_str() };

然后就可以直接推理了:

代码语言:javascript
复制
std::vectorort_outputs = session_.Run(Ort::RunOptions{ nullptr }, inputNames.data(), &input_tensor_, 1, outNames.data(), outNames.size());

再也看不到那个错误了,也会不返回-1073740791的崩溃错误了

启动ONNXRUNTIEM推理可以运行了,KeyPointRCNN+ONNXRUNTIEM C++ 的推理演示如下:

CPU与GPU推理

我下载了ONNXRUNTIEM1.13.1的GPU版本,然后使用CPU推理,发现速度比Python版本快了那么一点点,显示如下:

启动GPU选项之后的推理速度:

GPU版本如何启动

关于ONNXRUNTIEM1.13.1 GPU版本如何启动下载GPU版本下面有三个dll支持

代码语言:javascript
复制
onnxruntime.dll
onnxruntime_providers_cuda.dll
onnxruntime_providers_shared.dll
代码语言:javascript
复制
onnxruntime.dll是核心依赖库。
onnxruntime_providers_cuda.dll是跟版本匹配CUDA加速才启作用。
onnxruntime_providers_shared.dll表示支持兼容低版本CUDA比。

ONNXRUNTIEM1.13.1 GPU官方支持的是11.6版本,而我自己安装的版本是11.3,必须把上述三个dll文件放到项目文件夹下或者把路径配置到环境变量中去。启动GPU添加下面的代码:

代码语言:javascript
复制
this->session_options.SetGraphOptimizationLevel(ORT_ENABLE_BASIC);
OrtSessionOptionsAppendExecutionProvider_CPU(this->session_options, 0);

这样就可以启用GPU运行了,当没有GPU它会自动转到CPU模式去推理,真的很开发者友好。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 OpenCV学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档