• Tensorflow的部署3:TensorFlow Lite


      目录

      模型转换

      Android 部署

      配置 build.gradle

      配置 app/build.gradle

      添加 tflite 文件到 assets 文件夹

      加载模型

      运行输入

      运行输出

      运行及结果处理

      Quantization 模型转换

      visualize.py 使用方法

      总结

      TensorFlow Lite 是 TensorFlow 在移动和 IoT 等边缘设备端的解决方案,提供了 Java、Python 和 C++ API 库,可以运行在 Android、iOS 和 Raspberry Pi 等设备上。2019 年是 5G 元年,万物互联的时代已经来临,作为 TensorFlow 在边缘设备上的基础设施,TFLite 将会是愈发重要的角色。

      目前 TFLite 只提供了推理功能,在服务器端进行训练后,经过如下简单处理即可部署到边缘设备上。

      模型转换:由于边缘设备计算等资源有限,使用 TensorFlow 训练好的模型,模型太大、运行效率比较低,不能直接在移动端部署,需要通过相应工具进行转换成适合边缘设备的格式。

      边缘设备部署:本节以 android 为例,简单介绍如何在 android 应用中部署转化后的模型,完成 Mnist 图片的识别。

      模型转换

      转换工具有两种:命令行工具和 Python API

      TF2.0 对模型转换工具发生了非常大的变化,推荐大家使用 Python API 进行转换,命令行工具只提供了基本的转化功能。转换后的原模型为 FlatBuffers 格式。 FlatBuffers 原来主要应用于游戏场景,是谷歌为了高性能场景创建的序列化库,相比 Protocol Buffer 有更高的性能和更小的大小等优势,更适合于边缘设备部署。

      转换方式有两种:Float 格式和 Quantized 格式

      为了熟悉两种方式我们都会使用,针对 Float 格式的,先使用命令行工具 tflite_convert ,其跟随 TensorFlow 一起安装。

      在终端执行如下命令:

      tflite_convert -h

      输出结果如下,即该命令的使用方法:

      usage: tflite_convert [-h] --output_file OUTPUT_FILE

      (--saved_model_dir SAVED_MODEL_DIR | --keras_model_file KERAS_MODEL_FILE)

      --output_file OUTPUT_FILE

      Full filepath of the output file.

      --saved_model_dir SAVED_MODEL_DIR

      Full path of the directory containing the SavedModel.

      --keras_model_file KERAS_MODEL_FILE

      Full filepath of HDF5 file containing tf.Keras model.

      在 TensorFlow 模型导出 中,我们知道 TF2.0 支持两种模型导出方法和格式 SavedModel 和 Keras Sequential。

      SavedModel 导出模型转换:

      tflite_convert --saved_model_dir=saved/1 --output_file=mnist_savedmodel.tflite

      Keras Sequential 导出模型转换:

      tflite_convert --keras_model_file=mnist_cnn.h5 --output_file=mnist_sequential.tflite

      到此,已经得到两个 TensorFlow Lite 模型。因为两者后续操作基本一致,我们只处理 SavedModel 格式的,Keras Sequential 的转换可以按类似方法处理。

      Android 部署

      现在开始在 Android 环境部署,为了获取 SDK 和 gradle 编译环境等资源,需要先给 Android Studio 配置 proxy 或者使用镜像。

      配置 build.gradle

      将 build.gradle 中的 maven 源 google() 和 jcenter() 分别替换为阿里云镜像地址,如下:

      buildscript {

      repositories {

      maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }

      maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }

      }

      dependencies {

      classpath 'com.android.tools.build:gradle:3.5.1'

      }

      }

      allprojects {

      repositories {

      maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }

      maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }

      }

      }

      配置 app/build.gradle

      新建一个 Android Project,打开 app/build.gradle 添加如下信息:

      android {

      aaptOptions {

      noCompress "tflite" // 编译apk时,不压缩tflite文件

      }

      }

      dependencies {

      implementation 'org.tensorflow:tensorflow-lite:1.14.0'

      }

      其中,

      aaptOptions 设置 tflite 文件不压缩,确保后面 tflite 文件可以被 Interpreter 正确加载。

      org.tensorflow:tensorflow-lite 的最新版本号可以在这里查询 https://bintray.com/google/tensorflow/tensorflow-lite

      设置好后,sync 和 build 整个工程,如果 build 成功说明,配置成功。

      添加 tflite 文件到 assets 文件夹

      在 app 目录先新建 assets 目录,并将 mnist_savedmodel.tflite 文件保存到 assets 目录。重新编译 apk,检查新编译出来的 apk 的 assets 文件夹是否有 mnist_cnn.tflite 文件。

      点击菜单 Build->Build APK (s) 触发 apk 编译,apk 编译成功点击右下角的 EventLog。点击最后一条信息中的 analyze 链接,会触发 apk analyzer 查看新编译出来的 apk,若在 assets 目录下存在 mnist_savedmodel.tflite ,则编译打包成功,如下:

      assets

      |__mnist_savedmodel.tflite

      加载模型

      使用如下函数将 mnist_savedmodel.tflite 文件加载到 memory-map 中,作为 Interpreter 实例化的输入

      /** Memory-map the model file in Assets. */

      private MappedByteBuffer loadModelFile(Activity activity) throws IOException {

      AssetFileDescriptor fileDescriptor = activity.getAssets().openFd(mModelPath);

      FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());

      FileChannel fileChannel = inputStream.getChannel();

      long startOffset = fileDescriptor.getStartOffset();

      long declaredLength = fileDescriptor.getDeclaredLength();

      return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);

      }

      memory-map 可以把整个文件映射到虚拟内存中,用于提升 tflite 模型的读取性能。更多请参考: JDK API 介绍

      实例化 Interpreter,其中 acitivity 是为了从 assets 中获取模型,因为我们把模型编译到 assets 中,只能通过 getAssets() 打开。

      mTFLite = new Interpreter(loadModelFile(activity));

      memory-map 后的 MappedByteBuffer 直接作为 Interpreter 的输入, mTFLite ( Interpreter )就是转换后模型的运行载体。

      运行输入

      我们使用 MNIST test 测试集中的图片作为输入,mnist 图像大小 28*28,单像素,因为我们输入的数据需要设置成如下格式

      //Float模型相关参数

      // com/dpthinker/mnistclassifier/model/FloatSavedModelConfig.java

      protected void setConfigs() {

      setModelName("mnist_savedmodel.tflite");

      setNumBytesPerChannel(4);

      setDimBatchSize(1);

      setDimPixelSize(1);

      setDimImgWeight(28);

      setDimImgHeight(28);

      setImageMean(0);

      setImageSTD(255.0f);

      }

      // 初始化

      // com/dpthinker/mnistclassifier/classifier/BaseClassifier.java

      private void initConfig(BaseModelConfig config) {

      this.mModelConfig = config;

      this.mNumBytesPerChannel = config.getNumBytesPerChannel();

      this.mDimBatchSize = config.getDimBatchSize();

      this.mDimPixelSize = config.getDimPixelSize();

      this.mDimImgWidth = config.getDimImgWeight();

      this.mDimImgHeight = config.getDimImgHeight();

      this.mModelPath = config.getModelName();

      }

      将 MNIST 图片转化成 ByteBuffer ,并保持到 imgData ( ByteBuffer )中

      // 将输入的Bitmap转化为Interpreter可以识别的ByteBuffer

      // com/dpthinker/mnistclassifier/classifier/BaseClassifier.java

      protected ByteBuffer convertBitmapToByteBuffer(Bitmap bitmap) {

      int[] intValues = new int[mDimImgWidth * mDimImgHeight];

      scaleBitmap(bitmap).getPixels(intValues,

      0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());

      ByteBuffer imgData = ByteBuffer.allocateDirect(

      mNumBytesPerChannel * mDimBatchSize * mDimImgWidth * mDimImgHeight * mDimPixelSize);

      imgData.order(ByteOrder.nativeOrder());

      imgData.rewind();

      // Convert the image toFloating point.

      int pixel = 0;

      for (int i = 0; i < mDimImgWidth; ++i) {

      for (int j = 0; j < mDimImgHeight; ++j) {

      //final int val = intValues[pixel++];

      int val = intValues[pixel++];

      mModelConfig.addImgValue(imgData, val); //添加把Pixel数值转化并添加到ByteBuffer

      }

      }

      return imgData;

      }

      // mModelConfig.addImgValue定义

      // com/dpthinker/mnistclassifier/model/FloatSavedModelConfig.java

      public void addImgValue(ByteBuffer imgData, int val) {

      imgData.putFloat(((val & 0xFF) - getImageMean()) / getImageSTD());

      }

      convertBitmapToByteBuffer 的输出即为模型运行的输入。

      运行输出

      定义一个 1*10 的多维数组,因为我们只有 10 个 label,具体代码如下

      privateFloat[][] mLabelProbArray = newFloat[1][10];

      运行结束后,每个二级元素都是一个 label 的概率。

      运行及结果处理

      开始运行模型,具体代码如下

      mTFLite.run(imgData, mLabelProbArray);

      针对某个图片,运行后 mLabelProbArray 的内容就是各个 label 识别的概率。对他们进行排序,找出 Top 的 label 并界面呈现给用户.

      在 Android 应用中,作者使用了 View.OnClickListener() 触发 "image/*" 类型的 Intent.ACTION_GET_CONTENT ,进而获取设备上的图片(只支持 MNIST 标准图片)。然后,通过 RadioButtion 的选择情况,确认加载哪种转换后的模型,并触发真正分类操作。

      选取一张 MNIST 测试集中的图片进行测试,得到结果。

      注意我们这里直接用 mLabelProbArray 数值中的 index 作为 label 了,因为 MNIST 的 label 完全跟 index 从 0 到 9 匹配。如果是其他的分类问题,需要根据实际情况进行转换。

      Quantization 模型转换

      Quantized 模型是对原模型进行转换过程中,将 float 参数转化为 uint8 类型,进而产生的模型会更小、运行更快,但是精度会有所下降。

      前面我们介绍了 Float 模型的转换方法,接下来我们要展示下 Quantized 模型,在 TF1.0 上,可以使用命令行工具转换 Quantized 模型。在作者尝试的情况看在 TF2.0 上,命令行工具目前只能转换为 Float 模型,Python API 只能转换为 Quantized 模型。

      Python API 转换方法如下:

      import tensorflow as tf

      converter = tf.lite.TFLiteConverter.from_saved_model('saved/1')

      converter.optimizations = [tf.lite.Optimize.DEFAULT]

      tflite_quant_model = converter.convert()

      open("mnist_savedmodel_quantized.tflite", "wb").write(tflite_quant_model)

      最终转换后的 Quantized 模型即为同级目录下的 mnist_savedmodel_quantized.tflite 。

      相对 TF1.0,上面的方法简化了很多,不需要考虑各种各样的参数,谷歌一直在优化开发者的使用体验。

      在 TF1.0 上,我们可以使用 tflite_convert 获得模型具体结构,然后通过 graphviz 转换为 pdf 或 png 等方便查看。 在 TF2.0 上,提供了新的一步到位的工具 visualize.py ,直接转换为 html 文件,除了模型结构,还有更清晰的关键信息总结。

      visualize.py 目前看应该还是开发阶段,使用前需要先从 github 下载最新的 TensorFlow 和 FlatBuffers 源码,并且两者要在同一目录,因为 visualize.py 源码中是按两者在同一目录写的调用路径。

      下载 TensorFlow:

      git clone git@github.com:tensorflow/tensorflow.git

      下载 FlatBuffers:大连人流医院 http://mobile.84211111.cn/

      git clone git@github.com:google/flatbuffers.git

      编译 FlatBuffers:(作者使用的 Mac,其他平台请大家自行配置,应该不麻烦)

      下载 cmake:执行 brew install cmake

      设置编译环境:在 FlatBuffers 的根目录,执行 cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release

      编译:在 FlatBuffers 的根目录,执行 make

      编译完成后,会在根目录生成 flatc,这个可执行文件是 visualize.py 运行所依赖的。

      visualize.py 使用方法

      在 tensorflow/tensorflow/lite/tools 目录下,执行如下命令

      python visualize.py mnist_savedmodel_quantized.tflite mnist_savedmodel_quantized.html

      生成可视化报告的关键信息

      模型结构

      可见,Input/Output 格式都是 FLOAT32 的多维数组,Input 的 min 和 max 分别是 0.0 和 255.0。

      跟 Float 模型对比,Input/Output 格式是一致的,所以可以复用 Float 模型 Android 部署过程中的配置。

      暂不确定这里是否是 TF2.0 上的优化,如果是这样的话,对开发者来说是非常友好的,如此就归一化了 Float 和 Quantized 模型处理了。

      具体配置如下:

      // Quantized模型相关参数

      // com/dpthinker/mnistclassifier/model/QuantSavedModelConfig.java

      public class QuantSavedModelConfig extends BaseModelConfig {

      @Override

      protected void setConfigs() {

      setModelName("mnist_savedmodel_quantized.tflite");

      setNumBytesPerChannel(4);

      setDimBatchSize(1);

      setDimPixelSize(1);

      setDimImgWeight(28);

      setDimImgHeight(28);

      setImageMean(0);

      setImageSTD(255.0f);

      }

      @Override

      public void addImgValue(ByteBuffer imgData, int val) {

      imgData.putFloat(((val & 0xFF) - getImageMean()) / getImageSTD());

      }

      }

      运行效果。

      Float 模型与 Quantized 模型大小与性能对比:

      模型类别FloatQuantized

      模型大小312K82K

      运行性能5.858854ms1.439062ms

      可见, Quantized 模型在模型大小和运行性能上相对 Float 模型都有非常大的提升。不过,在作者试验的过程中,发现有些图片在 Float 模型上识别正确的,在 Quantized 模型上会识别错,可见 Quantization 对模型的识别精度还是有影响的。在边缘设备上资源有限,需要在模型大小、运行速度与识别精度上找到一个权衡。

      总结

      本节介绍了如何从零开始部署 TFLite 到 Android 应用中,包括:

      如何将训练好的 MNIST SavedModel 模型,转换为 Float 模型和 Quantized 模型

      如何使用 visualize.py 和解读其结果信息

      如何将转换后的模型部署到 Android 应用中

  • 相关阅读:
    学习动态性能表 第五篇V$SESSION
    学习动态性能表 第八篇V$LOCK
    学习动态性能表 第十九篇V$UNDOSTAT
    学习动态性能表 第四篇(1)V$SQLTEXT
    学习动态性能表 第十六篇V$ROWCACHE
    学习动态性能表 第十八篇V$SYSTEM_EVENT
    备份打开的数据库脚本
    学习动态性能表 第九篇V$FILESTAT
    学习动态性能表 第六篇(1)V$SESSION_WAIT
    学习动态性能表 第二十篇V$WAITSTAT
  • 原文地址:https://www.cnblogs.com/djw12333/p/14544230.html
Copyright © 2020-2023  润新知