2、TinyML模型部署基础:模型在MCU上的存储与执行流程、Flash与RAM的角色、权重数据的组织方式

TinyML模型部署的核心挑战在于:如何在资源极度受限的微控制器(MCU)上,让一个经过训练的神经网络模型高效、安全地运行。与云端或边缘Linux设备不同,MCU没有操作系统级别的文件管理,没有虚拟内存,通常只有几十KB到几MB的Flash(存储代码和常量数据)以及几KB到几百KB的RAM(运行时数据)。理解模型在MCU上的存储与执行流程,是后续实施权重加密、混淆和Flash读取风险防护的物理基础。本章将深入剖析模型从“编译产物”到“可执行二进制”的落地过程,厘清Flash与RAM的分工边界,并揭示权重数据在内存中的真实组织形态。

Flash 存储域(非易失) RAM 运行域(易失) 模型权重区 .rodata / 常量段 代码段 .text / 算子实现 启动配置 向量表 / 链接脚本 只读元数据 输入/输出尺度、层信息 特点:掉电不丢失 容量:256KB ~ 2MB 访问:直接寻址/映射 写入:需擦除/页编程 激活张量 中间特征图 输入/输出缓冲 传感器数据 / 推理结果 运行时栈 函数调用 / 局部变量 堆(可选) 动态分配(谨慎使用) 特点:掉电即丢失 容量:16KB ~ 512KB 访问:高速随机读写 写入:字节级操作 上电启动:Flash → 搬运/映射 → RAM 执行 权重直接寻址读取(无需拷贝) 关键点:权重通常常驻Flash,通过地址映射直接参与计算;RAM仅存放动态数据

上图展示了TinyML模型在MCU上的核心存储架构。左侧Flash区域保存了模型的权重、代码和只读元数据,右侧RAM区域则用于存放推理过程中的动态数据。理解这张图的关键在于:权重数据默认是“原地执行”的,即CPU直接从Flash地址读取权重参与乘加运算,而不是先拷贝到RAM。这一特性直接决定了后续章节中权重加密和混淆策略的设计边界——如果权重必须被解密后才能使用,那么解密后的临时副本放在哪里?如何防止在RAM中被嗅探?这些问题的答案都建立在对本章基础架构的深刻理解之上。

2.1 模型在MCU上的存储形态:从模型文件到二进制段

一个在PC上训练好的TensorFlow Lite模型(.tflite文件)并不能直接被MCU执行。它需要经过一系列转换,最终以C语言数组或特定二进制格式的形式,链接进固件镜像中。整个存储形态的转换流程如下:

  1. 模型转换(Quantization & Conversion):使用TensorFlow Lite Converter将浮点模型转换为定点(如int8)或浮点(float32)的FlatBuffer格式。此过程会生成一个包含模型结构、权重、算子调度信息的二进制文件。
  2. 字节数组化(Byte Array Embedding):通过工具(如xxd -i或TensorFlow的convert_bytes_to_c_source())将.tflite文件转换为C语言源文件,生成一个const unsigned char model_data[]数组。这个数组被编译后,会存放在Flash的只读数据段(.rodata)中。
  3. 链接与地址分配:链接脚本(Linker Script)决定model_data数组在Flash中的具体物理地址。通常,该数组会被放置在Flash的某个对齐地址上,以便CPU进行高效的直接内存访问(DMA)或Flash加速器读取。
  4. 运行时解析:MCU上的TFLite Micro解释器通过指针指向model_data数组的首地址,在Flash中直接解析FlatBuffer结构,获取算子列表、张量元数据(形状、类型、量化参数)以及权重数据的偏移量。

这种“原地解析”模式意味着整个模型文件在Flash中是一个连续的二进制大对象(BLOB)。从安全角度看,攻击者如果能够读取Flash内容(例如通过SWD调试接口或Bootloader漏洞),就能直接获得完整的模型结构和权重数据。因此,理解这个存储形态是评估Flash读取风险的第一步。

2.2 Flash与RAM的角色分工:为什么权重不常驻RAM?

在MCU上,Flash与RAM的物理特性和成本差异决定了它们的分工。Flash容量大(通常256KB~2MB)、成本低、掉电不丢失,但写入速度慢且需要按扇区擦除;RAM容量小(通常16KB~512KB)、速度快、可字节级读写,但掉电即失。TinyML推理引擎的设计原则是:尽可能让数据“只读”地待在Flash中,避免不必要的RAM拷贝。

