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卷积替代全连接层。

STM32N6 AI模型部署流程:从训练模型到可执行固件 ① 模型导出 PyTorch / TF → ONNX (opset 13) ② 量化校准 PTQ / QAT 校准集 100~500 样本 ③ 图优化与映射 算子融合 / 常量折叠 NPU vs CPU 分配 ④ C代码生成 Cube.AI 生成 network.c / network.h ⑤ 工程集成与验证 STM32CubeIDE 工程 · 链接脚本 · 内存布局 板端推理 · 精度对比 · 延迟测量 关键检查点 ONNX 输入输出 shape 静态,batch=1 量化前后 Top-1 精度下降 ≤ 2% NPU 算子覆盖率 ≥ 85%,CPU 回退算子已评估 生成代码的 RAM/Flash 占用满足链接脚本约束 板端推理结果与 PC 端逐层对比,误差在允许范围 实测单帧延迟满足应用实时性要求

阶段四: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工程后,需要完成以下集成工作:

  1. 内存布局配置:修改链接脚本(.ld文件),将network_data段定位到外部Flash或内部Flash的特定区域,将激活缓冲区定位到SRAM的连续块。STM32N6的NPU需要访问连续的物理内存,因此不能使用分散的堆分配。
  2. 输入预处理:摄像头或传感器数据需要转换为模型期望的格式(如RGB888→RGB888、归一化到[-1,1]或[0,1])。预处理通常在CPU上完成,注意与量化时的输入scale保持一致。
  3. 推理调用:在主循环中调用network_run,传入输入缓冲区和输出缓冲区。NPU异步执行,可通过中断或轮询等待完成。
  4. 结果后处理:输出张量是INT8格式,需要用输出层的scale和zero point反量化为浮点数,再执行Softmax、NMS等后处理。

验证阶段需要对比三个层面的结果:PC端ONNX Runtime的FP32输出、PC端量化模型的INT8输出、STM32N6板端的实际输出。三者应逐层接近。如果板端结果异常,优先检查输入数据的预处理是否与量化校准一致、内存对齐是否满足NPU要求(通常需要32字节对齐)、以及时钟配置是否使NPU运行在标称频率。

实践建议:首次部署时,先用一个极简模型(如3层CNN,输入32x32)跑通全流程,确认工具链版本、链接脚本、内存分配都正确后,再替换为真实模型。这样可以快速定位问题出在工具链配置还是模型本身。

整个流程中,最容易被低估的是量化校准和内存布局。量化校准决定了精度底线,内存布局决定了模型能否成功加载。建议在Cube.AI生成报告后,仔细查看“算子覆盖率”和“内存占用”两项指标,它们直接预示了部署的可行性和性能上限。