news 2026/9/13 9:19:22

NeMo 说话人日志(Speaker Diarization)数据集准备实战指南:Manifest 格式、长音频切分与训练/推理数据构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NeMo 说话人日志(Speaker Diarization)数据集准备实战指南:Manifest 格式、长音频切分与训练/推理数据构建

NeMo 说话人日志(Speaker Diarization)数据集准备实战指南:Manifest 格式、长音频切分与训练/推理数据构建

【免费下载链接】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(Speech)仓库中说话人日志系统的数据准备工作,系统讲解训练与推理共用的 JSON-lines manifest 格式、pathfiles_to_diarize_manifest.py脚本的完整用法、RTTM/UEM/CTM 标注格式规范,以及长音频按offset/duration虚拟切分、num_speakers语义与说话人数量覆盖等关键数据工程要点。读完后,你可以独立完成端到端(Sortformer)与级联(VAD+Embedding+Clustering)两种日志系统的数据构建与推理数据准备,并理解 NeMo dataloader 底层的窗口化标注实现。

一、总览:训练与推理共用的 Manifest 格式

说话人日志训练需要一个JSON-lines 格式的 manifest 文件:每一行描述一个训练片段,指向一个音频文件及其对应的 RTTM(Rich Transcription Time Marked)真值标签文件。训练与推理使用完全相同的 manifest 格式,这是 NeMo 说话人任务数据约定的核心。

原始文档中给出的单行 manifest 示例如下:

{"audio_filepath": "/path/to/abcd01.wav", "offset": 0, "duration": 90, "label": "infer", "text": "-", "num_speakers": 2, "rttm_filepath": "/path/to/rttm/abcd01.rttm"}

从 NeMo 源码 manifest_utils.py 中的create_manifest函数可以看到,由脚本生成的 manifest 每行实际包含如下完整字段集合:

{ "audio_filepath": "/path/to/abcd01.wav", "offset": 0, "duration": 90.0, "label": "infer", "text": "-", "num_speakers": 2, "rttm_filepath": "/path/to/rttm/abcd01.rttm", "uem_filepath": null, "ctm_filepath": null }

几个源码层面的细节值得注意:

  • num_speakers由 RTTM 自动统计create_manifest会读取对应 RTTM 文件,用Counter统计不同说话人 ID 的数量写入 manifest(若未提供 RTTM 文件则为null);
  • text字段的缺省值是"-":未提供转写文件时,脚本不会报错,而是填充占位符"-"
  • --add_duration通过 librosa 计算时长:脚本会逐文件加载音频(librosa.loadlibrosa.get_duration)把真实时长写入 manifest,因此对大文件目录该选项会显著拖慢生成速度,但能确保 manifest 中duration与音频真实长度一致。

二、用pathfiles_to_diarize_manifest.py生成 Manifest

NeMo 提供脚本 pathfiles_to_diarize_manifest.py,用于从「音频路径列表 + RTTM 路径列表」等文本清单直接生成 manifest。

2.1 训练场景的最小用法

原始文档给出的训练 manifest 生成命令(三个参数均为必需):

python <NeMo_git_root>/scripts/speaker_tasks/pathfiles_to_diarize_manifest.py \ --add_duration \ --paths2audio_files="/path/to/audio_file_path_list.txt" \ --paths2rttm_files="/path/to/rttm_file_list.txt" \ --manifest_filepath="/path/to/train_manifest.json"

其中--paths2audio_files--manifest_filepath是必填参数,--paths2rttm_files在训练场景中必须提供。音频与 RTTM 文件按基础文件名(base filename)配对:例如abcd01.wav对应abcd01.rttm

对应的路径列表文件内容示例:

  • audio_file_path_list.txt
/path/to/abcd01.wav /path/to/abcd02.wav
  • rttm_file_path_list.txt
/path/to/abcd01.rttm /path/to/abcd02.rttm

命名约束:所有提供的文件(音频、RTTM、文本)必须共享同一基础名,且每个基础名在整个数据集中必须唯一。这一点在脚本源码的模块注释中同样被强调:“make sure basename for each file is unique and rttm files also has the corresponding base name for mapping”。

