查看原文
其他

【他山之石】ONNX模型文件->可执行文件 C Runtime通路 具体实现方法

“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。

作者:知乎—CHUNer
地址:https://www.zhihu.com/people/CHUNerr


01

承前启后

下面所述内容为:在 ONNX-MLIR 工具链构建完成之后,使用工具链实现:将 ONNX 模型文件(文件名后缀为 .onnx 文件)转换成在linux命令行中可直接运行的可执行文件。若上述工具链构建成功之后,默认在文件夹onnx-mlir/build/bin/中有两个工具,分别为./onnx-mlir和./onnx-mli-opt。在此通路之中,只需使用工具./onnx-mlir的—-EmitLib参数,能够将 ONNX 模型转换到动态链接库 .so 文件。
注:官方工具链构建:https://github.com/onnx/onnx-mlir


02

实现平台以及操作系统
win10 + VMware 16.1.0 Pro + Ubuntu 20.10


03

方法总览
本方法在官方的网页中有比较详细的介绍,只需按照官网上的步骤即可完成此通路。步骤介绍:(1)生成 add.onnx 模型文件;(2)将模型文件转换成可调用的库文件;(3)自己写一个 C 程序;(4)链接编译生成可执行文件;(5)最后执行可执行文件。会在下面的具体实现方法中详细说明。
注:官方链接:http://onnx.ai/onnx-mlir//doxygen_html/index.html

04

具体实现方法
  • ONNX 模型文件的生成:
该ONNX模型文件的生成方式有二。其一,在之前的构建的过程中(具体步骤为:$ cmake --build .),会自动生成名为 add.onnx 的 ONNX 模型文件,生成文件的默认地址为onnx-mlir/build/test/unit/Runtime/DocExampleTest/add.onnx。其二,在本项目对应的论文(Compiling ONNX Neural Network Models Using MLIR)介绍中,提到 ONNX 模型文件是可以由 python 脚本生成,python 脚本的默认地址为onnx-mlir/build/test/unit/Runtime/DocExampleTest/gen_add_onnx.py。
注:下面命令中的 python 已经修改为默认 python3,执行命令后,在当前目录下,得到一个 add.onnx 模型文件。
$ python gen_add_onnx.py
  • 将ONNX 模型文件(add.onnx)转化为动态链接库文件(add.so):
    在执行命令的目录下,得到库文件 add.so。
$ ./onnx-mlir --EmitLib ${PWD}/add.onnx
命令行返回:
Shared library ${PWD}/add.so has been compiled.
  • 在官网上直接给出了对应刚刚生成的 add.so 库文件的C程序:
    在此直接给出C程序(该C程序与官网给出的C程序一样,作为备份),保存为 main.c。
注:如果出现什么问题,请往下在问题一览表中找对应问题的解决方法。
#include <OnnxMlirRuntime.h>#include <stdio.h>OMTensorList *run_main_graph(OMTensorList *);int main() { // Shared shape & rank. int64_t shape[] = {2, 2}; int64_t rank = 2; // Construct x1 omt filled with 1. float x1Data[] = {1., 1., 1., 1., 1., 1.}; int64_t *x1Shape = {2, 2}; OMTensor *x1 = omTensorCreate(x1Data, shape, rank, ONNX_TYPE_FLOAT); // Construct x2 omt filled with 2. float x2Data[] = {2., 2., 2., 2., 2., 2.}; int64_t *x2Shape = {2, 2}; OMTensor *x2 = omTensorCreate(x2Data, shape, rank, ONNX_TYPE_FLOAT); // Construct a list of omts as input. OMTensor *list[2] = {x1, x2}; OMTensorList *input = omTensorListCreate(list, 2); // Call the compiled onnx model function. OMTensorList *outputList = run_main_graph(input); // Get the first omt as output. OMTensor *y = omTensorListGetOmtByIndex(outputList, 0); float *outputPtr = (float *)omTensorGetDataPtr(y); // Print its content, should be all 3. for (int i = 0; i < 6; i++) printf("%f ", outputPtr[i]); return 0;}
  • 将文件 main.c 与 add.so 使用 gcc 链接编译得到生成可执行文件 add。
gcc main.c add.so -o add
  • 最后,执行可执行文件 ./.add。
$ ./add
命令行返回:
3.000000 3.000000 3.000000 3.000000 3.000000 3.000000

05

问题一览表
注:在上述通路过程中,可能会遇到如下的问题。
问题1:找不到头文件<OnnxMlirRuntime.h>;头文件<OnnxMlirRuntime.h>在构建结束之后默认地址为onnx-mlir/include/。
问题1的解决方法1:将头文件放到当前与 add.so 和 main.c 相同的目录下;需要修改C程序中对于头文件的调用方式,将【#include <...>】改为【#include "..."】;然后再次尝试编译。
问题1的解决方法2:找到虚拟机中默认的头文件搜索目录/usr/include/,该目录可能因人而异。将头文件<OnnxMlirRuntime.h>与文件夹/onnx-mlir/include/onnx-mlir一同 sudo 复制到/usr/include/目录下,而且需要重新启动虚拟机,再次尝试编译。
问题2:能够成功生成最终的可执行文件 add,而且可执行文件能够执行,但是在生成过程中,出现警告,具体警告内容如下(可以不用管);
main.c: In function ‘main’:main.c:12:23: warning: initialization of ‘int64_t *’ {aka ‘long int *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion] 12 | int64_t *x1Shape = {2, 2}; | ^main.c:12:23: note: (near initialization for ‘x1Shape’)main.c:12:26: warning: excess elements in scalar initializer 12 | int64_t *x1Shape = {2, 2}; | ^main.c:12:26: note: (near initialization for ‘x1Shape’)main.c:17:23: warning: initialization of ‘int64_t *’ {aka ‘long int *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion] 17 | int64_t *x2Shape = {2, 2}; | ^main.c:17:23: note: (near initialization for ‘x2Shape’)main.c:17:26: warning: excess elements in scalar initializer 17 | int64_t *x2Shape = {2, 2}; | ^main.c:17:26: note: (near initialization for ‘x2Shape’)
问题原因:官网上的程序在初始化的时候,语法并不规范,一般:不能直接使用整数数组直接定义一个指针变量。
解决方法:将 main.c 程序中的int64_t *x1Shape = {2, 2};修改为int64_t list1[] = {2, 2};和int64_t *x1Shape = &list1[0];,同理修改int64_t *x2Shape。
问题3:在打印结果的时候,程序并没有换行;
解决方法:将 main.c 程序中,在打印结果的代码之后(return0;之前),添加一行printf("\\n");即可。
到此,ONNX模型文件通过 C Runtime生成可执行文件的通路实现工作结束。

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。


“源头活水”历史文章


更多源头活水专栏文章,

请点击文章底部“阅读原文”查看



分享、在看,给个三连击呗!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存