尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

在 sherpa-onnx 中使用 WeNet 模型:CTC 分支 ONNX 导出与流式/非流式部署全指南

在 sherpa-onnx 中使用 WeNet 模型:CTC 分支 ONNX 导出与流式/非流式部署全指南 在 sherpa-onnx 中使用 WeNet 模型CTC 分支 ONNX 导出与流式/非流式部署全指南【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx导读本文围绕仓库 scripts/wenet 目录下的模型导出工具链系统讲解如何将 WeNetU2/U2 系列 Conformer训练得到的 PyTorch 模型导出为 ONNX 格式并在 sherpa-onnx 中完成离线非流式与在线流式语音识别部署。读完本文你将掌握 WeNet 模型导出脚本的参数含义、模型输入输出张量约定、int8 动态量化方法以及导出的 ONNX 模型如何在 sherpa-onnx 的 Python API 与底层 C 实现中被加载与运行。一、脚本概览与核心能力边界scripts/wenet目录中存放的是从 WeNet 到 sherpa-onnx 的模型转换与验证脚本文件组成如下文件作用README.md说明该目录用途与支持范围export-onnx.py导出**非流式non-streaming**模型export-onnx-streaming.py导出**流式streaming**模型run.sh一键式脚本安装依赖、下载预训练模型、执行导出与验证test-onnx.py非流式 ONNX 模型的 Python 推理验证test-onnx-streaming.py流式 ONNX 模型的分块推理验证根据 scripts/wenet/README.md 的说明这套工具链有三个明确的能力边界理解它们对后续使用至关重要流式与非流式模型均受支持即 U2/U2 架构中decoding_chunk_size-1非流式与按 chunk 前向流式两种模式都可以导出。只使用 CTC 分支。导出时仅取出编码器encoder与 CTC 层用ctc.log_softmax(encoder_out)得到帧级后验概率WeNet 自带的 attention decoder 重打分rescore不支持。支持 H、HL、HLG 三种图解码路径。导出的帧级 CTC log-probs 可以配合词表构建 H纯 CTC 贪心/前缀束搜索、HL加词典、HLG加语言模型解码图在 sherpa-onnx 侧通过 CTC 解码器完成解码。二、导出前的环境准备run.sh 中的install_dependencies函数给出了完整的依赖清单手动操作时可按同样步骤准备# 1. 安装 WeNet 本体导出脚本依赖其中的 init_model 等模块 pip install githttps://github.com/wenet-e2e/wenet.git # 2. 安装 ONNX 导出与推理相关库 pip install onnxruntime onnx pyyaml # 3. 安装 CPU 版 PyTorch仓库脚本使用的版本组合 pip install torch2.3.1cpu torchaudio2.3.1cpu -f https://download.pytorch.org/whl/torch_stable.html # 4. 安装 k2 与特征提取库验证脚本使用 pip install k21.24.4.dev20240606cpu.torch2.3.1 -f https://k2-fsa.github.io/k2/cpu.html pip install soundfile kaldi-native-fbankexport-onnx.py与export-onnx-streaming.py的头部注释还提示WeNet 源码中的部分子模块wenet/transducer/search、wenet/e_branchformer、wenet/ctl_model需要补齐到wenet包目录下因为导出的模型涉及 e_branchformer 编码器结构。run.sh中通过克隆 WeNet 仓库并将这些目录复制进已安装包的位置来完成该步骤wenet_dir$(dirname $(python3 -c import wenet; print(wenet.__file__))) git clone https://github.com/wenet-e2e/wenet cp -av ./wenet/wenet/transducer/search $wenet_dir/transducer cp -a ./wenet/wenet/e_branchformer $wenet_dir cp -a ./wenet/wenet/ctl_model $wenet_dir cp -av ./wenet/wenet/finetune $wenet_dir/此外run.sh开头执行export PYTHONPATH/tmp/wenet:$PYTHONPATH这是为了确保后续从/tmp/wenet中解析 WeNet 相关模块。三、非流式模型导出export-onnx.py3.1 模型封装与输入输出约定非流式导出脚本将 WeNet 模型的 encoder 与 ctc 两个子模块包装进一个torch.nn.Moduleclass OnnxModel(torch.nn.Module): def __init__(self, encoder: torch.nn.Module, ctc: torch.nn.Module): super().__init__() self.encoder encoder self.ctc ctc def forward(self, x, x_lens): encoder_out, encoder_out_mask self.encoder( x, x_lens, decoding_chunk_size-1, # -1 表示非流式一次性看到全部输入 num_decoding_left_chunks-1, ) log_probs self.ctc.log_softmax(encoder_out) log_probs_lens encoder_out_mask.int().squeeze(1).sum(1) return log_probs, log_probs_lens关键点在于输入x3 维 float32 张量形状(N, T, C)其中N为 batch sizeT为帧数C 80为 fbank 特征维度输入x_lens1 维 int64 张量形状(N,)记录每段有效帧数输出log_probs帧级 CTC log 后验形状(N, T, vocab_size)输出log_probs_lens对应每段的有效输出帧数由编码器输出的 mask 求和得到。3.2 导出参数与动态轴导出时使用opset_version 13并显式声明了动态轴torch.onnx.export( onnx_model, (x, x_lens), filename, opset_versionopset_version, input_names[x, x_lens], output_names[log_probs, log_probs_lens], dynamic_axes{ x: {0: N, 1: T}, x_lens: {0: N}, log_probs: {0: N, 1: T}, log_probs_lens: {0: N}, }, )即帧维度T与 batch 维度N都是动态的因此导出的model.onnx可接受任意长度的音频特征输入。脚本还使用了torch.jit.script对模型做脚本化后再导出以规避部分动态 shape 场景下的导出问题。3.3 写入元数据meta data导出完成后add_meta_data会向 ONNX 模型写入model.metadata_props供 sherpa-onnx 运行时识别模型类型与采样配置meta_data { model_type: wenet_ctc, version: 1, model_author: wenet, comment: non-streaming, subsampling_factor: torch_model.encoder.embed.subsampling_rate, vocab_size: torch_model.ctc.ctc_lo.weight.shape[0], url: url, # 来源于环境变量 WENET_URL }其中model_type wenet_ctc是 sherpa-onnx 识别该类模型的标志subsampling_factor来自编码器 embedding 层的下采样率Conformer 通常为 4vocab_size直接取 CTC 输出层权重矩阵的行数。3.4 int8 动态量化脚本末尾调用 onnxruntime 的quantize_dynamic生成 int8 量化版本filename_int8 model.int8.onnx quantize_dynamic( model_inputfilename, model_outputfilename_int8, op_types_to_quantize[MatMul], weight_typeQuantType.QInt8, )只对MatMul算子做权重量化weight-only量化类型为 QInt8。这样可以在不依赖校准数据集的情况下显著减小模型体积适合在嵌入式设备或移动端部署代价是有轻微精度损失。四、流式模型导出export-onnx-streaming.py4.1 分块前向的模型封装流式模型的关键是复用 WeNet 编码器的forward_chunk接口逐 chunk 推理并维护跨 chunk 的缓存class OnnxModel(torch.nn.Module): def forward( self, x: torch.Tensor, offset: torch.Tensor, required_cache_size: torch.Tensor, attn_cache: torch.Tensor, conv_cache: torch.Tensor, attn_mask: torch.Tensor, ): encoder_out, next_att_cache, next_conv_cache self.encoder.forward_chunk( xsx, offsetoffset, required_cache_sizerequired_cache_size, att_cacheattn_cache, cnn_cacheconv_cache, att_maskattn_mask, ) log_probs self.ctc.log_softmax(encoder_out) return log_probs, next_att_cache, next_conv_cache各输入张量的语义来自脚本 docstring为输入形状含义x(N, T, C)当前 chunk 的特征仅支持N 1offset标量 int64当前已处理的总帧偏移required_cache_size标量 int64注意力缓存所需的历史帧数attn_cache(num_blocks, head, required_cache_size, output_size/head*2)跨 chunk 的注意力 KV 缓存conv_cache(num_blocks, N, output_size, cnn_module_kernel-1)跨 chunk 的卷积缓存attn_mask(N, 1, required_cache_size chunk_size)bool因果注意力掩码输出为三元组当前 chunk 的log_probs形状(N, T, C)、next_att_cache与next_conv_cache后两者回填给下一次调用从而形成状态复用的流式推理闭环。4.2 chunk 与缓存尺寸的计算脚本从train.yaml的encoder_conf中读取模型结构参数并据此推导解码窗口head configs[encoder_conf][attention_heads] num_blocks configs[encoder_conf][num_blocks] output_size configs[encoder_conf][output_size] cnn_module_kernel configs[encoder_conf].get(cnn_module_kernel, 1) right_context torch_model.right_context() subsampling_factor torch_model.encoder.embed.subsampling_rate chunk_size 16 # 每个 chunk 的编码器帧数 left_chunks 4 # 左侧历史 chunk 数 decoding_window (chunk_size - 1) * subsampling_factor right_context 1 required_cache_size chunk_size * left_chunks这里chunk_size 16、left_chunks 4与 WeNet 训练时的--chunk-size 16 --num-left-chunks 4对齐decoding_window表示每个推理步需要送入编码器的 fbank 特征帧数它与subsampling_factor4、right_context共同决定。attn_mask的构造为前required_cache_size位置置 0遮蔽其余位置置 1attn_mask torch.ones(1, 1, required_cache_size chunk_size, dtypetorch.bool) attn_mask[:, :, :required_cache_size] 0初始时offset required_cache_sizeattn_cache、conv_cache均以零张量初始化。4.3 流式模型的动态轴与元数据流式导出的输入输出名称与动态轴声明如下input_names[x, offset, required_cache_size, attn_cache, conv_cache, attn_mask], output_names[log_probs, next_att_cache, next_conv_cache], dynamic_axes{ x: {0: N, 1: T}, attn_cache: {2: T}, attn_mask: {2: T}, log_probs: {0: N}, new_attn_cache: {2: T}, },流式模型的元数据比非流式更丰富完整保留了部署所需的全部结构参数meta_data { model_type: wenet_ctc, version: 1, model_author: wenet, comment: streaming, chunk_size: 16, left_chunks: 4, head: head, num_blocks: num_blocks, output_size: output_size, cnn_module_kernel: cnn_module_kernel, right_context: right_context, subsampling_factor: subsampling_factor, vocab_size: torch_model.ctc.ctc_lo.weight.shape[0], }这些字段与 sherpa-onnx 流式推理所需的 chunk 划分、缓存尺寸、注意力维度一一对应。同样脚本末尾会生成model-streaming.int8.onnx的 int8 动态量化版本。五、一键导出脚本run.sh支持的预训练模型run.sh 为六套经典 WeNet 预训练模型提供了完整的下载 → 解压 → 放置 global_cmvn → 导出 → 验证流水线函数模型用途aishellaishell_u2pp_conformer_exp中文普通话aishell-1aishell2aishell2_u2pp_conformer_exp中文普通话aishell-2multi_cnmulti_cn_unified_conformer_exp中文多方言wenetspeechwenetspeech_u2pp_conformer_exp海量中文wenetspeech 大模型librispeechlibrispeech_u2pp_conformer_exp英文librispeech 960hgigaspeechgigaspeech_u2pp_conformer_exp英文gigaspeech 超大规模以aishell为例脚本逻辑为wget -q https://huggingface.co/openspeech/wenet-models/resolve/main/aishell_u2pp_conformer_exp.tar.gz tar xvf aishell_u2pp_conformer_exp.tar.gz pushd aishell_u2pp_conformer_exp mkdir -p exp/20210601_u2_conformer_exp cp global_cmvn ./exp/20210601_u2_conformer_exp cp ../*.py . # 将导出与测试脚本复制进模型目录 export WENET_URL... # 记录模型来源写入 ONNX 元数据 wget -O 0.wav ... # 下载测试音频 ./export-onnx-streaming.py ./test-onnx-streaming.py # 流式导出 验证 ./export-onnx.py ./test-onnx.py # 非流式导出 验证 popd脚本约定导出脚本在当前目录下寻找final.ptcheckpoint、train.yaml训练配置与global_cmvn全局 CMVN 统计这与 WeNet 官方发布包的目录结构一致。执行整个run.sh会依次完成六套模型的转换最终输出目录树供检查。六、导出结果验证两个 Python 测试脚本6.1 非流式验证test-onnx.py该脚本用与 sherpa-onnx 一致的特征提取管线kaldi-native-fbank处理测试音频再送入 ONNX 模型做贪心解码特征提取torchaudio.load读 wav取单声道若采样率非 16k 则重采样音频乘 32768 转为整数刻度后用knf.OnlineFbank提取 80 维 fbankdither0、snip_edgesFalse与训练时配置一致。ONNX 推理创建ort.InferenceSession线程配置为inter_op_num_threads1、intra_op_num_threads4provider 为 CPU按x、x_lens两个输入调用。贪心解码对log_probs取argmax(dim1)用torch.unique_consecutive折叠连续重复的 blank/相同 token剔除索引 0WeNet 中通常是 blank最后通过units.txt将 token id 映射回文本并拼接输出。log_probs.shape (1, T, vocab_size) indexes log_probs.argmax(dim1) indexes torch.unique_consecutive(indexes) indexes indexes[indexes ! 0].tolist() text .join([id2word[i] for i in indexes])6.2 流式验证test-onnx-streaming.py流式验证的核心是模拟真实在线识别中的分块送入 缓存回填循环读取元数据从 ONNX 模型读取left_chunks、num_blocks、chunk_size、head、output_size、cnn_module_kernel、right_context、subsampling_factor并据此初始化attn_cache、conv_cache与offset分块计算chunk_length (chunk_size - 1) * subsampling_factor right_context 1为每步送入的帧数chunk_shift chunk_size * subsampling_factor为滑窗步长相邻 chunk 有重叠的右侧上下文掩码更新每次调用前按当前chunk_idx offset // chunk_size - left_chunks动态调整attn_mask在序列开头阶段历史不足时将多余的缓存位置遮蔽缓存回填推理后把返回的new_attn_cache、new_conv_cache写回并更新offset log_probs.shape[1]结果合并每个 chunk 的argmax结果经去重后累积到 token 列表最终映射为文本。测试音频尾部会拼接 50 帧零填充padding torch.zeros(50, 80)以兜底尾部未处理完的残帧。七、在 sherpa-onnx 中加载 WeNet ONNX 模型7.1 配置结构离线与在线两条路径sherpa-onnx 为 WeNet CTC 模型提供了两条独立的配置结构对应离线识别器与在线识别器离线offline-wenet-ctc-model-config.h 定义的OfflineWenetCtcModelConfig仅含一个model字段指向非流式导出的model.onnx在线online-wenet-ctc-model-config.h 定义的OnlineWenetCtcModelConfig包含三个字段struct OnlineWenetCtcModelConfig { std::string model; int32_t chunk_size 16; // 对应 WeNet 的 --chunk_size int32_t num_left_chunks 4; // 对应 WeNet 的 --num_left_chunks };chunk_size与num_left_chunks的默认值恰好与导出脚本中的chunk_size 16、left_chunks 4一致用户在部署时若使用默认导出参数无需额外修改。在线实现online-wenet-ctc-model.cc会读取 ONNX 元数据中的结构参数来初始化缓存因此导出时写入的 meta data 是流式推理正确性的前提。7.2 Python API 使用示例仓库的 online-decode-files.py 直接给出了流式 WeNet CTC 模型的完整调用方式curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-zh-wenet-wenetspeech.tar.bz2 tar xvf sherpa-onnx-zh-wenet-wenetspeech.tar.bz2 rm sherpa-onnx-zh-wenet-wenetspeech.tar.bz2 ./python-api-examples/online-decode-files.py \ --tokens./sherpa-onnx-zh-wenet-wenetspeech/tokens.txt \ --wenet-ctc./sherpa-onnx-zh-wenet-wenetspeech/model-streaming.onnx \ ./sherpa-onnx-zh-wenet-wenetspeech/test_wavs/0.wav \ ./sherpa-onnx-zh-wenet-wenetspeech/test_wavs/1.wav \ ./sherpa-onnx-zh-wenet-wenetspeech/test_wavs/8k.wav命令行参数在脚本中对应如下定义parser.add_argument(--wenet-ctc, typestr, helpPath to the wenet ctc model) parser.add_argument(--wenet-ctc-chunk-size, typeint, default16, helpThe --chunk-size parameter for streaming WeNet models) parser.add_argument(--wenet-ctc-num-left-chunks, typeint, default4, helpThe --num-left-chunks parameter for streaming WeNet models)内部通过sherpa_onnx.OnlineRecognizer.from_wenet_ctc(model..., chunk_size..., num_left_chunks...)构造在线识别器并逐帧accept_waveform、decode得到结果。非流式模型则可通过 offline-decode-files.py 中sherpa_onnx.OfflineRecognizer的wenet_ctc配置项加载model.onnx使用。7.3 解码方式说明由于导出的是帧级 CTC log-probssherpa-onnx 在解码时支持三种图路径H仅使用词表与 CTC 输出做贪心或束搜索最轻量HL在 H 基础上叠加词典lexicon约束解码路径为合法词序列HLG进一步叠加语言模型G获得带语言模型先验的解码图。对应的 C 示例可以参考 streaming-zipformer-buffered-tokens-hotwords-c-api.c 与 wenet-ctc-c-api.c 等基于 CTC 的解码示例Python 侧则可参考 online-zipformer-ctc-hlg-decode-file.py 中 HLG 图的加载方式。八、从导出到部署的完整链路小结综合以上内容WeNet 模型进入 sherpa-onnx 的完整链路可归纳为四步准备环境与模型安装 WeNet、onnxruntime、onnx、pyyaml、kaldi-native-fbank 等依赖下载 WeNet 预训练包含final.pt、train.yaml、global_cmvn、units.txt导出在模型目录下运行export-onnx.py得到model.onnx、model.int8.onnx与export-onnx-streaming.py得到model-streaming.onnx、model-streaming.int8.onnx或直接执行run.sh一键完成六套模型的导出与验证验证运行test-onnx.py/test-onnx-streaming.py确认输出文本与预期一致同时核对 ONNX 元数据model_typewenet_ctc、chunk_size、left_chunks等部署在 sherpa-onnx 中按在线/离线两条路径加载——在线使用OnlineRecognizer.from_wenet_ctc对应 online-wenet-ctc-model-config.h默认chunk_size16、num_left_chunks4离线使用OfflineRecognizer的 wenet_ctc 配置对应 offline-wenet-ctc-model-config.h并选择合适的 H/HL/HLG 解码图。需要注意的是本文描述的是仓库当前状态下的转换链路导出脚本仅覆盖 CTC 分支attention decoder rescore 不在支持范围内流式模型对 chunk 尺寸与左侧历史 chunk 数有固定约定若在 WeNet 训练阶段使用了不同的--chunk-size/--num-left-chunks部署时需通过--wenet-ctc-chunk-size、--wenet-ctc-num-left-chunks保持三者一致否则会导致推理结果错误。【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表