gdyshi | 孤独隐士 MindMap, Machine Learning, Medical Equipment

tensorflow模型部署系列————嵌入式部署


摘要

本文为系列博客tensorflow模型部署系列的一部分,用于实现通用模型的部署。本文主要实现用tflite接口调用tensorflow模型进行推理。实现了tensorflow在边缘计算及一些低成本方案、物联网或工业级应用中使用的轻量级模型部署方案,并提供相关示例源代码。相关源码见链接


引言

本文为系列博客tensorflow模型部署系列的一部分,用于tflite实现通用模型的部署。本文主要使用pb格式的模型文件,其它格式的模型文件请先进行格式转换,参考tensorflow模型部署系列————预训练模型导出。从模型文件中获取输入输出op名请参考博客tensorflow模型部署系列————单机python部署

主题

上一篇博文tensorflow模型部署系列————单机JAVA部署就如何使用JAVA语言加载模型文件并利用模型文件进行推理进行讲解,博文中也开放了使用JAVA进行模型推理的代码,目前多数智能终端都已经支持JAVA,但在一些低成本方案、物联网或工业级应用中更多的是使用嵌入式linux甚至是单片机,主要使用的编译语言是C。博文tensorflow模型部署系列————单机C++部署介绍了使用C/C++进行模型部署的方法,但实际应用时因为tensorflow库文件太大(已超过100M),限制了tensorflow在嵌入式上的应用。目前tensorflow提供了专门用于嵌入式的tflite框架(仅700K左右)。tflite的目标是嵌入式部署,嵌入式部署需要针对处理器进行特殊优化。google官方除了android库以外,并没有已编译好的文件可供下载。本文先对tflite及运行作简单介绍,然后分别对tflite在python c/c++ java上的部署进行解说。

tflite介绍

应用场景

tflite是google为深度学习在嵌入式物联网应用而推出的轻量级框架。它提供了python、java和C++接口,同时可以将浮点运算转换为整数运算,从而在特定的硬件平台上加快推理速度。tflite使用的模型不是pb文件,而是更小的基于FlatBuffers的模型文件。

tflite主要有两个组件:推理组件和模型转换组件。推理组件用于运行模型;模型转换组件用于将需要的模型转换为tflite模型文件

模型文件转换

模型文件转换时需要指定输入输出张量名,从模型文件中获取输入输出op名请参考博客tensorflow模型部署系列————单机python部署

从session转换

import tensorflow as tf

img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
var = tf.get_variable("weights", dtype=tf.float32, shape=(1, 64, 64, 3))
val = img + var
out = tf.identity(val, name="out")

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  converter = tf.lite.TFLiteConverter.from_session(sess, [img], [out])
  tflite_model = converter.convert()
  open("converted_model.tflite", "wb").write(tflite_model)

从pb文件转换

import tensorflow as tf

graph_def_file = "/path/to/Downloads/mobilenet_v1_1.0_224/frozen_graph.pb"
input_arrays = ["input"]
output_arrays = ["MobilenetV1/Predictions/Softmax"]

converter = tf.lite.TFLiteConverter.from_frozen_graph(
  graph_def_file, input_arrays, output_arrays)
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)

python部署

python语言目前在嵌入式系统上应用的还不多。这里把python也拿出来讲有两个原因:

  1. 可以快速的在上位机上评估tflite模型文件的准确率等性能
  2. 后面讲解java和c++部署代码时,参考python代码,以便于读者更好地理解tflite的运行原理

安装

python版不需要额外安装,安装完毕tensorflow pip install tensorflow 后就可以使用了 tf.lite

API讲解

tflite的python接口详见官方文档。这里针对常用接口做一个说明。

  • 加载模型文件

    interpreter = tf.lite.Interpreter(model_path=tflite_file)
    
  • 创建tensors

    interpreter.allocate_tensors()
    
  • 获取输入输出OP

    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()
    
  • 张量填充

    interpreter.set_tensor(input_details[0]['index'], d)
    
  • 运行推理

    interpreter.invoke()
    
  • 获取张量值

    interpreter.get_tensor(output_details[0]['index'])
    

部署代码

模型封装库

模型封装类主要包括两个方法:初始化和推理。

初始化只用于处理与invoke无关的所有操作,以减少推理时的操作,保证模型推理高效运行。初始化主要进行的操作包括:模型文件加载、获取输入输出张量。

推理主要进行的操作有:输入张量填充、interpreter.invoke、和输出张量获取。

模型封装类示例代码

经过模型封装类封装以后,示例代码就很简单了。只用准备数据,然后推理就行了。

C++部署

API讲解

这里针对tflite的c++常用接口做一个说明,并跟将C接口跟本文中的python部署代码进行对应,以便于读者更好地理解接口

  • 加载模型文件,对应python代码interpreter = tf.lite.Interpreter(model_path=tflite_file)

    std::unique_ptr<Interpreter> interpreter;
    std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(model_file);
    InterpreterBuilder builder(*model, resolver);
    builder(&interpreter);
    
  • 创建tensors,对应python代码interpreter.allocate_tensors()

    interpreter->AllocateTensors()
    
  • 获取输入输出OP,对应python代码interpreter.get_input_details() interpreter.get_output_details()

    in_index = interpreter->inputs()[0];
    out_index = interpreter->outputs()[0];
    
  • 张量填充,对应python代码interpreter.set_tensor(input_details[0]['index'], d)

    memcpy(interpreter->typed_tensor<float>(in_index), &input_vals[0], INPUT_SIZE*sizeof(input_vals[0]));
    
  • 运行推理,对应python代码interpreter.invoke()

    interpreter->Invoke()
    
  • 获取张量值,对应python代码interpreter.get_tensor(output_details[0]['index'])

    float* output = interpreter->typed_tensor<float>(out_index);
    memcpy(&output_vals[0], output, OUTPUT_SIZE*sizeof(output_vals[0]));
    

