You're reading the documentation for a development version.
For the latest stable release version, please have a look at master.

PNNA 推理 API 用户手册

PNNA 是一套基于 C 语言的神经网络推理接口,用于在 ARM 或 DSP 平台上部署和运行深度学习模型。接口对模型加载、硬件绑定、内存管理及推理流程进行了统一抽象,可运行于 Linux、RT-Thread 及裸机环境。

PNNA 适用于图像分类、目标检测等常见视觉任务。用户可通过注册预处理与后处理回调函数,快速构建完整的 “输入处理 → 模型推理 → 结果解析” 应用链路;同时也保留了更底层的模型接口,便于在性能优化或资源复用场景下进行深度定制。

本文档面向基于 PNNA API 进行模型部署与应用开发的工程人员,系统说明接口职责、资源生命周期及推荐使用方式。


示例工程结构

examples 目录包含多平台、多模型的完整部署示例:

 1examples
 2├── yolov5s                 # ARM Linux 平台部署示例
 3   ├── postprocess         # yolov5s 后处理实现
 4   ├── resource            # 模型与测试资源
 5   └── yolov5s_demo.c      # 完整推理流程示例
 6├── resnet18                # ARM Linux 平台部署示例
 7   ├── postprocess
 8   ├── resource
 9   └── resnet18_demo.c
10├── yolov5s_metal_c6x       # DSP 裸机(Metal)平台示例
11   ├── postprocess
12   ├── resource
13   └── yolov5s_demo.c
14└── README.md               # 示例说明文档

快速开始

环境要求

  • 操作系统:Linux / RT-Thread / 裸机

  • 目标硬件:ARM Cortex-A / Cortex-M / DSP C6x

  • 编译工具:对应平台的交叉编译工具链

PNNA 提供两套推理调用方式,分别面向应用集成与高级定制场景。


方式一:应用层接口(推荐)

应用层接口对模型生命周期和推理流程进行了完整封装,适合绝大多数应用场景。

pnna_open();

app_ctx_t *model = create_app_ctx(nbg_buffer, preprocess, postprocess);

buffer_t input = load_image(image_filename);
app(model, (void **)&output, &input);

free_image_buffer(&input);
destroy_app_ctx(model);

pnna_close();

应用层接口流程图

flowchart TB subgraph 初始化阶段 direction TB B1[打开NPU设备 - pnna_open] --> B2[创建推理上下文 - create_app_ctx] end subgraph 推理阶段 direction TB C1{是否继续推理?} -->|是| C2[获取输入 - get_input] C2 --> C3[执行推理 - app] C3 --> C1 C1 -->|否| D_entry[进入退出阶段] end subgraph 退出阶段 direction TB D1[销毁推理上下文 - destroy_app_ctx] --> D2[关闭NPU设备 - pnna_close] end A[开始] --> 初始化阶段 初始化阶段 --> 推理阶段 D_entry --> 退出阶段 退出阶段 --> E[结束]

方式二:模型接口

底层接口仅提供模型加载与推理能力,不参与任何输入输出处理,适用于对性能和资源控制要求较高的场景。

pnna_open();

nn_ctx_t *ctx = create_model(nbg_buffer);

buffer_t input = load_image("test.jpg");
buffer_t quant_input = custom_quantize(input, ctx->input_quant);

infer(ctx, &output, &quant_input);

custom_postprocess(&output);

destroy_model(ctx);
pnna_close();

底层接口流程图

flowchart TB %% 定义初始化阶段子图 subgraph 初始化阶段 direction TB B1[打开NPU设备 - pnna_open] --> B2[创建模型 - create_model] end %% 定义推理阶段子图 subgraph 推理阶段 direction TB C1{是否继续推理?} -->|是| C2[获取输入 - get_input] C2 --> C3[预处理 - preprocess] C3 --> C4[执行推理 - infer] C4 --> C5[处理结果 - postprocess] C5 --> C1 C1 -->|否| D_entry[进入退出阶段] end %% 定义退出阶段子图 subgraph 退出阶段 direction TB D1[销毁模型 - destroy_model] --> D2[关闭NPU设备 - pnna_close] end %% 主流程 A[开始] --> 初始化阶段 初始化阶段 --> 推理阶段 D_entry --> 退出阶段 退出阶段 --> E[结束]

