NPU模型部署指南*
本文主要讲解 软件工程师角色 如何把 算法工程师角色 训练出来的模型部署在SDK上。适用于第三方算法公司阅读。
- 算法工程师角色: 训练模型
- 软件工程师角色: 部署模型
提醒
一般来说,公司的算法工程师负责训练,软件工程师负责部署,当然也有牛逼的工程师既负责训练也负责部署。
1. NPU模型编译[算法工程师角色需要关注]*
- 阅读 NPU编译器使用 将 NPU模型 编译出来,发布给 软件工程师角色。
2. NPU模型格式说明[后面都是软件工程师角色需要关注]*
- 软件工程师拿到 NPU模型 后,需要先了解下 NPU模型的格式说明
3. 模型包的生成指南*
- 我们提供了一个 自动化工具 来生成模型包。路径在 lvp_tws/tools/auto_model/。使用指南如下:
3.1 了解自动化工具*
- 本工具用于协助部署kws模型到LVP工程
- 使用本工具需要用到如下文件:
- NPU模型文件: 如:vp_tws/tools/auto_model/example_npu_model.c
- 由算法工程师发布
- 自动部署配置文件:
- 默认为 config.json,可通过 -c 参数指定其他路径。
- 该文件为json文件,格式如下
{ "model_name": "user_model", "version": "v0.1.0", "source_path": "./example_npu_model.c", "support_SoftMax": "Y", "normal_ctc": "Y", "kws_list_path": "./kws.txt", "new_model": "Y", "input_stride": 4, "decoder": "user" }
- model_name: 模型部署名称,根据模型应用需求配置。如果是以部署模型的迭代,需保持与已有模型的命名一致。如有需要,使用下划线做分割。如:user_model。
- version: 模型发布版本,从模型发布信息中获取。格式为字母v开头三段点分数字,如:v0.1.0。
- source_path: 模型文件路径,在当前目录下可使用相对路径,非当前路径需使用绝对路径。
- support_SoftMax: 模型是否支持SoftMax,从模型发布信息中获取,支持时设为 Y,否则设为 N。注:没有额外说明时,设为Y。
- normal_ctc: 是否使用normal_ctc,根据发难需求设置,需要时设为 Y, 否则设为 N。注:没有额外说明时,设为Y。
- kws_list_path: 关键词列表文件路径。
- new_model: 新模型部署时设为Y,旧模型迭代部署时设为N。因为旧模型迭代时有部分文件不需要生成。
- input_stride: 模型输入步进,从模型发布信息中获取。该参数与编译配置中"PCM Frame Number in a Context"选项相对应。
- decoder: user 表示第三方客户算法模型。
注意
红色部分是必填项,其它字段第三方算法公司不用关心。
- NPU模型文件: 如:vp_tws/tools/auto_model/example_npu_model.c
3.2 利用自动化工具生成模型包*
-
按照上一节将配置文件配置好后,运行 auto_model,即可生成模型包
$ ./auto_model
-
部署
- 程序执行成功后将在 ./output 中生成 lvp/ 和 simulate/ 两个文件夹
- lvp/ 和 simulate/ 分别用于 lvp_tws/ 和 vpa_simulate/ 的模型部署
- 以 lvp/ 为例
- 其中可以看到以 model_name 命名的子文件夹
- 进入 model_name 后其中有以 version 命名的子文件夹
- 如果是新模型部署,还有另外三个文件: kws_list.h[第三方客户算法模型没有此文件], kws.name, kws_version.list
- 对于新模型部署,只需将以 model_name 命名的文件夹拷贝至 lvp_tws/lvp/vui/kws/models 中
- 对于旧模型迭代,只需将以 version 命名的文件夹拷贝至 lvp_tws/lvp/vui/kws/models/
中
- 对于 simulate/,部署路径 lvp/vpa_simulate/vpa/lvp/src/vui/kws/models/
3.3 menuconfig 选择模型包*
通过 make menuconfig ---> VUI Settings 选择,如下图:
注意
如果是 模拟器vpa_simulate那么通过 make menuconfig---> LVP Settings ---> VUI Settings.
- Keyword Deocder Type 必须选择 User Deocder
- KeyWord select 选择你要的模型包,本文选择的是 user model
重要
如果你的模型不是一个rnn网络,那么上图中的 Model Use Recurrent Neural Network 就不要勾
4. 模型包的目录结构分析*
- 本节讲解 模型包的生成指南 生成的模型后的目录结构以及所包含的文件解析:
4.1 目录结构*
lvp_tws/lvp/vui/kws/models/user_model | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
4.2 模型包之 Kconfig 解析*
lvp_tws/lvp/vui/kws/models/user_model | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
- KWS_SNPU_BUFFER_SIZE: 等于 model.h 中的 in_out 结构体的大小。每个 context 的 snpu_buffer 的大小等于KWS_SNPU_BUFFER_SIZE,可参考 lvp_tws/include/lvp_buffer.h。
- KWS_MODEL_FEATURES_DIM_PER_FRAME: 等于模型 model.h 中 npu_data_t Feats[1][15][40] 的最后一个维度大小。
- KWS_MODEL_INPUT_STRIDE_LENGTH: NPU模型每运行一次更新的帧数,等于自动化部署工具所用到的 config.json 中的 input_stride。
- KWS_MODEL_INPUT_WIN_LENGTH: NPU模型输入总帧数。
- KWS_MODEL_OUTPUT_LENGTH: NPU模型输出维度。
重要
menuconfig 中的 I/O Buffer Settings ---> ()PCM Frame Number in a Context 的值必须跟 KWS_MODEL_INPUT_STRIDE_LENGTH 保持一致
4.3 模型包之 ctc_model.c 和 model.h 解析*
- 主要对 ctc_model.c 的 API 接口进行了说明。在此前请阅读 NPU模型的格式说明。
API接口 | API接口说明 |
---|---|
int LvpModelGetOpsSize(void) | 返回模型 op 大小 |
int LvpModelGetDataSize(void) | 返回模型 data 大小 |
int LvpModelGetTmpSize(void) | 返回模型 tmp 大小 |
int LvpModelGetCmdSize(void) | 返回模型 cmd 大小 |
int LvpModelGetWeightSize(void) | 返回模型 weight 大小 |
void LvpSetSnpuTask(GX_SNPU_TASK* snpu_task) | 参数: 输入:snpu_task(snpu任务结构体:包含模型的参数信息) 输出:无 功能:将初始化的 snpu_task 拷贝到模型包的全局 s_snpu_task |
int LvpCTCModelInitSnpuTask(GX_SNPU_TASK *snpu_task) | 参数: 输入:snpu_task(OUT) 输出:0 表示成功, -1 表示失败 功能:将全局 s_snpu_task 赋值给输出参数 snpu_task |
const char *LvpCTCModelGetKwsVersion(void) | 返回模型的版本,版本在 model.h 中定义 |
void *LvpCTCModelGetSnpuOutBuffer(void *snpu_buffer) | 返回 snpu_buffer 最后一层输出地址 |
void *LvpCTCModelGetSnpuFeatsBuffer(void *snpu_buffer) | 返回 snpu_buffer 输入地址 |
void *LvpCTCModelGetSnpuStateBuffer(void *snpu_buffer) | 返回 snpu_buffer 状态地址 |
unsigned int LvpCTCModelGetSnpuFeatsDim(void) | 返回模型 特征输入(feats) 维度 |
unsigned int LvpCTCModelGetSnpuStateDim(void) | 返回模型 state 维度 |
5. 第三方模型包之解码器*
- GX8002 芯片 SDK 框架也做好了第三方模型包的解码器的 porting 接口,接口名叫 LvpDoUserDecoder。工程师只要基于此接口完成解码器的移植即可。
lvp/vui/kws/user_decoder.c | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
- output 实际就是模型的输出结果
- 一旦判断激活后,此接口只需要吧激活词的 id 赋值给 context->kws 即可
6. 模型包部署示例步骤*
6.1 生成模型包*
- lvp_tws/tools/auto_model 下默认包含一个算法工程师发布的NPU模型文件:example_npu_model.c,请阅读 利用自动化工具生成模型包 去生成模型包。
6.2 vpa_simulate 模拟器*
6.2.1 拷贝模型*
- 将生成好的跑在 vpa_simulate 模拟器上的模型包( lvp_tws/tools/auto_model/output/simulate/user_model )拷贝到 vpa_simulate/vpa/lvp/src/vui/kws/models/ 目录中去。
6.2.2 选择模型包*
- 本示例有两个方法:
- 方法一:请阅读 menuconfig 选择模型包 选择 user_model 模型包
- 方法二:使用 vpa_simulate 默认带着一个配置文件( vpa_simulate/vpa/lvp/configs/user_model_decoder.config )
6.2.3 编译、运行*
$ make menuconfig # 退出保存
$ make
$ ./output/vpa_main -f audio/tmjl.wav
audio_data/tmjl.wav
[2022-03-31 20:16:05][WAV]riff_id: RIFF
[2022-03-31 20:16:05][WAV]riff_size: 266276
[2022-03-31 20:16:05][WAV]riff_format: WAVE
[2022-03-31 20:16:05][WAV]format_id: fmt
[2022-03-31 20:16:05][WAV]format_size: 16
[2022-03-31 20:16:05][WAV]compression_code: 1
[2022-03-31 20:16:05][WAV]channels: 1
[2022-03-31 20:16:05][WAV]sample_rate: 16000
[2022-03-31 20:16:05][WAV]average_bytes_per_second: 32000
[2022-03-31 20:16:05][WAV]block_align: 2
[2022-03-31 20:16:05][WAV]bits_per_sample: 16
[2022-03-31 20:16:05][WAV]data_id: data
[2022-03-31 20:16:05][WAV]data_size: 266240
[2022-03-31 20:16:05]
[2022-03-31 20:16:05]wav_duration:8320 ms, duration:8320 ms
[2022-03-31 20:16:05][LVP_TWS]Kws Version: [user_model_v0.1.0_2022_0331]
[2022-03-31 20:16:05]Enter: ThreadRunAudioInRecoderSim, 60000
[2022-03-31 20:16:05]Enter: ThreadRunLvpSim
[2022-03-31 20:16:05][LVP_AUD]vad param 3 [0]
[2022-03-31 20:16:05][LVP_TWS]Ctx:0, Vad:1, Ns:0
moudel output: 0.000000 , 0.000157
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.011093
moudel output: 0.000068 , 0.003191
moudel output: 0.000337 , 0.033539
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.001714
moudel output: 0.000000 , 0.000815
moudel output: 0.000794 , 0.002771
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.000000
moudel output: 0.000000 , 0.000114
[2022-03-31 20:16:06][LVP_TWS]Ctx:15, Vad:1, Ns:0
6.3 lvp_tws 方案*
6.3.1 拷贝模型*
- 将生成好的跑在 lvp_tws 方案上的模型包( lvp_tws/tools/auto_model/output/lvp/user_model )拷贝到 lvp_tws/lvp/vui/kws/models/ 目录中去。
6.3.2 选择模型包*
- 本示例有两个方法:
- 方法一:请阅读 menuconfig 选择模型包 选择 user_model 模型包
- 方法二:使用 lvp_tws 默认带着一个配置文件( lvp_tws/configs/grus_user_model_decoder.config )
6.3.3 编译、烧录、运行*
- 编译:
$ make menuconfig # 退出保存 $ make
- 烧录:请阅读串口升级
- 运行:
串口打印
[LVP]Low-Power Voice Preprocess [LVP]Copyright (C) 2001-2020 NationalChip Co., Ltd [LVP]ALL RIGHTS RESERVED! [LVP]Board Model: [grus_gx8002b_dev_1v] [LVP]MCU Version: [bcd1a41] [LVP]Release Ver: [0x42555858] [LVP]Build Date : [2022-03-31, 20:21:35] [LVP]Flash vendor:[PUYA] [LVP]Flash type: [p25q40l] [LVP]Flash ID: [0x856013] [LVP]Flash size: [520192 Byte] [LVP]CPU Freq: [8192000 Hz][fix] [LVP]SRAM Freq: [8192000 Hz] [LVP]NPU Freq: [8192000 Hz] [LVP]FLASH Freq: [24576000 Hz] [LVP]Ldo Trim: [924 mV] [LVP_KWS ]Kws Use:237 ms [AB]amic [1 Channel] [AB]amic pga_gain:[24 dB] [AB]amic Ain_gain:[0 dB] [LVP_AUD]vad param 3 [0] [LVP_TWS]Ctx:0, Vad:1, Ns:0, R:0 moudel output: 0.000063 , 0.000669 moudel output: 0.000000 , 0.000540 moudel output: 0.000000 , 0.001191 moudel output: 0.000000 , 0.000000 moudel output: 0.000000 , 0.000523 moudel output: 0.000000 , 0.000000 moudel output: 0.000000 , 0.000299 moudel output: 0.000000 , 0.003605 moudel output: 0.000000 , 0.001276 moudel output: 0.000000 , 0.013138 moudel output: 0.000000 , 0.000000 moudel output: 0.000000 , 0.000138 moudel output: 0.000126 , 0.001716 moudel output: 0.000000 , 0.000000