Przeglądaj źródła

修正了文档信息

刘恒言 5 lat temu
rodzic
commit
d2e968aeb3
5 zmienionych plików z 627 dodań i 33 usunięć
  1. 202 0
      docs/ModelConvert.md
  2. 40 17
      docs/introduction.md
  3. 0 3
      docs/port.md
  4. 160 4
      docs/samples.md
  5. 225 9
      docs/user-guide.md

+ 202 - 0
docs/ModelConvert.md

@@ -0,0 +1,202 @@
+# 模型转换器(Converter)的 Python API 指南
+
+此md提供了一个关于在 TensorFlow 2.0 中如何使用 TensorFlow Lite 转换器(TensorFlow Lite converter) Python API 的示例。
+Python API
+
+在 TensorFlow 2.0 中,用来将原始的 TensorFlow 模型格式转换为 TensorFlow Lite 的 Python API 是 tf.lite.TFLiteConverter。在 TFLiteConverter 中有以下的类方法(classmethod):
+
+    TFLiteConverter.from_saved_model():用来转换 SavedModel 格式模型。
+    TFLiteConverter.from_keras_model():用来转换 tf.keras 模型。
+    TFLiteConverter.from_concrete_functions():用来转换 concrete functions。
+
+注意: 在 TensorFlow Lite 2.0 中有一个不同版本的 TFLiteConverter API, 该 API 只包含了 from_concrete_function。 本文中用到的的新版本 API 可以通过 pip 安装 tf-nightly-2.0-preview。
+
+本文展示了 API 的 示例用法,不同 TensorFlow 版本的 API 详细列表请看 1.X 版本到 2.0 版本 API 的改变,和 安装 TensorFlow 来安装和使用。
+## 示例
+
+### 转换 SavedModel 格式模型
+
+以下示例展示了如何将一个 SavedModel 转换为 TensorFlow Lite 中的 FlatBuffer格式。
+
+    import tensorflow as tf
+    
+    # 建立一个简单的模型。
+    root = tf.train.Checkpoint()
+    root.v1 = tf.Variable(3.)
+    root.v2 = tf.Variable(2.)
+    root.f = tf.function(lambda x: root.v1 * root.v2 * x)
+    
+    # 保存模型。
+    export_dir = "/tmp/test_saved_model"
+    input_data = tf.constant(1., shape=[1, 1])
+    to_save = root.f.get_concrete_function(input_data)
+    tf.saved_model.save(root, export_dir, to_save)
+    
+    # 转换模型。
+    converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)
+    tflite_model = converter.convert()
+
+此 API 不支持指定输入向量的维度。 如果您的模型需要指定输入向量的维度,请使用 `from_concrete_functions` 来完成。 示例:
+
+    model = tf.saved_model.load(export_dir)
+    concrete_func = model.signatures[
+    tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]
+    concrete_func.inputs[0].set_shape([1, 256, 256, 3])
+    converter = TFLiteConverter.from_concrete_functions([concrete_func])
+
+### 转换 Keras 模型
+
+以下示例展示了如何将一个 tf.keras 模型 转换为 TensorFlow Lite 中的 FlatBuffer 格式。
+
+    import tensorflow as tf
+    
+    # 创建一个简单的 Keras 模型。
+    x = [-1, 0, 1, 2, 3, 4]
+    y = [-3, -1, 1, 3, 5, 7]
+    
+    model = tf.keras.models.Sequential(
+        [tf.keras.layers.Dense(units=1, input_shape=[1])])
+    model.compile(optimizer='sgd', loss='mean_squared_error')
+    model.fit(x, y, epochs=50)
+    
+    # 转换模型。
+    converter = tf.lite.TFLiteConverter.from_keras_model(model)
+    tflite_model = converter.convert()
+
+### 转换 `concrete function`
+
+以下示例展示了如何将 TensorFlow 中的` concrete function` 转换为TensorFlow Lite 中的 FlatBuffer 格式。
+
+    import tensorflow as tf
+    
+    # 建立一个模型。
+    root = tf.train.Checkpoint()
+    root.v1 = tf.Variable(3.)
+    root.v2 = tf.Variable(2.)
+    root.f = tf.function(lambda x: root.v1 * root.v2 * x)
+    
+    # 生成 concrete function。
+    input_data = tf.constant(1., shape=[1, 1])
+    concrete_func = root.f.get_concrete_function(input_data)
+    
+    # 转换模型。
+    ## `from_concrete_function` 的传入参数被设计为一个个 concrete functio的列表,然而
+    # 现阶段仅支持每次调用时仅接受一个concrete function。
+    # 同时转换多个concrete function的功能正在开发中。
+    converter =     tf.lite.TFLiteConverter.from_concrete_functions([concrete_func])
+    tflite_model = converter.convert()
+
+## 端到端 MobileNet 转换
+
+以下示例展示了如何将将一个提前训练好的 tf.keras MobileNet 模型转换为 TensorFlow Lite 支持的类型并运行推断 (inference)。 随机数据分别在 TensorFlow 和 TensorFlow Lite 模型中运行的结果将被比较。如果是从文件加载模型,请使用 model_path 来代替 model_content。
+
+    import numpy as np
+    import tensorflow as tf
+    
+    # 加载 MobileNet tf.keras 模型。
+    model = tf.keras.applications.MobileNetV2(
+        weights="imagenet", input_shape=(224, 224, 3))
+    
+    # 转换模型。
+    converter = tf.lite.TFLiteConverter.from_keras_model(model)
+    tflite_model = converter.convert()
+    
+    # 加载 TFLite 模型并分配张量(tensor)。
+    interpreter = tf.lite.Interpreter(model_content=tflite_model)
+    interpreter.allocate_tensors()
+    
+    # 获取输入和输出张量。
+    input_details = interpreter.get_input_details()
+    output_details = interpreter.get_output_details()
+    
+    # 使用随机数据作为输入测试 TensorFlow Lite 模型。
+    input_shape = input_details[0]['shape']
+    input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
+    interpreter.set_tensor(input_details[0]['index'], input_data)
+    
+    interpreter.invoke()
+    
+    # 函数 `get_tensor()` 会返回一份张量的拷贝。
+    # 使用 `tensor()` 获取指向张量的指针。
+    tflite_results = interpreter.get_tensor(output_details[0]['index'])
+    
+    # 使用随机数据作为输入测试 TensorFlow 模型。
+    tf_results = model(tf.constant(input_data))
+    
+    # 对比结果。
+    for tf_result, tflite_result in zip(tf_results, tflite_results):
+    np.testing.assert_almost_equal(tf_result, tflite_result, decimal=5)
+
+## 总结 1.X 版本到 2.0 版本 API 的改变
+
+本节总结了从 1.X 到 2.0 版本 Python API 的改变。 如果对某些改动有异议,请提交 GitHub issue。
+
+### TFLite转换器 支持的格式类型
+
+`TFLite转换器`在 2.0 版本中支持由` 1.X `版本和` 2.0 `版本生成的 SavedModels 和 Keras 模型。但是,转换过程不再支持由 1.X 版本冻结的 GraphDefs。 开发者可通过调用 tf.compat.v1.lite.TFLiteConverter 来把冻结的 GraphDefs 转换到 TensorFlow Lite 版本。
+
+#### 量化感知训练(Quantization-aware training)
+
+以下与 量化感知训练(Quantization-aware training) 有关的属性和方法在 TensorFlow 2.0 中从TFLiteConverter 中被移除。
+
+    inference_type
+    inference_input_type
+    quantized_input_stats
+    default_ranges_stats
+    reorder_across_fake_quant
+    change_concat_input_ranges
+    post_training_quantize - 在 1.X API 中被弃用
+    get_input_arrays()
+
+支持量化感知训练的重写器(rewriter)函数不支持由 TensorFlow 2.0 生成的模型。此外,TensorFlow Lite 的量化 API 已按支持 Keras 中量化感知训练 API 的思路重新设计和精简。 在新的量化 API 部署前,这些属性将不会出现在 2.0 的 API 中。开发者可以使用 tf.compat.v1.lite.TFLiteConverter 来转换由重写器函数生成的模型。
+
+#### 关于 TFLiteConverter 中属性的改变
+
+属性 target_ops 已成为 TargetSpec 中的属性且作为未来对优化框架的补充被重命名为 supported_ops。
+
+此外,以下属性被移除:
+
+- drop_control_dependency (default: True) - TFLite 现不支持控制流(control flow),所以此属性将恒为 True。
+- Graph visualization - 在 TensorFlow 2.0 中,推荐使用 visualize.py 实现对 TensorFlow Lite 图(graph)的可视化。 不同于 GraphViz, 它支持开发者对已进行过 post training 量化的图(graph)可视化。以下与图可视化的属性将被移除:
+- output_format
+  - dump_graphviz_dir
+  - dump_graphviz_video
+
+## 通用 API 的改变
+#### 转换方法
+
+以下在 1.X 中被弃用的方法不会在 2.0 中出现:
+
+    lite.toco_convert
+    lite.TocoConverter
+
+#### lite.constants
+
+在 2.0 中,为了减少 TensorFlow 和 TensorFlow Lite 之间的重复移除了` lite.constants` API。以下的列表展示了` lite.constant `中的类型在 TensorFlow 中对应的类型:
+
+    lite.constants.FLOAT: tf.float32
+    lite.constants.INT8: tf.int8
+    lite.constants.INT32: tf.int32
+    lite.constants.INT64: tf.int64
+    lite.constants.STRING: tf.string
+    lite.constants.QUANTIZED_UINT8: tf.uint8
+
+此外,lite.constants.TFLITE 和 lite.constants.GRAPHVIZ_DOT 被移除(由于 TFLiteConverter 中的 flage output_format被移除)。
+lite.OpHint
+
+由于 API OpHint 与 2.0 的 API 不兼容,故暂不可用。 此 API可用于转换基于 LSTM 的模型。 在 2.0 中对 LSTMs 的支持正在被探究。所有与 lite.experimental 有关的 API 都因此被移除。
+
+### 安装 TensorFlow
+#### 安装 TensorFlow 2.0 nightly
+
+可用以下命令安装 TensorFlow 2.0 nightly:
+
+    pip install tf-nightly-2.0-preview
+
+#### 在已安装的 1.X 中使用 TensorFlow 2.0
+
+可通过以下代码片段从最近安装的 1.X 中使用 TensorFlow 2.0。
+
+    import tensorflow.compat.v2 as tf
+    
+    tf.enable_v2_behavior()