2.2 推理场景的完整用法

推理(同时支持端到端与级联系统)的 manifest 生成支持更多可选文件类型。原始文档给出的完整命令如下:

python pathfiles_to_diarize_manifest.py \ --paths2audio_files /path/to/audio_file_path_list.txt \ --paths2txt_files /path/to/transcript_file_path_list.txt \ --paths2rttm_files /path/to/rttm_file_path_list.txt \ --paths2uem_files /path/to/uem_file_path_list.txt \ --paths2ctm_files /path/to/ctm_file_path_list.txt \ --manifest_filepath /path/to/manifest_output/input_manifest.json

argparse定义(见脚本源码)对照,全部参数及语义如下:

参数是否必需说明
--paths2audio_files必需音频文件绝对路径清单文本文件
--manifest_filepath必需输出 manifest 文件路径
--paths2txt_files可选真值转写文件清单(用于“日志 + ASR”联合推理的转写比对)
--paths2rttm_files可选RTTM 真值清单;提供后自动启动 DER 评估
--paths2uem_files可选UEM 计分区间清单
--paths2ctm_files可选CTM 词级时间戳清单(用于词级日志评估)
--add_duration可选通过加载音频计算并写入真实时长

生成后,推理时把 manifest 绝对路径通过 Hydra 配置传入(diarizer节点下的必填项):

diarizer.manifest_filepath="path/to/manifest/input_manifest.json"

完整的推理侧 Hydra 配置(VAD、Speaker Embedding、Clustering、ASR 各模块参数)请参考 configs.rst 以及examples/speaker_tasks/diarization/conf/下的示例 YAML。

三、标注文件格式详解:RTTM、UEM 与 CTM

3.1 RTTM(Rich Transcription Time Marked)

RTTM 提供逐说话人的语音活动时间戳,每行结构为:

SPEAKER TS3012d.Mix-Headset 1 32.679 0.671 <NA> <NA> MTD046ID <NA> <NA>

各字段依次为:类型(SPEAKER)、会话名、通道 ID、起始时间、持续时间、<NA><NA>、说话人 ID、<NA><NA>

在训练数据加载过程中,RTTM 行会经过 audio_to_diar_label.py 中的convert_rttm_line/extract_frame_info_from_rttm解析为(start, end, speaker)三元组,再转换为帧级多通道 one-hot 标签矩阵(每个说话人一行列的激活图),并进一步聚合到模型的目标帧率上。源码中还会自动跳过 start > end 的非法 RTTM 行,因此制作标注数据时即使存在个别坏行也不会直接中断训练,但会静默丢失对应区间。

3.2 UEM(计分区间文件)

UEM 用于指定评估时真正计分的音频区间,行格式为<uniq-id> <channel ID> <start time> <end time>,其中通道 ID 固定为 1:

TS3012d.Mix-Headset 1 12.31 108.98 TS3012d.Mix-Headset 1 214.00 857.09

典型用途是排除长录音中无人说话的开头/结尾,或剔除设备未开启的区间,避免这些区间拉高 DER。

3.3 CTM(词级时间戳文件)

CTM 用于词级日志结果与词时间戳对齐的评估,行格式为<session name> <channel ID> <start time> <duration> <word> <confidence> <type of token> <speaker>

TS3012d.Mix-Headset 1 12.879 0.32 okay NA lex MTD046ID TS3012d.Mix-Headset 1 13.203 0.24 yeah NA lex MTD046ID

注意事项:

  • <speaker>必须与 RTTM 中的说话人 ID完全一致
  • 评估日志结果不需要置信度,故<confidence>NA
  • token 类型为词时,<type of token>lex

四、Manifest 各字段说明(推理场景)

推理输入 manifest 的完整一行示例:

{"audio_filepath": "/path/to/abcd.wav", "offset": 0, "duration": null, "label": "infer", "text": "-", "num_speakers": null, "rttm_filepath": "/path/to/rttm/abcd.rttm", "uem_filepath": "/path/to/uem/abcd.uem"}

