news 2026/9/15 6:37:55

sherpa-onnx 模型接入实战:NVIDIA Parakeet Unified EN 0.6B 从 NeMo 到 ONNX 的非流式与缓冲流式导出全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sherpa-onnx 模型接入实战:NVIDIA Parakeet Unified EN 0.6B 从 NeMo 到 ONNX 的非流式与缓冲流式导出全解析

sherpa-onnx 模型接入实战:NVIDIA Parakeet Unified EN 0.6B 从 NeMo 到 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/nemo/parakeet-unified-en-0.6b 目录展开,完整讲解如何将 NVIDIA 开源的 Parakeet Unified EN 0.6B(一个支持统一流式/非流式的英文语音识别模型,内部为EncDecRNNTBPEModel)从 NeMo 格式导出为 sherpa-onnx 可加载的 encoder/decoder/joiner 三段式 ONNX 模型。读完本文你将掌握:该模型的网络结构与关键超参数、ONNX 各子模型的输入输出张量约定、int8 动态量化流程、以及基于 buffered streaming 机制的低延迟流式导出方案与仓库内的验证方法。

一、模型与导出目录概览

parakeet-unified-en-0.6b是 sherpa-onnx 的 NeMo 系列模型导出脚本之一(同目录下还包含 canary、parakeet-tdt-0.6b-v2、GigaAM 等)。该目录提供了两条导出链路:

  • 非流式export_onnx.py+ run.sh;
  • 缓冲流式export_onnx_streaming.py+ run-streaming.sh。

两条链路都包含配套的 ONNX Runtime 冒烟测试脚本(test_onnx.py/test_onnx_streaming.py),用于验证导出结果并计算 RTF(实时率)。notes.md 正是这两次导出实验的记录档案:它依次给出了 PyTorch 模型结构(asr_model)、完整训练/推理配置(asr_model.cfg)、三个 ONNX 子图的输入输出张量签名、int8 量化后的文件体积以及实测推理结果。

二、模型结构拆解:EncDecRNNTBPEModel 的四段式架构

notes.md 中记录的asr_model结构属于 NeMo 的EncDecRNNTBPEModel(BPE 子词 + RNN-Transducer)。整个模型由四大部分组成:前端预处理器、Conformer 编码器、RNNT 预测网络(decoder)与联合网络(joint)。

2.1 预处理器:AudioToMelSpectrogramPreprocessor

(preprocessor): AudioToMelSpectrogramPreprocessor( (featurizer): FilterbankFeatures() )

负责将 16 kHz 原始波形转为 128 维 FBank 特征。对应的配置(详见下文asr_model.cfg):窗长 25 ms、帧移 10 ms、hann 窗、n_fft=512normalize='per_feature'。注意该预处理器在导出 ONNX 时不包含在模型中——特征提取由 sherpa-onnx 侧的 kaldi-native-fbank 完成(见test_onnx.py中的create_fbank(),其参数window_type="hann"num_bins=128is_librosa=True与 NeMo 配置一一对应)。

2.2 编码器:24 层 ConformerEncoder

编码器是模型的体积与算力主体,由三部分串联:

1) ConvSubsampling(时间维 8 倍下采样)