+ 40 - 17
docs/introduction.md

@@ -1,31 +1,54 @@
-# hello 介绍
+# TensorFlow Lite Micro软件包
 
-> 说明:你需要在这里对 hello 软件进行简单的介绍,描述背景、功能特点等等……
+适用于微控制器的 TensorFlow Lite 是 TensorFlow Lite 的实验性端口,专门用于在微控制器和其他只有几千字节内存的设备上运行机器学习模型。
 
-## 软件架构
+它不需要操作系统支持、任何标准 C/C++ 库或动态内存分配。核心运行时在 Arm Cortex M3 上占用 16 KB 的内存,并且具有足够多的运算符来运行语音关键字检测模型,总共占用 22 KB 的内存。
 
-> 简单介绍
+一些示例应用演示了如何使用微控制器执行唤醒字词检测,根据加速度计数据进行手势分类,以及使用相机数据进行图像分类等任务。
 
-### 软件架构图
+## 使用入门
 
-![hello 软件架构图](./figures/framework.png)
+如需试用示例应用并了解如何使用该 API,请参阅[微控制器使用入门](https://tensorflow.google.cn/lite/microcontrollers/get_started)。
 
-如上 hello 软件架构图所示, 共分为 x 层,……,每一层描述。
+## 为什么微控制器非常重要
 
-## hello 功能特点
+微控制器通常是小型低功耗计算设备,往往嵌入到需要执行基本计算的硬件(包括家用电器和物联网设备)中。微控制器的年生产量高达数十亿。
 
-### 功能一
+微控制器通常经过优化以实现低能耗和小尺寸,但处理能力、内存和存储空间会受到影响。一些微控制器具有旨在优化机器学习任务的性能的功能。
 
-hello 的功能一
+通过在微控制器上运行机器学习推断,开发者可以在不依靠网络连接的情况下向各种硬件设备添加 AI 功能,这通常受带宽和功率限制,并且会导致长时间延迟。由于数据无需离开设备,因此在设备端运行推断也有助于保护隐私
 
-- 功能
-- 功能
-- 功能
+## 开发者工作流程
 
-### 功能二
+为了将 TensorFlow 模型部署到微控制器,您需要遵循以下流程:
 
-功能二
+1. **创建或获取 TensorFlow 模型**
 
-### 功能三
+   该模型必须足够小,在转换后适合目标设备,并且只能使用[支持的操作](https://tensorflow.google.cn/lite/microcontrollers/build_convert#operation_support)。如果您想使用目前不受支持的操作,可以提供自己的实现。
 
-功能三
+2. **将模型转换为 TensorFlow Lite FlatBuffer**
+
+   您可以使用 [TensorFlow Lite 转换器](https://tensorflow.google.cn/lite/microcontrollers/build_convert#model_conversion)将模型转换为标准 TensorFlow Lite 格式。您可能希望输出量化模型,因为它们更小而且执行效率更高。
+
+3. **将 FlatBuffer 转换为 C 字节数组**
+
+   模型保存在只读程序内存中,并以简单 C 文件的形式提供。您可以使用标准工具[将 FlatBuffer 转换为 C 数组](https://tensorflow.google.cn/lite/microcontrollers/build_convert#convert_to_a_c_array)。
+
+4. **集成适用于微控制器的 TensorFlow Lite C++ 库**
+
+   编写微控制器代码以收集数据、使用 [C++ 库](https://tensorflow.google.cn/lite/microcontrollers/library)执行推断,并利用结果。
+
+5. **部署到您的设备**
+
+   构建程序并将其部署到您的设备。
+
+## 限制
+
+适用于微控制器的 TensorFlow Lite 专为满足微控制器开发的特定限制条件而设计。如果您使用的是更强大的设备(例如 Raspberry Pi 等嵌入式 Linux 设备),那么标准 TensorFlow Lite 框架可能更易于集成。
+
+应考虑以下限制条件:
+
+- 支持的 TensorFlow 操作[有限](https://tensorflow.google.cn/lite/microcontrollers/build_convert#operation_support)
+- 支持的设备有限
+- 需要手动内存管理的低阶 C++ API
+- 不支持训练

+ 0 - 3
docs/port.md

@@ -1,3 +0,0 @@
-# 移植说明
-
-> 在不同的硬件平台上使用,需要做的移植工作说明。

+ 160 - 4
docs/samples.md

@@ -2,10 +2,166 @@
 
 此示例使用一个简单的 [音频识别模型](https://tensorflow.google.cn/tutorials/sequences/audio_recognition) 来识别语音中的关键字。示例代码从设备的麦克风中捕获音频。模型通过对该音频进行实时分类来确定是否说过“是”或“否一词。
 
-## 运行示例 ##
+## 运行推断
 
-示例应该怎么运行
+以下部分将介绍[微语音](https://tensorflow.google.cn/lite/microcontrollers/get_started#微语音示例)示例中的 [main.cc](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/main.cc) 文件并解释了它如何使用用于微控制器的 Tensorflow Lite 来运行推断
 
-## 示例结果 ##
+### 包含项
 
-这是示例的结果。
+要使用库,必须包含以下头文件:
+
+```C++
+#include "tensorflow/lite/micro/kernels/all_ops_resolver.h"
+#include "tensorflow/lite/micro/micro_error_reporter.h"
+#include "tensorflow/lite/micro/micro_interpreter.h"
+#include "tensorflow/lite/schema/schema_generated.h"
+#include "tensorflow/lite/version.h"
+```
+
+- [`all_ops_resolver.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/kernels/all_ops_resolver.h) 提供给解释器(interpreter)用于运行模型的操作。
+- [`micro_error_reporter.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/micro_error_reporter.h) 输出调试信息。
+- [`micro_interpreter.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/micro_interpreter.h) 包含处理和运行模型的代码。
+- [`schema_generated.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema_generated.h) 包含 TensorFlow Lite [`FlatBuffer`](https://google.github.io/flatbuffers/) 模型文件格式的模式。
+- [`version.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/version.h) 提供 Tensorflow Lite 架构的版本信息。
+
+示例还包括其他一些文件。以下这些是最重要的:
+
+```C++
+#include "tensorflow/lite/micro/examples/micro_speech/feature_provider.h"
+#include "tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h"
+#include "tensorflow/lite/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h"
+```
+
+- [`feature_provider.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/feature_provider.h) 包含从音频流中提取要输入到模型中的特征的代码。
+- [`tiny_conv_micro_features_model_data.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h) 包含存储为 `char` 数组的模型。阅读 [“构建与转换模型”](https://tensorflow.google.cn/lite/microcontrollers/build_convert) 来了解如何将 Tensorflow Lite 模型转换为该格式。
+- [`micro_model_settings.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h) 定义与模型相关的各种常量。
+
+### 设置日志记录
+
+要设置日志记录,需要使用一个指向 `tflite::MicroErrorReporter` 实例的指针来创建一个 `tflite::ErrorReporter` 指针:
+
+```C++
+tflite::MicroErrorReporter micro_error_reporter;
+tflite::ErrorReporter* error_reporter = &micro_error_reporter;
+```
+
+该变量被传递到解释器(interpreter)中,解释器允许它写日志。由于微控制器通常具有多种日志记录机制,`tflite::MicroErrorReporter` 的实现是为您的特定设备所定制的。
+
+### 加载模型
+
+在以下代码中,模型是从一个 `char` 数组中实例化的,`g_tiny_conv_micro_features_model_data` (要了解其是如何构建的,请参见[“构建与转换模型”](https://tensorflow.google.cn/lite/microcontrollers/build_convert))。 随后我们检查模型来确保其架构版本与我们使用的版本所兼容:
+
+```C++
+const tflite::Model* model =
+    ::tflite::GetModel(g_tiny_conv_micro_features_model_data);
+if (model->version() != TFLITE_SCHEMA_VERSION) {
+  error_reporter->Report(
+      "Model provided is schema version %d not equal "
+      "to supported version %d.\n",
+      model->version(), TFLITE_SCHEMA_VERSION);
+  return 1;
+}
+```
+
+### 实例化操作解析器
+
+解释器(interpreter)需要一个 [`AllOpsResolver`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/kernels/all_ops_resolver.h) 实例来访问 Tensorflow 操作。可以扩展此类以向您的项目添加自定义操作:
+
+```C++
+tflite::ops::micro::AllOpsResolver resolver;
+```
+
+### 分配内存
+
+我们需要预先为输入、输出以及中间数组分配一定的内存。该预分配的内存是一个大小为 `tensor_arena_size` 的 `uint8_t` 数组,它被传递给 `tflite::SimpleTensorAllocator` 实例:
+
+```C++
+const int tensor_arena_size = 10 * 1024;
+uint8_t tensor_arena[tensor_arena_size];
+tflite::SimpleTensorAllocator tensor_allocator(tensor_arena,
+                                               tensor_arena_size);
+```
+
+注意:所需内存大小取决于您使用的模型,可能需要通过实验来确定。
+
+### 实例化解释器(Interpreter)
+
+我们创建一个 `tflite::MicroInterpreter` 实例,传递给之前创建的变量:
+
+```C++
+tflite::MicroInterpreter interpreter(model, resolver, &tensor_allocator,
+                                     error_reporter);
+```
+
+### 验证输入维度
+
+`MicroInterpreter` 实例可以通过调用 `.input(0)` 为我们提供一个指向模型输入张量的指针,其中 `0` 代表第一个(也是唯一一个)输入张量。我们检查这个张量以确认它的维度与类型是我们所期望的:
+
+```C++
+TfLiteTensor* model_input = interpreter.input(0);
+if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
+    (model_input->dims->data[1] != kFeatureSliceCount) ||
+    (model_input->dims->data[2] != kFeatureSliceSize) ||
+    (model_input->type != kTfLiteUInt8)) {
+  error_reporter->Report("Bad input tensor parameters in model");
+  return 1;
+}
+```
+
+在这个代码段中,变量 `kFeatureSliceCount` 和 `kFeatureSliceSize` 与输入的属性相关,它们定义在 [`micro_model_settings.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h) 中。枚举值 `kTfLiteUInt8` 是对 Tensorflow Lite 某一数据类型的引用,它定义在 [`c_api_internal.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/c/c_api_internal.h) 中。
+
+### 生成特征
+
+我们输入到模型中的数据必须由微控制器的音频输入生成。[`feature_provider.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/feature_provider.h) 中定义的 `FeatureProvider` 类捕获音频并将其转换为一组将被传入模型的特征集合。当该类被实例化时,我们用之前获取的 `TfLiteTensor` 来传入一个指向输入数组的指针。`FeatureProvider` 使用它来填充将传递给模型的输入数据:
+
+```C++
+  FeatureProvider feature_provider(kFeatureElementCount,
+                                   model_input->data.uint8);
+```
+
+以下代码使 `FeatureProvider` 从最近一秒的音频生成一组特征并填充进输入张量:
+
+```C++
+TfLiteStatus feature_status = feature_provider.PopulateFeatureData(
+    error_reporter, previous_time, current_time, &how_many_new_slices);
+```
+
+在此例子中,特征生成和推断是在一个循环中发生的,因此设备能够不断地捕捉和处理新的音频。
+
+当在编写自己的程序时,您可能会以其它的方式生成特征,但您总需要在运行模型之前就用数据填充输入张量。
+
+### 运行模型
+
+要运行模型,我们可以在 `tflite::MicroInterpreter` 实例上调用 `Invoke()`:
+
+```C++
+TfLiteStatus invoke_status = interpreter.Invoke();
+if (invoke_status != kTfLiteOk) {
+  error_reporter->Report("Invoke failed");
+  return 1;
+}
+```
+
+我们可以检查返回值 `TfLiteStatus` 以确定运行是否成功。在 [`c_api_internal.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/c/c_api_internal.h) 中定义的 `TfLiteStatus` 的可能值有 `kTfLiteOk` 和 `kTfLiteError`。
+
+### 获取输出
+
+模型的输出张量可以通过在 `tflite::MicroIntepreter` 上调用 `output(0)` 获得,其中 `0` 代表第一个(也是唯一一个)输出张量。
+
+在示例中,输出是一个数组,表示输入属于不同类别(“是”(yes)、“否”(no)、“未知”(unknown)以及“静默”(silence))的概率。由于它们是按照集合顺序排列的,我们可以使用简单的逻辑来确定概率最高的类别:
+
+```C++
+    TfLiteTensor* output = interpreter.output(0);
+    uint8_t top_category_score = 0;
+    int top_category_index;
+    for (int category_index = 0; category_index < kCategoryCount;
+         ++category_index) {
+      const uint8_t category_score = output->data.uint8[category_index];
+      if (category_score > top_category_score) {
+        top_category_score = category_score;
+        top_category_index = category_index;
+      }
+    }
+```
+
+在示例其他部分中,使用了一个更加复杂的算法来平滑多帧的识别结果。该部分在 [recognize_commands.h](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/recognize_commands.h) 中有所定义。在处理任何连续的数据流时,也可以使用相同的技术来提高可靠性。

+ 225 - 9
docs/user-guide.md

@@ -4,20 +4,236 @@
 
 它不需要操作系统支持、任何标准 C/C++ 库或动态内存分配。核心运行时在 Arm Cortex M3 上占用 16 KB 的内存,并且具有足够多的运算符来运行语音关键字检测模型,总共占用 22 KB 的内存。
 
-## 准备工作(可选)
+# 建立与转换模型
 
-## 背景信息(可选)
+由于嵌入式设备具有有限的 RAM 和存储空间,因此限制了深度学习模型的规模。此外,本TensorFlow Lite Micro 目前只支持有限的一部分运算,因此并非所有的模型结构都是可行的。
 
-## 步骤一(可选)
+本部分介绍将一个 TensorFlow 模型转换为可在嵌入式设备中上运行的过程。本部分也概述了可支持的运算,并对设计与训练一个模型以使其符合内存限制给出了一些指导。
 
-## 步骤二(可选)
+## 模型转换
 
-## 步骤三(可选)
+将一个已训练好的 TensorFlow 模型转换为可以在嵌入式设备中运行的Tensorflow Lite模型可以使用 [TensorFlow Lite 转换器 Python API](https://tensorflow.google.cn/lite/convert/python_api) 。它能够将模型转换成 [`FlatBuffer`](https://google.github.io/flatbuffers/) 格式,减小模型规模,并修改模型以使用 TensorFlow Lite 支持的运算。
 
-## 预期结果(可选)
+### 量化
 
-## 示例(可选)
+为了获得尽可能小的模型规模,你应该考虑使用[训练后量化](https://tensorflow.google.cn/lite/performance/post_training_quantization)。它会降低你模型中数字的精度,从而减小模型规模。不过,这种操作可能会导致模型准确性的下降,对于小规模模型来说尤为如此。在量化前后分析你模型的准确性以确保这种损失在可接受范围内是非常重要的。
 
-## 完成后的操作(可选)
+以下的 Python 代码片段展示了如何使用预训练量化进行模型转换:
 
-## 引用链接(可选)
+```python
+import tensorflow as tf
+converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
+converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
+tflite_quant_model = converter.convert()
+open("converted_model.tflite", "wb").write(tflite_quant_model)
+```
+
+### 转换为一个 C 数组
+
+许多微控制器平台没有本地文件系统的支持。从程序中使用一个模型最简单的方式是将其以一个 C 数组的形式包含并编译进你的程序。
+
+以下的 unix 命令会生成一个以 `char` 数组形式包含 TensorFlow Lite 模型的 C 源文件:
+
+```bash
+xxd -i converted_model.tflite > model_data.cc
+```
+
+其输出类似如下:
+
+```c
+unsigned char converted_model_tflite[] = {
+  0x18, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x00, 0x00, 0x0e, 0x00,
+  // <Lines omitted>
+};
+unsigned int converted_model_tflite_len = 18200;
+```
+
+一旦你已经生成了此文件,你可以将它包含入你的程序。在嵌入式平台上,将数组声明改变为 `const` 类型以获得更好的内存效率是重要的。
+
+一个如何在你的程序中包含及使用模型的例子,请见微型语音示例中的 [`tiny_conv_micro_features_model_data.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h) 。
+
+## 模型结构与训练
+
+在设计一个面向微控制器的模型时,考虑模型的规模、工作负载,以及用到的运算是非常重要的。
+
+### 模型规模
+
+一个模型必须在二进制和运行时方面都足够小,以使其可以和你程序的其他部分一起符合你目标设备的内存限制。
+
+为了创建一个更小的模型,你可以在你的结构里使用更少和更小的层。然而,小规模的模型更易面临欠拟合问题。这意味着对于许多问题,尝试并使用符合内存限制的尽可能大规模的模型是有意义的。但是,使用更大规模的模型也会导致处理器工作负载的增加。
+
+注:在一个 Cortex M3 上,面向微控制器的 TensorFlow Lite 的核心运行时占 16 KB。
+
+### 工作负载
+
+工作负载受到模型规模与复杂度的影响。大规模、复杂的模型可能会导致更高的占空比,即导致你所用设备处理器的工作时间增长、空闲时间缩短。视你的应用,这种情况所带来的电力消耗与热量输出的增加可能会成为一个问题。
+
+### 运算支持
+
+面向微控制器的 TensorFlow Lite 目前仅支持有限的部分 TensorFlow 运算,这影响了可以运行的模型结构。我们正致力于在参考实现和针对特定结构的优化方面扩展运算支持。
+
+已支持的运算可以在文件 [`all_ops_resolver.cc`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/kernels/all_ops_resolver.cc) 中看到。
+
+## 运行推断
+
+以下部分将介绍[微语音](https://tensorflow.google.cn/lite/microcontrollers/get_started#微语音示例)示例中的 [main.cc](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/main.cc) 文件并解释了它如何使用用于微控制器的 Tensorflow Lite 来运行推断。
+
+### 包含项
+
+要使用库,必须包含以下头文件:
+
+```C++
+#include "tensorflow/lite/micro/kernels/all_ops_resolver.h"
+#include "tensorflow/lite/micro/micro_error_reporter.h"
+#include "tensorflow/lite/micro/micro_interpreter.h"
+#include "tensorflow/lite/schema/schema_generated.h"
+#include "tensorflow/lite/version.h"
+```
+
+- [`all_ops_resolver.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/kernels/all_ops_resolver.h) 提供给解释器(interpreter)用于运行模型的操作。
+- [`micro_error_reporter.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/micro_error_reporter.h) 输出调试信息。
+- [`micro_interpreter.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/micro_interpreter.h) 包含处理和运行模型的代码。
+- [`schema_generated.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema_generated.h) 包含 TensorFlow Lite [`FlatBuffer`](https://google.github.io/flatbuffers/) 模型文件格式的模式。
+- [`version.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/version.h) 提供 Tensorflow Lite 架构的版本信息。
+
+示例还包括其他一些文件。以下这些是最重要的:
+
+```C++
+#include "tensorflow/lite/micro/examples/micro_speech/feature_provider.h"
+#include "tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h"
+#include "tensorflow/lite/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h"
+```
+
+- [`feature_provider.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/feature_provider.h) 包含从音频流中提取要输入到模型中的特征的代码。
+- [`tiny_conv_micro_features_model_data.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h) 包含存储为 `char` 数组的模型。阅读 [“构建与转换模型”](https://tensorflow.google.cn/lite/microcontrollers/build_convert) 来了解如何将 Tensorflow Lite 模型转换为该格式。
+- [`micro_model_settings.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h) 定义与模型相关的各种常量。
+
+### 设置日志记录
+
+要设置日志记录,需要使用一个指向 `tflite::MicroErrorReporter` 实例的指针来创建一个 `tflite::ErrorReporter` 指针:
+
+```C++
+tflite::MicroErrorReporter micro_error_reporter;
+tflite::ErrorReporter* error_reporter = &micro_error_reporter;
+```
+
+该变量被传递到解释器(interpreter)中,解释器允许它写日志。由于微控制器通常具有多种日志记录机制,`tflite::MicroErrorReporter` 的实现是为您的特定设备所定制的。
+
+### 加载模型
+
+在以下代码中,模型是从一个 `char` 数组中实例化的,`g_tiny_conv_micro_features_model_data` (要了解其是如何构建的,请参见[“构建与转换模型”](https://tensorflow.google.cn/lite/microcontrollers/build_convert))。 随后我们检查模型来确保其架构版本与我们使用的版本所兼容:
+
+```C++
+const tflite::Model* model =
+    ::tflite::GetModel(g_tiny_conv_micro_features_model_data);
+if (model->version() != TFLITE_SCHEMA_VERSION) {
+  error_reporter->Report(
+      "Model provided is schema version %d not equal "
+      "to supported version %d.\n",
+      model->version(), TFLITE_SCHEMA_VERSION);
+  return 1;
+}
+```
+
+### 实例化操作解析器
+
+解释器(interpreter)需要一个 [`AllOpsResolver`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/kernels/all_ops_resolver.h) 实例来访问 Tensorflow 操作。可以扩展此类以向您的项目添加自定义操作:
+
+```C++
+tflite::ops::micro::AllOpsResolver resolver;
+```
+
+### 分配内存
+
+我们需要预先为输入、输出以及中间数组分配一定的内存。该预分配的内存是一个大小为 `tensor_arena_size` 的 `uint8_t` 数组,它被传递给 `tflite::SimpleTensorAllocator` 实例:
+
+```C++
+const int tensor_arena_size = 10 * 1024;
+uint8_t tensor_arena[tensor_arena_size];
+tflite::SimpleTensorAllocator tensor_allocator(tensor_arena,
+                                               tensor_arena_size);
+```
+
+注意:所需内存大小取决于您使用的模型,可能需要通过实验来确定。
+
+### 实例化解释器(Interpreter)
+
+我们创建一个 `tflite::MicroInterpreter` 实例,传递给之前创建的变量:
+
+```C++
+tflite::MicroInterpreter interpreter(model, resolver, &tensor_allocator,
+                                     error_reporter);
+```
+
+### 验证输入维度
+
+`MicroInterpreter` 实例可以通过调用 `.input(0)` 为我们提供一个指向模型输入张量的指针,其中 `0` 代表第一个(也是唯一一个)输入张量。我们检查这个张量以确认它的维度与类型是我们所期望的:
+
+```C++
+TfLiteTensor* model_input = interpreter.input(0);
+if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
+    (model_input->dims->data[1] != kFeatureSliceCount) ||
+    (model_input->dims->data[2] != kFeatureSliceSize) ||
+    (model_input->type != kTfLiteUInt8)) {
+  error_reporter->Report("Bad input tensor parameters in model");
+  return 1;
+}
+```
+
+在这个代码段中,变量 `kFeatureSliceCount` 和 `kFeatureSliceSize` 与输入的属性相关,它们定义在 [`micro_model_settings.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h) 中。枚举值 `kTfLiteUInt8` 是对 Tensorflow Lite 某一数据类型的引用,它定义在 [`c_api_internal.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/c/c_api_internal.h) 中。
+
+### 生成特征
+
+我们输入到模型中的数据必须由微控制器的音频输入生成。[`feature_provider.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/micro_features/feature_provider.h) 中定义的 `FeatureProvider` 类捕获音频并将其转换为一组将被传入模型的特征集合。当该类被实例化时,我们用之前获取的 `TfLiteTensor` 来传入一个指向输入数组的指针。`FeatureProvider` 使用它来填充将传递给模型的输入数据:
+
+```C++
+  FeatureProvider feature_provider(kFeatureElementCount,
+                                   model_input->data.uint8);
+```
+
+以下代码使 `FeatureProvider` 从最近一秒的音频生成一组特征并填充进输入张量:
+
+```C++
+TfLiteStatus feature_status = feature_provider.PopulateFeatureData(
+    error_reporter, previous_time, current_time, &how_many_new_slices);
+```
+
+在此例子中,特征生成和推断是在一个循环中发生的,因此设备能够不断地捕捉和处理新的音频。
+
+当在编写自己的程序时,您可能会以其它的方式生成特征,但您总需要在运行模型之前就用数据填充输入张量。
+
+### 运行模型
+
+要运行模型,我们可以在 `tflite::MicroInterpreter` 实例上调用 `Invoke()`:
+
+```C++
+TfLiteStatus invoke_status = interpreter.Invoke();
+if (invoke_status != kTfLiteOk) {
+  error_reporter->Report("Invoke failed");
+  return 1;
+}
+```
+
+我们可以检查返回值 `TfLiteStatus` 以确定运行是否成功。在 [`c_api_internal.h`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/c/c_api_internal.h) 中定义的 `TfLiteStatus` 的可能值有 `kTfLiteOk` 和 `kTfLiteError`。
+
+### 获取输出
+
+模型的输出张量可以通过在 `tflite::MicroIntepreter` 上调用 `output(0)` 获得,其中 `0` 代表第一个(也是唯一一个)输出张量。
+
+在示例中,输出是一个数组,表示输入属于不同类别(“是”(yes)、“否”(no)、“未知”(unknown)以及“静默”(silence))的概率。由于它们是按照集合顺序排列的,我们可以使用简单的逻辑来确定概率最高的类别:
+
+```C++
+    TfLiteTensor* output = interpreter.output(0);
+    uint8_t top_category_score = 0;
+    int top_category_index;
+    for (int category_index = 0; category_index < kCategoryCount;
+         ++category_index) {
+      const uint8_t category_score = output->data.uint8[category_index];
+      if (category_score > top_category_score) {
+        top_category_score = category_score;
+        top_category_index = category_index;
+      }
+    }
+```
+
+在示例其他部分中,使用了一个更加复杂的算法来平滑多帧的识别结果。该部分在 [recognize_commands.h](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/micro_speech/recognize_commands.h) 中有所定义。在处理任何连续的数据流时,也可以使用相同的技术来提高可靠性。