跳转至

Zipformer

1 概述

1.1 背景介绍

Zipformer 是k2-fsa/icefall生态主推的新一代语音识别编码器(ASR),兼顾高效建模与低延迟流式推理,具有速度快、显存占用低、精度高等特点。icefall完整实现了流式/非流式、RNNT-T架构下的全套训练、推理流程。

详情可参考官方说明

https://github.com/k2-fsa/icefall/blob/master/README.md

该示例采用流式推理+RNNT-T的架构进行部署,模型下载地址如下: - kws_librispeech

注意:该模型是基于librispeech数据集训练的,如有其他需求可从icefall官网下载其他模型进行转换。

1.2 使用说明

Linux SDK-alkaid中默认带了已经预先转换好的离线模型及板端示例, 相关文件路径如下:

  • 板端示例程序路径

    Linux_SDK/sdk/verify/opendla/source/asr/zipformer
    
  • 板端离线模型路径

    Linux_SDK/project/board/${chip}/dla_file/ipu_open_models/asr/zipformer_encoder.img
    Linux_SDK/project/board/${chip}/dla_file/ipu_open_models/asr/zipformer_decoder.img
    Linux_SDK/project/board/${chip}/dla_file/ipu_open_models/asr/zipformer_joiner.img
    
  • 板端测试音频路径

    Linux_SDK/sdk/verify/opendla/source/resource/zipformer/1188-133604-0000.wav
    
  • 板端测试字典路径

    Linux_SDK/sdk/verify/opendla/source/resource/zipformer/tokens.txt
    Linux_SDK/sdk/verify/opendla/source/resource/zipformer/tokenizer_config.json
    Linux_SDK/sdk/verify/opendla/source/resource/zipformer/preprocessor_config.json
    

如果用户不需要转换模型可直接跳转至第3章节。

2 模型转换

2.1 onnx模型转换

  • python环境搭建

    $conda create -n zipformer python==3.11
    $conda activate zipformer
    $git clone https://github.com/k2-fsa/icefall.git
    

    注意: 这里提供的python环境搭建, 仅作为参考示例, 具体搭建过程请参考官方源码运行教程:

    https://k2-fsa.github.io/icefall/installation/index.html
    
  • 模型导出

    • 安装依赖库

      $pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/simple
      
    • 下载的模型会包含

      • pretrianed.pt
      • data/lang_bpe_500/tokens.txt
    • 运行icefall自带的模型转换脚本, 确保icefall环境配置正确。

      $cd icefall/egs/librispeech/ASR/
      $export PYTHONPATH=$PYTHONPATH:$(pwd)/../../../
      $python ./zipformer/export-onnx-streaming-new.py \
          --exp-dir icefall-asr-librispeech-streaming-zipformer-2023-05-17/exp \
          --tokens icefall-asr-librispeech-streaming-zipformer-2023-05-17/data/lang_bpe_500/tokens.txt \
          --use-averaged-model 0 \
          --epoch 30 \
          --avg 1 \
          --chunk-size 16 \
          --left-context-frames 128 \
          --decoder-dim 512 \
          --joiner-dim 512 \
          --num-encoder-layers 2,2,3,4,3,2 \
          --feedforward-dim 512,768,1024,1536,1024,768 \
          --encoder-dim 192,256,384,512,384,256 \
          --encoder-unmasked-dim 192,192,256,256,256,192 \
          --causal 1
      

    其中, exp即为从官网上下载的模型文件夹, 可自行命名。模型转换成功后, 输出的log信息中会打印: Export decoder to path/decoder-epoch-99-avg-1-chunk-16-left-128.onnx Export encoder to path/encoder-epoch-99-avg-1-chunk-16-left-128.onnx Export jointer to path/jointer-epoch-99-avg-1-chunk-16-left-128.onnx

    此时已经可以转换出onnx, 但是还无法部署到到我们的平台, 需要对某些算子进行修改。

  • 模型修改

    模型转换代码中已提供代码修改的patch,可直接运行修改,运行命令如下:

    $cp 0001-modify-export-onnx-streaming.py-for-fixed-inference.patch ./
    $git am 0001-modify-export-onnx-streaming.py-for-fixed-inference.patch
    

    修改完成后, 再执行模型导出步骤, 即可生成可以部署的zipformer.onnx模型。 - encoder_optimized-epoch-30-avg-1-chunk-16-left-128.onnx - decoder-epoch-30-avg-1-chunk-16-left-128.onnx - joiner-epoch-30-avg-1-chunk-16-left-128.onnx

