【2023 · CANN训练营第一季】模型的加载与推理
【2023 · CANN训练营第一季】模型的加载与推理
文档参考:CANN文档社区版: 6.0.RC1.alpha001
在模型转换,数据预处理后,我的学习终于终于来到了模型的加载与推理。是时候用我预处理后的图片加上我ATC转换的模型进行一个推理和目标检测的步骤了!
一、接口调用流程
首次进行代码学习的时候,其实我们可以参考官方的文档。
模型加载:
以下为使用不同模型的加载接口的示意图:

如果我们要使用同一个模型加载接口,还可以创建一个config的形式来固定加载接口:

note:
1.当由用户管理内存时,为确保内存不浪费,在申请工作内存、权值内存前,需要调用aclmdlQuerySize接口查询模型运行时所需工作内存、权值内存的大小。
2.如果模型输入数据的Shape不确定,则不能调用aclmdlQuerySize接口查询内存大小,在加载模型时,就无法由用户管理内存,因此需选择由系统管理内存的模型加载接口(例如,aclmdlLoadFromFile、aclmdlLoadFromMem)。
模型执行:
模型执行的参考流程如下:

输入/输出数据类型结构准备:
这里还用到了一个数据类型集合的使用:
使用aclmdlDataset类型的数据描述模型的输入/输出数据,因为模型可能存在多个输入、多个输出
调用aclmdlDataset类型下的操作接口添加aclDataBuffer类型的数据、获取aclDataBuffer的个数等
每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述。调用aclDataBuffer类型下的操作接口获取内存地址、内存大小等。

上述数据类型结构准备流程:

模型卸载:
模型卸载主要就是要注意不要有漏网之鱼,毕竟我们创建的东西太多了,没有释放会导致内存泄漏等一系列问题,可以参考以下内容检查是否有释放完全:
1.卸载模型
2.释放模型描述信息
3.释放模型运行的工作内存
4.释放模型运行的权值内存
二、接口简介
以下简单介绍一下我用到了的几个API接口:
note:模型加载、模型执行、模型卸载的操作必须在同一个Context下。这就意味着,我们必须在同一个线程下进行使用,因为context无法跨线程使用
创建输入输出类型结构的相关接口可以参考手册了解,太多了这里就不写了。
三、代码验证与学习
代码思路:
1.初始化相关通道,创建输入输出所需的一些内容,加载模型
2.读取图片,将图片整合进输入流,执行模型
3.读出结果,处理模型输出的数据
4.依次销毁所有创建的环境
接下来我们直接进入我们的代码运行过程!!!
进行代码编译,编译成功!

首先,我们先进行代码验证,在之前的文章中,我自己用ATC工具,转了一个om模型,首次验证,我们先不使用它,因为我无法保证我自己的模型是否正常,所以我的思路是,先找了一个确定能行的我用过的om模型,进行代码验证。如果我要是用我自己的模型,这样排查是我的模型加载相关代码异常还是我的模型本身就有异常会造成一定的弯路。
代码验证如下:

结果是识别到了两个小车车,一个小停止路牌标志,让我们回顾一下,我的resize文章中输出的640x640的原图。

能识别出两个车,效果还算不错了,虽然挡住的那个不太好识别。各位有兴趣的小伙伴也可以用cv画出bounding box看看识别情况
接下来!我们就使用我之前ATC的文章中转好的yolov5s_bs.om进行验证吧!!!!!!
结果如下:

哦豁!有错误,让我们看看这个错误码是什么意思!

查看昇腾的C++应用文档对比错误码,发现是参数校验失败,根据我们上面的验证思路,问题可能出在了模型上。
将两个模型用netron打开进行对比: 这是正常模型的输入这里,可以看到还是进行了AIPP的,但是不知道为啥没有显示这个算子

这是我自己转的om的情况:

接下来我们再查看输出,这是运行正常的模型:

以下这是我的异常模型:

同志们!!!!我们发现了问题,这怎么是三个特征图在输出呢!
这里我们需要用到YoloPreDetection和YoloV5DetectionOutput两个算子,对输出的onnx模型进行调整。
以下参考昇腾社区modelZoo的his版本中的yolov5案例
首先,
(1)我们修改models/common.py文件,对其中Focus的forward函数做修改
(2)修改models/yolo.py脚本,使后处理部分不被导出
(3)修改models/experimental.py文件,将其中的attempt_download()所在行注释掉
(4)修改models/export.py文件,将转换的onnx算子版本设为11,v6.0无需修改
修改完成,我们把模型放在yolov5文件夹下,使用官方的export.py运行如下命令
这里我使用的是v6.0版本的git版本,在windows下运行也不用export那个pwd获取的路径

添加后处理算子
在这里生成了一个yolov5s_t.onnx


使用atc命令进行转换:
转换结果如下:

生成了yolov5s_t_bs1.om

将该模型使用我们代码进行加载和执行:

看到推理成功并且输出了很多很多很多的结果!说明我们终于终于终于成功了。
但是保留一个我自己的小疑问是不知道为啥推理的结果有这么多,而且结果不太对,等后面有空我在讨论一下这个模型本身的问题。本文我们要解决的是如何让我们的模型顺利的加载并推理。我们完成了我们的目标。
以下附上我的测试的代码。写的有些粗糙,因为最近时间比较紧张。
runInfer.cpp
inference.cpp(推理功能实现文件)
inference.h
本文实现了模型的加载,推理,和后处理结果,如果有同学有需要,也可以使用cv,将后处理后的推理结果的boundingbox画出来看看效果哦。
ps:该文仅是为了记录CANN训练营的学习过程所用,不参与任何商业用途,有任何代码问题可以和我一起讨论修改哦!