部署代码

模型封装库

模型封装类主要包括两个方法:初始化和推理。

初始化只用于处理与invoke无关的所有操作,以减少推理时的操作,保证模型推理高效运行。初始化主要进行的操作包括:模型文件加载、获取输入输出张量。

推理主要进行的操作有:输入张量填充、interpreter.invoke、和输出张量获取。

模型封装类示例代码

经过模型封装类封装以后,示例代码就很简单了。只用准备数据,然后推理就行了。

编译

tflite的编译需要使用tensorflow源代码,下面给出简单的编译步骤。后续在我的博客中会放出各平台下已编译好的tflite库文件,请持续关注

  1. 下载tensorflow源码

  2. 拷贝代码文件夹C++tensorflow/lite/tools/make/

  3. tensorflow/lite/tools/make/Makefile文件中增加如下代码

    HELLOW_TFLIET := hellow_tf
    HELLOW_TFLIET_BINARY := $(BINDIR)$(HELLOW_TFLIET)
    
    HELLOW_TFLIET_SRCS := \
    tensorflow/lite/tools/make/C++/model.cc \
    tensorflow/lite/tools/make/C++/example.cc
    
    INCLUDES += \
    -Itensorflow/lite/tools/make/C++/
    
    ALL_SRCS += \
      $(HELLOW_TFLIET_SRCS)
    
    CORE_CC_EXCLUDE_SRCS += \
    $(wildcard tensorflow/lite/tools/make/C++/model.cc) \
    $(wildcard tensorflow/lite/tools/make/C++/example.cc)
    
    HELLOW_TFLIET_OBJS := $(addprefix $(OBJDIR), \
    $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(HELLOW_TFLIET_SRCS))))
    
    $(HELLOW_TFLIET): $(LIB_PATH) $(HELLOW_TFLIET_OBJS)
    	@mkdir -p $(BINDIR)
    	$(CXX) $(CXXFLAGS) $(INCLUDES) \
    	-o $(HELLOW_TFLIET_BINARY) $(HELLOW_TFLIET_OBJS) \
    	$(LIBFLAGS) $(LIB_PATH) $(LDFLAGS) $(LIBS)
    
  4. 执行编译命令make hellow_tf -j8 -f tensorflow/lite/tools/make/Makefile

JAVA(android)部署

tflite官方代码提供了直接在android代码上使用tflite库的方法,本文针对android中使用库为例进行说明。

android环境配置

要在android代码中使用tflite库,只需在工程配置中加入implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'即可,另外需要注意的是android打包时会对assets下不认识的文件压缩,这会导致模型文件读取失败,解决方法是在配置文件添加

android {
    ...
    aaptOptions {
        noCompress "tflite"
    }
    ...
}

部署代码

模型封装库

模型封装类主要包括两个方法:初始化、关闭和推理。

初始化只用于处理与invoke无关的所有操作,以减少推理时的操作,保证模型推理高效运行。初始化主要进行的操作包括:模型文件加载。

推理主要进行的操作有:interpreter.invoke

模型封装类示例代码

经过模型封装类封装以后,示例代码就很简单了。只用准备数据,然后推理就行了。

示例代码

后记

因最近在做一个嵌入式移植的实际项目,前面我放出的源码需要修改多处Makefile,容易出错,加上评论区有人咨询代码编译相关问题,故对嵌入式部署博文及代码进行更新。主要目的是简化编译操作,不需要下载整个tensorflow代码,只需引用相关头文件就可完成编译。

要达到以上目的,需要去除编译自已写的代码时对tflite相关源码的依赖,一种方式是将tensorflow代码先编译为库,这样大家在编译自己的特殊应用时只需链接编译好的库即可。因为官方的编译脚本也在不断更新中,我这里主要把修改点进行说明。同样的,我会把相关代码放到github里。

编译tflib

主要通过修改官方编译示例的编译脚本来实现。

  1. 复制一份编译脚本

     cp Makefile Makefile_tflib
     cp build_rpi_lib.sh build_tflib.sh
    
  2. Makefile_tflib文件修改如下行

     $(LIB_PATH): tensorflow/lite/schema/schema_generated.h $(LIB_OBJS)
         @mkdir -p $(dir $@)
         $(AR) $(ARFLAGS) $(LIB_PATH) $(LIB_OBJS)
     ++++$(CC) -shared -o $(LIBDIR)libtensorflow-lite.so $(LIB_OBJS)
    
  3. build_tflib.sh文件中将Makefile替换为Makefile_tflib

  4. 加载交叉编译环境,然后执行./build_tflib.sh进行编译

    编译完成后,会在./gen/rpi_armv7l/lib下生成libtensorflow-lite.alibtensorflow-lite.so文件。只要嵌入式环境的编译器没变,我们就可以一直用生成的这两个文件

  5. 打包库文件。

     mkdir -p mpapp/tflib/lib
     cp ./gen/rpi_armv7l/lib/libtensorflow* mpapp/tflib/lib
    

编译应用代码

执行build.sh即可

附录

参考



Similar Posts

Content