TinyML是什么?如何从零开始入门学习?本系列上一篇教程为大家介绍了 TensorFlow 的基础原理,在这一篇中,我们将结合代码实例,教你如何去编写微控制器能够运行的 ML 应用,并进一步掌握项目运行的整体流程。
本文来自社区投稿与征集,作者王玉成,ML&IoT Google Developers Expert,温州大学智能锁具研究院总工程师。了解更多:https://blog.csdn.net/wfing
Hello World — 梦开始的地方(中)
在前面的准备工作中,我们完成了模型训练,并且将模型的二进制格式转化成为 C++ 可以识别的数组。但是,这仅仅是基于微控制器项目前面的一小步。后续还有许多工作要做。
项目中的代码也是完全基于 C++ 11 的标准版来开发的,避免了复杂的逻辑。这份代码也可以当作 C++ 模板进行开发。但是不要一提到 C++ 就特别怕,在这一篇文章会用已有的代码去描述整体的工程流程。
正式阅读项目的代码之前,我们需要关注一下项目运行的整体流程:
-
传感器采集数据
-
对数据进行前处理,送到 ML 解释器
-
ML 模型预测数据
-
对预测的数据做出最终的判断
-
设备响应 ML 的推理结果,进行必要的操作
3.1 从测试角度来看流程
进行代码阅读之前,我们最好通过一些测试用例来了解代码流程。
通过有效的测试用例,我们可以看到在何时运行了错误的代码。在编写完成之后,测试通常会自动的运行,并且不断验证是否仍然在执行我们所希望的方法。在这个示例中,我们会根据测试用例来加载建模,并且对模型推理进行检测:检验模型的预测是否符合我们的预期。通过测试代码来入手,而非进行代码阅读,将会使我们更快地了解代码的流程。
3.2 头文件依赖
首先我们来看一下这些代码依赖于哪些头文件:
/home/ycwang/work/ycwang/desktop/ai/tensorflow/tensorflow-master/是 tensorflow 源代码的一级目录/根目录的位置。
头文件结构如下:
#include 是用于导入 C++ 的头文件,类似于 JAVA 用 import 导入包的用法。头文件引用指向的目录为 tensorflow 源码的根目录。我们先来看看这些头文件的含义:
-
tensorflow/lite/micro/examples/hello_world/sine_model_data.h
在上一章的最后,我们使用 xxd 命令把训练后的模型,转换为 C ++ 的二进制模型 -
tensorflow/lite/micro/kernels/all_ops_resolver.h
一个允许解释器加载模型使用的操作的类 -
tensorflow/lite/micro/micro_error_reporter.h
可以记录错误并输出以帮助调试的类 -
tensorflow/lite/micro/micro_interpreter.h
TensorFlow Lite for Microcontrollers 解释器,模型将在解释器中运行 -
tensorflow/lite/micro/testing/micro_test.h
编写测试的轻量级框架,我们可以将该文件作为测试运行 -
tensorflow/lite/schema/schema_generated.h
定义 TensorFlow Lite FlatBuffer 数据结构的模型,用于理解 sine_model_data.h 中的模型数据 -
tensorflow/lite/version.h
模式的当前版本号
大致了解了头文件依赖之后,我们就可以试着通过阅读源代码去了解测试流程。所有的代码来自于 hello_world_test.cc 文件。
3.3 建立测试
我们进入了代码的正文。TensorFlow Lite 将代码用于微控制器测试框架。看起来像这样:
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(LoadModelAndPerformInference){
...
}
在 C ++ 中,您可以定义特殊命名的代码块, 这些代码块称为宏。此处名称为 TF_LITE_MICRO_TESTS_BEGIN 和 TF_LITE_MICRO_TEST 的宏,它们在文件 micro_test.h 中定义。
这些宏将我们其余的代码包装在必要的设备中,以使其得以执行。
我们不需要知道它到底是如何工作的;我们只知道我们可以将这些宏用作设置测试的快捷方式。
第二个宏名为 TF_LITE_MICRO_TEST,接受一个参数。在这种情况下,传入的参数是 LoadModelAndPerformInference。这个说法是测试名称,并在运行测试时将其与测试结果一起输出这样我们就可以查看测试是否通过。
3.4 准备Log
// Set up logging
tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
在29行中,我们定义一个 MicroErrorReporter 实例。MicroErrorReporter 类在 micro_error_reporter.h 中定义。它提供了记录调试机制推理过程相关的信息。我们将其称为打印调试信息,然后 TensorFlow Lite for Microcontrollers 解释器将使用它来打印遇到的任何错误。
您可能已经注意到了每个 tflite ::前缀输入名称,例如 tflite :: MicroErrorReporter。这是一个名字空间,这只是帮助组织 C ++ 代码的一种方式。TensorFlow Lite 在名称空间 tflite 下定义了所有有用的东西,这意味着如果另一个库碰巧实现了类具有相同的名称,它们不会与 TensorFlow Micro 提供的名称冲突。
第一个声明似乎很简单,但是第二个看起来带 * 和 & 字符,看着很奇怪?当我们已经有 MicroErrorReporter,为什么要声明一个 ErrorReporter 呢?
tflite :: ErrorReporter * error_reporter =&micro_error_reporter;
为了解释这里发生的事情,我们需要了解一些背景信息。MicroErrorReporter 是 ErrorReporter 类的子类,它提供了一个关