ONNX模型是按照google protobuf格式保存的,模型训练的目的就是为了得到变量的权值,只不过是纯数字罢了,但是我们也不能就这样把这些数字一个一个地写入文件,因为在要保存的模型文件里,不光要保存权值,也要告诉之后用这个模型的人,模型结构是怎么样的,所以需要合理地设计保存文件的格式。不同的机器学习框架都有自己的模型保存格式,例如 Keras 的模型格式是 h5,而 Tensorflow 和 onnx 的保存格式就是 protobuf。

其实 protobuf 使用起来非常简单方便,就是自己先定义一个数据保存格式,然后用 protoc 自动生成各个语言的解析代码,现在支持 C, C++, C#, Java, Javascript, Objective-C, PHP, Python, Ruby。

举个例子,我们创建这么个文件 amessage.proto

syntax = "proto3";

message AMessage {
  int32 a=1; 
  int32 b=2;
}

那么我们就定义了一种二进制的数据存储格式,里面包含 2 个数字,其中 a = 1,代表 id 为 1 的数据是个 int32 类型,它的名字是 a,并不是代表 a 这个变量数值是 1,同样的道理,id 为 2 的数据是个 int32 类型,它的名字是 b。这里 id 是不能重复的。

所以使用 protobuf 需要先定义一个数据格式,然后自动生成 编码 和 解码 的代码,供不同语言使用,因为能自动生成代码,所以 protobuf 简单好用,非常受欢迎,建议大家使用 protov3。

为了帮助我们更加直观的看到模型结构,这里推荐一个工具 protobuf editor,可以很方便地解析 protobuf 文件,它的下载地址是

ProtoBufEditor download | SourceForge.nethttps://sourceforge.net/projects/protobufeditor/软件下载下来后,按照下面的流程就可以解析之前我们生产的 mnist.onnx 文件了, onnx.proto3格式的定义可以在这里下载:

protobuf安装环境需要事先安装jre环境,安装成功的界面如下所示:

解析mnist模型:

将模型文件和模型protobuf描述所在位置设置给proto buffer editor工具,选择模型文件和proto格式文件,然后点击edit开始解析:

解析视图:

libonnx protobuf代码生成:

首先安装protobuf c编译器

sudo apt install protobuf-c-compiler

之后在GITHUB下载onnx3.proto描述文件,执行

protoc-c --c_out=. onnx3.proto
protoc-c --c_out=./ ./hello.protoc

onnx3生成分析

上面介绍了protoc-c工具将onnx3.proto解析后产生了onnx3.pb-c.c和onnx3.pb-c.h两个文件,在onnx3.pb-c.c中,有一个非常重要的函数"onnx__model_proto__unpack".

这个函数之所以重要,是因为整个ONNX文件的解析就靠它一个就可以完成了,XBOOT的libonnx推理小框架实现中,用的就是这个函数,如下图所示:

函数的返回类型也定义在生成的onnx3.pb-c.h中

既然函数和结构体定义都在生成的文件中,那么生成的文件是自包含的吗?可以自举完成任务吗?显然不是的,因为onnx__model_proto__unpack调用的一个重要函数protobuf_c_message_unpack在生成文件中没有定义。

那么它在哪里定义呢?我们想搞清楚这个问题,写如下一个测试用例。

发现确实有很多函数找不到定义,出现连接错误,看来我们还缺少一环。

 缺少哪一环呢?经过白嫖度娘,因为protobuf是没有C语言支持的,所以找了个三方的C语言支持,就是上面的protobuf-c但是由于我们是直接安装的二进制,导致有些库没有拉进来,所以才导致这个问题,当务之急是要安装protobuf-c的开发环境。
先安装protobuf,安装包下载下来之后,解压,进入目录

我们找个TAG版本一策完全:

之后配置编译:

$./autogen.sh
$./configure --prefix=/home/caozilong/Workspace/proto-c/install --libdir=/home/caozilong/Workspace/proto-c/install/lib
$make
$make install
$sudo /sbin/ldconfig -v

至此,回到安装目录,我们就有了proto-c的开发环境了

现在回到上面的连接错误问题,首先用新工具重新PARSE PROTO文件

 /home/caozilong/Workspace/proto-c/install/bin/protoc-c --c_out=./ onnx3.proto

执行编译命令

gcc main.c  onnx3.pb-c.c -I/home/caozilong/Workspace/proto-c/install/include -L/home/caozilong/Workspace/proto-c/install/lib -lprotobuf-c

顺利编译通过,所以可以看出,onnx3.pb-c.c依赖的运行时就是libprotobuf-c.a.

libonnx同样对libprotobuf-c.a有依赖,不过它的做法比较灵活,通过下图可以看到libprotobuf-c.a只有一个.c文件,他直接将.c拷贝到了它的环境中直接用了。

参考资料:

结束