跳转至

NPU模型部署指南*

本文主要讲解 软件工程师角色 如何把 算法工程师角色 训练出来的模型部署在SDK上。适用于第三方算法公司阅读。

  • 算法工程师角色: 训练模型
  • 软件工程师角色: 部署模型

提醒

一般来说,公司的算法工程师负责训练,软件工程师负责部署,当然也有牛逼的工程师既负责训练也负责部署。

1. NPU模型编译[算法工程师角色需要关注]*

  • 阅读 NPU编译器使用NPU模型 编译出来,发布给 软件工程师角色

2. 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 表示第三方客户算法模型。

        注意

        红色部分是必填项,其它字段第三方算法公司不用关心。

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.

VUI 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
$ tree
.
├── kws.name
├── kws_version.list
└── v0.1.0
    ├── Kconfig # 模型包的参数比如 snpu buffer size,模型的 input 帧数等信息
    ├── Makefile
    ├── ctc_model.c # 模型包的 api 接口,比如 模型初始化、模型版本、模型的输入维度、输出维度 等信息
    ├── ctc_model.h
    ├── kws_version.name
    └── model.h # 模型文件,比 example_npu_model.c 多一些信息,感兴趣可以对比差异了解下

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
menu "Model Param Setting:"
    depends on LVP_KWS_USER_MODEL_V0DOT1DOT0_2022_0331
    config NORMAL_CTC_SCORE
      default y

    config KWS_MODEL_SUPPORT_SOFTMAX
      default y
    # Byte
    config KWS_SNPU_BUFFER_SIZE
        default 2996

    # Frames
    config KWS_MODEL_FEATURES_DIM_PER_FRAME
        default 40

    config KWS_MODEL_INPUT_STRIDE_LENGTH
        default 4

    config KWS_MODEL_INPUT_WIN_LENGTH
        default 15

    config KWS_MODEL_OUTPUT_LENGTH
        default 65

    config KWS_MODEL_DECODER_STRIDE_LENGTH
        int "KWS Lantency (unit of Context)"
        default 1
        range 1 4

    config KWS_MODEL_DECODER_WIN_LENGTH
        int "KWS Model Decoder Window Length (unit of context)"
        default 25
endmenu
  • 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.hnpu_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 解析*

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
int LvpDoUserDecoder(LVP_CONTEXT *context)
{
#ifdef CONFIG_LVP_ENABLE_KEYWORD_RECOGNITION
    gx_dcache_invalid_range((uint32_t *)context->snpu_buffer, context->ctx_header->snpu_buffer_size);
    float *output = (float *)LvpCTCModelGetSnpuOutBuffer(context->snpu_buffer);
    printf("moudel output: %f , %f\n", output[0], output[1]);
    /*
    todo: decode model output
     */
#endif
    return 0;
}
  • output 实际就是模型的输出结果
  • 一旦判断激活后,此接口只需要吧激活词的 id 赋值给 context->kws 即可

6. 模型包部署示例步骤*

6.1 生成模型包*

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