ConvSubsampling( (out): Linear(in_features=4096, out_features=1024, bias=True) (conv): MaskedConvSequential( (0): Conv2d(1, 256, (3,3), stride=(2,2), padding=(1,1)) (1): ReLU (2): Conv2d(256, 256, (3,3), stride=(2,2), groups=256) # depthwise (3): Conv2d(256, 256, (1,1)) (4): ReLU (5): Conv2d(256, 256, (3,3), stride=(2,2), groups=256) # depthwise (6): Conv2d(256, 256, (1,1)) (7): ReLU ) )

采用dw_striding下采样方式:三次 stride=2 卷积将帧率降为 1/8(即subsampling_factor=8,这也是 10 ms 帧移对应 80 ms 编码帧间隔的来源),depthwise + pointwise 的组合控制参数量。

2) RelPositionalEncoding(相对位置编码)

用于配合 `self_attention_model='rel_pos'' 的相对位置自注意力,支持最长 5000 帧位置,dropout 0.1。

3) 24 个 ConformerLayer(d_model=1024)

每个层按顺序包含:

  • feed_forward1/feed_forward2Linear(1024→4096) → Swish → Dropout(0.1) → Linear(4096→1024),即 FFN 扩展因子 4;
  • convpointwise_conv1 (1024→2048) → CausalConv1D(1024, kernel=9, groups=1024) → BatchNorm1d → Swish → pointwise_conv2 (1024→1024),卷积核 9、组卷积,conv_norm_type='batch_norm'
  • self_attn:RelPositionMultiHeadAttention,8 头,Q/K/V/out 均为 1024 维,另有linear_pos位置映射;
  • 每层带两个 LayerNorm、残差与Dropout(0.1)

2.3 预测网络:RNNTDecoder(双层 LSTM)

RNNTDecoder( (prediction): ModuleDict( (embed): Embedding(1025, 640, padding_idx=1024) (dec_rnn): LSTMDropout( (lstm): LSTM(640, 640, num_layers=2, dropout=0.2) ) ) )

词嵌入表 1025 项(1024 个 BPE 子词 + 1 个 blank),隐层 640 维(pred_hidden=640),双层 LSTM。从源码结构看,这是标准 RNNT 预测网络:逐帧输入已预测 token,输出 640 维预测表示,与编码器输出在 joint 中融合。

2.4 联合网络:RNNTJoint

RNNTJoint( (pred): Linear(640→640) (enc): Linear(1024→640) (joint_net): Sequential(ReLU → Dropout(0.2) → Linear(640→1025)) )

先分别将编码器表示(1024 维)与预测网络表示(640 维)映射到 640 维公共空间并相加,再经 ReLU 与线性层输出 1025 维 logits(1024 个 token + blank),用于贪婪搜索或 beam 搜索解码。

此外训练期还包含RNNTLossSpectrogramAugmentation(SpecAugment)与WER计算模块,这些在推理 ONNX 中均不需要。

三、核心配置 asr_model.cfg 逐段解读

notes.md 完整保存了导出时的模型配置(nemo_version: 2.7.0rc0),这是复现训练超参、理解推理前处理的关键一手资料。

3.1 全局与数据

{'sample_rate': 16000, 'rnnt_reduction': 'mean_volume', 'skip_nan_grad': False, 'model_defaults': {'enc_hidden': 1024, 'pred_hidden': 640, 'joint_hidden': 640}, 'train_ds': {'sample_rate': 16000, 'batch_size': 16, 'num_workers': 8, 'max_duration': 40.0, 'min_duration': 0.1, 'text_field': 'answer', 'use_bucketing': True, 'use_lhotse': True}, 'validation_ds': {}}
  • 采样率统一 16 kHz;训练数据最长 40 s、最短 0.1 s;
  • 三个隐藏维度的统一入口:编码器 1024、预测网络 640、联合网络 640;
  • 导出脚本(export_onnx.py第 58 行)会把validation_ds置空,避免导出时加载验证集。

3.2 前端与增强

'preprocessor': {'_target_': 'AudioToMelSpectrogramPreprocessor', 'sample_rate': 16000, 'normalize': 'per_feature', 'window_size': 0.025, 'window_stride': 0.01, 'window': 'hann', 'features': 128, 'n_fft': 512, 'dither': 1e-05}, 'spec_augment': {'freq_masks': 2, 'time_masks': 10, 'freq_width': 27, 'time_width': 0.05}

推理前处理要点normalize='per_feature'意味着每段音频的 FBank 特征要按帧维做均值/方差归一化。在test_onnx.py(第 239–245 行)与buffered_streaming_helpers.pynormalize_per_feature()中都有完全一致的实现:

mean = features.mean(axis=0, keepdims=True) std = features.std(axis=0, keepdims=True) + 1e-5 # 加 1e-5 防除零 features = (features - mean) / std

3.3 编码器配置

'encoder': {'n_layers': 24, 'd_model': 1024, 'subsampling': 'dw_striding', 'subsampling_factor': 8, 'subsampling_conv_channels': 256, 'ff_expansion_factor': 4, 'self_attention_model': 'rel_pos', 'n_heads': 8, 'att_context_size': [-1, -1], 'att_chunk_context_size': [[70], [1, 2, 7, 13], [0, 1, 2, 3, 4, 7, 13]], 'att_context_style': 'chunked_limited_with_rc', 'conv_kernel_size': 9, 'conv_norm_type': 'batch_norm', 'dropout': 0.1, 'pos_emb_max_len': 5000}

这里的关键是att_context_size=[-1, -1](全上下文,即非流式)。注意att_chunk_context_size中已经内置了三档流式 chunk 方案(对应[[70], [1,2,7,13], [0,1,2,3,4,7,13]]),这正是 Parakeet Unified 系列"统一"能力的体现:同一套权重既能全上下文推理,也能通过设置 chunk 上下文切到流式。流式导出正是利用这一机制,见第五节。

3.4 预测网络与联合网络

'decoder': {'prednet': {'pred_hidden': 640, 'pred_rnn_layers': 2, 'dropout': 0.2}, 'blank_as_pad': True, 'vocab_size': 1024}, 'joint': {'jointnet': {'joint_hidden': 640, 'activation': 'relu', 'dropout': 0.2, 'encoder_hidden': 1024, 'pred_hidden': 640}, 'num_classes': 1024}

与 2.3/2.4 节结构一一对应。num_classes=1024再加上 blank 即 joint 输出的 1025 维。

3.5 解码、损失与优化器

'decoding': {'strategy': 'greedy_batch', 'greedy': {'max_symbols': 10}, 'beam': {'beam_size': 2, 'score_norm': True, 'tsd_max_sym_exp': 50, 'alsd_max_target_len': 2.0}}, 'loss': {'loss_name': 'default', 'offline_loss_weight': 0.3, 'streaming_loss_weight': 0.7}, 'optim': {'name': 'adamw', 'lr': 0.0001, 'betas': [0.9, 0.98], 'weight_decay': 0.001, 'sched': {'name': 'CosineAnnealing', 'warmup_steps': 3000, 'min_lr': 5e-06}}
  • 默认解码策略为 greedy_batch,每帧最多发射 10 个 tokenmax_symbols=10),这一约束在 ONNX 推理循环(test_onnx.py第 252 行、test_onnx_streaming.py第 126 行)中被原样保留;
  • 损失函数同时加权了离线(0.3)与流式(0.7)训练目标,印证了"unified"训练方式;
  • 优化器 AdamW,学习率 1e-4,CosineAnnealing 调度、3000 步 warmup、最小学习率 5e-6。

3.6 词表与 tokenizer

'tokenizer': {'type': 'bpe', 'model_path': 'nemo:c9e35cde64e14bdc87cf70d543842217_tokenizer.model', 'vocab_path': 'nemo:28f042954ba747e99209b8ca5a223ba3_vocab.txt'}

BPE 词表共 1024 个子词单元。export_onnx.py(第 61–65 行)会遍历asr_model.joint.vocabulary生成 tokens.txt,并在末尾追加一行<blk> 1024作为 blank 符号——因此 ONNX 推理时 blank 的索引恒为vocab_size(1024)。词表以空格符标记词首(如▁the),解码后需将其替换为空格再 strip。

四、非流式 ONNX 导出与子图张量签名

4.1 导出流程

export_onnx.py 的流程为:

  1. 加载模型:优先restore_from("./parakeet-unified-en-0.6b.nemo"),否则from_pretrained("nvidia/parakeet-unified-en-0.6b")
  2. 写出tokens.txt(1024 子词 +<blk>);
  3. 分别调用encoder.export()decoder.export()joint.export(),得到三个 ONNX 文件;
  4. 对三个子图分别执行 onnxruntime 动态量化(quantize_dynamic),encoder 用QUInt8,decoder/joiner 用QInt8
  5. 向 encoder 模型写入自定义元数据(add_meta_data),关键字段包括:model_type=EncDecRNNTBPEModelnormalize_type=per_featurepred_rnn_layers=2pred_hidden=640subsampling_factor=8feat_dim=128vocab_size=1024version=2comment="This model contains only the non-streaming part"。这些字段是 sherpa-onnx 加载模型时的依据(在线模型实现 中通过SHERPA_ONNX_READ_META_DATA读取同类字段)。

由于 encoder 体量巨大(fp32 权重约 2.3 GB),add_meta_dataencoder.onnx使用外部权重存储(encoder.weights)以避免单个文件超限。

4.2 三个子图的输入输出签名(来自 notes.md)

encoder(输入 128 维特征)

输入: audio_signal float [dynamic, 128, dynamic] # 布局为 [B, C, T] length int64 [dynamic] 输出: outputs float [dynamic, 1024, dynamic] # [B, 1024, T_enc] encoded_lengths int64 [dynamic]

decoder(预测网络)

输入: targets int32 [dynamic, dynamic] target_length int32 [dynamic] states.1 float [2, dynamic, 640] # 双层 LSTM 初始状态 onnx::Slice_3 float [2, 1, 640] 输出: outputs float [dynamic, 640, dynamic] prednet_lengths int32 [dynamic] states float [2, dynamic, 640] 162 float [2, dynamic, 640]

joiner(联合网络)

输入: encoder_outputs float [dynamic, 1024, dynamic] decoder_outputs float [dynamic, 640, dynamic] 输出: outputs float [dynamic, dynamic, dynamic, 1025] # 末维 1025 = vocab + blank

4.3 非流式推理主循环

test_onnx.py 给出了完整的贪婪搜索流程,可直接作为参考实现:

  1. 用 kaldi-native-fbank 提取 128 维 FBank(hann 窗、is_librosa=True),并按per_feature归一化;
  2. 在音频末尾追加 2 秒静音(tail_padding,第 229 行),保证尾部 token 有足够上下文完成发射;
  3. 初始化 decoder 状态为全零[2, 1, 640],输入 blank 得到初始decoder_out
  4. 整段音频一次性过 encoder 得到encoder_out[B, 1024, T_enc]
  5. 对每个编码帧,最多循环max_symbols=10次:joiner 融合encoder_out[:,:,t:t+1]decoder_out→ argmax → 非 blank 则更新 decoder 状态并继续发射,blank 则跳到下一帧;
  6. 收集 token 序列,替换为空格,strip()后输出文本。

五、缓冲流式导出:三种延迟档位

Parakeet Unified 的核心卖点是通过set_default_att_context_size([left, chunk, right])把非流式模型"剪"成流式。export_onnx_streaming.py 预置了三个延迟档位:

档位leftchunkright对应特征帧(×8)
1120ms7077左 560 / chunk 56 / 右 56
560ms7025左 560 / chunk 16 / 右 40
240ms7012左 560 / chunk 8 / 右 16
  • 三个档位共享相同的左上下文(70 个编码帧,约 560 ms),保证首帧即可依赖足够的历史;
  • chunk越小延迟越低(chunk=1 编码帧对应 80 ms 输入帧),但依赖的右侧未来帧越少,精度与延迟的权衡点就在right
  • 导出的 encoder 元数据新增:streaming_model_type="nemo_parakeet_unified_streaming"buffered_streaming=1left/chunk/right_encoder_framesleft/chunk/right_feature_frames(编码帧 × 8 换算为特征帧)。decoder 也写入streaming_model_type便于识别。

该导出方式被称作buffered streaming:每次只把"左上下文 + chunk + 右上下文"的特征窗口送入 encoder,只取窗口中央 chunk 对应的编码帧参与解码,随后窗口整体向右滑动。buffered_streaming_helpers.py 的slice_feature_buffer()实现了窗口切片与首尾补零,test_onnx_streaming.pydecode_buffered()展示了逐 chunk 推理与仅取中央帧(t_start = left_encoder_frames)的解码方式。

在 sherpa-onnx 运行时侧,该模型类型由 online-recognizer-transducer-nemo-parakeet-unified-impl.h 与 online-transducer-nemo-parakeet-unified-model.cc 支持(online-recognizer-impl.cc中依据streaming_model_type == "nemo_parakeet_unified_streaming"分发),并通过元数据读取left_encoder_frames/chunk_encoder_frames/right_encoder_frames驱动流式解码;配套的贪婪搜索实现见 online-transducer-greedy-search-nemo-parakeet-unified-decoder.cc。

六、端到端验证:脚本、RTF 与产物

6.1 一键导出脚本

非流式一键流程见 run.sh:

# 1. 下载 .nemo 模型与测试音频 2086-149220-0033.wav curl -SL -O https://huggingface.co/nvidia/parakeet-unified-en-0.6b/resolve/main/parakeet-unified-en-0.6b.nemo curl -SL -O https://dldata-public.s3.us-east-2.amazonaws.com/2086-149220-0033.wav # 2. 安装依赖(NeMo、kaldi-native-fbank、onnxruntime 等) pip install "nemo_toolkit[asr] @ git+https://github.com/NVIDIA/NeMo.git" \ "numpy<2" kaldi-native-fbank librosa onnx onnxruntime soundfile # 3. 导出 + int8 量化 python3 ./export_onnx.py # 4. 冒烟测试:int8 全套 与 int8 encoder + fp32 decoder/joiner 混合 python3 ./test_onnx.py --encoder ./encoder.int8.onnx --decoder ./decoder.int8.onnx \ --joiner ./joiner.int8.onnx --tokens ./tokens.txt --wav 2086-149220-0033.wav python3 ./test_onnx.py --encoder ./encoder.int8.onnx --decoder ./decoder.onnx \ --joiner ./joiner.onnx --tokens ./tokens.txt --wav 2086-149220-0033.wav

流式版本见 run-streaming.sh:对1120ms/560ms/240ms三个档位循环执行export_onnx_streaming.py --latency $latency与流式测试脚本,并将产物整理打包为sherpa-onnx-nemo-parakeet-unified-en-0.6b(-int8)-streaming-<latency>.tar.bz2(含 encoder/decoder/joiner、tokens.txt、测试音频及模型说明文档),可直接供 sherpa-onnx 使用。

6.2 实测结果(notes.md 记录)

以测试音频2086-149220-0033.wav为例,导出与推理结果完全一致:

  • 识别文本Well, I don't wish to see it any more, observed Phoebe, turning away her eyes it is certainly very like the old portrait
  • RTF(实时率,越低越快):int8 组合约0.100,int8 encoder + fp32 decoder/joiner 混合约0.123(CPU 单线程环境),均远小于 1,满足实时推理要求;
  • token 输出示例[218, 32, 961, 34, 220, 966, 943, 7, 302, 22, ...](共 49 个非 blank token,索引范围 0–1023);
  • 特征形状features.shape (942, 128),即 942 帧 × 128 维,对应约 9.42 秒音频(含 2 秒尾静音)。

6.3 导出产物文件体积

文件体积
encoder.onnx + encoder.weights(fp32)40 MB + 2.3 GB
encoder.int8.onnx624 MB
decoder.onnx / decoder.int8.onnx28 MB / 6.9 MB
joiner.onnx / joiner.int8.onnx6.6 MB / 1.7 MB

可见参数量集中在编码器(约 6 亿参数规模的 0.6B 命名来源),int8 量化可将 encoder 体积压缩约 4 倍(2.3 GB → 624 MB),decoder 与 joiner 同样明显瘦身,非常适合边缘设备部署。

七、总结与接入建议

Parakeet Unified EN 0.6B 在 sherpa-onnx 中的接入路径清晰完整:

  1. 非流式场景:直接使用 run.sh 导出的 encoder/decoder/joiner 三件套(推荐 encoder 使用 int8 以换取约 4 倍体积缩减,测试表明 RTF 反而更低);
  2. 流式场景:根据延迟预算选择 run-streaming.sh 中的档位——低延迟选240ms,追求精度选1120ms
  3. 元数据是运行时契约streaming_model_typesubsampling_factornormalize_typeleft/chunk/right_encoder_frames等字段必须写入 ONNX 元数据,sherpa-onnx 的 nemo parakeet unified 实现 依赖它们完成特征归一化、状态管理与 chunk 调度;
  4. 特征前处理不可省略:128 维 FBank(hann 窗、is_librosa)与per_feature归一化是模型输入格式的一部分,任何一端缺失都会导致识别结果异常。

如果需要验证导出的 ONNX 与运行时行为一致,可同时运行仓库内的 test_onnx.py(非流式)与 test_onnx_streaming.py(流式),二者均以2086-149220-0033.wav为基准音频,输出与 notes.md 记录一致的文本即可确认链路无误。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 6:37:48

4大模型翻译对决:第38周质量评测,gpt-o3 以 8.3 分领跑

4大模型翻译对决&#xff1a;第38周质量评测&#xff0c;gpt-o3 以 8.3 分领跑 本周 357 篇翻译任务&#xff0c;由 4 个模型完成。抽样 3 篇进行多模型盲评对比&#xff0c;综合最佳&#xff1a;gpt-o3&#xff08;均分 8.3/10&#xff09;。 本周翻译统计模型语言翻译量平均耗…

作者头像 李华
网站建设 2026/9/15 6:34:43

OpenHarmony Flutter应用深色模式适配指南

1. 为什么OpenHarmony应用需要深色模式适配在移动应用开发领域&#xff0c;深色模式&#xff08;Dark Mode&#xff09;已经从一个可选项变成了必备功能。根据2023年移动用户体验调查报告&#xff0c;超过78%的用户会在支持深色模式的设备上启用该功能&#xff0c;其中63%的用户…

作者头像 李华
网站建设 2026/9/15 6:31:36

NPM供应链攻击原理与防御实战指南

1. NPM供应链攻击事件深度解析2023年爆发的这场针对NPM生态系统的供应链攻击&#xff0c;堪称近年来影响范围最广的开源软件安全事件之一。攻击者精心设计了能够自我传播的恶意软件&#xff0c;通过187个被污染的软件包形成连锁感染&#xff0c;最终导致大量开发者的开发环境沦…

作者头像 李华
网站建设 2026/9/15 6:31:34

神马 AI 系统架构解析:第四代 AI 招聘平台是怎么炼成的?

2026年是招聘行业AI架构全面迭代的关键年份&#xff0c;传统关键词匹配招聘模式弊端持续凸显&#xff0c;错配岗位、虚假岗位、僵尸岗位成为求职与招聘的普遍痛点。结合各平台公开披露数据来看&#xff0c;招聘垂直领域微调的神马AI模型&#xff0c;通过四层架构体系、知识图谱…

作者头像 李华
网站建设 2026/9/15 6:31:00

AI论文写作助手:查重、降重与智能生成全解析

1. 论文写作痛点与AI解决方案全景解析写论文这件事&#xff0c;从本科到博士阶段都是学术路上的必经关卡。我指导过上百位学生的论文写作&#xff0c;发现90%的焦虑都集中在三个环节&#xff1a;文献查重时战战兢兢怕超标、降重改写时绞尽脑汁想表达、初稿撰写时面对空白文档毫…

作者头像 李华
网站建设 2026/9/15 6:29:51

硬盘数据恢复工具底层原理与工程实现全解析

在数据恢复这个行当里摸爬滚打这么多年&#xff0c;我越来越觉得&#xff0c;绝大多数人对于硬盘数据恢复工具的理解&#xff0c;都停留在“扫描一下、找回文件”的魔法层面。但真正等到误删了重要文件、硬盘咔咔作响、分区表不翼而飞的时候&#xff0c;又急得像热锅上的蚂蚁&a…

作者头像 李华