接口层次说明

PNNA 采用分层设计:

  1. 应用层接口

    • 管理模型生命周期

    • 自动分配输入/输出缓存

    • 统一预处理、推理、后处理流程

  2. 底层模型接口

    • 仅负责模型加载与推理执行

    • 不参与数据处理与内存管理


核心 API 说明

设备管理接口

int pnna_open();
int pnna_close();
  • 全局接口,通常在进程生命周期内各调用一次

  • 不支持多次重复初始化或跨模块反复调用


应用层接口

create_app_ctx()

app_ctx_t *create_app_ctx(buffer_t *nbg_buffer,
                          preprocess_cb model_preprocess,
                          postprocess_cb model_postprocess);

功能说明:

  • 内部调用 create_model() 加载模型并绑定硬件

  • 根据模型输入量化信息创建预处理缓存

  • 根据输出张量数量分配输出缓存

  • 将预处理 / 后处理回调绑定到上下文

destroy_app_ctx()

void destroy_app_ctx(app_ctx_t *model);

释放内容包括:

  • 底层模型资源

  • 输入/输出临时 buffer

  • 应用层上下文自身内存


模型接口

create_model() / destroy_model()

nn_ctx_t *create_model(buffer_t *nbg_buffer, int options);
void destroy_model(nn_ctx_t *ctx);
  • create_model():解析 NBG 模型并完成硬件绑定,生成 nn_ctx_t

  • destroy_model():仅释放模型及硬件相关资源

底层接口不涉及任何预处理、后处理或 buffer 管理逻辑。


推理接口说明

infer()

int infer(nn_ctx_t *ctx, buffer_t *output, buffer_t *input);

特性:

  • 仅执行模型推理

  • 不检查输入合法性

  • 不做预处理或后处理

  • 输出为原始网络结果

app()

int app(app_ctx_t *model, void **output, buffer_t *input);

app() 封装完整推理链路:

  1. 参数检查

  2. 可选预处理

  3. 调用 infer() 执行推理

  4. 可选后处理

  5. 组织并返回结果


app() 输出规则说明

配置后处理函数

  • output 指向用户自定义结构体

  • 输出内存由调用者分配

detection *output = malloc(sizeof(detection) * 10);
app(model, (void **)&output, &input);

注意事项:

  • 必须传入 output 的地址(void **),而不是直接传结构体数组

  • output 参数仅用于返回结果指针,app() 不会在内部申请结构化输出内存

错误示例(不推荐):

detection output[10];   // 不推荐
app(model, (void **)output, input);

上述写法会导致指针层级不匹配,可能引发未定义行为。

未配置后处理函数

  • output 类型为 buffer_t *

  • 输出 buffer 由框架内部管理

buffer_t *output = NULL;
app(model, (void **)&output, &input);

注意事项:

  • 初始将 output 置为 NULL 即可

  • app() 内部会将网络输出缓冲区指针赋值给 output

  • 调用者只负责读取该缓冲区,不负责释放(释放规则以模型生命周期为准)


多输入模型支持

int input_count = model->ctx->input_count;
buffer_t *inputs = malloc(sizeof(buffer_t) * input_count);

for (int i = 0; i < input_count; ++i) {
    inputs[i] = load_image(image_paths[i]);
}

app(model, output, inputs);
  • 输入 buffer 生命周期由调用者管理

  • app() 不负责释放输入数据


资源管理对照表

资源

创建接口

释放接口

硬件设备

pnna_open

pnna_close

应用上下文

create_app_ctx

destroy_app_ctx

底层模型

create_model

destroy_model

图像数据

load_image

free_image_buffer


推荐标准流程

  1. pnna_open()

  2. create_app_ctx()

  3. 准备输入数据

  4. app()

  5. 解析结果

  6. destroy_app_ctx()

  7. pnna_close()


作者 @xyy xunyingya@hngwg.com