第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
这个流程我踩过不少坑。简单说,分三步走:
- 训练模型:在PC上用Keras或PyTorch训练好模型
- 转换成TFLite:用TFLite Converter把模型转成.tflite格式
- 转换成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,也没那么难。