前言:
仔细想了一下,本来想要另开一个主题来继续接下来的内容,但是想到可视化专ai模这个步骤是在前面三篇的基础上继续的,因此这里就不打算另开一个主题了。
如果没有了解过之前内容的人员,或者想要再次回顾之前的内容,可以点击如下的链接按照序号以此查看。
你或许也想拥有专属于自己的AI模型文件格式-(1)https://blog.csdn.net/Pengcode/article/details/121754272你或许也想拥有专属于自己的AI模型文件格式-(2)https://blog.csdn.net/Pengcode/article/details/121776674你或许也想拥有专属于自己的AI模型文件格式-(3)https://blog.csdn.net/Pengcode/article/details/121843704
接下来的目标-(可视化专ai模)
前三篇文章,我们成功定义了专属于自己的AI模型文件格式,同时在第三篇文章中,也成功地生成了第一个模型文件,这个模型文件是真正意义上的完完全全自定义出来的。毫不夸张地说,恭喜你成功地定义了一套可以叫做onnx-mini的模型。
可是,总觉得还不够完美,还记得我们上次怎么查看模型内部的信息的吗?我们使用flatc工具把模型解析成了json文本,然后打开json文本查看内部信息,这传出去让别人知道了,感觉得多没面子呀!!一想到别人家的Caffe、Tensorflow、Pytorch都可以可视化,我们的专ai模也势必要拿下可视化这个槛!!
因此,决定了,接下来的目标就是实现在netron可视化我们的自己专ai模。我们要编译出一份适配我们模型的netron来!!
或不多说,开干!!!
netron可视化自定义模型格式文件
之前我采用了flatbuffers实现了自定义模型文件的数据结构描述,得到了后缀名为PzkM的模型。编写了相应的C++代码来生成基于自定义格式的第一个模型文件,上次只是采用了flatc把二进制文件解析成了json文本文件,这样依然不太完美,因此这次就是打算让我们的自定义模型文件可以适配netron,达到可视化的目的。
这个计划在我源码编译netron的时候就已经陆陆续续地进行着,为此花费了不少时间,因此目标是适配自定义模型,而我采用了flatbuffers工具,为了更好地参考,我以tensorflow lite的tflite这个同样使用了flatbuffers工具来描述模型文件的模型格式作为主要研究对象,试图加快开发速度。
一、netron适配源码解读
一开始,看到如此庞大的js代码,实在是无所适从。这次采用的直接chrome的开发工具调试,追踪netron的一个按钮,即入口是index.html文件,重点查看一个id=”open-file-buttom”的button标签;button绑定的操作中,其整体流程如下所示:
(1)用户打开一个模型文件后,js获取了模型文件信息(路径、文件名、文件后缀、好像还当作二进制代码进行了读操作);
(2)js根据模型文件信息,根据一定的判断逻辑,判断出该模型文件对应哪个框架,比如.tflite模型文件对应tensorflow lite,那么之后会调用类成员函数require(id)获取到名为tflite.js这个源代码,会export到tflite.js内的模型适配接口,其通过”module.exports.ModelFactory = tflite.ModelFactory;”这句js代码获取到了模型适配接口;
(3)然后调用了ModelFactory.match(context),ModelFactory.open(context,match)这两个接口实现了tflite模型文件的适配工作;
(4)最后netron再调用.open()后的信息绘制出了模型可视化图像(这部分的源码未阅读,后续待解读)
二、tflite的schema的设计思想解读
tflite也存在一个schema文件,同我们自定义的模型文件类似。因此想要阅读tflite的适配源码,读懂其schmea文件也是非常重要。
从tflte的设计思想来看,其主要的有向图连接信息主要体现在ops上,ops中同样含有input-id、output-id这些信息,这样也就可以得到连接信息了。
三、flatbuffes在javascript的使用
因为netron的编程语言是javascript,目前遇到比较棘手的问题就是最新版的flatbuffers对于javascript的支持已经抛弃,需要走schema->typescript->javascript的路线。而flatbuffers官网的关于javascript的教程却没有对应的更新,而且schema->typescript生成的typescript代码是千差万别的,因此不建议跟随flatbuffers官网的关于javascript的教程进行操作(因为无法完全复现官网的教程)。
通过一通摸索,终于知道了如何在javascript使用flatbuffers工具了。我将继续延续之前的schema文件来说明如何在javascript中来读取我们之前生成的模型,也就是解析我们的专ai模的第一个模型文件。
3.1. 使用flatc编译schema文件为typescript
#/bin/bash
# cd到存放schema文件的目录
$ cd schema-path
# 使用flatc编译器编译schema文件为typescript
$ flatc --ts pzk-schema.fbs
# 将会在当前目录下生成pzk-model的文件夹和pzk-schema.ts文件
$ ls
#pzk-model pzk-schema.ts pzk-schema.fbs
3.2. 安装相关依赖库和工具
该部分主要有两部分:一部分是flatbuffers在javascript中使用需要在node中进行安装依赖库;第二就是typescript转换到javascript这个步骤需要使用到tsc这个代码转换工具。
相关的安装命令如下所示:
#/bin/bash
# 全局安装flatbuffer关于javascript的依赖库
$ npm install -g flatbuffers
# 全局安装tsc工具
$ npm install -g typescript
# 以上的库可以不安装在全局环境中,当你希望只在当前工程中使用,那么只需要在去掉-g参数即可;强烈建议你安装在全局环境中,因此这样比较容易使用tsc命令
3.3. typescript->javascript实操
这部分是至关重要的,因为从这部分开始,按照官网的教程就无法走通了。同时,如果你自己的自定义模型情况不一致,还需要结合我接下的操作实际修改。
3.3.1. 第一步:找出需要转换的主typescript文件
因为flatc编译器的目标是typescript时,其将会生成多个typescript代码文件。如上所示,之前我所定义的schema生成了一个pzk-schema.ts和一个pzk-model文件夹,这个pzk-model文件夹下拥有多个typescript文件,如下所示:
序号 |
typescript文件名 |
跟schema文件对应关系 |
1 |
attr-meta.ts |
table AttrMeta |
2 |
attributes.ts |
table Attributes |
3 |
connect.ts |
table Connect |
4 |
data-layout.ts |
enum DataLayout |
5 |
data-type.ts |
enum DataType |
6 |
layer.ts |
table Layer |
7 |
p-model.ts |
table PModel |
8 |
tensor-shape.ts |
enum DataLayout |
9 |
tensor-type.ts |
enum TensorType |
10 |
tensor.ts |
table Tensor |
11 |
time.ts |
table time |
12 |
weights.ts |
table Weights |
因此,我们很容易看出flatc把我们定义的所有数据结构都通过封装成多个typescript文件,而且这些typescript文件的名称非常有规律,就是按照驼峰命名法进行转换,比如我的schema中存在一个叫做AxxxxBxxxxCxxx的数据结构,那么flatc编译成typescript时,那么就会产生一个叫做axxxx-bxxxx-cxxx.ts的文件。
因此知道了这层关系后,我们就知道该对那个typescript文件进行转换了。没错,就是p-model.ts是我们的主程序文件,因为我们的定义的schema文件中PModel作为了我们的根数据类型。(按照flatbuffers官网的说明,那个最外面的pzk-schema.ts是主程序文件,但是你按照这个思路去转换,你将会发现有的数据结构没有被转换成javascript)。
因此,我们紧接如下操作。
3.3.2. 第二步:正式转换typescript->javascript
上步我们知道了主程序文件,将下来使用如下的转换命令:
#/bin/bash
# 使用tsc转换工具
$ tsc pzk-model/p-model.ts
# 清除不必要的typescript文件
$ find . -name "*.ts" |xargs rm -rfv
至此,操作无误的话,我们就会得到如下的js文件:
#/bin/bash
$ ls pzk-model
:<<!
#得到如下的javascript代码文件
attributes.js connect.js data-type.js p-model.js tensor-shape.js time.js
attr-meta.js data-layout.js layer.js tensor.js tensor-type.js weights.js
!
3.4. 在javascript中实际使用flatbuffers
在此将使用上步生成的javascript代码来实际解析读取专ai模的第一个模型文件。
新建一个javascript文件,我这里取名为try-flatc.js,其内部代码如下所示:
const fs = require('fs');
var flatbuffers = require('flatbuffers');
var PModel = require('./pzk-model/p-model').PModel;
var builder = new flatbuffers.Builder(1024);
var author = builder.createString("name");
var version = builder.createString("v1.0");
var modelname = builder.createString("what fuck");
console.log("try use flatbuffer");
console.log(author == undefined);
console.log("try load custom model file");
// 读取二进制模型文件
var bytes = new Uint8Array(fs.readFileSync('/home/pengzhikang/project/custom-model/build/release/first.PZKM'));
// 使用flatbuffers接口解析二进制文件
var buf = new flatbuffers.ByteBuffer(bytes);
// 从解析后的buf中获取到我们的PModel数据结构体
var mymodel = PModel.getRootAsPModel(buf);
// 获取模型的作者信息
console.log("author is " + mymodel.author());
// 获取模型的版本号
console.log("model version is " + mymodel.version());
// 获取模型的名称
console.log("model name is " + mymodel.modelName());
// 获取模型的创建时间
var model_time = mymodel.createTime();
// 打印时间信息
console.log("mode create time is " + model_time.year() + "/" + model_time.month() + "/"
+ model_time.day() + " " + model_time.hour() + ":" + model_time.min() +
":" + model_time.sec());
// 以下是从模型文件中读取出列表的操作
var inputid_array = mymodel.modelRuntimeInputIdArray();
console.log("model runtime input id list is above:");
for(x in inputid_array)
{
console.log(x + ",");
}
var tensor_array = mymodel.tensorBuffer();
console.log("model tensor length is "+ mymodel.tensorBufferLength());
for (var i = 0; i < mymodel.tensorBufferLength(); i++)
{
console.log(mymodel.tensorBuffer(i).id() + ":" + mymodel.tensorBuffer(i).name());
}
运行try-flatc.js文件:
#/bin/bash
$ node try-flatc.js
:<<!
# 其将会打印出如下所示的模型信息:作者、版本号、创建时间、张量信息等
try use flatbuffer
false
try load custom model file
author is pengzhikang
model version is v2.1
model name is holly-model
mode create time is 2021/12/9 23:38:53
model runtime input id list is above:
0,
model tensor length is 4
0:model_input_0
1:tensor_1
2:tensor_2
3:tensor_3
!
至此,我们在javascript中解析专ai模的第一个模型文件的实例就完成了。
限于控制篇幅和本人是边开发边写作的情况,所以就有点随性和文章长短不一。请大家多多包涵以及及时提出意见,如果有兴趣一起来进行开发也是可以的,热烈欢迎!