2.2 离线模型转换

2.2.1 offline模型转换流程

注意:1)OpenDLAModel对应的是压缩包image-dev_model_convert.tar解压之后的smodel文件。2)与其他算法不同的是,该算法不需要进入docker即可转换。

  • 拷贝onnx模型到转换代码目录

    $cp path/encoder_optimized-epoch-30-avg-1-chunk-16-left-128.onnx OpenDLAModel/asr/zipformer/onnx
    $cp path/decoder-epoch-30-avg-1-chunk-16-left-128.onnx OpenDLAModel/asr/zipformer/onnx
    $cp path/joiner-epoch-30-avg-1-chunk-16-left-128.onnx OpenDLAModel/asr/zipformer/onnx
    
  • 转换命令

    #进入到OpenDLAModel目录
    $cd /work/SGS_XXX/OpenDLAModel
    $bash convert.sh -a asr/zipformer -c config/asr_zipformer.cfg -p SGS_IPU_Toolchain(绝对路径) -s false
    
  • 最终生成的模型地址

    output/${chip}_${时间}/zipformer_encoder.img
    output/${chip}_${时间}/zipformer_decoder.img
    output/${chip}_${时间}/zipformer_joiner.img
    

2.2.2 预&后处理说明

  • 预处理

    该模型的输入为fbank特征,fbank的提取流程为:对16kHz原始音频依次进行预加重、分帧(帧长25ms、帧移10ms)、加汉明窗、FFT计算幅度谱,再通过80维Mel滤波器组并取对数,得到80维对数梅尔滤波器组特征,作为encoder的输入。

  • 后处理

    该模型有多种后处理(解码方式),这里介绍两种:prefix_beam_search或者greedy search

    • greedy search(贪心解码)

      每一步直接选取 logit 最大值对应的 token 作为输出,路径唯一、速度快、计算量小。

    • Prefix Beam Search(前缀束搜索)

      • 维护多个候选序列(前缀),每个时间步对所有候选扩展 token,按累计概率保留 top-N(beam size,对应 -n 参数)条路径。
      • 对相同前缀的分数做合并,避免重复路径,最后输出累计分数最高的序列。

      这种解码方式准确率更高、更鲁棒;代价是计算和显存开销更大,速度更慢。

2.2.3 关键脚本参数解析

  • encoder_config.ini

    [INPUT_CONFIG]
    inputs=x,encoder_states_0,encoder_states_1,encoder_states_2,encoder_states_3,encoder_states_4,encoder_states_5,embed_states,processed_lens;
    input_formats=RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_F32_NHWC,RAWDATA_S16_NHWC;
    quantizations=TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE;
    
    [OUTPUT_CONFIG]
    outputs=encoder_out,new_encoder_states_0,new_encoder_states_1,new_encoder_states_2,new_encoder_states_3,new_encoder_states_4,new_encoder_states_5,new_embed_states,new_processed_lens;
    dequantizations=TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE;
    
    [OPTIMIZE_CONFIG]
    optimize_layernorm_precision=TRUE;
    
    [CONV_CONFIG]
    input_format=ALL_FP16;              # int16量化时, 如果是mhera之后的chip,都是ALL_FP16
    
  • decoder_config.ini

    [INPUT_CONFIG]
    inputs=y;
    input_formats=RAWDATA_S16_NHWC;
    quantizations=TRUE;
    
    [OUTPUT_CONFIG]
    outputs=decoder_out;
    dequantizations=TRUE;
    
    [OPTIMIZE_CONFIG]
    optimize_layernorm_precision=TRUE;
    
    [CONV_CONFIG]
    input_format=ALL_FP16;              # int16量化时, 如果是mhera之后的chip,都是ALL_FP16;否则,填ALL_INT16
    
  • joiner_config.ini

    [INPUT_CONFIG]
    inputs=encoder_out,decoder_out;
    input_formats=RAWDATA_F32_NHWC,RAWDATA_F32_NHWC;
    quantizations=TRUE,TRUE;
    
    [OUTPUT_CONFIG]
    outputs=logit;
    dequantizations=TRUE;
    
    [OPTIMIZE_CONFIG]
    optimize_layernorm_precision=TRUE;
    
    [CONV_CONFIG]
    input_format=ALL_FP16;              # int16量化时, 如果是mhera之后的chip,都是ALL_FP16
    

