如何将MindSpore模型转ONNX格式并使用OnnxRuntime推理---全流程开发指导
前言
1 本次Onnx模型推理任务不需要详细读原论文和源码,只需要清楚模型网络结构即可。
2 我们只用找到需要映射的算子就可以开始开发,本文也会依照以下流程进行讲解,完整讲述本次Onnx模型推理任务的开发流程:
3 不需要开发的同学:经过模型导出及算子分析后,如果发现所有算子都已经实现,请直接从第五章-编译导出开始阅读。
4 需要开发的同学:经过模型导出及算子分析后,如果发现有算子未实现,可阅读此文档第四章-算子映射,此章节将在用Pad算子来介绍详细介绍开发流程中最常用的方法和接口,希望大家能耐心看完,这样就可以清楚整个开发流程。
5 此文档也有网页版:https://bbs.huaweicloud.cn/blogs/360051 ,大家愿意的话可以支持一下,刷一刷阅读量。
6 如有错误,欢迎修正和反馈
常用网址
3 MindSpore 官方模型 checkpoint 下载网址
7 CNN+CTC ONNX 导出 onnx_exporter.cc 文件 PR
8 CNN+CTC ONNX 导出脚本 + 推理脚本 PR
9 如何将 MindSpore 模型转 ONNX 格式并使用 OnnxRuntime 推理 --- 全流程指导
博客整体流程
一、环境配置
二、MindSpore IR导出
三、算子分析
四、算子映射
五、编译导出
六、推理测试
七、总结
一、环境配置
1. 连接服务器
(1) ssh跳转连接
使用服务器链接软件在同一个连接会话中,依次连续输入以下命令:
① 跳转服务器1:
ssh xxx@xxx.xxx.xxx.xxx passwd: xxxxxxx
② 跳转服务器2:
ssh xxx@xxx.xxx.xxx.xxx passwd: xxxxxxx
③ Onnx服务器:
ssh xxx@xxx.xxx.xxx.xxx passwd: xxxxxxx
输入ls,到达此界面即为连接成功!
(2) VPN连接方法
① 点击右下角网络(WiFi那个标识),点击网络和Internet设置。
② 点击VPN,添加VPN(我已经设置过一个,请忽略)。
③ 设置GPU算子开发网络连接,输入如下用户名和密码,点击保存。
VPN信息:
服务器地址:xxx.xxx.xxx.xxx
用户名:xxx
密码:xxxxxxxxxxxxx
④ 打开控制面板-网络连接,并选择刚创建的vpn右键属性
⑤ 选择网络-IPV4-属性-高级,取消勾选“在远程网络上使用默认网关”
⑥ 选择安全,选择允许使用这些协议,点击确定
⑦ 连接VPN后直接连接服务器
ssh xxx@xxx.xxx.xxx.xxx passwd: xxxxxxxx
输入ls,到达此界面即为连接成功!
2. 安装mindspore
(1) 在服务器上创建自己的文件夹
mkdier xxx
cd xxx
(2) 创建conda虚拟环境并激活
conda create -n xxx python=3.7
服务器如果拥挤的话可能需要一段时间。
source activate xxx
(3) 依照官网命令下载安装mindspore
Mindspore官网安装教程:https://www.mindspore.cn/install
3. clone model库
git clone https://gitee.com/mindspore/models
下载官方模型仓库:https://gitee.com/mindspore/models ,在/models/official路径下找到自己模型所属的类型,进而找到自己的模型源码。
阅读模型的README.md文件,了解模型的导出命令格式,方便后面MindSpore IR的导出。
4. 下载模型checkpoint
模型MindSpore IR文件的导出需要用到训练好的checkpoint文件,而这个文件在云端有现成的,不需要自己训练,网址如下:
https://mindspore.cn/resources/hub
在搜索框搜索自己的模型,下载对应的checkpoint文件
依据模型README文档的建议或要求,将checkpoint文件放到相应的路径下(若无要求则自己创建文件夹保存)
二、MindSpore IR导出
1. mindir文件导出
(1) 修改模型配置文件
将device_target修改为GPU(当然如果你用CPU版mindspore就改成CPU)
(2) 修改保存图选项save_graphs
按照华为模型验收规范,这个属性应该是在train里面,如果不在请自行在源码中查找一下。
(3) 导出MindSpore IR
使用README文档中的命令,输入相应的参数,得到MindSPore IR文件,其格式为xxx.mindir。
python export.py --ckpt_file [CKPT_PATH] --file_format [EXPORT_FORMAT] --TEST_BATCH_SIZE [BATCH_SIZE]
① 命令中的CKPT_PATH要具体到checkpoint文件,其他参数具体模型具体配置,以cnnctc为例:
CHPT_PATH = ./checkpoint/cnnctc_ascend_v160_mjstiiit_official_cv_acc85.97.ckpt
② 命令中的file_format目前有三种[ONNX,MINDIR,AIR],而我们要开发onnx所以只有后面两种可以导出的形式,又由于AIR是Ascend版的,因此我们只能选择MINDIR
输入命令后导出cnnctc.mindir文件:此节完成
2. mindir->ms文件解密
(1) MINDIR是一个加密文件
官方模型加密保护文档中这样描述:https://www.mindspore.cn/mindarmour/docs/zh-CN/r1.7/model_encrypt_protection.html?highlight=MindSpoer%20Lite#
大家查看网址后可以看到其中有这两个属性需要输入:
因此大家需要查看自己模型的export.py文件中有没有设置这两个值,如果没有就不管,有的话之后会用到。
(2) MindSpore Lite模型转换
(之前的服务器在模型转换时会出现权限不足的问题,2022.6.14华为给的新服务器不知道有没有这个情况,如果仍然有问题大家可以更换我们自己实验室的GPU服务器来完成这一步,我用的是GPU算子的服务器 )
这一小节的目的就是使用MindSpore Lite工具将MINDIR文件转化为明码的MS文件。官方教程网址:https://www.mindspore.cn/lite/docs/zh-CN/r1.7/quick_start/one_hour_introduction.html#%E4%B8%80%E5%B0%8F%E6%97%B6%E5%85%A5%E9%97%A8
具体步骤如下:
① 下载MindSpore Lite
点击这个超链接,下载MindSpore Lite1.7 Linux-x86_64版本
下载后将其传输到服务器上,放在哪自己随意,记得就行:
② 解压mindspore-lite-1.7.0-linux-x64.tar.gz文件
tar -zxvf mindspore-lite-1.7.0-linux-x64.tar.gz
③ 环境设置
将转换工具需要的动态链接库加入环境变量LD_LIBRARY_PATH。
export LD_LIBRARY_PATH=${PACKAGE_ROOT_PATH}/tools/converter/lib:${LD_LIBRARY_PATH}
${PACKAGE_ROOT_PATH}是mindspore-lite解压后的路径。
④ 进入转换工具所在目录
执行如下命令,进入转换工具所在目录。
cd ${PACKAGE_ROOT_PATH}/tools/converter/converter
${PACKAGE_ROOT_PATH}是mindspore-lite解压后的路径。
将第(1)节导出的cnnctc.minir文件传到此目录下
⑤ 执行转换
使用如下命令进行模型转换:
./converter_lite --fmk=MINDIR --modelFile=cnnctc.mindir --outputFile=cnnctc
转换命令可以设置一些参数,如果需要请自行查看官方教程。
转换完成后输出如下:
导出的MS文件:
⑥ Netron可视化
将导出的cnnctc.ms文件下载到本地,然后打开Netron在线官网:
点击Open Model,选择下载到本地的cnnctc.ms文件,得到网络图如下:
此图中每一个方框就是我们本次onnx需要映射的算子。
三、算子分析
1. 开发文件下载
clone mindspore库
git clone https://gitee.com/EJaven/mindspore.git
这里clone的是我们团队的仓库:https://gitee.com/EJaven/mindspore ,之后可以从这里提pr,当然也可以自己fork官方仓库然后提pr。
cd mindspore/mindspore/ccsrc/transform/express_ir/
在此路径下可以找到我们开发所用的这个onnx_exporter.cc文件。
2. 算子统计
依据第二章最后打开的Netron网络图,以cnnctc模型为例,统计其中用到的算子:
MindSpore |
Conv2D |
MaxPool |
ReLU |
Transpose |
Reshape |
MatMul |
这些就是cnnctc中需要映射的算子。
3. 算子查询
找齐所有需要映射的算子之后我们就可以去mindspore/ccsrc/transform/expr ess_ir/onnx_exporter.cc文件中查找这些算子是否已经实现过。
同样以cnnctc模型为例,查询结果如下:Onnx 算子查询库
MindSpore |
是否已实现? |
Onnx |
Conv2D |
是 |
Conv |
MaxPool |
是 |
MaxPool |
ReLU |
是 |
ReLU |
Transpose |
是 |
Transpose |
Reshape |
是 |
Reshape |
MatMul |
是 |
MatMul |
Pad |
否,需要映射 |
Pad |
Add |
是 |
Add |
可以看到大部分算子已经实现过,仅剩下Pad算子需要映射开发,那接下来就是研究和分析MindSpore中的Pad算子和Onnx中Pad算子的输入,输出以及属性之间存在哪些差异,然后根据分析结果确定算子映射开发流程。
4. 算子映射
如果存在没有实现的算子,那接下来就是开发流程,大家可以先查阅华为官方给的markdown文件,了解基础后,再继续阅读此文档。
至此我们就完成了此次onnx项目的所有前期准备工作,如果经过分析发现你的模型中所有算子都已经实现,可跳转到第五章-编译导出,若有需要开发的算子请继续顺序阅读。
四、算子映射
1. 基本概念
(1) 图
华为官网对于图的定义如下:
实际上开发onnx并不需要完全熟悉和清晰图的概念,相反将图抽象的理解为一个计算流程图会更好理解他的作用和调用流程,假设现在我们需要计算如下表达式:F(x, y, z)=(x + y)* z - b,则其对应的计算流程图如下:
图1 F(x, y, z)计算流程图
图1中的每一个圆圈或者椭圆都是一个节点,而所有这些节点合起来就构成了我们F(x, y, z)这个函数的计算流程图,而“图”就是用计算机代码来表示和记录这种计算流程。而在这张图中,我们可以看到有三种不同颜色的节点,对于我们本次onnx映射来说他们就是最常用到的三种节点:输入数值节点(橙色x, y, z)、常量数值节点(灰色b)和算子节点(蓝色+, *, -)。大家也注意到了我把图中的蓝色节点(+, *, -)称之为算子而不是运算符,这就是因为其实这些运算符就是最基础最基本的算子,而我们所用到的算子实际上就是这样基础的算子不断叠加起来的,也就是我们调用的库方法和库函数。因此可以将图1 转换为算子计算图2:
图2 算子计算图
至此我们就对“图”有了一个简单但是我个人觉得完全够用了的理解,下面将节点合并为两类:数值节点和算子节点,并介绍节点的相关概念。
(2) 节点
① 节点的name
可以注意到图2中我除了把运算符(+, *, -)换成算子的名字外,还在每一个节点边上标了一个序号,这个序号可以理解为每个节点的唯一标识符,也就是计算机用来存储和记录运算流程的一个标记。这种标记在本次onnx算子映射中被设为这个节点的“name”,同时对于这张图来说存在一个变量用于存储其中所有节点的“name”,也就是官方文档中的:“node_map_ptr” <节点,节点序号>。
② 数值节点
前面的输入数值节点和常量数值节点都是用来存储数据数值的节点,其主要包括以下信息:
Name(序号):该节点的唯一标识符(如果不好理解就全部理解为序号)。
Value(数值):输入或者自己设定的常量的数值,如1,10,1000等。
Type(类型):该节点所存储数值的数据类型。
Output(输出):数值节点需要传输给其他节点进行计算,因此需要设定此节点的输出(即传什么给下游节点),实际上大多数情况都是add_output(name),也就是把自己的序号传下去,到时候下游节点就可以通过name访问数值了。
③ 算子节点
算子节点中定义了数据的计算逻辑,其计算部分是“算子开发”所需要做的事情,本次onnx映射仅仅只需要调整和映射以下信息即可:
Name(序号):该节点的唯一标识符(如果不好理解就全部理解为序号)。
OpType(算子类型):这个可以简单理解为此算子的名字,比如“+”他的OpType设为“Add”。
Input(输入):定义此算子的输入是什么,即将什么数值节点或者其他算子节点的输出传给此算子,大部分情况下都是add_input(name_xxx),即告诉计算机我要序号为name_xxx的节点作为我的输入。
Attr(属性):有些算子除了输入外还有属性值,属性值是在算子初始化的时候赋值的,而在此次项目中可以将其与输入看为同一个东西,只不过获取的方法不同罢了。
Output(输出):定义此算子的输出是什么,即将什么数值传给下游算子,实际上大多数情况也是add_output(name1),即此算子的输出就是其唯一标识符,下游算子需要调用的话可以add_input(name1)来设置这个算子为输入。
通过设置好每一个数值节点的value,type,output和算子节点的input和output信息后,整张图就连接起来了。
2. 开发流程
此次开发流程将以我本次onnx推理的CNN+CTC模型中缺少的Pad算子为例,以功能描述+代码+映射对应图的形式讲解开发中需要用到的方法,学会了这些方法后就可以通过拼接和组合完成其他算子的映射。
(1) 算子映射分析
① MindSpore Pad算子接口分析
MindSpore官方算子查询网址:
https://www.mindspore.cn/docs/zh-CN/r1.7/index.html
这个算子其实非常简单,输入一个任意维度的Tensor,根据初始化时赋值的paddings属性在对应的维度前和后增加0值,以2维Tensor为例:
输入input_x如下:
属性paddings如下:
input_x的shape是(2,4),这个paddings属性取值的含义就是,在input_x的第0个维度增加3(前面1,后面2),对于这个例子就是增加三行(上面1行,下面2行),然后在input_x的第1个维度(列)增加3列(左边2列,右边一列),所有增加的行和列全部填充数值0,结果如下:
而mode属性值是设定填充的方式,分为三种,具体大家可以看官网案例,本次网络使用默认值故此处不分析。Onnx Pad算子接口分析
Onnx算子查询网址:
https://github.com/onnx/onnx/blob/main/docs/Operators.md
可以看到Onnx中也是有这个Pad算子,并且简单查阅后发现功能是相同的,那么就进入下一步。
② 差异分析
通过对比分析我们可以看出MindSpore中的Pad算子和Onnx中的Pad算子功能是相同的,但是在输入和属性上存在差异,具体为:
MindSpore中用来设置增加维度的paddings是一个属性,而Onnx中起到相同功能的pads是一个输入,因此我们需要写paddings到pads的映射。
同时paddings要求是一个shape为[N, 2](N为input_x的维度)的tuple,其第D行的第0个元素代表在input_x的第D维前面增加的数量,而第1个元素代表在后面增加的元素。而pads则必须为一个1D的tensor其shape为[2 * N],其数值形式为[dim1_begin, dim2_begin,…, dim1_end, dim2_end]其中dim1_begin和dim1_end就代表在input_x的第1个维度前和后增加的数量。
文字看的可能比较晕,那我们看下面的图:
这张图就可以很清晰的看出paddings和pads之间的对应关系,同时可以得到paddings到pads映射所需要做的事情:
• MindSpore属性paddings映射为Onnx输入pads
• 将paddings从2D tuple类型映射为1D tensor类型
• 将paddings的数值正确映射为pads的数值
而由于CNN+CTC网络中使用的是默认mode属性值CONSTANT,往新增行列填充0,故此处不分析,同样也不需要映射,如果mode属性值设定了某个特定的数值则需要增加对mode属性的映射。
至此我们完成了对需要映射算子的分析,下面就可以进入开发阶段了,如果对于应该在那一行或者哪里写相应的代码可以查看CNN+CTC ONNX导出的PR链接,其中有清晰的增添删改代码和代码行号:
https://gitee.com/mindspore/mindspore/pulls/35915/files
(如果你发现需要映射的算子能够完美映射,即MindSpore XXX算子与Onnx XXX算子的输入输出完全一致,那么可以跳过此章,直接进入第 3 节官方案例分析 )。
(2) 声明映射方法
在OnnxExporter类下声明类私有方法ExportPrimXXX:
其中四个参数的含义分别为:
• func_graph是MindSpore的图。
• node是图中当前的节点,该函数中就是Pad算子节点。
• node_map_ptr存储图中所有节点<节点,节点序号>。
• graph_proto是ONNX的图。
(3) 注册算子映射表
在OnnxExporter::ExportCNode方法中注册算子映射表:
(4) 实现映射方法
实现第(2)步声明的映射方法ExportPrimXXX:
(5) 获取节点输入与属性
获取MindSpore Pad算子第1个输入input_x的name:
获取MindSpore Pad算子的属性paddings值:
(6) 数值映射
将paddings(2D tuple)转化为pads_sequence(1D vector),并完成数值映射
(7) 添加数值节点
将数值pads_sequence注册为数值节点pads:
在node_map_ptr中登记pads常量数值节点并获取其name:
在ONNX图graph_proto中新建一个常量数值节点pads_node,并指定其输出为pads常量数值节点的name:
为pads_node节点添加一个value数值属性,并将数值节点pads中的数值转换为onnx::tensor后赋值给value:
(8) 添加算子节点
本次Pad算子映射不涉及算子节点的添加,第 3 节的 BatchMatMul 算子 映射中会介绍如何添加算子节点。
(9) Onnx图添加算子节点
在node_map_ptr中登记当前MindSpore Pad算子节点并获取其name:
在ONNX图graph_proto中新建ONNX Pad算子节点:
指定ONNX Pad算子节点的输出为MindSpore Pad算子节点的输出(name)即ms_pad_node_name:
指定ONNX Pad算子节点的第1个输入为MindSpore Pad算子的第1个输入input_x的name即x_name:
指定ONNX Pad算子节点的第2个输入为(7)中创建的pads数值节点的name即pads_name:
映射完成后:
3. 官方案例分析
官方案例就不画图了,以功能描述加代码的方法分析,同时映射方法的声明和算子注册也不赘述,直接分析核心映射代码。
(1) 一对一(完全对应):MS Conv2D -> Onnx Conv
① 映射分析
官方案例,就不分析了,结果就是MindSpore Conv2D 和Onnx Conv 的输入和属性能够完全对应上,那么就只需要写一段代码,把相同作用的参数名映射一下就好了:
(2) 一对一(不完全对应):MS ExpandDims -> Onxx Reshape
① 映射分析
MindSpore ExpandDims 算子 -> ONNX Reshape 算子 , 输入输出可以对应,属性含义不同,无法直接对应, ExpanDims输入axis(int)是要扩展维度的轴,Reshape输入shape是扩展后的维度,需要在转换时作特殊处理。因此我们明确映射所需要做的事:
• 获取MindSpore ExpandDims算子的输入axis并结合输入input_x的shape计算出扩维之后的new_shape,而这个new_shape就是Onnx Reshape算子所需要的输入shape。
② 获取节点输入与属性
获取MindSpore ExpandDims算子的第1个输入input_x和第2个输入axis:
③ MindSpore输入axis映射为Onnx输入shape
获取MindSpore第1个输入input_x的shape值x_shape:
通过MindSpore的输入axis和x_shape推导出正确的Onnx输入new_shape,具体算法不分析,如果感兴趣可以自己查看:
④ 添加数值节点
将数值new_shape注册为数值节点shape:
在node_map_ptr中登记shape常量数值节点并获取其name(name_shape):
在ONNX图graph_proto中新建一个常量数值节点node_proto,并指定其输出为常量数值节点的name(name_shape):
为node_proto节点添加一个value数值属性,并将数值节点shape中的数值转换为onnx::tensor后赋值给value:
⑤ Onnx图添加算子节点
在node_map_ptr中登记当前MindSpore ExpandDims算子节点并获取其name:
在ONNX图graph_proto中新建ONNX Reshape算子节点:
指定ONNX Reshape算子节点的输出为MindSpore ExpandDims算子节点的输出(name)即node_name:
指定ONNX Pad算子节点的第1个输入为MindSpore ExpandDims算子的第1个输入input_x的name即input_x:
指定ONNX Pad算子节点的第2个输入为④中创建的shape数值节点的name即name_shape:
至此完成映射
(3) 一对多:MS BatchMatMul -> ONNX Transpose + MatMul
① 映射分析
MindSpore的 BatchMatMul 算子 有transpose_a,transpose_b属性,控制是否将输入转置,ONNX的 MatMul 算子 无转置属性,因此,需要判断BatchMatMul的transpose_a,transpose_b属性是否为true,如果为true,则需要在MatMul对应输入前添加Transpose算子作转换。因此我们可以明确需要做的事:
• 获取MindSpore BatchMatMul算子的属性transpose_a和transpose_b
• 获取MindSpore BatchMatMul算子的输入input_x和input_y
• 若transpose_a为true,则需要增加一个Onnx Transpose节点将输入input_x进行转置
• 若transpose_b为true,则需要增加一个Onnx Transpose节点将输入input_y进行转置
② 获取节点输入与属性
获取MindSpore BatchMatMul算子的第1个输入input_x和第2个输入input_y:
获取MindSpore BatchMatMul算子的第1个属性transpose_a和第2个属性transpose_b:
③ 若transpose_a为True则在input_x后增加Transpose算子节点
获取输入input_x的shape:
在node_map_ptr中登记一个transpose_input_x_name常量数值节点来存储转置后的input_x数值:
在ONNX图graph_proto中新建ONNX Transpose算子节点transpose_inputx_node_proto,并且指定其输入为MindSpore BatchMatMul算子节点的第1个输入input_x,输出为刚才创建的常量节点transpose_input_x_name:
为transpose_inputx_node_proto节点添加一个perm数值属性,并依据input_x的shape为其赋值:
至此对input_x进行转置的Onnx Transpose算子节点添加完成。
④ 若transpose_b为True则在input_y后增加Transpose算子节点
获取输入input_y的shape:
在node_map_ptr中登记一个transpose_input_y_name常量数值节点来存储转置后的input_y数值:
在ONNX图graph_proto中新建ONNX Transpose算子节点transpose_inputy_node_proto,并且指定其输入为MindSpore BatchMatMul算子节点的第1个输入input_y,输出为刚才创建的常量节点transpose_input_y_name:
为transpose_inputy_node_proto节点添加一个perm数值属性,并依据input_y的shape为其赋值:
至此对input_y进行转置的Onnx Transpose算子节点添加完成。
⑤ Onnx图添加算子节点
在node_map_ptr中登记当前MindSpore BatchMatMul算子节点并获取其name(node_name):
在ONNX图graph_proto中新建ONNX MatMul算子节点:
指定ONNX Reshape算子节点的输出为MindSpore BatchMatMul算子节点的输出(name)即node_name:
下面这行代码我也不知道啥意思,如果有同学知道了请联系我修改:
若transpose_a为True,则说明input_x需要转置,那么指定ONNX MatMul算子节点的第1个输入为转置后的常量数值节点transpose_input_x_name;若为False,则说明input_x不需要转置,那么指定ONNX MatMul算子节点的第1个输入为原始的input_x:
若transpose_b为True,则说明input_y需要转置,那么指定ONNX MatMul算子节点的第1个输入为转置后的常量数值节点transpose_input_y_name:若为False,则说明input_y不需要转置,那么指定ONNX MatMul算子节点的第1个输入为原始的input_y:
至此完成映射。
五、编译导出
1. MindSpore编译
在MindSpore路径下输入以下命令开始编译:
sh build.sh -e gpu -j32
初次编译需要下载很多第三方包所以很慢,之后再次编译就很快了。
编译报错则根据报错修改,编译成功则显示如下界面:
2. MindSpore包重安装或Python运行路径设置(推荐)
(1) MindSpore包重安装
编译完的MindSpore包会生成在mindspore/build/package/路径下:
可以在mindspore路径下输入以下命令重新安装mindspore包:
pip uninstall build/package/xxx.whl
pip install build/package/xxx.whl
(2) Python运行路径设置
输入以下命令设置Python运行路径:
export PYTHONPATH=/disk1/user14/wzb/test/mindspore/build/package:$PYTHONPATH
把其中的路径换成自己编译的mindspore存放的绝对路径,通过这种方式可以避免重复卸载再安装mindspore包的麻烦,只需要设置一次后就可以直接调用每次编译出来的新mindspore包了。
3. Onnx模型导出
更新完MindSpore包后,在Models文件夹中自己模型路径下调用README文件中的export命令,设置导出格式--file_format为ONNX,若成功导出则在模型路径下生成xxx.onnx文件:
若报错则根据报错信息修改算子映射文件onnx_exporter.cc并重复(1)(2)(3)步骤,直到能够导出ONNX文件为止。
六、推理测试
1. 数据集下载
(1) 依据Models README下载
查看Models中README文件中对于数据集的描述,通过其给出的超链接下载数据集并放在对应的路径下:
(2) 依据模型原论文源码中的README下载
若发现Models的README文件给出的超链接中数据集太大、下载很慢或者保存在谷歌云盘上需要翻墙,那么可以查阅一下模型原论文中给出的源码链接,查看源码中README提供的数据下载地址。
比如我的CNN+CTC模型Models中给出的超链接是用谷歌云盘存储的,并且把训练集、验证集和测试集打包在一起,导致数据集高达18G,又需要翻墙,直接下载经常中断:
屡次尝试未果后,我查阅了原论文的代码并且找到了其readme文件中给出的数据集下载链接:
点开后我发现了原作者的数据集中既有Models中给出的集合包,还有单独的训练集、验证集和测试集压缩包,由于我们只涉及推理所以只需要下载测试集压缩包即可,其大小只有160M,下载非常顺利:
2. MindSpore模型推理
(1) MindSpore推理
这一步骤只需要参照自己Models模型中的README文件中关于MindSpore模型eval的说明即可,只是需要注意有些模型的脚本可能由于MindSpore的更新导致一些算子的用法发生了改变,如果报错可以自己看着修改一下:
3. Onnx模型推理
(1) Onnx推理文件
Onnx推理文件就是使用onnxruntime接口导入第二章中导出的onnx文件,然后传入符合网络要求的输入,最终输出Onnx离线推理的模型精度。
这一步骤实际上很简单,主要包含两步,完整推理文件可以参考此PR中的eval.py和infer_cnnctc_onnx.py:https://gitee.com/mindspore/models/pulls/2913/files
① 数据获取dataloader
使用Models模型eval脚本中的数据集dataloader获取数据,然后将其从MindSpore::Tesnor转化为Onnx一般的输入numpy格式:
② 网络替换:MindSpore Net -> Onnx
将Models模型eval脚本中关于网络的定义替换成Onnx中onnxruntime导入的网络:
MindSpore:
Onnx:
(2) Onnx推理脚本
推理脚本就是仿照eval脚本写一个可以用bash调用的.sh文件,使得用户可以通过bash命令一键推理,详细可参考此PR中的run_infer_onnx.sh:
https://gitee.com/mindspore/models/pulls/2913/files
4. PR提交
(1) Leader创建分支
联系小组Leader fork官方MindSpore和Models仓库并给每个模型创建分支。
(2) 下载新MindSpore和Models仓库
下载小组用来提交PR的新MindSpore和Models仓库:
git clone https://gitee.com/xxxx/mindspore.git
git clone https://gitee.com/xxxx/models.git
(3) 更新替换文件
进入MindSpore和Models路径后切换到自己的分支:
git checkput cnnctc
将自己需要修改的文件传入新下载的MindSpore和Models文件夹对应路径下进行替换。
(4) 上传代码
使用如下命令提交pr:
① 查看已修改文件:
git status
② 提交到本地仓库:
依据①中输出的已修改文件,核对是否均是自己修改的,如果是则直接git add .若不全是则一个个的git add xxxx文件。
MindSPore仓库:
git add .
git commit -am " ONNX converter: CNN+CTC support"
保存并退出:点击ESC,然后按住shift和 ”:” ,输入wq,回车
Models仓库:
git add .
git commit -am " ONNX infer: CNN+CTC support"
保存并退出:点击ESC,然后按住shift和 ”:” ,输入wq,回车
③ 提交到远程仓库分支:
git push origin cnnctc
5. 创建PR
联系Leader创建PR,或自己前往提交代码的仓库创建PR:
6. 门禁测试
① 开启测试
在自己PR的评论区输入/retest即可开启测试:
点击系统自动评论中的Link超链接即可打开门禁界面:
② 测试结果
报错后会中断门禁测试,在评论区找报错信息,点击报错评论中的超链接即可查看门禁报错信息:
一段代码错误会以两段相同代码的不同格式输出,下面一段为自己当前写的代码,而上面一段为门禁系统的修改意见,只需要按照上面一段改就行了(字有点小放大点看)。
如果通过则系统自动回复如下:
七、总结
本次Onnx模型推理和导出项目整体难度不大,由于大部分模型都是基于卷积神经网络,各种模型中的算子具有高度重复性,因此可能有很多人选到的模型是所有算子都已经实现的,这种情况下只需要依据指南完成精度推理和交付件编写即可。若开发中遇到问题(如ckpt文件精度不对,需要申请训练资源)请联系华为负责工程师吴工(吴逸群)。
预祝大家本次Onnx项目开发顺利,早日提交交付件,都能评上金质量奖!!!
武汉理工大学-王振邦
2022年6月25日
- 点赞
- 收藏
- 关注作者
评论(0)