摘要
本文为系列博客tensorflow模型部署系列的一部分,用于实现通用模型的部署。本文主要实现用JAVA接口调用tensorflow模型进行推理。相关源码见链接
引言
本文为系列博客tensorflow模型部署系列的一部分,用于JAVA
语言实现通用模型的部署。本文主要使用pb格式的模型文件,其它格式的模型文件请先进行格式转换,参考tensorflow模型部署系列————预训练模型导出。从模型文件中获取输入输出op名请参考博客tensorflow模型部署系列————单机python部署
主题
上一篇博文tensorflow模型部署系列————单机C++部署就如何使用C/C++语言加载模型文件并利用模型文件进行推理进行讲解,博文中也开放了使用python
进行模型推理的代码,本文参照python模型部署代码进行C++模型部署。
实现方案选择
有两种方案可以做tensorflow的java部署
- 直接使用tensorflow官方提供的java库。优点是可直接近乎无脑使用,这里是tensorflow java库官方安装指南。缺点是它目前是google的实验性API,后续的更改不保证向后兼容,基于此库的应用代码可能需要重写。
- 在上一篇博文tensorflow模型部署系列————单机C++部署基础上加入jni接口。这一块只是对C接口的封装,优点是tensorflow模型部署系列————单机C++部署封装用的C库属于tensorflow api稳定性保障范围内,可以简单的更新到新的tensorflow中。缺点是需要自己做C和java之间的接口封装。下面分别针对这两种方式分别进行介绍
官方java库
安装
进入网页下载jar包和jni文件,目前已编译的jni支持linux-x86 windows-x86 macOS, arm处理器并不支持,需要自行编译。关于tensorflow源码编译过程请关注我的博客,后续会有专门博文讲解
API讲解
tensorflow的java接口详见官方文档。这里针对常用接口做一个说明,并跟将JAVA接口与tensorflow模型部署系列————单机python部署](https://blog.csdn.net/chongtong/article/details/90693787)中的python代码进行对应,以便于读者更好地理解接口
-
创建图,直接使用java封装类Graph即可
Graph graph = new Graph()
-
加载模型文件,对应python代码
tensors = tf.import_graph_def(output_graph_def, name="")
graph.importGraphDef()
-
创建session,直接使用java封装类Graph即可,对应python代码
self.__sess = tf.Session()
Session session = new Session(graph)
-
获取输入输出OP,对应python代码
self.__input = graph.get_tensor_by_name("input:0")
op名称的获取方式见tensorflow模型部署系列————单机python部署。
java代码直接使用op名称作为参数即可
-
张量填充,无对应python代码,python可直接赋值
java需创建Tensor,在创建的同时进行数据填充
Tensor<T> input = Tensor.create()
-
运行OP,对应python代码
output = self.__sess.run(self.__output, feed_dict={self.__input: input})
使用
session.runner().run()
运行sessiontenss= session.runner().feed("sequential_1_input", input).fetch("output/Softmax").run() 其中 runner()用于获取运行对象 feed()用于填充输入占位符 fetch()用于指定需获取的tensor run()用于运行session
-
获取张量值,无对应python代码,因python可直接获取
op运行完毕后就可以通过
TF_TensorData
找到张量的数据缓冲区地址,并获取输出数据了Tensor<Float> output = tenss.get(0).expect(Float.class) 其中 get()用于获取运行后的张量值 expect()作用为类型检查
java部署代码
模型封装库
模型封装类主要包括两个方法:初始化和推理。
初始化只用于处理与sess.run
无关的所有操作,以减少推理时的操作,保证模型推理高效运行。初始化主要进行的操作包括:模型文件加载、获取计算图和计算session。
推理主要进行的操作有:输入张量填充、sesson.run
、和输出张量获取。从模型文件中获取输入输出op名请参考博客tensorflow模型部署系列————单机python部署
模型封装类示例代码
经过模型封装类封装以后,示例代码就很简单了。只用准备数据,然后推理就行了。
自定义jni封装接口
jni接口封装
jni接口封装我参考android ndk开发包中的工具源码,并在此基础上做了部分修改。
jni接口封装将上一篇博文tensorflow模型部署系列————单机C++部署的封装库接口包装成jni接口。参考android ndk开发包中的工具源码将需要包装的接口函数放进如下数组中
static JNINativeMethod gjniDiagnoseMethods[] = {
{(char *)"model_init", (char *)"(Ljava/lang/String;)I", (void *) jni_model_init},
{(char *)"model_deinit", (char *)"()I", (void *) jni_model_deinit},
{(char *)"model_inference", (char *)"(I[F[F)I", (void *) jni_model_inference},
};
然后定义调用此jni接口的类名#define JNI_ADAPTER_CLASS "JavaModel"
即可
java部署代码
模型封装库
模型封装类主要包括三个方法:初始化、去初始化和推理。
初始化只用于处理与sess.run
无关的所有操作,以减少推理时的操作,保证模型推理高效运行。初始化主要进行的操作包括:模型文件加载、获取计算图和计算session、根据输入输出tensor名称获取输入输出tensor。从模型文件中获取输入输出op名请参考博客tensorflow模型部署系列————单机python部署
推理主要进行的操作有:数据类型转换、输入张量填充、sesson.run
、和输出张量获取
去初始化用于释放资源,这一块是python所没有的。主要操作包括:session的关闭和释放、计算图的释放
模型封装类示例代码
经过模型封装类封装以后,示例代码就很简单了。只用准备数据,然后推理就行了。
示例代码
-
使用官方java库进行模型部署代码
-
自定义jni封装接口进行模型部署代码