NeMo 流式 / 缓冲 / 分块 ASR 推理实战:AED 与 Transducer 模型的 chunked 与 streaming 解码指南
【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech
本指南围绕 NeMo(当前仓库)中examples/asr/asr_chunked_inference目录下的推理脚本,系统讲解 Streaming(流式)、Buffered(缓冲)、Chunked(分块)三种长音频 ASR 推理方式的原理、参数与实战用法。读完本文,你将掌握 Transducer(RNNT/TDT)模型与 MultitaskAED(如 Canary 系列)模型各自的分块推理脚本、流式解码策略(Wait-k / AlignAtt / Middle-Token / LCS)以及 chunk 大小与延迟、准确率之间的权衡方法,可直接复现仓库中的命令行示例。
三种推理模式的定位与区别
该目录同时承载了三类推理脚本,分别面向 Transducer 模型(RNNT、TDT)和 MultitaskAED 模型(如nvidia/canary-1b系列):
| 推理模式 | 适用模型 | 核心脚本 |
|---|---|---|
| Chunked Inference | MultitaskAED(Canary 等) | aed/speech_to_text_aed_chunked_infer.py |
| AED 流式推理 | MultitaskAED(Canary 等) | aed/speech_to_text_aed_streaming_infer.py |
| Buffered Inference | RNNT / TDT / Hybrid | rnnt/speech_to_text_buffered_infer_rnnt.py |
| 流式 / 缓冲推理(新) | RNNT / TDT / Hybrid | rnnt/speech_to_text_streaming_infer_rnnt.py |
对于 CTC 模型,本目录不适用,README 明确指引使用独立的 asr_streaming_inference.py 脚本。
Streaming 与 Buffered 的本质差异
README 指出:streaming ASR 与 buffered ASR 的主要区别在于 chunk size(块大小)和总上下文缓冲区(total context buffer)的大小,二者只是同一套“滑动窗口”机制的不同参数配置:
- 减小 chunk size:首个预测结果的延迟更低,模型输出文本的“滞后感”更短;但由于每个块内携带的信息变少,会导致更高的 WER(词错误率)。
- 增大 chunk size:语音与转写之间的延迟增加(这正是 buffered ASR 的形态);但模型拥有更多上下文来正确转写,能获得更准确的转录文本。
这一权衡贯穿本目录全部四个脚本:所有脚本都通过chunk(块)与left/right context(左右上下文)两个维度来控制“延迟 vs 准确率”的平衡点。
Chunked Inference:AED 模型的长音频分段推理
对于 MultitaskAED 模型,仓库提供 speech_to_text_aed_chunked_infer.py 执行分块推理。该脚本将输入音频切分为不重叠的chunk_len_in_secs秒片段,对每个片段单独执行推理,最后将各片段结果拼接成完整转录文本。
输入方式:audio_dir 与 dataset_manifest
脚本支持两种输入,且优先 audio_dir(从源码main()可见,audio_dir存在时manifest会被置为None,见 speech_to_text_aed_chunked_infer.py):
audio_dir:音频文件目录,脚本会以audio_type(默认wav)递归匹配目录下所有音频文件;dataset_manifest:JSON manifest 文件路径。
README 特别强调:推荐使用 manifest 输入,否则模型只会执行带标点和大小写的英文 ASR。manifest 每行格式如下:
{ "audio_filepath": "/path/to/audio.wav", # path to the audio file "duration": 10000.0, # duration of the audio "taskname": "asr", # use "s2t_translation" for AST "source_lang": "en", # Set `source_lang`==`target_lang` for ASR. Currently supported for 25 EU languages. "target_lang": "de", # See https://huggingface.co/nvidia/canary-1b-v2 }其中taskname为asr时执行语音识别,为s2t_translation时执行语音翻译(AST);source_lang与target_lang相等时即 ASR,仓库源码注释显示当前支持 25 种欧盟语言。
命令行示例与核心参数
python examples/asr/asr_chunked_inference/aed/speech_to_text_aed_chunked_infer.py \ model_path=null \ pretrained_name="nvidia/canary-1b-flash" \ audio_dir="<(optional) path to folder of audio files>" \ dataset_manifest="<(optional) path to manifest>" \ output_filename="<(optional) specify output filename>" \ chunk_len_in_secs=40.0 \ batch_size=16 \ decoding.beam.beam_size=1核心参数(依据脚本中TranscriptionConfigdataclass,见 speech_to_text_aed_chunked_infer.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
model_path/pretrained_name | None | 二者必填其一:本地.nemo文件路径,或 Hugging Face 预训练模型名(如nvidia/canary-1b-flash) |
audio_dir/dataset_manifest | None | 二者必填其一:音频目录或 manifest 路径 |
chunk_len_in_secs | 40.0 | 每个分块的时长(秒) |
model_stride | 8 | 模型下采样因子:FastConformer 为 8,Conformer 为 4 |
batch_size | 8 | 并行处理的 chunk 数量 |
timestamps | False | 输出词级与段级时间戳(仅部分模型支持) |
compute_langs | False | 输出语言 ID 信息 |
output_filename | None | 输出文件名;为None时输出到输入文件同目录 |
cuda | None | 指定 CUDA 设备;负数表示纯 CPU 推理 |
amp/amp_dtype | False/float16 | 自动混合精度开关,amp_dtype可为float16或bfloat16 |
overwrite_transcripts | True | 是否覆盖已存在的转录结果文件 |
calculate_wer | True | 计算词错误率,需要 manifest 中含text字段 |
时间戳与长音频的注意事项
- 源码在
main()入口处有一个重要约束:当timestamps=True而chunk_len_in_secs != 10.0时,脚本会打警告并强制将chunk_len_in_secs重置为 10.0(见 speech_to_text_aed_chunked_infer.py)。即开启时间戳时建议使用 10 秒分块以获得最优结果。 - Canary-1b-v2 长文推理:该模型可通过
.transcribe()方法进行长文推理,内部采用带重叠窗口的动态分块以获得更好性能。这一行为在转写单个音频文件或batch_size=1时自动启用。
底层原理:FrameBatchMultiTaskAED
从源码看,chunked 推理的核心类为 FrameBatchMultiTaskAED,它接收frame_len=chunk_len_in_secs、total_buffer=chunk_len_in_secs、batch_size三个参数——注意此处frame_len与total_buffer相等,即各 chunk 之间互不重叠,与 README 中“non-overlapping chunks”的描述一致。真正的推理循环由 get_buffered_pred_feat_multitaskAED() 驱动:它先根据preprocessor_cfg构建一个不执行归一化的预处理器(归一化在 frame_bufferer 内按 buffer 完成),逐条读取音频/ manifest,调用asr.read_audio_file()与asr.transcribe()得到假设结果。脚本在推理前还会把preprocessor.dither与preprocessor.pad_to置零,并要求模型使用per_feature归一化(见 speech_to_text_aed_chunked_infer.py)。
推理完成后,结果通过write_transcription()写入输出 manifest,若calculate_wer=True则调用cal_write_wer()计算并输出 WER/CER 汇总。
AED 流式推理:Wait-k 与 AlignAtt 解码策略
面向实时场景,仓库提供 speech_to_text_aed_streaming_infer.py 支持 AED(Canary)模型的流式解码,目前支持Wait-k与AlignAtt两种策略:
- Wait-k 策略:每个新的语音 chunk 只预测一个 token,整体延迟较高。由于不清楚在有限缓冲区下何时可以丢弃左侧上下文,建议将左上下文设为最大可能值(无限左上下文)。
- AlignAtt 策略:根据每次下一个 token 预测时的交叉注意力(cross-attention)条件决定:若条件满足,则无需增加音频输入即可继续预测下一个 token;否则需要扩大音频缓冲区。相比 Wait-k,该策略延迟更低,也适合固定左上下文的窗口识别(但可能损失部分准确率)。
音频缓冲区推荐配置
脚本 docstring 给出推荐的缓冲区设置:
- 以1.5s 延迟进行流式推理:
10-1-0.5(左上下文 10s、chunk 1s、右上下文 0.5s)。
命令行示例与关键参数
python examples/asr/asr_chunked_inference/aed/speech_to_text_aed_streaming_infer.py \ pretrained_name=nvidia/canary-1b-v2 \ model_path=null \ audio_dir="<optional path to folder of audio files>" \ dataset_manifest="<optional path to manifest>" \ output_filename="<optional output filename>" \ right_context_secs=0.5 \ chunk_secs=1.0 \ left_context_secs=10.0 \ batch_size=32 \ clean_groundtruth_text=False \ langid='en' \ decoding.streaming_policy=alignatt该脚本的配置在TranscriptionConfig中定义(见 speech_to_text_aed_streaming_infer.py),关键参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
chunk_secs | 2 | 流式 chunk 时长(秒) |
left_context_secs | 10.0 | 左上下文:增大可提升质量且不影响理论延迟 |
right_context_secs | 2 | 右上下文,直接影响延迟 |
decoding.streaming_policy | — | 必须是alignatt或waitk,否则脚本抛ValueError(见 speech_to_text_aed_streaming_infer.py) |
decoding.alignatt_thr | 8 | AlignAtt 策略的交叉注意力阈值 |
decoding.waitk_lagging | 2 | 起始等待的 chunk 数,对两种策略均生效 |
decoding.exclude_sink_frames | 8 | 计算 xatt 分数时排除的帧数(AlignAtt 专用) |
decoding.xatt_scores_layer | -2 | 获取交叉注意力分数的层(AlignAtt 专用) |
decoding.hallucinations_detector | True | 检测预测 token 中的幻觉(两种策略均生效) |
calculate_bleu | False | AST 任务下计算 BLEU |
calculate_latency | True | 计算 LAAL 解码延迟 |
解码过程的工程细节
从源码看,该脚本对上下文做了帧级对齐处理:先由make_divisible_by()将“音频采样数→特征帧→编码器帧”的换算因子对齐到编码器下采样因子的整数倍,再通过ContextSize把 left/chunk/right 三个秒级配置换算成编码器帧数与音频采样数(见 speech_to_text_aed_streaming_infer.py)。脚本会在日志中输出“修正后的上下文(秒)”与理论延迟:
Theoretical latency: {latency_secs:.2f} seconds即(chunk + right_context)对应的音频时长。解码主循环使用StreamingBatchedAudioBuffer维护滑动音频缓冲区,每次向模型送入完整的[left | chunk | right]缓冲,随后仅解码 chunk 对应帧的编码器输出,并滑动窗口继续处理下一个 chunk(见 speech_to_text_aed_streaming_infer.py)。
延迟评估:LAAL 指标
脚本内置延迟评估:calculate_latency=True时,按策略调用compute_waitk_lagging()或compute_alignatt_lagging(),输出基于Length-Adaptive Average Lagging (LAAL)度量的平均解码延迟(毫秒)。两种策略的取舍可概括为:
- Wait-k:准确率更高,需要更大的左上下文,延迟更高;
- AlignAtt:延迟更低,每个 chunk 可预测多个 token,更适合生产环境。
更多参数细节可参考仓库配套文档 canary_chunked_and_streaming_decoding.rst,其中还强调了 AST 任务下应使用+prompt.pnc、+prompt.task、+prompt.source_lang、+prompt.target_lang等 prompt 参数控制模型行为。
RNNT Buffered 推理:Middle Token、LCS 与 TDT 合并算法
对于 Transducer 系模型,speech_to_text_buffered_infer_rnnt.py 提供缓冲推理。缓冲推理是音频片段长于 20~30 秒时的首选转写方式——尤其对 Conformer 这类时间与内存随音频时长二次增长的模型至关重要。它与流式推理的区别同样是 chunk 大小:缓冲推理使用大 chunk(5~10 秒)加额外上下文缓冲;流式推理使用小 chunk(0.1~0.25 秒)加额外右上下文缓冲。
Middle Token 合并算法
python examples/asr/asr_chunked_inference/rnnt/speech_to_text_buffered_infer_rnnt.py \ model_path=null \ pretrained_name=null \ audio_dir="<remove or path to folder of audio files>" \ dataset_manifest="<remove or path to manifest>" \ output_filename="<remove or specify output filename>" \ total_buffer_in_secs=4.0 \ chunk_len_in_secs=1.6 \ batch_size=32 \ clean_groundtruth_text=True \ langid='en'Longer Common Subsequence (LCS) 合并算法
python examples/asr/asr_chunked_inference/rnnt/speech_to_text_buffered_infer_rnnt.py \ model_path=null \ pretrained_name=null \ audio_dir="<remove or path to folder of audio files>" \ dataset_manifest="<remove or path to manifest>" \ output_filename="<remove or specify output filename>" \ total_buffer_in_secs=4.0 \ chunk_len_in_secs=1.6 \ batch_size=32 \ merge_algo="lcs" \ lcs_alignment_dir=<OPTIONAL: Some path to store the LCS alignments>此外可用DEBUG=1环境变量启动脚本,打印模型预测与 manifest 中的真实文本(如果存在)。
合并算法与模型类型的对应关系
脚本在运行时自动检测模型类型并选择默认算法:若模型损失为TDTLossNumba(TDT 模型),默认merge_algo="tdt",否则默认"middle";TDT 模型强制要求merge_algo='tdt',否则抛异常(见 speech_to_text_buffered_infer_rnnt.py)。三种算法的底层实现类均位于 streaming_utils.py:
middle:使用 BatchedFrameASRRNNT,基于中间 token拼接相邻 chunk 的预测;lcs:使用 LongestCommonSubsequenceBatchedFrameASRRNNT,基于最长公共子序列做文本对齐合并,可指定lcs_alignment_dir保存中间对齐结果,且需通过frame_asr.lcs_delay = floor((total_buffer - chunk_len) / model_stride_in_secs)设置算法延迟;tdt:使用 BatchedFrameASRTDT。
关键参数与解码配置
| 参数 | 默认值 | 说明 |
|---|---|---|
chunk_len_in_secs | 1.6 | chunk 时长(秒) |
total_buffer_in_secs | 4.0 | 总缓冲区时长(chunk + 左右填充),秒 |
merge_algo | None | middle/lcs/tdt;None时按模型类型自动选择 |
max_steps_per_timestep | 5 | 每个声学时间步最多解码的 token 数 |
stateful_decoding | False | 是否启用跨 buffer 的状态保持解码 |
compute_timestamps | False | 输出时间戳(仅部分模型支持) |
脚本会自动改写解码配置:启用stateful_decoding或 TDT 时使用greedy策略,否则使用greedy_batch;同时强制preserve_alignments=True(计算中间 token 必需)、fused_batch_size=-1(临时关闭融合批处理)、beam.return_best_hypothesis=True(仅返回最优假设,见 speech_to_text_buffered_infer_rnnt.py)。
注意:该脚本运行时会打印弃用警告,推荐改用新的speech_to_text_streaming_infer_rnnt.py。
RNNT 新一代流式 / 缓冲推理脚本
speech_to_text_streaming_infer_rnnt.py 是 RNNT 缓冲推理的替代与升级版本,同时支持缓冲与流式两种模式。其理论延迟(不含模型推理时间的纯延迟)=chunk 时长 + 右上下文时长;保持较大的左上下文(约 10s)并非必需,但可以提升转录质量。
推荐设置
脚本 docstring 给出的经验配置:
- 长文件转写:多数场景下
10-10-5(左上下文 10s、chunk 10s、右上下文 5s)可获得接近离线推理的结果; - 4s 延迟流式:
10-2-2通常与10-0.16-3.84效果相当或更好,且显著更快。
命令行示例
python examples/asr/asr_chunked_inference/rnnt/speech_to_text_streaming_infer_rnnt.py \ pretrained_name=nvidia/parakeet-rnnt-1.1b \ model_path=null \ audio_dir="<optional path to folder of audio files>" \ dataset_manifest="<optional path to manifest>" \ output_filename="<optional output filename>" \ right_context_secs=2.0 \ chunk_secs=2 \ left_context_secs=10.0 \ batch_size=32 \ clean_groundtruth_text=False \ langid='en'相比旧脚本的新增能力
从TranscriptionConfig(见 speech_to_text_streaming_infer_rnnt.py)可以看出,新脚本在旧脚本基础上扩展了多项能力:
- 上下文三要素独立配置:
left_context_secs/chunk_secs/right_context_secs,脚本会打印修正后的上下文与理论延迟; - 小 chunk 显存优化:脚本在导入 PyTorch/NeMo 之前自动向
PYTORCH_CUDA_ALLOC_CONF注入expandable_segments:True,注释表明使用小 chunk 时最多可节省超过 10 倍的 GPU 显存; - 解码策略:支持
greedy_batch(含 Label-Looping)、malsd_batch、maes_batch,分别对应GreedyBatchedLabelLoopingComputerBase、ModifiedALSDBatchedRNNTComputer、ModifiedAESBatchedRNNTComputer; - Per-Stream Biasing:
use_per_stream_biasing=True时可按 manifest 中每条样本的biasing_request做上下文偏置; - 模拟解码:
simulated=True时先按 chunk 计算编码器输出并拼接,再一次性解码,用于快速实验不同解码算法(理论上应与支持流式解码的算法结果一致); - 时间戳与置信度:
timestamps输出词级时间戳(TDT 模型通过tdt_include_token_duration支持),confidence输出词级置信度; - 性能评估:
calculate_rtfx=True时输出 RTFx(实时率因子倒数),calculate_wer=True时计算 WER/CER。
流式主循环源码解析
与 AED 脚本类似,主循环按[left | chunk | right]窗口滑动:StreamingBatchedAudioBuffer.add_audio_batch_()填充缓冲区 → 编码器一次性处理整段缓冲 → 裁掉左右上下文、仅保留 chunk 对应的编码器帧 → 解码器在上一 chunk 状态(prev_batched_state)基础上继续解码 → 假设通过merge_()与历史合并,窗口前移一个 chunk(见 speech_to_text_streaming_infer_rnnt.py)。对于att_context_style='chunked_limited_with_rc'的统一 ASR 模型,脚本还会通过set_default_att_context_size()把注意力窗口与解码 chunk 对齐,这是实现极低延迟的关键。
参数速查与最佳实践总结
通用输入约束
四个脚本共同的约束(均由源码校验):
model_path与pretrained_name必须至少提供一个,否则抛ValueError;audio_dir与dataset_manifest必须至少提供一个;- 模型预处理器必须使用
per_feature归一化,否则脚本报错(仅记录错误日志); - 流式场景下
dither=0.0、pad_to=0会被强制设置,以保证分块特征一致性。
不同场景的参数选型建议
| 场景 | 模型类型 | 推荐配置 | 预期延迟 |
|---|---|---|---|
| 长音频离线转写 | RNNT | left=10s, chunk=10s, right=5s | 接近离线 |
| 流式转写(4s 延迟) | RNNT | left=10s, chunk=2s, right=2s | 约 4s |
| 流式转写(1.5s 延迟) | Canary (AED) | left=10s, chunk=1s, right=0.5s | 约 1.5s |
| 高准确率流式 | Canary (AED) | Wait-k + 大左上下文 | 较高 |
| 低延迟生产流式 | Canary (AED) | AlignAtt + 固定窗口 | 较低 |
实践要点
- chunk 越小延迟越低、WER 越高:chunk 大小是调节“首字延迟”与“准确率”的核心旋钮,所有脚本的 README 与 docstring 都围绕这一点展开。
- 左上下文只影响质量、不影响理论延迟:左右上下文与 chunk 独立配置,理论延迟恒为
chunk + right。 - manifest 优于裸音频目录:manifest 可以指定
taskname、source_lang、target_lang,从而控制 ASR/AST 任务与语言方向,避免默认英文 ASR。 - 开启时间戳注意 chunk 限制:AED chunked 脚本在
timestamps=True时会强制chunk_len_in_secs=10.0。 - 小 chunk 记得启用显存优化:RNNT 新脚本默认注入
expandable_segments,如手动设置PYTORCH_CUDA_ALLOC_CONF请保留该选项。
延伸阅读
- 配套文档:Canary Chunked and Streaming Decoding,包含 Wait-k / AlignAtt 参数与
+prompt用法; - 流式相关教程:Streaming_ASR.ipynb、Buffered_Transducer_Inference.ipynb、Buffered_Transducer_Inference_with_LCS_Merge.ipynb;
- CTC 模型流式推理:asr_streaming_inference;
- 底层实现:streaming_utils.py(
FrameBatchMultiTaskAED、BatchedFrameASRRNNT、LongestCommonSubsequenceBatchedFrameASRRNNT、ContextSize、StreamingBatchedAudioBuffer)、transcribe_utils.py(get_buffered_pred_feat_rnnt、get_buffered_pred_feat_multitaskAED)。
【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考