
前言本文面向希望在 MCU微控制器上部署 TensorFlow Lite Micro 模型的开发者系统梳理从模型训练、格式转换到算子移植的完整链路。内容覆盖模型训练与推理流程、TFLite Micro 支持的算子集合、模型转换与量化压缩以及把 Lite 参考算子移植到 Micro、编写优化 kernel 的实践步骤。无论你是第一次把模型跑上单片机还是需要为新算子补齐 Micro 端支持都可以按章节顺序阅读或直接跳到关心的环节。TL;DR部署三步走训练小模型 → 用 TFLite Converter 转成 FlatBuffer → 转成 C 字节数组烧进只读存储再用 C 库在设备上跑推理。模型要小、算子要支持训练阶段就要控制模型体积并确保所用算子落在 TFLite Micro 的支持列表内第 4 节给出了完整算子清单及对应 commit。转换与压缩用 TensorFlow Lite converter Python API 完成转换优先考虑训练后量化以获得最小模型体积最后用xxd -i生成 C 数组。算子移植分三步PR1 抽出算子参数解析函数、PR2 把参考实现抽成独立头文件、PR3 把 kernel 与测试从 Lite 搬到 Micro并补齐单元测试。性能优化在参考实现之上为特定硬件如 Xtensa Fusion F1增加优化 kernel通过重构共享代码并接入优化 NN 库来降低延迟。info1. Deploy and run a TensorFlow model on a microcontroller 2. Train a model for TFLite Micro 3. Run inference 4. Supported operations 5. Model conversion 6. Porting reference ops from Lite to Micro 7. Optimized kernel implementations1. Deploy and run a TensorFlow model on a microcontrollerTrain a model Generate a small TensorFlow model that can fit your target device and contains supported operations.Convert to a TensorFlow Lite model using the TensorFlow Lite converter.Convert to a C byte array using standard tools to store it in a read-only program memory on device.Run InferenceRun inference on device using the C library and process the results.上面两步构成了 TFLite Micro 的最小闭环离线训练与转换在 PC 上完成在线推理在 MCU 上完成。理解这条链路后后续章节分别展开其中的关键环节——模型如何训练得足够小第 2 节、推理如何在设备上跑起来第 3 节、哪些算子被支持第 4 节、模型如何转换与压缩第 5 节以及当算子缺失时如何从 Lite 移植到 Micro第 6、7 节。2. Train a Model for TFLite Micro训练阶段的核心约束不是精度而是体积与算子控制模型规模MCU 的 Flash 通常只有几百 KB 到几 MBRAM 更是只有几十到几百 KB。应优先选择 MobileNet、MicroNet 这类轻量骨干网络减少通道数与层数避免过大的全连接层。只用受支持的算子训练时就要确认网络中的算子落在 TFLite Micro 的支持列表内见第 4 节否则转换后会在设备端报Didnt find op for builtin opcode之类的错误。预留量化空间训练时可按量化友好方式设计如避免极端激活值范围为后续的训练后量化打好基础。一个常见的做法是先用 Keras 定义并训练模型保存为 SavedModel再进入第 5 节的转换流程。3. Run Inference在设备上跑推理时通常需要准备三样东西模型数据即第 5 节生成的 C 字节数组model_data.cc编译进固件并放在只读存储中。算子解析器MicroMutableOpResolver显式注册模型用到的算子未注册的算子会在推理时失败。相比全量注册按需注册能显著减小固件体积。张量竞技场tensor arena一块由调用方提供的内存缓冲区TFLite Micro 在其中分配中间张量大小需根据模型估算并留出余量。典型调用顺序为MicroInterpreter构造 →AllocateTensors()→ 填充输入张量 →Invoke()→ 读取输出张量。若AllocateTensors()返回kTfLiteError多半是 arena 太小或算子未注册。4. Supported operations备注commit ID21779d8e717c126f3e9128f109b1d967f63de415序号算子简要描述1Abs逐元素取绝对值2Add逐元素相加支持广播3AddN多个张量逐元素求和4ArgMax返回指定维度上最大值的索引5ArgMin返回指定维度上最小值的索引6AssignVariable给变量张量赋值用于有状态模型7AveragePool2D二维平均池化下采样特征图8BatchToSpaceNd由批次维度重排回空间维度9CallOnce仅执行一次的初始化子图10Ceil逐元素向上取整11Concatenation沿指定维度拼接多个张量12Conv2D二维卷积CNN 的核心计算13Cos逐元素余弦14CumSum沿指定维度累加求和15DepthToSpace深度维重排到空间维像素重排16DepthwiseConv2D逐通道卷积MobileNet 系列核心算子17Dequantize量化整数还原为浮点18DetectionPostprocess检测头输出解码为边界框与类别19Elu指数线性激活函数20Equal逐元素相等比较输出布尔张量21EthosU卸载到 Ethos-U NPU 执行22Exp逐元素自然指数23ExpandDims在指定位置插入长度为 1 的维度24Fill用给定值填充指定形状的张量25Floor逐元素向下取整26FloorDiv逐元素向下取整除法27FloorMod逐元素取模结果符号随除数28FullyConnected全连接层即矩阵乘加偏置29Greater逐元素大于比较30GreaterEqual逐元素大于等于比较31HardSwishHardSwish 激活函数MobileNetV3 常用32L2Normalization沿通道维做 L2 归一化33L2Pool2D二维 L2 池化34LeakyRelu带小负斜率的 ReLU35Less逐元素小于比较36LessEqual逐元素小于等于比较37Log逐元素自然对数38LogicalAnd逐元素逻辑与39LogicalNot逐元素逻辑非40LogicalOr逐元素逻辑或41LogisticSigmoid 激活输出 0~1 概率42MaxPool2D二维最大池化下采样特征图43Maximum逐元素取较大值44Mean沿指定维度求均值45Minimum逐元素取较小值46Mul逐元素相乘支持广播47Neg逐元素取负48NotEqual逐元素不等比较49Pack把多个张量堆叠成高维张量50Pad用零填充张量边界51PadV2用指定常量填充张量边界52Prelu参数化 ReLU负半轴斜率可学习53Quantize浮点量化为整数54ReadVariable读取变量张量的当前值55ReduceMax沿指定维度求最大值56Relu小于 0 置零的激活函数57Relu6输出截断到 [0, 6] 的 ReLU量化模型常用58Reshape改变张量形状不改变数据59ResizeBilinear双线性插值缩放图像60ResizeNearestNeighbor最近邻插值缩放图像61Round逐元素四舍五入62Rsqrt逐元素平方根倒数63Shape返回张量各维度大小64Sin逐元素正弦65Softmax归一化为概率分布分类层常用66SpaceToBatchNd空间维拆分到批次维67SpaceToDepth空间维重排到深度维68Split沿维度把张量均分为多份69SplitV沿维度按指定大小切分张量70Sqrt逐元素平方根71Square逐元素平方72Squeeze删除长度为 1 的维度73StridedSlice按步长切片提取子张量74Sub逐元素相减支持广播75Svdf单层奇异值分解滤波器语音关键词识别常用76Tanh双曲正切激活输出 -1~177Transpose按给定顺序重排张量维度78TransposeConv转置卷积即反卷积上采样79Unpack把高维张量沿维度拆成多个张量80VarHandle创建变量张量的句柄算子分类速览上表按字母序排列便于查找若想按功能理解可参考下面的分类。TFLite Micro 的算子大体可分为以下几类基础算术Add、Sub、Mul、FloorDiv、FloorMod、Neg、Square、Sqrt、Rsqrt、Ceil、Floor、Round、Cos、Sin、Log、Exp。逐元素运算是绝大多数网络的底层构件通常也是最先被优化 kernel 覆盖的对象。矩阵与卷积Conv2D、DepthwiseConv2D、TransposeConv、FullyConnected、Svdf。计算量最大的部分直接决定推理耗时FullyConnected与Conv2D是量化与优化实现的重点见第 7 节。池化与归一化AveragePool2D、MaxPool2D、L2Pool2D、L2Normalization、Softmax、Logistic。用于下采样与概率输出Softmax常见于分类模型最后一层。激活函数Relu、Relu6、LeakyRelu、Prelu、Elu、HardSwish、Tanh。Relu6是 MobileNet 系列的标准配置量化模型里尤其常见。比较与逻辑Equal、NotEqual、Greater、GreaterEqual、Less、LessEqual、LogicalAnd、LogicalOr、LogicalNot、Maximum、Minimum。多用于条件分支与后处理逻辑。张量形状与数据搬运Reshape、Squeeze、ExpandDims、Transpose、Pack、Unpack、Split、SplitV、Concatenation、StridedSlice、Pad、PadV2、Fill、Shape、BatchToSpaceNd、SpaceToBatchNd、DepthToSpace、SpaceToDepth。这类算子本身计算量小但会带来额外的内存布局转换开销在 MCU 上需留意 arena 占用。归约与统计Mean、ReduceMax、ArgMax、ArgMin、CumSum。常用于全局池化与结果解析。量化相关Quantize、Dequantize。在 int8/uint8 与 float 之间转换混合精度模型或调试阶段会用到。变量与状态AssignVariable、ReadVariable、VarHandle、CallOnce。支撑有状态模型如 RNN、流式推理与一次性初始化逻辑。图像与检测后处理ResizeBilinear、ResizeNearestNeighbor、DetectionPostprocess。前者用于输入图像缩放后者把检测头的原始输出解码成边界框与类别。硬件加速EthosU。把算子卸载到 Ethos-U NPU需要目标芯片具备对应硬件与驱动支持。按这个分类排查模型时可以先看计算密集的卷积/全连接是否都有对应实现再确认形状变换类算子是否齐全——后者缺失往往表现为转换成功但推理阶段报算子未注册。使用这张表时有几点需要注意列表随版本变化上表对应 commit21779d8e不同版本的 TFLite Micro 支持算子会增减实际开发应以所用版本的tensorflow/lite/micro/kernels/目录与all_ops_resolver.cc为准。支持 ≠ 高效表中算子大多有参考实现但参考实现以正确性为先性能未必最优对性能敏感的算子可参考第 7 节编写优化 kernel。部分算子有平台限制如EthosU依赖 Ethos-U NPU 硬件DetectionPostprocess面向目标检测后处理使用前需确认目标平台与输入输出约定。5. Model ConversionConvert a trained TensorFlow model to a TFLite modelTo convert a trained TensorFlow model to run on microcontrollers, you should use the TensorFlow Lite converter Python API (https://www.tensorflow.org/lite/convert/). This will convert the model into a FlatBuffer, reducing the model size, and modify it to use TensorFlow Lite operations.To obtain the smallest possible model size, you should consider using post-training quantization.Convert a TFLite model to a C arrayxxd -i converted_model.tflite model_data.ccThe output will look similar to the following: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;6. Porting Reference Ops from Lite to MicroPR1. Extract the code for parsing op parameters to a functionExtract the code for parsing op parameters out of the switch statement in [ParseOpDataTfLite()](https://github.com/tensorflow/tensorflow/blob/d8394a6d774f5e3c02d97f1fc18ff445199db598/tensorflow/lite/core/api/flatbuffer_conversions.cc#L135) in lite/core/api/flatbuffer_conversions.cc into a standalone function, and call that function from the switch statement. This standalone function is now available to be called by the Micro op resolver, which also needs to parse the op parameters, in a future change.Example:A simple example is PR #45307A more complicated example is PR #46021.Modify Test:Use clang-format to make sure the code is properly formatted.clang-format --stylegoogle -i $(git ls-files -m | grep -E \.cc|\.h)Make sure your code is lint-free.cpplint.py $(git ls-files -m)Unit testbazel test tensorflow/lite/kernels:allPR2. Extract the reference for the op to a standalone headerMove the reference implementation of the op in reference_ops.h to a standalone header so that Micro can include it without including unrelated dependencies via reference_ops.h.Example:A good example is PR #45311Copy an existing header from tensorflow/lite/kernels/internal/reference/ to tensorflow/lite/kernels/internal/reference/NEW_OP.H to create the boilerplate. Replace NEW_OP.H with the name of the new operator.Move the implementation from tensorflow/lite/kernels/internal/reference/reference_ops.h to tensorflow/lite/kernels/internal/reference/NEW_OP.H.Add the new header to the build by adding to the library definitions reference_base and legacy_reference_base in the file tensorflow/lite/kernels/internal/BUILD. See, for example, this change for operator FILL.Modify test as PR1.PR3. Port the op from Lite to MicroCopy the kernel and test from Lite to Micro (example is commit a2ca1fd).Remove Lite-specific code from copies (A good example is commit a5a87b4).Port the op and the test (A good example is the changes made in PR #45647).Modify test as PR1.and test add more:$ bazel test tensorflow/lite/micro/kernels:${op}_test $ bazel test tensorflow/lite/micro/kernels:all $ make -f tensorflow/lite/micro/tools/make/Makefile test_kernel_${op}_test $ make -f tensorflow/lite/micro/tools/make/Makefile test7. Optimized kernel implementationsExample: add an optimized implementation for fully_connected for the Xtensa Fusion F1. The steps:PR 1: refactor for reference fallbacks and a baseline latency.PR 2: refactor to share code between reference and optimized kernels.PR 3: add the code needed to use the optimized NN lib and document the latency improvement.总结部署三步走先在 TensorFlow 中训练一个体积可控、算子受支持的小模型再用 TFLite Converter 转成 FlatBuffer最后用xxd -i转成 C 字节数组放进只读存储由设备上的 C 库完成推理。模型体积与算子约束MCU 的 Flash 与 RAM 都很紧张训练阶段就要控制模型规模同时必须确认所用算子落在 TFLite Micro 的支持列表内第 4 节给出了完整清单及对应 commit否则需要在部署前替换算子或自行移植。量化压缩转换阶段优先考虑训练后量化在精度可接受的前提下显著缩小模型体积是让模型塞进单片机的最有效手段之一。算子移植三个 PR 的分工PR1 把算子参数解析逻辑从ParseOpDataTfLite()的 switch 中抽成独立函数供 Micro 的 op resolver 复用PR2 把reference_ops.h中的参考实现抽成独立头文件避免 Micro 引入无关依赖PR3 把 kernel 与测试从 Lite 搬到 Micro去掉 Lite 专有代码并补齐单元测试。优化 kernel 的收益在参考实现之上为特定硬件如 Xtensa Fusion F1编写优化 kernel通过重构共享代码、接入优化 NN 库可以在保持接口一致的同时明显降低推理延迟。初学者上手路径建议先跑通官方hello_world示例熟悉模型转换、C 数组生成、在目标板上的编译与推理流程确认整条链路打通后再尝试移植自定义算子按 PR1 → PR2 → PR3 的顺序小步提交并始终用bazel test与make的 kernel 测试保证不回归。