存储介质 典型容量 访问速度 写入方式 在TinyML中的角色
Flash(代码/常量) 256KB ~ 2MB 中等(带缓存加速) 扇区擦除后编程(ms级) 存储模型权重、算子代码、只读元数据
RAM(数据/栈) 16KB ~ 512KB 高速(单周期访问) 字节级直接写入(ns级) 存储输入/输出张量、中间激活值、栈帧
EEPROM/外部Flash 可选扩展 慢(I2C/SPI) 字节/页写入 存储用户配置或离线模型备份

一个常见的误区是认为“把权重加载到RAM中运行会更快”。实际上,对于大多数Cortex-M系列MCU,Flash接口带有预取缓冲和缓存(如STM32的ART加速器),顺序读取Flash中的权重数据并不会比从RAM读取慢太多。相反,如果将所有权重拷贝到RAM,会占用宝贵的RAM空间,导致中间激活值无法分配,甚至直接导致内存溢出(OOM)。因此,默认策略是:权重留在Flash,RAM只分配必要的“工作缓冲区”。

工程实践要点:在STM32CubeIDE或Keil中,可以通过链接脚本(.ld文件)的KEEP()指令和ALIGN()属性,将模型数组强制放置在Flash的特定区域(例如内部Flash的末尾或外部QSPI Flash映射区)。这有助于优化Flash访问效率,并为后续的固件加密或权重混淆提供固定的地址边界。

2.3 权重数据的组织方式:FlatBuffer与张量存储布局

TFLite模型采用FlatBuffer序列化格式,这是一种零拷贝、高效的内存映射序列化库。在MCU上,解释器直接通过指针访问Flash中的FlatBuffer数据,无需反序列化到RAM。权重数据在FlatBuffer中的组织方式如下:

  • 张量(Tensor)元数据:每个张量包含名称、形状(dims)、数据类型(如int8、uint8、float32)、量化参数(scale、zero_point)以及缓冲区索引(buffer_index)。这些元数据存储在模型的tensors表中。
  • 缓冲区(Buffer)数据:权重数据实际存储在buffers表中。每个缓冲区是一个字节数组,对于常量张量(如卷积核权重),其数据直接内嵌在FlatBuffer的data字段中。对于变量张量(如RNN状态),缓冲区可能为空,数据在运行时分配在RAM中。
  • 算子(Operator)引用:每个算子(如CONV_2D、DEPTHWISE_CONV_2D)通过输入/输出张量索引引用权重。解释器执行时,根据算子类型调用对应的内核函数,并从缓冲区中读取权重数据。

以int8量化卷积为例,权重数据的组织遵循“NHWC”或“NCHW”布局(取决于转换时的配置)。对于MCU上的TFLite Micro,默认通常使用NHWC布局,即按[输出通道][输入通道][核高][核宽]的顺序存储。这种布局有利于在Cortex-M内核上使用SIMD指令(如CMSIS-NN)进行高效的乘加运算。

// 典型的模型数组定义(由转换工具生成)
// 该数组位于Flash的.rodata段
const unsigned char model_data[] = {
  0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33,
  // ... 省略的FlatBuffer内容 ...
  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  // 权重数据段:int8量化值,范围[-128, 127]
  0x3c, 0x8a, 0x5f, 0x1b, 0x04, 0x9e, 0x71, 0x2d,
  // ... 后续权重字节 ...
};

// 解释器初始化时,仅传递指针,不拷贝数据
static tflite::MicroErrorReporter micro_error_reporter;
static tflite::MicroInterpreter interpreter(
    tflite::GetModel(model_data), resolver, tensor_arena, tensor_arena_size);

从安全角度看,这种组织方式意味着权重数据在Flash中是以明文形式连续存放的。即使没有完整的模型结构信息,攻击者通过分析Flash的二进制转储,也能识别出权重数据的起始位置和大致范围(通常表现为大量看似随机的字节,但具有特定的统计特征)。这为后续的“权重混淆”和“加密存储”提供了明确的攻击面。

2.4 执行流程详解:解释器如何“跑”起一个模型

在MCU上,TFLite Micro解释器的执行流程是一个典型的“解析-调度-执行”循环。整个过程完全在Flash和RAM之间协同完成,不依赖操作系统。具体步骤如下:

  1. 模型解析(Model Parsing):调用tflite::GetModel(model_data),在Flash中直接解析FlatBuffer的根表,获取模型版本、算子列表、子图(Subgraph)信息。此过程不分配RAM。
  2. 内存规划(Memory Planning):解释器遍历所有张量,计算每个张量的内存需求(大小、对齐要求),并在预先分配的tensor_arena(一块静态RAM缓冲区)中进行内存分配。权重张量(常量)不占用RAM,它们直接指向Flash中的缓冲区;只有激活张量(变量)才会在RAM中分配空间。
  3. 算子注册(Op Resolver):将模型中的算子名称与MCU上已实现的内核函数进行匹配。如果遇到未注册的算子,解释器会报错并停止。
  4. 推理循环(Invoke Loop):按拓扑顺序遍历子图中的算子节点。对于每个算子,解释器从Flash中读取输入张量的指针(可能指向RAM或Flash),调用对应的内核函数(如conv_per_channel()),内核函数从Flash中读取权重,执行计算,将结果写入RAM中的输出张量。
  5. 输出提取:推理完成后,输出张量的数据位于RAM中,用户代码可以直接读取该内存区域获取分类结果或回归值。

