AI 模型部署指南
SDK AI 模型应用类型
目前,FreeRTOS 和 Arduino SDK 提供了多个预部署模型,如下表所示:
类别 |
模型 |
仓库 |
|---|---|---|
目标检测 |
Yolov3-tiny Yolov4-tiny Yolov7-tiny |
|
目标检测 |
YOLOv7-tiny-pt |
|
人脸检测 |
SCRFD |
https://github.com/deepinsight/insightface/tree/master/detection/scrfd |
人脸识别 |
MobileFaceNet |
https://github.com/deepinsight/insightface/tree/master/recognition |
声音分类 |
YAMNet |
https://github.com/tensorflow/models/tree/master/research/audioset/yamnet |
参考: NN 模型库
Arduino SDK 定制模型部署指南
本指南说明如何在 Ameba IC 上使用 Arduino SDK 部署自定义的 NN 模型( .nb 文件)。
从 Flash 加载模型
步骤 1:重命名 .nb 文件
将自定义的 .nb 文件重命名为预期的文件名。有关预期的文件名,请参考 默认支持的模型文件名 章节。
步骤 2:复制到项目文件夹
C:\Users\<USERNAME>\AppData\Local\Arduino15\packages\realtek\hardware\<Ameba_IC>\<VERSION>\libraries\NeuralNetwork\examples
/home/<USERNAME>/.arduino15/packages/realtek/hardware/<Ameba_IC>/<VERSION>/libraries/NeuralNetwork/examples
步骤 3:打开示例
在 Arduino IDE 中,导航至:
File → Examples → AmebaNN → ObjectDetectionCallback
步骤 4:设置加载源
Tools → NN Model Load from: → Load from Flash
步骤 5:更新 modelSelect()
更新代码中的模型选择:
ObjDet.modelSelect(OBJECT_DETECTION, CUSTOMIZED_YOLOV4TINY, NA_MODEL, NA_MODEL);
步骤 6:编译并上传
在 Arduino IDE 中点击 上传。 .nb 文件将自动打包到 flash 中。
从 SD 卡加载模型
步骤 1:重命名 .nb 文件
将自定义的 .nb 文件重命名为预期的文件名。请参考 默认支持的模型文件名 章节。
步骤 2:打开示例
在 Arduino IDE 中,导航至:
File → Examples → AmebaNN → ObjectDetectionCallback
步骤 3:设置加载源
Tools → NN Model Load from: → Load from SD
步骤 4:更新 modelSelect()
ObjDet.modelSelect(OBJECT_DETECTION, CUSTOMIZED_YOLOV4TINY, NA_MODEL, NA_MODEL);
步骤 5:准备 SD 卡
将 .nb 文件放置在 SD 卡的以下目录结构中:
SD card root/
└── NN_MDL/
└── yolov4_tiny.nb
步骤 6:(可选)在代码中更改 SD 文件名
编辑 SD_Model.cpp,路径为:
Arduino15\packages\realtek\hardware\<Ameba IC>\<VERSION>\libraries\NeuralNetwork\src\SD_Model.cpp
static void *yolov4_get_SD_filename(void)
{
return (void *)"sd:/NN_MDL/yolov4_tiny.nb"; // update filename here
}
步骤 7:编译、上传并运行
点击 上传,然后在按下复位按钮之前插入 SD 卡。
默认支持的模型文件名
分类 |
预期文件名 |
|---|---|
物体检测 |
yolov3_tiny.nbyolov4_tiny.nbyolov7_tiny.nb |
人脸检测 |
scrfd_500m_bnkps_640x640_u8.nbscrfd_500m_bnkps_576x320_u8.nb |
人脸识别 |
mobilefacenet_int8.nbmobilefacenet_int16.nb |
音频分类 |
yamnet_fp16.nbyamnet_s_hybrid.nb |
图像分类 |
mobilenetv2_int16.nbimg_class_cnn.nb |
手势识别 |
palm_detection_lite_int16.nbhand_landmark_lite_int16.nb |
备注
对于 SD 卡加载,所有文件必须放置在 SD 卡
NN_MDL/目录下,路径格式为sd:/NN_MDL/+ 文件名 +.nb对于 Flash 加载(Arduino SDK),文件名必须完全匹配并放置在 NeuralNetwork 示例文件夹中。
同时运行物体检测和人脸检测时,使用
scrfd_500m_bnkps_576x320_u8.nb搭配yolov4_tiny_576x320.nb(相同分辨率)。为获得更好的人脸识别精度,推荐使用
mobilefacenet_int16.nb而非 int8 版本。
FreeRTOS SDK 定制模型部署指南
本指南说明如何在 Ameba IC 上使用 FreeRTOS SDK 部署自定义的 NN 模型( .nb 文件)。
相关说明基于 mmf2_video_example_vipnn_rtsp_init YOLOv4 示例。
关键文件
文件 |
用途 |
|---|---|
|
控制 Flash 与 SD 加载方式 |
|
包含 USER_LOAD_MODEL 块的主示例 |
|
YOLO 模型的前/后处理 |
从 Flash 加载模型
步骤 1:重命名 .nb 文件
将自定义的 .nb 文件重命名为预期的文件名。有关预期的文件名列表,请参考
Arduino 部署指南中的 默认支持的模型文件名 章节。
步骤 2:将 .nb 复制到 SDK 中
将模型二进制文件放置到以下路径:
project/realtek_<ameba_ic>_v0_example/src/test_model/
└── model_nb/
└── yolov4_tiny.nb ← 替换为您的自定义模型
步骤 3:确认 MODEL_SRC 为 Flash(默认)
在 component/file_system/nn/nn_file_op.c 中:
#define MODEL_SRC MODEL_FROM_FLASH // 默认值,无需修改
步骤 4:在 FWFS JSON 中注册模型
编辑 project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/mp/<ameba_ic>_fwfs_nn_models.json:
{
"msg_level": 3,
"PROFILE": ["FWFS"],
"FWFS": {
"files": ["MODEL0"]
},
"MODEL0": {
"name": "yolov4_tiny.nb",
"source": "binary",
"file": "yolov4_tiny.nb"
}
}
备注
仅列出您实际使用的模型——未使用的模型会增加最终固件的体积。
步骤 5:配置示例
在 mmf2_video_example_vipnn_rtsp_init.c 中,设置模型和输入分辨率:
#define YOLO_MODEL 1
#define USE_NN_MODEL YOLO_MODEL
#if (USE_NN_MODEL == YOLO_MODEL)
#define NN_WIDTH 416
#define NN_HEIGHT 416
static float nn_confidence_thresh = 0.4;
static float nn_nms_thresh = 0.3;
#endif
在 video_example_media_framework.c 中启用示例:
mmf2_video_example_vipnn_rtsp_init();
步骤 6:编译
cmake .. -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -DVIDEO_EXAMPLE=ON
cmake --build . --target flash_nn
输出:flash_ntz.nn.bin 位于 GCC-RELEASE/build/
步骤 7:烧录到开发板
uartfwburn.exe -p COM? -f flash_ntz.nn.bin -b 3000000 -U -x 32
./uartfwburn.linux -p /dev/ttyUSB? -f ./flash_ntz.nn.bin -b 3000000 -U -x 32
从 SD 卡加载模型
步骤 1:重命名 .nb 文件
将自定义的 .nb 文件重命名为预期的文件名。
步骤 2:准备 SD 卡
SD card root/
└── NN_MDL/
└── yolov4_tiny.nb
步骤 3:将 MODEL_SRC 设置为 SD
在 component/file_system/nn/nn_file_op.c 中:
#define MODEL_FROM_FLASH 0x01
#define MODEL_FROM_SD 0x02
#define MODEL_SRC MODEL_FROM_SD
步骤 4:在示例中启用 USER_LOAD_MODEL
在 mmf2_video_example_vipnn_rtsp_init.c 中,将 USER_LOAD_MODEL 设置为 1
并定义模型对象:
#define USER_LOAD_MODEL 1
#include "vfs.h"
static void *example_get_model_name(void)
{
return (void *)"sd:/NN_MDL/yolov4_tiny.nb";
}
extern void yolov4_set_network_init_info(void *m);
extern int yolo_preprocess(void *data_in, nn_data_param_t *data_param, void *tensor_in, nn_tensor_param_t *tensor_param);
extern int yolo_postprocess(void *tensor_out, nn_tensor_param_t *param, void *res);
extern void yolo_set_confidence_thresh(void *confidence_thresh);
extern void yolo_set_nms_thresh(void *nms_thresh);
nnmodel_t yolov4_tiny_from_sd = {
.nb = example_get_model_name,
.set_init_info = yolov4_set_network_init_info,
.preprocess = yolo_preprocess,
.postprocess = yolo_postprocess,
.model_src = MODEL_SRC_FILE,
.set_confidence_thresh = yolo_set_confidence_thresh,
.set_nms_thresh = yolo_set_nms_thresh,
.name = "YOLOv4t_SD"
};
在 video_example_media_framework.c 中启用示例:
mmf2_video_example_vipnn_rtsp_init();
步骤 5:编译
cmake .. -G\"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -DVIDEO_EXAMPLE=ON
cmake --build . --target flash_nn
步骤 6:烧录并运行
uartfwburn.exe -p COM? -f flash_ntz.nn.bin -b 3000000 -U -x 32
./uartfwburn.linux -p /dev/ttyUSB? -f ./flash_ntz.nn.bin -b 3000000 -U -x 32
然后,在按下复位按钮之前插入 SD 卡。
進階新模型部署指南
本指南涵盖在 Ameba IC 上部署定制化神经网络模型的高级主题,包括 SDK 配置、内存评估、预处理节点、模型名称修改、模型安全性以及后处理 PC 开发工具。
定制化神经网络模型的 SDK 配置
将模型转换为 .nb 格式后,下一步是将其添加到 SDK 中,并实现必要的预处理和后处理。
SDK 中的神经网络相关文件
以下目录和文件与 RTL8735B SDK 中的 AI 模型部署相关:
<Ameba_ic>_SDK/
|-- component/media/mmfv2/ --> 多媒体模块 (mmf)
| |-- module_vipnn.c --> 调用 viplite 驱动 API 部署神经网络模型并触发推理
| |-- module_vipnn.h
|-- project/realtek_<ameba_ic>_v0_example/src/test_model/ --> 测试神经网络模型和数据处理
| |-- model_yolo.c --> YOLO 系列模型的预处理和后处理
| |-- model_yolo.h
| |-- model_nb/ --> 模型二进制文件
| |-- yolov3_tiny.nb
| |-- yolov4_tiny.nb
|-- project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/mp/
| |-- <ameba_ic>_partitiontable.json --> 闪存分区表,为神经网络模型分配区域
| |-- <ameba_ic>_fwfs_nn_models.json --> 选择要包含在最终固件中的模型
|-- project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/application/
| |-- <rtl_ic>_ram.ld --> 链接脚本,为神经网络设置 DDR 内存空间
|-- component/file_system/nn/
|-- nn_file_op.c --> viplite 驱动神经网络文件操作层(闪存或 SD 加载)
将定制化模型网络二进制文件添加到 SDK
生成 AmebaNet.nb 后,将其添加到 SDK:
project/realtek_<ameba_ic>_v0_example/src/test_model/
|-- model_nb/
| |-- yolov3_tiny.nb
| |-- yolov4_tiny.nb
| |-- yolov7_tiny.nb
| |-- AmebaNet.nb
|-- model_yolo.c
|-- model_yolo.h
|-- model_AmebaNet.c --> AmebaNet 的预处理和后处理实现
|-- model_AmebaNet.h
备注
记得将你的 model_AmebaNet.c 添加到
project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/application/application.cmake。
同时检查闪存和 DDR 配置是否满足你的模型需求。
接下来,在
project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/mp/<ameba_ic>_fwfs_nn_models.json
中注册模型:
{
"msg_level": 3,
"PROFILE": ["FWFS"],
"FWFS": {
"files": ["MODEL0", "MODEL1"]
},
"MODEL0": {
"name": "yolov4_tiny.nb",
"source": "binary",
"file": "yolov4_tiny.nb"
},
"MODEL1": {
"name": "AmebaNet.nb",
"source": "binary",
"file": "AmebaNet.nb"
}
}
备注
在 FWFS → files 中只选择你实际使用的模型。包含
未使用的模型会使最终的固件体积不必要地增大。
为 VIPNN 模块创建模型对象
vipnn 模块使用模型对象( nnmodel_t)来部署模型、
运行预处理、触发推理以及运行后处理。
在 model_AmebaNet.c 中创建该对象:
nnmodel_t AmebaNet = {
.nb = AmebaNet_get_network_filename,
.preprocess = AmebaNet_preprocess,
.postprocess = AmebaNet_postprocess,
.model_src = MODEL_SRC_FILE,
.name = "AmebaNet"
};
设置模型文件名:
void *AmebaNet_get_network_filename(void)
{
return (void *) "NN_MDL/AmebaNet.nb";
}
备注
神经网络驱动程序默认使用固件文件系统( component/file_system/fwfs)
从闪存中打开和读取模型。详情请参阅
component/file_system/nn/nn_file_op.c。
实现预处理:
int AmebaNet_preprocess(void *data_in, nn_data_param_t *data_param,
void *tensor_in, nn_tensor_param_t *tensor_param)
{
void **tensor = (void **)tensor_in;
// do pre-process here, user can refer to model_yolo.c
(uint8_t *)data_in;
(uint8_t *)tensor[0];
(uint8_t *)tensor[1];
// clean cache since data will be accessed by NN engine directly
dcache_clean_by_addr((uint32_t *)tensor[0], data_length);
return 0;
}
实现后处理:
int AmebaNet_postprocess(void *tensor_out, nn_tensor_param_t *param, void *res)
{
void **tensor = (void **)tensor_out;
int output_count = param->count;
// decode tensor data, user can refer to model_yolo.c
for (int n = 0; n < output_count; n++) {
(uint8_t *)tensor[n];
}
// fill result
int od_num = 0;
objdetect_res_t *od_res = (objdetect_res_t *)res;
for (int i = 0; i < box_idx; i++) {
box_t *obj = &res_box[i];
if (obj->invalid == 0) {
od_res[od_num].result[0] = obj->class_idx;
od_res[od_num].result[1] = obj->prob;
od_res[od_num].result[2] = obj->x;
od_res[od_num].result[3] = obj->y;
od_res[od_num].result[4] = obj->x + obj->w;
od_res[od_num].result[5] = obj->y + obj->h;
od_num++;
}
}
return od_num;
}
神经网络内存与闪存使用评估
评估模型大小,以确保 DDR 内存和闪存空间充足。
Category |
Model |
Input size |
Quantized |
DDR memory |
File size |
|---|---|---|---|---|---|
Object detection |
Yolov3-tiny
Yolov4-tiny
Yolov4-tiny
Yolov7-tiny
NanoDet-Plus-m
NanoDet-Plus-m
|
416x416
416x416
576x320
416x416
416x416
576x320
|
uint8
uint8
uint8
uint8
uint8
uint8
|
6.9 MB (6,946,128 bytes)
7.7 MB (7,712,412 bytes)
7.48 MB (7,840,836 bytes)
8.2 MB (8,597,072 bytes)
4.33 MB (4,542,016 bytes)
4.53 MB (4,746,556 bytes)
|
5.6 MB (5,568,384 bytes)
4.1 MB (4,131,712 bytes)
3.85 MB (4,043,136 bytes)
4.44 MB (4,664,512 bytes)
1.86 MB (1,959,040 bytes)
1.83 MB (1,924,096 bytes)
|
Face detection |
SCRFD
SCRFD
|
640x640
576x320
|
uint8
uint8
|
4.1 MB (4,291,200 bytes)
2.6 MB (2,753,864 bytes)
|
0.68 MB (715,584 bytes)
0.56 MB (583,232 bytes)
|
Face Recognition |
MobileFaceNet
MobileFaceNet
|
112x112
112x112
|
int8
int16
|
1.72 MB (1,799,716 bytes)
5.1 MB (5,343,948 bytes)
|
0.86 MB (904,576 bytes)
3.42MB (3,590,656 bytes)
|
Sound classification |
YAMNet
YAMNet_s
|
15600x1
96x64
|
fp16
hybrid
|
9.2 MB (9,172,348 bytes)
0.73 MB (729,608 bytes)
|
8.7 MB (8,669,888 bytes)
0.67 MB (678,336 bytes)
|
使用 NBinfo 评估内存使用
使用 Verisilicon_SW_VIP_NBInfo 在 PC 上评估 DDR 内存使用情况:
********************************************************************************
Memory Info
********************************************************************************
Total Read Only Memory (bytes): 3737536
Total Command buffer (bytes): 167552
Total Load States (bytes): 34176
Total NN and TP instruction (bytes): 132864
Total PPU instruction (bytes): 512
********************************************************************************
Total Operation Memory (bytes): 3939264
Total Input Memory (bytes): 519168
Total Output Memory (bytes): 215552
Memory Pool (bytes): 2769920
Video memory heap node reserved (bytes): 20480
********************************************************************************
Total Video Memory (bytes): 7464448
Total System Memory (bytes): 247964
********************************************************************************
确保链接脚本中的神经网络 DDR 区域足够大。检查并修改:
project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/application/<rtl_ic>_ram.ld
/* DDR memory */
VOE (rwx) : ORIGIN = 0x70000000, LENGTH = 0x70100000 - 0x70000000 /* 1MB */
DDR (rwx) : ORIGIN = 0x70100000, LENGTH = 0x73000000 - 0x70100000 /* 49MB */
NN (rwx) : ORIGIN = 0x73000000, LENGTH = 0x74000000 - 0x73000000 /* 16MB */
备注
同时修改 bootloader 目录中的 <rtl_ic>_boot_mp.ld 以保持
神经网络区域一致。对于 TrustZone 项目,请修改
<rtl_ic>_ram_ns.ld。
评估闪存上的模型大小
确保分区表中的神经网络区域大于你的模型大小。 对于单个模型(例如,为 4MB 的 yolov4-tiny 分配 7MB):
"nn": {
"start_addr" : "0x770000",
"length" : "0x700000",
"type": "PT_NN_MDL",
"valid": true
}
对于多个模型,将所有模型大小相加并分配足够的闪存空间。
例如,如果您要部署 4 个模型:yolov4-tiny、yamnet-s、mobilefacenet 和 centerface,则 "<ameba_ic>_fwfs_nn_models.json" 的内容将变为:
{
"msg_level":3,
"PROFILE":["FWFS"],
"FWFS":{
"files":[
"MODEL0",
"MODEL1",
"MODEL2",
"MODEL3"
]
},
"MODEL0":{
"name" : "yolov4_tiny.nb",
"source":"binary",
"file":"yolov4_tiny.nb"
},
"MODEL1":{
"name" : "yamnet_s.nb",
"source":"binary",
"file":"yamnet_s.nb"
},
"MODEL2":{
"name" : "mobilefacenet_int16.nb",
"source":"binary",
"file":"mobilefacenet_int16.nb"
},
"MODEL3":{
"name" : "centerface_uint8.nb",
"source":"binary",
"file":"centerface_uint8.nb"
}
}
检查每个模型的大小,并计算总大小: 1,535KB + 3,507KB + 663KB + 4,053KB = 9,740KB. 因此,NN 至少需要 10MB 的 Flash 空间。
模型网络 NB 文件大小
因此,以下文件中的 nn 区域长度:
"project\realtek_<ameba_ic>_v0_example\GCC-RELEASE\mp\<ameba_ic>_partitiontable.json"
应不小于 10MB。
"nn":{
"start_addr" : "0x770000",
"length" : "0xA00000", --> 10MB > total size(9,740KB)
"type": "PT_NN_MDL",
"valid": true
},
如何添加预处理节点(可选)
有时你需要在推理之前执行数据预处理。Acuity Toolkit 可以自动生成一个在神经网络引擎上运行的预处理节点, 从而减轻 CPU 的负担。该节点负责颜色空间转换、缩放和裁剪。
通过配置``inputmeta.yml`` 来启用它:
input_meta:
databases:
- path: dataset.txt
type: TEXT
ports:
- lid: input.1_137
category: image
dtype: float32
sparse: false
tensor_name:
layout: nchw
shape:
- 1
- 3
- 320
- 576
fitting: scale
preprocess:
reverse_channel: false
mean:
- 127.5
- 127.5
- 127.5
scale: 0.0078125
preproc_node_params:
add_preproc_node: true
preproc_type: IMAGE_NV12
preproc_image_size:
- 576
- 320
备注
此功能需要 Acuity 6.18.8 和 VIPLite 驱动 1.12.0 或 更新版本。较旧版本不完全支持此功能。
如何在转换后修改定制化模型名称(可选)
网络二进制图格式在偏移量 12 字节处存储网络名称, 长度为 64 字节。
Section |
Field |
Data Type |
Count |
Size in Bytes |
Meaning |
Header |
Magic ... ... Network_name ... |
CHAR UINT32 UINT32 CHAR UINT32 |
4 1 1 64 1 |
4 4 4 64 4 |
Must be "VPMN" for a valid binary graph file ... ... Indicates the name of a network ... |
你可以使用任何十六进制编辑器编辑这 64 个字节。
该名称在运行时通过以下方式在 module_vipnn.c 查询:
vip_query_network(ctx->network, VIP_NETWORK_PROP_NETWORK_NAME, ctx->network_name);
dprintf(LOG_INF, "network name:%s\n\r", ctx->network_name);
修改后,你应该会看到以下日志,
注意: 用户可能需要将调试日志级别更改为 LOG_INF,才能在控制台中看到此信息。
模型名称
YOLOv9-tiny 部署指南
SDK 配置自定义 YOLOv9-tiny 模型
上一节已获取模型二进制文件 (.nb)。本节将介绍如何将该模型二进制文件添加到 SDK, 并实现必要的前处理和后处理。
SDK 中与 NN 相关的文件
以下是 Ameba SDK 中与 AI 模型部署相关的目录和文件:
Ameba_SDK/
|-- component/media/mmfv2/ --> 多媒体模块 (mmf: multi-media framework)
|-- module_vipnn.c --> 调用 viplite 驱动 API 部署 NN 模型并触发推理的模块
|-- module_vipnn.h
|-- project/realtek_<ameba_ic>_v0_example/src/test_model/ --> 提供 NN 测试模型及其数据处理
|-- model_yolov9.c --> yolov9 模型的前处理与后处理实现
|-- model_yolov9.h
|-- model_nb/ --> 模型二进制文件所在文件夹
|-- yolov9_tiny.nb --> yolov9-tiny 模型二进制文件
|-- project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/mp/
|-- <ameba_ic>_partitiontable.json --> 闪存分区表,需为 NN 模型安排合适的区域
|-- <ameba_ic>_fwfs_nn_models.json --> 选择使用的模型,这些模型将被合并到最终固件中
|-- project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/application/
|-- rtl8735b_ram.ld --> 链接脚本,为 NN 设置足够的 DDR 内存空间
|-- component/file_system/nn/
|-- nn_file_op.c --> viplite 驱动通过此 "nn 文件操作层" 从闪存或 SD 卡加载模型(默认:闪存文件系统)
将自定义模型网络二进制文件添加到 SDK
生成 yolov9_tiny.nb 后,请将该文件添加到 SDK 文件夹:
project/realtek_<ameba_ic>_v0_example/src/test_model/model_nb。所有
模型网络二进制文件都将放置在此处,结构如下:
project/realtek_<ameba_ic>_v0_example/src/test_model/
|-- model_nb/
| |-- yolov3_tiny.nb --> yolov3-tiny 网络二进制图文件
| |-- yolov4_tiny.nb --> yolov4-tiny 网络二进制图文件
| |-- yolov7_tiny.nb --> yolov7-tiny 网络二进制图文件
| |-- yolov9_tiny.nb --> yolov9-tiny 网络二进制图文件
|-- model_yolo.c
|-- model_yolo.h
|-- model_yolov9.c --> yolov9 前处理与后处理实现
|-- model_yolov9.h
备注
请记得将您的 model_yolov9.c 添加到 project/realtek_<ameba_ic>_v0_example/scenario.cmake。同时,请检查闪存大小和 DDR 大小的配置是否足以容纳 NN 模型。请参考 進階新模型部署指南 中的"评估模型在闪存中的大小"章节进行评估。
接下来,将模型添加到模型列表。
进入
project/realtek_<ameba_ic>_v0_example/GCC-RELEASE/mp/<ameba_ic>_fwfs_nn_models.json
并将 yolov9_tiny.nb 添加到此列表:
{
"msg_level":3,
"PROFILE":["FWFS"],
"FWFS":{
"files":[
"MODEL0",
"MODEL1"
]
},
"MODEL0":{
"name" : "yolov4_tiny.nb",
"source":"binary",
"file":"yolov4_tiny.nb"
},
"MODEL1":{
"name" : "yolov9_tiny.nb",
"source":"binary",
"file": "yolov9_tiny.nb"
}
}
备注
如果您只想使用 yolov9_tiny.nb,请在 "FWFS"-"files" 中仅选择 "MODEL1"。否则,最终固件将因包含一些未使用的模型二进制文件而变得非常庞大。
创建可供 VIPNN 模块使用的模型对象
vipnn 模块将使用模型对象来部署模型、进行模型前处理、触发模型推理以及进行模型后处理。
因此,请在 model_yolov9.c 中创建 nnmodel_t yolov9_tiny。
以下是 VIPNN 模块将使用的必要函数,
请在完成实现后将这些函数指针注册到 yolov9_tiny 对象中:
nnmodel_t yolov9_tiny = {
.nb = yolov9_get_network_filename,
.preprocess = yolov9_preprocess,
.postprocess = yolov9_postprocess,
.model_src = MODEL_SRC_FILE,
.name = "YOLOv9t"
};
设置 NN 驱动使用的模型文件名
需要设置模型名称,以便 NN 驱动在运行时部署时能够通过文件系统打开并加载 网络二进制文件。
void *yolov9_get_network_filename(void)
{
return (void *) "NN_MDL/yolov9_tiny.nb";
}
备注
NN 驱动默认使用固件文件系统 (component/file_system/fwfs) 从闪存中打开并读取模型。更多信息,请参考 NN 驱动使用的 "nn 文件操作层" – component/file_system/nn/nn_file_op.c。
通过 NN 驱动设置 NN 模型的 desired_class
由于 yolov9 的输出特性,算法的运行速度取决于待检测目标的数量(3549 个锚点和 80 个类别),这意味着 无需检测全部 80 个类别,只需查找所需类别的相关信息即可。 因此,仅输入所需类别数组以减少后处理函数的运行时间。
要在 VIPNN 模块中注册所需类别,请在 module_vipnn.c 和 module_vipnn.h 中创建 nn_desired_class_t。
以下是将添加到 VIPNN 模块中的步骤:
在 module_vipnn.h 中
定义结构体,并在结构体
nnmodel_t中添加nn_desired_class_t定义
set_desired_class函数,并在nnmodel_t结构体中添加nn_set_desired_class_t
typedef struct nn_desired_class_s {
int *class_info;
int len;
} nn_desired_class_t;
typedef void (*nn_set_desired_class_t)(nn_desired_class_t *desired_class_list);
typedef struct nnmodel_s {
...
nn_set_confidence_thresh_t set_confidence_thresh;
nn_set_nms_thresh_t set_nms_thresh;
nn_set_desired_class_t set_desired_class;
...
} nnmodel_t;
- 在 module_vipnn.c 中
定义注册 vipnn 的情况
case CMD_VIPNN_SET_DESIRED_CLASS:
if (ctx->params.model->set_desired_class) {
ctx->params.model->set_desired_class((nn_desired_class_t *)arg);
}
break;
在 model_yolov9.c 中
为 yolov9 设置所需类别
void yolov9_set_desired_class(nn_desired_class_t *desired_class_list)
{
yolov9_desired_class_list_len = desired_class_list->len;
yolov9_desired_class_list = desired_class_list->class_info;
}
- 在 mmf2_video_example_vipnn_rtsp_init.c 中
指定目标类别并通过 vipnn 模块注册
static int desired_class_list[] = {0, 2, 5, 7};
static const int class_size = (sizeof(desired_class_list) / sizeof(int));
static nn_desired_class_t desired_class_param = {
.class_info = desired_class_list,
.len = class_size
};
mm_module_ctrl(vipnn_ctx, CMD_VIPNN_SET_DESIRED_CLASS, (int)&desired_class_param);
实现自定义前处理和后处理
用户可以在将图像传递给 NN 模型推理之前进行自定义前处理; 此外,用户还可以进行自定义后处理,以解码推理结果中的输出张量。
在 model_yolov9.c 中实现前处理:
int yolov9_preprocess(void *data_in, nn_data_param_t *data_param, void *tensor_in, nn_tensor_param_t *tensor_param)
{
void **tensor = (void **)tensor_in;
//在此处进行前处理,用户可参考 model_yolo.c
(uint8_t *)data_in;
(uint8_t *)tensor[0];
//…
//清理缓存,因为数据将由 NN 引擎直接访问
dcache_clean_by_addr((uint32_t *)tensor[0], data_length);
return 0;
}
在 model_yolov9.c 中实现后处理:
yolov9 张量输出格式
yolov9_tiny 的锚点数量为 3549,类别数量为 80,yolov9_tiny 的输出包括 center_x、center_y、width、height 以及每个类别的得分(共 80 个类别)
cx, cy, ow, oh: center_x, center_y, width, height
p1: 类别 1 的概率,以此类推
下标: 锚点编号,例如 p2_1 表示锚点 1 中类别 2 的得分,p80_3500 表示锚点 3500 中类别 80 的得分
int yolov9_postprocess(void *tensor_out, nn_tensor_param_t *param, void *res)
{
void **tensor = (void **)tensor_out;
for(int idx=0; i < num_anchor; idx++){
int cur_label = yolov9_desired_class_list[0]*num_anchor;
uint8_t *tmp_pred_u8 = (uint8_t *)preds + idx;
//该算法执行更快,因为只搜索 desired_class_list
for(int i=0; i < yolov9_desired_class_list_len; i++){
if (tmp_pred_u8[yolov9_desired_class_list[i]*num_anchor] > tmp_pred_u8[cur_label]) {
cur_label = yolov9_desired_class_list[i]*num_anchor;
}
}
//该算法执行较慢,因为搜索全部 80 个类别
for (int label = 0; label < num_class; label++) {
if (tmp_pred_u8[label*num_anchor] > tmp_pred_u8[cur_label]) {
cur_label = label*num_anchor;
}
}
}
}
模型安全
部分客户拥有内部自训练的模型需要保护。 SDK 支持 模型认证 和 模型加密 功能, 以保护知识产权。
模型图二进制认证(完整性 + 可信度)
模型图二进制加密(机密性)
认证和解密由加密硬件加速器处理。用于解密的密钥存储在芯片内 eFuse OTP 中。
安全特性 |
支持的算法 |
密钥管理 |
|---|---|---|
模型认证 |
Hash: sha256
Signature: EdDSA_ED25519
|
使用私钥在 PC 或服务器上签名模型。 使用公钥验证模型签名。 使用固件签名密钥签名模型。NN 模块使用固件清单中的公钥在运行时验证签名。 注意:启用可信启动,使公钥通过信任链验证。 |
模型加密 |
AES_256_CBC |
使用 AES-256 密钥在 PC 或服务器上加密模型。 使用 "user eFuse OTP KEY 0" 加密模型。NN 模块在运行时使用此密钥解密。 注意:若无 "user eFuse OTP KEY 0",可通过 |
模型认证 — 哈希与签名检查
模型认证包括完整性检查和可信度检查。对模型签名后,
SHA-256 哈希值和 EdDSA 签名会附加在 .nb 文件末尾。
签名验证哈希的可信度,哈希验证模型的完整性。
签名后模型格式:
仅加密:
[model header (encrypted)] [rest of model] [IV]
仅签名:
[model] [hash] [signature]
签名 + 加密:
[model header (encrypted)] [rest of model] [IV] [hash] [signature]
模型加密
用户可对模型图二进制文件加密以防止解析。通常只需加密模型的前 512 字节 (固定头部)。
段 |
大小(字节) |
头部和表格 |
512(固定) |
数据段 |
动态 |
SDK 使用用户 OTP eFuse 密钥通过 AES-256-CBC 解密加密的模型头部。
备注
设备上的硬件加密引擎可加速解密过程。
安全功能的 SDK 配置
模型签名验证和解密 默认禁用。在 platform_opts.h 中启用:
/* For NN configuration */
#define CONFIG_NN_AES_ENCRYPTION 1
#define CONFIG_NN_HASH_SIGNATURE_CHECK 1
备注
这两个功能可独立启用。
安全部署流程
在 SDK 中启用 NN 解密或哈希/签名校验功能后,模型将按照如下流程进行安全部署。
NN 安全部署
签名与加密 PC 工具
工具位于:
project/realtek_<ameba_ic>_v0_example/src/test_model/model_nb/model_signature/model_sign_ed25519.py
安装所需包 (PyNaCl, PyCrypto):
$ pip install pynacl
$ pip install pycryptodome
任何密钥的格式都是一个十六进制字符串文件。例如,32 字节签名密钥(model-sign-key)的内容如下所示:
104008de9c2fed8fbb20139ea3eafb6b60e8fb8a603b488c90586e2750b7f3ae
以下是用于对模型进行签名或加密的命令用法。该工具也提供了相应的验证命令。
仅签名:
使用签名密钥对模型进行签名(ED25519 公钥)
$ python3 model_sign_ed25519.py --sign-key "model-sign-key" --model "../yolov4_tiny.nb"
使用验证密钥验证模型(ED25519 私钥)
$ python3 model_sign_ed25519.py --verify-key "model-verify-key" --signed-model "../yolov4_tiny.nb.sig"
备注
签名后,用户可以得到已签名模型 yolov4_tiny.nb.sig。用户应将该模型下载到 Flash 分区或文件系统中。
仅加密:
使用 AES 密钥对模型进行加密(AES-256-CBC 对称密钥),IV 将由工具随机生成。
$ python3 model_sign_ed25519.py --model "../yolov4_tiny.nb" --enc-key "model-enc-key"
使用相同的 AES 密钥解密模型
$ python3 model_sign_ed25519.py --signed-model "../yolov4_tiny.nb.enc" --enc-key "model-enc-key"
备注
加密后,用户可以得到已加密模型 yolov4_tiny.nb.enc。用户应将该模型下载到 Flash 分区或文件系统中。
签名并加密:
使用签名密钥和加密密钥对模型进行签名并加密
$ python3 model_sign_ed25519.py --sign-key "model-sign-key" --model "../yolov4_tiny.nb" --enc-key "model-enc-key"
使用验证密钥和加密密钥解密并验证模型签名
$ python3 model_sign_ed25519.py --verify-key "model-verify-key" --signed-model "../yolov4_tiny.nb.enc.sig" --enc-key "model-enc-key"
备注
签名并加密后,用户可以得到带签名的加密模型 yolov4_tiny.nb.enc.sig。用户应将该模型下载到 Flash 分区或文件系统中。
备注
签名后模型:
yolov4_tiny.nb.sig加密后模型:
yolov4_tiny.nb.enc签名并加密:
yolov4_tiny.nb.enc.sig
将生成的文件下载到闪存分区或文件系统中。
性能测试结果
安全特性 |
测试项 |
时间 (ms) |
备注 |
|---|---|---|---|
认证 |
签名检查 |
3 |
检查 32 字节模型哈希签名。时间固定。 |
认证 |
哈希检查 |
38 |
取决于模型大小(yolov4-tiny: 4MB)。 |
解密 |
密文解密 |
3 |
始终解密 512 字节固定头部。时间固定。 |
后处理 PC 开发工具
您可以在部署到设备之前,先在 PC 上开发和验证后处理。在 Acuity 工具包中运行推理脚本后, 您可以获取模型的输出张量并在 PC 上解码。
PC 工具和 <RTL_IC> 设备使用的后处理 API 接口相同,便于从开发过渡到部署。
步骤
步骤 1: 在 model_yolo_sim.c 中开发后处理。
步骤 2: 在 main.c 中根据 NB 文件设置张量参数:
static void yolo_pc_configure_tensor_param(nn_tensor_param_t *input_param, nn_tensor_param_t *output_param)
{
char *nbg_filename = "../../test_model/model_nb/yolov4_tiny.nb";
config_param_from_nb_file(nbg_filename, input_param, output_param);
}
int yolo_simulation(void)
{
nn_tensor_param_t input_param, output_param;
yolo_pc_configure_tensor_param(&input_param, &output_param);
// ...
}
步骤 3: 从 Acuity 推理获取输出张量并设置文件路径:
int yolo_simulation(void)
{
// ...
char *acuity_tensor_name[16];
acuity_tensor_name[0] = "../data/yolo_data/iter_0_output_30_65_out0_1_255_13_13.tensor";
acuity_tensor_name[1] = "../data/yolo_data/iter_0_output_37_76_out0_1_255_26_26.tensor";
void *pp_tensor_out[16];
memset(pp_tensor_out, 0, sizeof(pp_tensor_out));
acuity_output_tensor_conversion(acuity_tensor_name, pp_tensor_out, &output_param);
// ...
}
步骤 4: 编译:
mkdir build && cd build
cmake .. -G"Unix Makefiles"
make -j4
步骤 5: 执行:
./nn_postprocess
步骤 6: 检查结果。带有边界框的图像将保存在
data/yolo_data/prediction.jpg。
检查结果
附录 A:Acuity 支持的操作层
ONNX 到 ACUITY 操作映射
ONNX Operation |
ACUITY Operation |
|---|---|
Abs |
abs |
Add |
add |
And |
logical_and |
ArgMax |
argmax |
ArgMin |
argmin |
Atan |
atan |
Atanh |
atanh |
BatchNormalization |
batchnormalize |
Cast |
cast |
CastLike |
cast |
Ceil |
ceil |
Celu |
celu |
Clip |
clipbyvalue |
Concat |
concat |
Conv |
conv1d/group_conv1d/depthwise_conv1d/convolution/conv2d_op/depthwise_conv2d_op/conv3d |
ConvTranspose |
deconvolution/deconvolution1d |
Cos |
cos |
Cumsum |
cumsum |
DepthToSpace |
depth2space |
DequantizeLinear |
dequantize |
DFT |
dft |
Div |
divide |
Dropout |
dropout |
Einsum |
einsum |
Elu |
elu |
Equal |
equal |
Erf |
erf |
Exp |
exp |
Expand |
expand_broadcast |
Floor |
floor |
Gather |
gather |
GatherElements |
gather_elements |
GatherND |
gathernd |
Gemm |
matmul/fullconnect |
Greater |
greater |
GreaterOrEqual |
greater_equal |
GridSample |
gridsample |
GRU |
gru |
HammingWindow |
hammingwindow |
HannWindow |
hannwindow |
HardSigmoid |
hard_sigmoid |
HardSwish |
hard_swish |
InstanceNormalization |
instancenormalize |
LeakyRelu |
leakyrelu |
Less |
less |
LessOrEqual |
less_equal |
Log |
log |
Logsoftmax |
log_softmax |
LRN |
localresponsenormalization |
LSTM |
lstm |
MatMul |
matmul/fullconnect |
Max |
eltwise(MAX) |
MaxPool/AveragePool/GlobalAveragePool/GlobalMaxPool |
pooling/pool1d/pool3d |
MaxRoiPool |
roipooling |
Mean |
eltwise(MEAN) |
MeanVarianceNormalization |
instancenormalize |
Min |
eltwise(MIN) |
Mish |
mish |
Mod |
mod |
Mul |
multiply |
Neg |
neg |
NonZero |
nonzero |
OneHot |
onehot |
Or |
logical_or |
Pad |
pad |
Pow |
pow |
Prelu |
prelu |
QLinearConv |
convolution/conv1d |
QLinearMatMul |
matmul |
QuantizeLinear |
quantize |
Reciprocal |
variable+divide |
ReduceL1 |
abs+reducesum |
ReduceL2 |
reducesum+multiply+sqrt |
ReduceLogSum |
reducesum+log |
ReduceLogSumExp |
exp+reducesum+log |
ReduceMax |
reducemax |
ReduceMean |
reducemean |
ReduceMin |
reducemin |
ReduceProd |
reduceprod |
ReduceSum |
reducesum |
ReduceSumSquare |
multiply+reducesum |
Relu |
relu |
Reshape/Squeeze/Unsqueeze/Flatten |
reshape |
Resize |
image_resize |
ReverseSequence |
reverse_sequence |
Round |
round |
ScatterND |
scatter_nd_update |
Selu |
selu |
Shape |
shapelayer |
Sigmoid |
sigmoid |
Sign |
sign |
Silu |
swish |
Sin |
sin |
Size |
size |
Slice |
slice/stridedslice |
Softmax |
softmax |
Softplus |
softrelu |
Softsign |
abs+add+divide+variable |
SpaceToDepth |
space2depth |
Split |
split/slice |
Sqrt |
sqrt |
Squeeze |
squeeze |
STFT |
stft |
Sub |
subtract |
Sum |
eltwise(SUM) |
Tanh |
tanh |
Tile |
tile |
TopK |
topk |
Transpose |
permute |
Unsqueeze |
reshape |
Upsample |
image_resize |
Where |
where |
Xor |
not_equal |
Darknet 到 ACUITY 操作映射
Darknet Operation |
ACUITY Operation |
|---|---|
avgpool |
pooling |
batch_normalize |
batchnormalize |
connected |
fullconnect |
convolutional |
convolution |
depthwise_convolutional |
convolution |
leaky |
leakyrelu |
logistic |
sigmoid |
maxpool |
pooling |
mish |
mish |
region |
region |
relu |
relu |
reorg |
reorg |
route |
concat/slice |
scale_channels |
multiply |
shortcut |
add/slice+add/pad+add |
softmax |
softmax |
swish |
swish |
upsample |
upsampling |
yolo |
yolo |
附录 B:Acuity 支持的 AI 框架
AI Framework |
Import File Format |
|---|---|
Caffe |
.caffemodel |
TensorFlow |
.pb |
TensorFlow Lite |
.tflite |
Darknet |
.cfg |
ONNX |
.onnx |
PyTorch |
.pt |
Keras |
.h5 |
备注
对于从已量化的 ONNX、TensorFlow 或 TensorFlow Lite 模型转换而来的 Acuity 网络,无需再进行量化。 逐通道 量化模型 不受 NPU 支持——请确保您的模型使用 逐张量 量化。
小技巧
对于 PyTorch 框架,强烈建议先将文件导出为 .onnx 格式,以确保转换成功。
参考:ACUITY Toolkit 用户指南
NN MMF 示例
请参阅 应用笔记 了解关于结合 VIPNN 模块使用 NN MMF 示例的说明。