逐字段说明:

  • audio_filepath(必需):音频文件绝对路径字符串;
  • num_speakers(可选):已知说话人数量时填整数,未知时填null。在级联系统的聚类的oracle_num_speakers: True场景下会使用该值(见 configs.rst 中 clustering 配置);
  • rttm_filepath(可选):提供后自动发起日志评估(DER 等指标);
  • text(可选):用于“日志 + ASR”推理时提供真值转写字符串,例如{"text": "this is an example transcript"}
  • uem_filepath(可选):指定计分区间(见第 3.2 节);
  • ctm_filepath(可选):词级评估(见第 3.3 节)。

audio_filepath外所有字段均可缺省,按需组合即可覆盖不同推理设置。

五、长音频处理:offset/duration 虚拟切分

长录音不需要物理切分成多个短文件。NeMo 的日志 dataloader 只会读取每条 manifest 记录中offsetduration指定的音频区间;对应的 RTTM 文件也会自动窗口化(windowed)到相同时间范围,无需为每个片段单独制作 RTTM。

切分长录音的做法是:创建多条 manifest 记录,引用同一个音频文件和同一个 RTTM 文件,但赋予不同的offset/duration。每条记录必须带有唯一的uniq_id,以便 dataloader 区分来自同一源文件的不同片段。推荐的命名约定是<base_name>#<index>#<offset>#<duration>

原始文档中,把一段 45 分钟录音(EN2001a.Mix-Headset.wav)按 90 秒窗口切分后,其中一条记录示例如下(整段会话有 4 位说话人,但该片段内只有 3 位活跃):

{"uniq_id": "EN2001a.Mix-Headset#116#932.92#90.0", "offset": 932.92, "duration": 90, "num_speakers": 3, "audio_filepath": "/path/to/wav/EN2001a.Mix-Headset.wav", "rttm_filepath": "/path/to/rttm/EN2001a.Mix-Headset.rttm"}

从源码结构看,这一机制的实现链路是:manifest 由 collections.py 中的EndtoEndDiarizationSpeechLabel解析出每条样本的offset/duration,随后 audio_to_diar_label.py 中的extract_frame_info_from_rttm(offset, duration, rttm_lines)只保留与该时间窗相交的 RTTM 区间,并把起止时间裁剪到窗口内(start, end = max(start, rttm_stt), min(end, rttm_end));get_frame_targets_from_rttm再把裁剪后的区间栅格化为帧级标签。get_uniq_id_with_range方法则进一步以文件基名_offset_endtime(毫秒精度)的形式为训练样本生成唯一 ID,印证了文档中“同一源文件产生多个训练样本时必须可区分”的约束。

六、num_speakers与部分说话人出现

一个训练片段可能只包含整段录音中部分说话人。num_speakers字段应反映该片段中实际活跃的说话人数,而不是整个会话的总人数(如第五节示例中:会话共 4 人,片段记 3 人)。

实操上,num_speakers可选字段——dataloader 可以从片段时间窗内的 RTTM 标签自动推断说话人数。原始文档将其归因于audio_to_diar_label.py中的DiarizationLabelDataset类;按当前仓库源码核实,该文件中的端到端日志数据集类实际名为AudioToSpeechE2ESpkDiarDataset(内部基类_AudioToSpeechE2ESpkDiarDataset),其parse_rttm_for_targets_and_lens等方法正是从 RTTM 窗口化标签生成训练目标的过程,推断逻辑一致。

此外源码还有一个值得留意的边界行为:当片段内说话人数超过模型配置的max_num_of_spks时,get_frame_targets_from_rttm会发出 warning 并只保留前max_spks位说话人(“Only {max_spks} first speakers remain, and this will affect frame metrics!”)。这解释了为什么训练时片段内说话人数与模型上限对齐很重要——超限片段不会报错,但标签会被静默截断,影响训练质量。

七、训练数据的说话人数量覆盖