2.3 模型仿真

  • 获取float/fixed/offline模型输出

    $bash convert.sh -a asr/zipformer -c config/asr_zipformer.cfg -p SGS_IPU_Toolchain(绝对路径) -s true
    

    执行上述命令后,根据输入的参数,会对floatfixedoffline进行推理,可在代码中添加打印获取模型输出tensor。

  • 模型精度对比

    python asr/zipformer/sim_onnx_zipformer.py \
        --encoder output/${chip}_${时间}/zipformer_decoder.img \
        --decoder output/${chip}_${时间}/zipformer_decoder.img \
        --joiner output/${chip}_${时间}/zipformer_joiner \
        --tokens asr/zipformer/tokens.txt \
        --wav asr/zipformer/test_wavs/1089-134686-0001.wav \
        --sample-rate 16000 \
        --feature-dim 80 \
        --optimized
    

    执行上述命令后,会输出onnx模型的推理结果,可在中间添加打印,获取模型的输出tensor。

3 板端部署

3.1 程序编译

示例程序编译之前需要先根据板子(nand/nor/emmc, ddr型号等)选择deconfig进行sdk整包编译, 具体可以参考alkaid sdk sigdoc《开发环境搭建》文档。

  • 编译板端zipformer示例。

    $cd sdk/verify/opendla
    $make clean && make source/asr/zipformer -j8
    
  • 最终生成的可执行文件地址

    sdk/verify/opendla/out/${AARCH}/app/prog_asr_zipformer
    

3.2 运行文件

运行程序时, 需要先将以下几个文件拷贝到板端

  • prog_asr_zipformer
  • 1188-133604-0000.wav
  • tokens.txt
  • tokenizer_config.json
  • preprocessor_config.json
  • zipformer_decoder.img
  • zipformer_encoder.img
  • zipformer_joiner.img

3.3 运行说明

  • Usage: ./prog_asr_zipformer -d model -w wav -b use_beam_search -n beam_size(执行文件使用命令)

  • Required Input:

    • d:模型路径
    • e:encoder 模型名称
    • D:decoder 模型名称
    • j:joiner 模型名称
    • w:音频路径
    • b:是否使用prefix_beam_search
    • n: 使用的beam数量
  • Typical Output:

    ./prog_asr_zipformer -d models/zipformer/ -e zipformer_encoder.img -D zipformer_decoder.img -j zipformer_joiner.img  -w models/zipformer/1188-133604-0000.wav -b true -n 5
        Using models - encoder: zipformer_encoder.img, decoder: zipformer_decoder.img, jointer: zipformer_joiner.img
        load dict from models/zipformer/tokens.txt
        Loading Zipformer model from: models/zipformer/
        init zipformer
        init decoder
        init encoder
        init jointer
        Model loaded successfully.
    
        Opened file: models/zipformer/1188-133604-0000.wav
        feats shape = [85680]
        zipformer response is :  YOU WILL FIND ME CONTINUALLY SPEAKING OF FOUR MEN TITIAN HOBINE TURNER AND TINKORAT IN ALMOST THE SAME TERMS