从零开始学TensorFlow Lite微控制器开发

基于 TensorFlow Lite Micro 的微控制器开发:从零开始的探索

1. 引言

嵌入式系统领域正在经历一场由机器学习(ML)驱动的变革。将智能推向边缘设备,即那些直接与现实世界交互的传感器和执行器,已成为大势所趋。这种趋势催生了对在资源受限的微控制器(MCU)上部署机器学习模型的需求。TensorFlow Lite Micro (TFLM) 正是为满足这一需求而生,它是 TensorFlow Lite 的一个专门版本,专为在微控制器和其他内存有限的设备上运行而设计。

本文旨在为初学者提供一份详尽的指南,引导他们从零开始学习基于 TFLM 的微控制器开发。将详细阐述开发流程的各个环节,从环境搭建到模型部署,并辅以实例说明。

2. TensorFlow Lite Micro 概述

2.1 什么是 TensorFlow Lite Micro?

TensorFlow Lite Micro 是一个开源的机器学习框架,它是 TensorFlow Lite 的一个子集。其核心设计目标是在内存仅有几千字节(KB)的微控制器上运行机器学习模型。它通过一系列优化技术来实现这一目标,包括:

  • 轻量级运行时: TFLM 的运行时非常小巧,通常只占用几十 KB 的空间。
  • 内核优化: 针对常见的微控制器架构(如 ARM Cortex-M 系列)进行了优化,充分利用硬件特性,如 SIMD 指令。
  • 量化支持: 支持将模型权重和激活值从浮点数转换为 8 位整数,从而显著减小模型大小和计算量。
  • 模型剪枝: 可以移除模型中不重要的连接,进一步减小模型大小。

2.2 TFLM 与 TensorFlow Lite 的对比

TFLM 和 TensorFlow Lite 都旨在将 TensorFlow 模型部署到边缘设备,但它们的应用场景和特性有所不同。

应用场景:

  • TensorFlow Lite: 主要面向移动设备(如智能手机、平板电脑)和嵌入式 Linux 系统(如 Raspberry Pi)。
  • TFLM: 专门针对资源极其有限的微控制器,如 Cortex-M 系列。

特性区别:

  • 模型大小:
    TFLM 进一步减小了模型大小,使其可以适应 KB 级别的内存。而 TensorFlow Lite 针对的是拥有更大内存和处理能力的设备。

  • 支持的操作:
    TFLM 支持的操作是 TensorFlow Lite 的一个子集。 一些复杂的操作,如某些类型的循环和条件语句,在 TFLM 中可能不被支持。

  • 运行时大小:
    TFLM 的运行时远小于 TensorFlow Lite,因为它只包含必要的组件。

  • 依赖性:
    TFLM 的依赖性更少,使其更易于集成到资源受限的嵌入式项目中。

  • 开发流程:
    TFLM 的开发流程涉及更多的手动优化和硬件相关的考虑。

简单来说,TFLM 是 TensorFlow Lite 针对微控制器环境的“精简版”和“定制版”。

3. 开发环境搭建

3.1 硬件选择

开发 TFLM 应用通常需要一块支持的微控制器开发板。常见的选择包括:

  • Arduino Nano 33 BLE Sense: 集成了多种传感器,非常适合开发传感器相关的机器学习应用。
  • SparkFun Edge: 基于 Ambiq Apollo3 Blue 微控制器,功耗极低。
  • STM32F746G-DISCO: 功能强大,具有丰富的开发资源。
  • ESP32: 具备Wi-Fi和蓝牙功能.

选择开发板时,需要考虑以下因素:

  • 处理器核心: TFLM 主要针对 ARM Cortex-M 系列处理器进行了优化。
  • 内存大小: 确保开发板的 RAM 和 Flash 足够容纳 TFLM 运行时和模型。
  • 传感器: 如果应用需要使用传感器数据,选择集成所需传感器的开发板可以简化开发。
  • 开发工具支持: 确保开发板有完善的开发工具链支持,如编译器、调试器等。

3.2 软件工具链

开发 TFLM 应用需要安装以下软件工具:

  1. Arduino IDE 或 PlatformIO:
    如果选择 Arduino 系列开发板,可以使用 Arduino IDE。对于其他开发板,PlatformIO 是一个更通用的选择,它支持多种开发板和框架。

  2. TensorFlow:
    用于模型训练和转换。可以通过 pip 安装:
    bash
    pip install tensorflow

  3. GNU Arm Embedded Toolchain:
    用于编译针对 ARM Cortex-M 处理器的代码。可以从 ARM 官网下载。

  4. Make:
    用于构建项目。在 Linux 和 macOS 上通常已预装。Windows 用户可以安装 Chocolatey 或 Cygwin。

  5. Python 脚本环境:
    用于执行一些辅助脚本,如模型转换脚本。