模型无法泛化到训练中从未遇到过的说话人数量。若目标场景最多涉及N位说话人,训练集必须包含从 1 到 N 的每一个说话人数出现的片段。当高说话人数的自然数据稀缺时,原始文档给出三种补充手段:

  1. **过采样(Oversampling)**包含更多说话人的片段;
  2. 数据增强(Augmenting):用模拟多人混合语音(如基于 LibriSpeech 的合成混合)补足。仓库中 multispeaker_simulator.py 与配套教程 Multispeaker_Simulator.ipynb 提供了合成多说话人混音数据的工具链;
  3. 按说话人数量扩展数据量:例如 1 人片段 100 小时、2 人片段 200 小时、3 人片段 300 小时,以此类推。

训练与推理场景的说话人数量分布匹配,对取得好的日志性能至关重要。

八、训练 vs 推理:两种数据准备路径的差异小结

维度端到端(End-to-End)训练推理(端到端 + 级联通用)
manifest 必需字段audio_filepath+rttm_filepathaudio_filepath
生成脚本pathfiles_to_diarize_manifest.py--paths2audio_files--paths2rttm_files--manifest_filepath均必需,常加--add_duration同一脚本,txt/uem/ctm 等参数按需提供
长音频切分多条记录共享同一音频/RTTM,offset/duration+uniq_id区分单条记录即可,offset/duration支持局部推理
级联系统额外数据不适用端到端训练 manifest 之外,级联训练还需进一步加工为“成对双说话人会话”文件(原始文档仅作提示,具体流程见examples/speaker_tasks/diarization/下的训练入口与配置)

需要提醒的是:原始文档在级联训练处仅提示“manifest 应进一步加工以生成 pairwise two-speaker session files”,未展开具体步骤;如果你使用的是级联(VAD+Embedding+Clustering)路线,建议结合 configs.rst 中的train_ds配置项与examples/speaker_tasks/diarization/conf/下的 YAML 继续查证字段含义。

九、实操检查清单

在把 manifest 交给 NeMo 训练/推理流程前,建议逐项核对:

  1. 基名唯一性:每个音频文件的基础名在全数据集中唯一;RTTM/TXT/UEM/CTM 文件与音频严格同基名、仅扩展名不同(脚本以基名为 key 配对,重名会被静默覆盖);
  2. RTTM 合法性:确认每行duration > 0(即 start < end),非法行会被 dataloader 跳过而非报错;
  3. 说话人 ID 一致性:RTTM 与 CTM 中的 speaker ID 字符串必须完全一致;
  4. num_speakers语义:记录的是片段内活跃人数,可留空由 RTTM 推断;确保片段内人数不超过模型max_num_of_spks
  5. 说话人数量覆盖:训练集覆盖 1..N 全部说话人数,并按第 7 节策略补足高人数数据;
  6. 时长字段:使用--add_duration生成后抽查若干条durationffprobe/真实音频长度一致。

按以上流程构建的 manifest 可直接用于 neural_diarizer 训练入口 及examples/speaker_tasks/diarization/conf/inference/下的推理配置,完成从数据准备到日志系统训练/评估的完整闭环。

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

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

HyperFrames深度解析:多传感器数据的高维张量容器

1. 从名字说起&#xff1a;hyperframes 到底是什么 我最早接触“hyperframes”这个词&#xff0c;是在一次处理高维传感器数据的项目里。当时团队要融合多台激光雷达和惯性测量单元&#xff08;IMU&#xff09;的输出&#xff0c;每个时刻采集到的数据都是一个几十维的向量&…

作者头像 李华
网站建设 2026/9/13 9:11:35

bip批量转FBX:用MAXScript打造高效动画转换脚本

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 9:11:29

Q-Learning在无人机三维避障路径规划中的实践

1. 项目概述&#xff1a;当无人机遇上强化学习 在三维空间中实现无人机自主避障一直是个令人着迷的技术挑战。想象一下&#xff0c;当无人机在充满动态障碍物的复杂环境中飞行时&#xff0c;它需要像经验丰富的飞行员一样实时做出决策——这正是我们研究Q-Learning算法在无人机…

作者头像 李华
网站建设 2026/9/13 9:10:58

Jetson Orin Nano上jtop重启死循环的systemd根源与修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华