如何用 NeMo Sortformer 完成多说话人音轨的说话人分离
【免费下载链接】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
如果你有一段多人对话或会议的录音,需要知道“谁在什么时候说话”(who spoke when),而不是把它拆成 VAD、说话人嵌入提取、聚类三个模块自己拼装,NeMo Speech 提供的端到端模型 Sortformer 可以直接完成这件事:输入音频,输出按说话人切分的时间段。NeMo 提供了三个预训练 Sortformer 检查点,全部支持最多 4 个说话人:
| 模型 | 类型 | 适用场景 |
|---|---|---|
nvidia/diar_sortformer_4spk-v1 | Offline | 离线批处理分离 |
nvidia/diar_streaming_sortformer_4spk-v2 | Streaming | 流式实时分离 |
nvidia/diar_streaming_sortformer_4spk-v2.1 | Streaming | 流式,对会议场景语音鲁棒性更好 |
下面给出一条从加载模型到批量出结果的完整路径:先写三行 Python 代码验证模型能跑,再用仓库自带的推理脚本处理多条音轨,最后用 RTTM 输出和 DER 指标核对结果。
选择哪种 Sortformer 模型
选择依据以 模型文档 和 README 为准:
- 离线批处理:直接用
diar_sortformer_4spk-v1,它是离线版本,一条模型同时覆盖分离任务。 - 实时/流式:用 streaming 版本。streaming 模型按小块重叠音频处理,用 Arrival-Order Speaker Cache(AOSC)保存之前出现过的说话人声学嵌入,保证同一个人跨块保持同一标签。
- 用 streaming 模型做离线分离:文档明确说明 online 版本可以通过设置足够长的 chunk size 用于离线分离(见 models.rst),此时需要手动设置下面几段代码中的
sortformer_modules参数。
三个模型都限制为最多 4 个说话人(4spk),超过这个数的音轨不在预训练模型的支持范围内。
最短路径:三行代码分离一条音频
前提:已安装 NeMo(见 安装文档 中 “installing NeMo” 一节)。from_pretrained会按 HuggingFace 模型名下载检查点,先登录:
pip install -U "huggingface_hub[cli]" huggingface-cli login离线模型(README 中的示例,/path/to/audio.wav替换为你的音频路径):
from nemo.collections.asr.models import SortformerEncLabelModel diar_model = SortformerEncLabelModel.from_pretrained("nvidia/diar_sortformer_4spk-v1") diar_model.eval() predicted_segments = diar_model.diarize(audio="/path/to/audio.wav", batch_size=1)streaming 模型:在调用diarize之前需要设置四个流式推理参数(下面示例用 v2.1,参数取自我文 README 中的官方示例):
from nemo.collections.asr.models import SortformerEncLabelModel diar_model = SortformerEncLabelModel.from_pretrained("nvidia/diar_streaming_sortformer_4spk-v2.1") diar_model.eval() diar_model.sortformer_modules.chunk_len = 340 diar_model.sortformer_modules.chunk_right_context = 40 diar_model.sortformer_modules.fifo_len = 40 diar_model.sortformer_modules.spkcache_update_period = 300 predicted_segments = diar_model.diarize(audio="/path/to/audio.wav", batch_size=1)返回值predicted_segments就是模型为输入音频预测出的说话人分离时间段。streaming 版本的文档性能表列出了 0.32s 到 30.4s 多档延迟下的 DER,说明不同流式参数组合对应不同延迟档位;上表参数组是文档给出的可用配置,不自行换算其与延迟档位的对应关系。
批量处理多条音轨:manifest + 推理脚本
单条音频验证通过后,批量任务用仓库脚本 e2e_diarize_speech.py。它读取 JSON-lines manifest,对每条音频跑分离推理,可选地输出 RTTM 并计算 DER。
1. 准备 manifest 文件
每行一个 JSON 对象,audio_filepath为必填字段,其余可选(格式来自 datasets 文档):
{"audio_filepath": "/path/to/abcd.wav", "offset": 0, "duration": null, "label": "infer", "text": "-", "num_speakers": null, "rttm_filepath": "/path/to/rttm/abcd.rttm"}其中rttm_filepath指向该音频的参考 RTTM 标注文件,是后面计算 DER 的依据。不想手写的话,用仓库脚本从路径清单生成 manifest(--paths2audio_files和--manifest_filepath必填,音频与 RTTM 文件按相同基础文件名配对,如abcd01.wav对应abcd01.rttm):
python scripts/speaker_tasks/pathfiles_to_diarize_manifest.py \ --paths2audio_files /path/to/audio_file_path_list.txt \ --paths2rttm_files /path/to/rttm_file_path_list.txt \ --manifest_filepath /path/to/manifest_output/input_manifest.json两个路径清单文件每行一个绝对路径,例如audio_file_path_list.txt:
/path/to/abcd01.wav /path/to/abcd02.wav长录音不需要预先物理切短:数据加载器只读取 manifest 条目中offset和duration指定的时间段,对同一音频可建多个不同时间窗的条目(见 datasets 文档 的 Long-Form Audio 一节)。
2. 运行推理
以离线模型为例(/path/to/...均替换为你自己的实际路径):
python examples/speaker_tasks/diarization/neural_diarizer/e2e_diarize_speech.py \ batch_size=1 \ model_path=/path/to/diar_sortformer_4spk-v1.nemo \ dataset_manifest=/path/to/diarization_manifest.json \ out_rttm_dir=/path/to/rttm_output关键参数说明:
batch_size=1:文档明确建议用 1 以获得最长推理窗口和最高精度。model_path:本地.nemo(或.ckpt)文件。注意 README 称此处也可传 HuggingFace 模型名,但脚本源码中model_path不以.nemo/.ckpt结尾时会直接抛出ValueError,两条文档不一致,按脚本实现以本地检查点文件为准。out_rttm_dir:推理得到的预测 RTTM 会写入该目录(仅在不跳过 DER 分支时生效,即不传no_der=True)。postprocessing_yaml:可选。默认情况下脚本跳过 post-processing、只做二值化;若要复现模型卡上报告的 DER 分数,需要传入 post-processing 配置。仓库自带两套优化好的参数(在 post_processing 目录 下):
python examples/speaker_tasks/diarization/neural_diarizer/e2e_diarize_speech.py \ batch_size=1 \ model_path=/path/to/diar_sortformer_4spk-v1.nemo \ postprocessing_yaml=examples/speaker_tasks/diarization/conf/post_processing/sortformer_diar_4spk-v1_callhome-part1.yaml \ dataset_manifest=/path/to/diarization_manifest.json注意这两套参数是按域优化出来的:*_dihard3-dev.yaml在 DIHARD3 开发集上优化,*_callhome-part1.yaml对应通话语音,换域使用时不要假定其中任意一套都是全局最优。以离线 v1 的 DIHARD3 参数为例(sortformer_diar_4spk-v1_dihard3-dev.yaml):
parameters: onset: 0.64 # 检测语音段开始的阈值 offset: 0.74 # 检测语音段结束的阈值 pad_onset: 0.06 # 每个语音段开头增加的长度 pad_offset: 0.0 # 每个语音段结尾增加的长度 min_duration_on: 0.1 # 短于该时长的语音段被移除 min_duration_off: 0.15 # 短于该时长的静音被移除no_der:默认False,manifest 里带rttm_filepath时脚本会计算 DER;若只做推理不评估,可传no_der=True跳过 DER 计算。
用 streaming 检查点跑脚本:model_path换成 streaming 模型的.nemo文件即可,脚本检测到 streaming 模式后会自动应用流式参数,并允许用同名 Hydra 参数覆盖,例如:
python examples/speaker_tasks/diarization/neural_diarizer/e2e_diarize_speech.py \ batch_size=1 \ model_path=/path/to/diar_streaming_sortformer_4spk-v2.nemo \ dataset_manifest=/path/to/diarization_manifest.json \ chunk_len=340 \ chunk_right_context=40 \ fifo_len=40 \ spkcache_update_period=300这组值与上文 Python 路径中 README 示例一致;流式场景还可用async_streaming、async_pad_to_max等脚本参数调整(见脚本头部 DiarizationConfig 的定义)。
验证分离结果
结果核对分两层:
- 看输出文件:
out_rttm_dir下会生成每条音频对应的预测 RTTM(SPEAKER <会话> <通道> <起始> <时长> ...逐行时间戳),可以直接检查说话人时间段是否符合预期。 - 看 DER 分数:脚本日志会输出
Running offline diarization evaluation...和Evaluating the model on {N} audio segments...,随后打印按collar(默认 0.25 秒)计算的 Diarization Error Rate。manifest 中未提供 RTTM 的文件无法参与 DER 评分,因此需要评估时就务必带上rttm_filepath。
DER 的合理量级可对照官方模型卡数据(README,均为含重叠语音的官方评测,仅供参考基准而非你的必得结果):
| 模型 | 数据集 | Collar | DER (无后处理) | DER (有后处理) |
|---|---|---|---|---|
| diar_sortformer_4spk-v1 | DIHARD3-Eval (≤4spk) | 0.0s | 16.28 | 14.76 |
| diar_sortformer_4spk-v1 | CALLHOME-part2 (2spk) | 0.25s | 6.49 | 5.85 |
| diar_sortformer_4spk-v1 | CH109 | 0.25s | 6.27 | 6.86 |
| diar_streaming_sortformer_4spk-v2 | DIHARD III Eval (≤4spk) | 0.0s | — | 13.45(30.4s 延迟) |
限制与边界
- 说话人数上限 4:三个预训练模型都只覆盖最多 4 个说话人,更多说话人的场景文档没有提供预训练方案。
- 文件名一致性:用
pathfiles_to_diarize_manifest.py生成 manifest 时,音频、RTTM(及可选的 txt/uem/ctm)必须共享唯一的基础文件名,只有扩展名不同。 - 离线与流式是两种使用形态,不要混在同一条操作链里:本文的 Python 三行路径和脚本路径都默认离线批处理;streaming 参数(
chunk_len等)只在加载 streaming 检查点时才有意义。 - post-processing 参数是域相关的:
dihard3-dev与callhome-part1两套 yaml 分别在不同域的数据上优化,不要跨域照搬后仍期望模型卡级别的 DER。
下一步
如果预训练模型在你的目标域上不够用,仓库文档给出了 Sortformer 的继续训练入口 sortformer_diar_train.py(Hydra 配置加manifest_filepath等参数即可启动训练);post-processing 参数也可以用脚本的launch_pp_optim=True选项交给 Optuna 在你的验证集上搜索。这两条都是独立任务,需要时按 results 文档 与脚本参数说明另行执行。
【免费下载链接】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),仅供参考