4. AI模型部署流程总览
在PC上训练出一个精度满意的神经网络,只是整个工程的一半。真正让模型在STM32N6上跑起来,需要经过一条完整的“模型→固件”转换链路。这条链路的核心矛盾在于:训练框架(PyTorch、TensorFlow)使用32位浮点张量和动态图,而STM32N6的NPU(Neural-ART加速器)只接受经过量化、静态形状、特定算子集的定点模型。因此,部署流程本质上是一次“翻译+瘦身+适配”的过程。
整个流程可以划分为五个阶段:模型导出 → 量化校准 → 图优化与算子映射 → C代码生成 → 工程集成与验证。下面逐一展开每个阶段的关键输入、操作和判断依据。
阶段一:模型导出为中间表示
无论原始模型来自PyTorch还是TensorFlow,第一步都是导出为ONNX(Open Neural Network Exchange)格式。ONNX是STM32Cube.AI工具链的入口格式,它用静态计算图描述网络结构,每个节点对应一个算子,每条边对应一个张量。
以PyTorch为例,导出命令如下:
torch.onnx.export(
model, # 训练好的模型
dummy_input, # 示例输入,shape=(1,3,224,224)
"model.onnx",
opset_version=13, # 建议11~15
input_names=["input"],
output_names=["output"],
dynamic_axes=None # 必须静态shape
)
导出后需要检查两点:一是所有算子是否在ONNX opset中合法;二是输入输出张量的shape是否完全静态(batch维度固定为1)。如果模型包含自定义算子或动态控制流,需要先改写为等效的静态子图。
阶段二:量化与校准
STM32N6的NPU支持INT8和INT16推理,其中INT8是主流选择。量化将FP32权重和激活值映射到8位整数,同时记录缩放因子(scale)和零点(zero point)。量化分为两种模式:
- 训练后量化(PTQ):用一批校准数据(通常100~500张代表性样本)统计每层激活值的动态范围,计算scale和zero point。速度快,适合大多数CNN。
- 量化感知训练(QAT):在训练阶段插入伪量化节点,让模型适应量化误差。精度更高,但需要重新训练。
校准数据的分布必须与真实推理场景一致。例如,一个用于工业质检的模型,校准集应包含正常样本和缺陷样本,而不是随机噪声。校准完成后,用ONNX Runtime或STM32Cube.AI的量化工具生成量化模型,并对比量化前后的精度差异。如果Top-1精度下降超过2%,需要检查敏感层(通常是第一层和最后一层)是否适合保留FP32或改用INT16。
阶段三:图优化与算子映射
量化后的模型进入STM32Cube.AI编译器。编译器会执行一系列图优化:常量折叠、算子融合(Conv+BN+ReLU合并为单个NPU指令)、死代码消除、内存复用规划。然后,每个算子被映射到NPU的硬件单元或CPU的软件实现。
STM32N6的NPU对卷积、深度可分离卷积、全连接、池化、激活函数有硬件加速;对Resize、Transpose、自定义LSTM等算子则回退到Cortex-M55 CPU执行。因此,部署前应尽量用NPU友好的算子替换CPU回退算子。例如,将Transpose+Reshape合并为一次内存重排,或用1x1卷积替代全连接层。
阶段四:C代码生成
STM32Cube.AI以命令行或STM32CubeMX插件形式运行,输入量化后的ONNX模型,输出一组C文件:network.c、network.h、network_data.c(权重和偏置的二进制数组)以及ai_platform.h。生成代码包含三个核心接口:
ai_handle network_create(void);
ai_error network_init(ai_handle handle);
ai_error network_run(ai_handle handle, const ai_buffer* input, ai_buffer* output);
其中network_data.c中的权重数组通常被标记为const,存放在Flash中;激活缓冲区(activation buffer)则需要在RAM中分配。STM32N6拥有4.2MB的片上SRAM和外部PSRAM接口,对于大型模型,需要将权重放在外部Flash(通过XSPI接口),激活值放在内部SRAM或PSRAM中。
阶段五:工程集成与验证
将生成的C文件加入STM32CubeIDE工程后,需要完成以下集成工作:
- 内存布局配置:修改链接脚本(.ld文件),将
network_data段定位到外部Flash或内部Flash的特定区域,将激活缓冲区定位到SRAM的连续块。STM32N6的NPU需要访问连续的物理内存,因此不能使用分散的堆分配。 - 输入预处理:摄像头或传感器数据需要转换为模型期望的格式(如RGB888→RGB888、归一化到[-1,1]或[0,1])。预处理通常在CPU上完成,注意与量化时的输入scale保持一致。
- 推理调用:在主循环中调用
network_run,传入输入缓冲区和输出缓冲区。NPU异步执行,可通过中断或轮询等待完成。 - 结果后处理:输出张量是INT8格式,需要用输出层的scale和zero point反量化为浮点数,再执行Softmax、NMS等后处理。
验证阶段需要对比三个层面的结果:PC端ONNX Runtime的FP32输出、PC端量化模型的INT8输出、STM32N6板端的实际输出。三者应逐层接近。如果板端结果异常,优先检查输入数据的预处理是否与量化校准一致、内存对齐是否满足NPU要求(通常需要32字节对齐)、以及时钟配置是否使NPU运行在标称频率。
整个流程中,最容易被低估的是量化校准和内存布局。量化校准决定了精度底线,内存布局决定了模型能否成功加载。建议在Cube.AI生成报告后,仔细查看“算子覆盖率”和“内存占用”两项指标,它们直接预示了部署的可行性和性能上限。