这个流程的关键特征是“零拷贝”:权重数据从Flash到CPU寄存器,中间不经过RAM缓冲。这意味着,如果攻击者能够通过调试接口在推理过程中读取RAM,他们只能看到中间激活值,而无法直接看到权重。然而,如果攻击者直接读取Flash,则权重完全暴露。因此,Flash读取风险是TinyML模型保护的首要威胁。

2.5 实战场景:基于STM32F4的模型部署与内存观测

为了更直观地理解上述概念,我们以一个实际的部署案例来说明。假设我们有一个用于关键词唤醒(KWS)的int8量化模型,模型大小为120KB,部署在STM32F407(Cortex-M4F,1MB Flash,192KB RAM)上。

初始现象与目标:模型转换后,生成的model_data[]数组约120KB。在集成到固件后,编译报告显示Flash占用增加了约120KB,RAM静态占用增加了约30KB(用于tensor_arena)。目标是在不增加外部存储的前提下,确保推理时间小于50ms,并评估模型在Flash中的实际存储位置。

关键输入数据与参数:

  • 模型结构:3层卷积(3x3核)+ 1层全连接,输入特征为40x40x1。
  • 量化参数:int8,权重范围[-128, 127],激活范围[-128, 127]。
  • 链接脚本:FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1M,RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K。

分析判断过程:通过查看生成的.map文件,我们发现model_data数组被放置在Flash地址0x08020000处,紧跟在代码段之后。RAM中的tensor_arena被分配在0x20005000,大小为28KB。推理时,CPU从Flash读取权重,通过ART加速器缓存,实际测得推理时间为38ms,满足要求。

实际操作步骤与结果对比:

  1. 使用objdump -h firmware.elf查看段信息,确认.rodata段包含了模型数据。
  2. 通过STM32CubeProgrammer的“内存查看”功能,直接读取Flash地址0x08020000处的数据,可以看到与model_data[]数组完全一致的十六进制字节流。
  3. 在推理过程中,通过调试器暂停CPU,查看RAM地址0x20005000处的数据,发现是中间激活值(非权重),且数值随输入变化。

风险与代价:该部署方案虽然性能达标,但存在明显的安全风险:任何能够通过SWD接口连接MCU的人,都可以使用STM32CubeProgrammer的“读取”功能,将整个Flash内容导出为二进制文件,然后使用工具(如tflite-analyzer)解析出模型结构和权重。这意味着模型知识产权完全暴露。

复盘经验:此案例揭示了TinyML部署的“默认不安全”状态。后续章节中,我们将探讨如何通过Flash读保护(RDP)、权重加密和运行时混淆来缓解这一风险。但无论采用何种保护措施,都必须基于对本章所述存储与执行流程的精确理解——例如,加密后的权重无法被CPU直接执行,必须在运行时解密到RAM的临时缓冲区,这就会引入新的RAM占用和性能开销。

2.6 常见误区与设计总结

在TinyML模型部署的实践中,开发者常陷入以下误区,这些误区会直接影响后续安全方案的有效性:

误区一:认为“模型在RAM中运行更安全”。实际上,将权重从Flash拷贝到RAM不仅浪费宝贵的RAM空间,而且使得攻击者可以通过调试器在RAM中直接搜索权重模式(例如查找int8量化后的特定值分布),反而降低了攻击门槛。正确的做法是让权重留在Flash,并通过硬件保护机制(如RDP)限制Flash的外部读取。

误区二:忽略链接脚本对模型地址的影响。如果模型数组被链接到Flash的随机位置,且未进行对齐,可能导致Flash加速器缓存命中率下降,推理时间变长。更重要的是,不固定的地址会增加后续固件升级或安全补丁的复杂度。

误区三:混淆“权重数据”与“模型结构”。权重是模型的参数,而模型结构(层数、算子类型、连接方式)同样具有商业价值。在Flash中,模型结构以FlatBuffer的Schema形式存在,攻击者可以解析出完整的网络拓扑。因此,单纯加密权重而不处理结构信息,无法完全保护模型知识产权。