第4章:TensorFlow Lite Micro简介

好,咱们今天聊聊TFLM。说实话,我第一次接触TinyML时,最头疼的就是选框架。后来试了一圈,还是回到了TensorFlow Lite Micro。为什么?因为它够轻、够稳、生态够好。

TFLM,全称TensorFlow Lite Micro,是Google专门为微控制器打造的推理引擎。你想想看,一个只有几十KB内存的MCU,要跑神经网络,这本身就是个挑战。TFLM就是来解决这个问题的。

4.1 TFLM架构:到底长什么样?

先看整体架构。TFLM的设计思路很清晰——分层、模块化。我画个简图给你看:

+------------------+
|  应用层代码       |
+------------------+
|  解释器(Interpreter)|
+------------------+
|  算子内核(Ops)    |
+------------------+
|  内存管理器       |
+------------------+
|  硬件抽象层(HAL)  |
+------------------+
|  MCU硬件          |
+------------------+

最上层是你的应用代码,负责采集数据、处理结果。往下是解释器,它负责解析模型、调度算子。再往下是算子内核,就是那些卷积、全连接之类的计算函数。内存管理器负责分配和回收内存。最底层是硬件抽象层,屏蔽了不同MCU的差异。

我个人习惯把TFLM看作一个「瘦解释器」。它不像TensorFlow那样有复杂的图优化,而是直接加载一个已经优化好的FlatBuffer模型,然后逐层执行。

核心要点:TFLM不训练模型,只做推理。训练还是在PC上用TensorFlow完成,然后转换成TFLite格式,最后部署到MCU上。

4.2 模型转换流程:从PC到MCU

这个流程我踩过不少坑。简单说,分三步走:

  1. 训练模型:在PC上用Keras或PyTorch训练好模型
  2. 转换成TFLite:用TFLite Converter把模型转成.tflite格式
  3. 转换成C数组:用xxd工具把.tflite文件转成C语言的头文件

具体命令长这样:

# 第一步:训练模型(略,假设你已经有了model.h5)
# 第二步:转换成TFLite
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_model = converter.convert()
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

# 第三步:转成C数组
# 在终端执行
xxd -i model.tflite > model_data.h

嗯,这里要注意。转换时一定要做量化。我曾经有个项目,模型在PC上跑得好好的,部署到STM32上直接内存溢出。后来才发现,没做量化,模型大小是原来的4倍。

我的经验:对于8位MCU,建议用int8量化。对于Cortex-M4/M7这类带FPU的,可以用float16。量化后模型大小能缩小4倍,速度提升2-3倍。

4.3 算子支持列表:哪些能用,哪些不能用?

这个问题很关键。TFLM支持的算子比TensorFlow少得多。说白了,你不能把PC上的模型直接扔到MCU上,得看看算子是不是都支持。

我整理了一份常用算子列表:

算子名称 支持情况 备注
Conv2D ✅ 支持 深度可分离卷积也支持
DepthwiseConv2D ✅ 支持 MobileNet的核心算子
FullyConnected ✅ 支持 全连接层,最常用
ReLU / ReLU6 ✅ 支持 激活函数
Softmax ✅ 支持 分类任务必备
AveragePool2D ✅ 支持 池化操作
Reshape ✅ 支持 张量变形
Concatenation ✅ 支持 拼接操作
BatchNorm ❌ 不支持 需要融合到Conv中
LSTM ⚠️ 部分支持 只有基础版本

为什么会这样?因为MCU资源有限,复杂的算子实现起来太占空间。比如BatchNorm,在推理时其实可以融合到卷积层里,没必要单独实现。

避坑指南:我曾经有个项目用了Transformer的Self-Attention,结果发现TFLM根本不支持。最后只能换成LSTM。所以,设计模型前一定要先查算子支持列表。Google官方文档有完整列表,建议收藏。

4.4 内存管理策略:怎么在几十KB里跑模型?

这是TFLM最精妙的地方。MCU的内存通常只有几十到几百KB,而一个模型可能就有几百KB。怎么塞进去?

TFLM用了三种策略:

  • 静态内存分配:在初始化时就分配好所有内存,运行时不再动态申请。避免了内存碎片问题。
  • 内存复用:不同层的中间结果可以共用同一块内存。比如,卷积层的输出和池化层的输入,其实可以放在同一个缓冲区。
  • 分段加载:模型权重可以分段加载,用一部分加载一部分。适合Flash大但RAM小的场景。

具体实现上,TFLM使用了一个叫做「Tensor Arena」的内存区域。所有张量都分配在这个区域里。初始化时,解释器会计算好每个张量的大小和生命周期,然后统一分配。

// 伪代码示例:TFLM内存分配
// 假设模型有3个中间张量
// 张量A: 生命周期 1-3, 大小 10KB
// 张量B: 生命周期 2-4, 大小 20KB
// 张量C: 生命周期 3-5, 大小 15KB

// 如果按顺序分配,需要 10+20+15 = 45KB
// 但TFLM会复用:A和C的生命周期不重叠,可以共用内存
// 实际只需要 20+15 = 35KB

我记得有一次调试一个语音识别模型,模型本身只有80KB,但运行时内存却需要120KB。后来发现是中间张量太多,没有做好复用。调整了网络结构后,内存降到了60KB。

优化技巧:减少网络深度,增加宽度。深度越深,中间张量越多,内存占用越大。宽度大一点,虽然计算量增加,但内存复用效率更高。

好了,这一章就到这里。TFLM的核心就是「轻量」和「高效」。下一章我们会实际动手,在开发板上跑第一个TinyML程序。到时候你会发现,原来在MCU上跑AI,也没那么难。