3.3 环境配置

以 Arduino Nano 33 BLE Sense 为例,说明环境配置步骤:

  1. 安装 Arduino IDE:
    从 Arduino 官网下载并安装 Arduino IDE。

  2. 安装开发板支持包:
    在 Arduino IDE 的“开发板管理器”中搜索并安装“Arduino Mbed OS Nano Boards”。

  3. 安装 TensorFlow Lite for Microcontrollers 库:
    在 Arduino IDE 的“库管理器”中搜索并安装“Arduino_TensorFlowLite”。

  4. 验证环境:
    打开 Arduino IDE 中的示例项目“File -> Examples -> Arduino_TensorFlowLite -> hello_world”,编译并上传到开发板。如果串口监视器输出模型的预测结果,则说明环境配置成功。

4. 模型开发与转换

4.1 模型训练

TFLM 应用的模型通常在 TensorFlow 中进行训练。可以使用 Keras API 构建和训练模型。

以下是一个简单的示例,展示如何使用 Keras 构建一个用于识别手写数字(MNIST 数据集)的模型:

```python
import tensorflow as tf

加载 MNIST 数据集

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

数据预处理

x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)

构建模型

model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

编译模型

model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])

训练模型

model.fit(x_train, y_train, epochs=5)

评估模型

loss, accuracy = model.evaluate(x_test, y_test)
print('Test accuracy:', accuracy)

保存模型

model.save('mnist_model.h5')
```

4.2 模型转换

训练好的 TensorFlow 模型需要转换为 TFLM 格式才能在微控制器上运行。转换过程包括以下步骤:

  1. 转换为 TensorFlow Lite 格式:
    使用 TFLiteConverter 将 Keras 模型(.h5)或 SavedModel 转换为 TensorFlow Lite 格式(.tflite)。

  2. 量化(可选):
    为了进一步减小模型大小和提高推理速度,可以将模型权重和激活值从浮点数转换为 8 位整数。TFLM 支持多种量化方法,如训练后量化和量化感知训练。

  3. 转换为 C 数组:
    TFLM 运行时需要将模型以 C 数组的形式加载。可以使用 xxd 工具(Linux 和 macOS 上通常已预装)将 .tflite 文件转换为 C 数组。

以下是一个示例,展示如何将训练好的 MNIST 模型转换为 TFLM 格式并生成 C 数组:

```python
import tensorflow as tf

加载训练好的模型

model = tf.keras.models.load_model('mnist_model.h5')

转换为 TensorFlow Lite 格式

converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()

保存 TensorFlow Lite 模型

with open('mnist_model.tflite', 'wb') as f:
f.write(tflite_model)

量化(可选)

converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

保存量化后的 TensorFlow Lite 模型

with open('mnist_model_quant.tflite', 'wb') as f:
f.write(tflite_quant_model)
```

```bash

转换为 C 数组 (假设使用量化后的模型)

xxd -i mnist_model_quant.tflite > mnist_model_quant.cc
``
生成的
mnist_model_quant.cc文件包含一个名为g_model` 的 C 数组,它包含了量化后的 TFLM 模型。

5. 模型部署

5.1 集成 TFLM 运行时

将 TFLM 运行时集成到微控制器项目中有两种主要方法:

  1. 使用 Arduino 库:
    如果使用 Arduino IDE,可以直接使用“Arduino_TensorFlowLite”库。该库提供了 TFLM 运行时的封装,简化了模型加载和推理过程。

  2. 手动集成:
    对于其他开发环境或需要更精细控制的情况,可以手动将 TFLM 源代码集成到项目中。可以从 TensorFlow 仓库获取 TFLM 源代码。

5.2 加载模型

将模型以 C 数组的形式包含到项目中后,可以使用 TFLM API 加载模型。

以下是一个示例,展示如何使用 Arduino_TensorFlowLite 库加载模型:

```c++

include

include "mnist_model_quant.h" // 包含模型 C 数组

// 定义模型和解释器对象
tflite::MicroInterpreter interpreter = nullptr;
tflite::ErrorReporter
error_reporter = nullptr;

// 定义输入和输出张量
TfLiteTensor input = nullptr;
TfLiteTensor
output = nullptr;

// 定义内存区域
constexpr int kTensorArenaSize = 2000;
uint8_t tensor_arena[kTensorArenaSize];

void setup() {
// 初始化串口通信
Serial.begin(9600);
while (!Serial);

// 初始化错误报告器
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = &micro_error_reporter;

// 加载模型
tflite::MicroMutableOpResolver<1> resolver;
resolver.AddBuiltin(
tflite::BuiltinOperator_FULLY_CONNECTED,
tflite::ops::micro::Register_FULLY_CONNECTED());

static tflite::MicroInterpreter static_interpreter(
tflite::GetModel(g_model), resolver, tensor_arena, kTensorArenaSize,
error_reporter);
interpreter = &static_interpreter;

// 分配内存
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
error_reporter->Report("AllocateTensors() failed");
return;
}

// 获取输入和输出张量
input = interpreter->input(0);
output = interpreter->output(0);
}
``
以上代码做了以下工作:
* 包含了 TensorFlow Lite 的头文件和模型的 C 数组。
* 定义了解释器,错误报告器,输入和输出张量的对象.
* 定义了用于存储模型和中间数据的内存空间.
* 在
setup函数中:
* 初始化错误报告器.
* 使用
MicroMutableOpResolver指定要使用的算子(这里只有全连接层).
* 创建
MicroInterpreter对象,加载模型,并传入内存空间和错误报告器。
* 使用
AllocateTensors()为模型的张量分配内存。
* 使用
interpreter->input(0)interpreter->output(0)` 获取输入和输出张量的指针。

5.3 模型推理

模型加载完成后,可以进行推理。推理过程包括以下步骤:

  1. 准备输入数据:
    将输入数据填充到输入张量中。

  2. 调用解释器:
    使用 interpreter->Invoke() 执行推理。

  3. 获取输出数据:
    从输出张量中获取推理结果。

以下是一个示例,展示如何对 MNIST 模型进行推理:

```c++
void loop() {
// 准备输入数据(这里使用一个随机的 28x28 图像作为示例)
for (int i = 0; i < 28 * 28; i++) {
input->data.f[i] = random(0, 255) / 255.0; // 假设输入是浮点数
}

// 调用解释器
TfLiteStatus invoke_status = interpreter->Invoke();
if (invoke_status != kTfLiteOk) {
error_reporter->Report("Invoke() failed");
return;
}

// 获取输出数据(这里假设输出是一个包含 10 个元素的数组,表示每个数字的概率)
for (int i = 0; i < 10; i++) {
Serial.print(output->data.f[i]); // 假设输出是浮点数
Serial.print(" ");
}
Serial.println();

delay(1000);
}
```

6. 优化与调试

6.1 模型优化

为了在资源受限的微控制器上获得最佳性能,需要对模型进行优化。常见的优化方法包括:

  • 量化:
    如前所述,量化可以将模型权重和激活值从浮点数转换为 8 位整数,从而显著减小模型大小和计算量。

  • 剪枝:
    移除模型中不重要的连接,进一步减小模型大小。

  • 算子融合:
    将多个算子融合成一个算子,减少计算量和内存访问次数。

  • 内核优化:
    针对特定的微控制器架构进行优化,充分利用硬件特性,如 SIMD 指令。

6.2 调试

TFLM 应用的调试通常比较困难,因为微控制器的调试资源有限。常见的调试方法包括:

  • 串口输出:
    使用串口打印调试信息,如模型的输入、输出和中间结果。

  • 调试器:
    一些开发板支持使用调试器进行单步调试。

  • 模拟器:
    可以使用模拟器在 PC 上模拟微控制器的运行环境,方便调试。

  • 日志记录:
    在代码中添加详细的日志记录,有助于定位问题。

7. 更进一步

本文提供了一个 TFLM 微控制器开发的入门指南。为了更深入地学习和应用 TFLM,可以进一步探索以下方向:

  • 探索更多示例项目: TensorFlow 仓库中包含了多个 TFLM 示例项目,涵盖了不同的应用场景,如关键词识别、姿态估计等。
  • 学习自定义算子: 如果需要使用 TFLM 不支持的算子,可以学习如何自定义算子。
  • 研究模型优化技术: 深入研究各种模型优化技术,如量化感知训练、剪枝算法等。
  • 了解硬件加速: 了解如何利用微控制器的硬件加速器(如 DSP、FPU)来提高 TFLM 应用的性能。
  • 参与社区: 积极参与 TensorFlow 社区,与其他开发者交流经验,获取帮助。

通过持续的学习和实践,可以掌握 TFLM 微控制器开发的技能,并将机器学习的力量带到各种嵌入式应用中。

THE END