SpeechBrain 实战:基于 ECAPA-TDNN 深度嵌入与谱聚类的 AMI 说话人日志(Speaker Diarization)完整指南

SpeechBrain 实战:基于 ECAPA-TDNN 深度嵌入与谱聚类的 AMI 说话人日志(Speaker Diarization)完整指南 SpeechBrain 实战基于 ECAPA-TDNN 深度嵌入与谱聚类的 AMI 说话人日志Speaker Diarization完整指南【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain说话人日志Speaker Diarization回答的问题是谁在什么时候说了话——它把一段多说话人的会议录音切分成属于不同说话人的时间片段是会议分析、语音识别前端、司法取证等场景的核心技术。SpeechBrain 在recipes/AMI/Diarization/目录下提供了一套完整、可直接运行的 AMI 语料库说话人日志方案其核心思路是深度说话人嵌入 谱聚类先用预训练的说话人识别模型把语音切成固定短片段并提取嵌入向量再用聚类算法把属于同一说话人的片段归并起来最终输出标准 RTTM 格式的分段结果并自动评测 Diarization Error RateDER。读完本文你将掌握AMI 语料库的下载与元数据准备流程、两条可复现的基线ECAPA-TDNN 与 X-vector、完整 YAML 超参数逐项含义、从嵌入提取到谱聚类的源码级调用链以及如何在 Dev 集上自动调参并在 Dev/Eval 集上得到可复现的 DER 指标。一、方案总览Deep Embedding Spectral Clustering整个方案基于论文ECAPA-TDNN Embeddings for Speaker DiarizationarXiv:2104.01466。系统可以拆解为如下流水线每一步都能在仓库源码中找到对应实现数据准备从 AMI 官方手动标注segments/*.xml生成参考 RTTM 与子段元数据 JSON —— 见 recipes/AMI/Diarization/ami_prepare.pyOracle VAD用 ground truth 的语音区间切分音频得到 3 秒左右、相邻重叠 1.5 秒的子段sub-segments深度嵌入提取对每个子段提取 Fbank 特征送入预训练说话人嵌入模型ECAPA-TDNN 或 X-vector得到固定维度向量 —— 见 recipes/AMI/Diarization/experiment.py 中的compute_embeddings()谱聚类基于余弦相似度构造亲和矩阵、做 p-pruning、计算拉普拉斯特征向量、k-means 聚类得到每个子段的说话人标签 —— 见 speechbrain/integrations/alignment/diarization.py后处理合并相邻同说话人子段、均分重叠区间写出 RTTM 分段文件评测用speechbrain.utils.DER计算 DER。该流程在 recipes/AMI/Diarization/experiment.py 的模块 docstring 中也有明确说明This recipe implements diarization system using deep embedding extraction followed by spectral clustering且注明其逐条录音per-recording迭代的处理方式对 GPU 显存友好、代码更易理解。二、环境准备与依赖安装1. 安装额外依赖本方案除了 SpeechBrain 本身还需要scikit-learn谱聚类、k-means 均依赖它。仓库为此提供了专门的依赖文件 recipes/AMI/Diarization/extra_requirements.txt内容仅一行scikit-learn安装命令即 README 中给出的pip install -r extra_requirements.txt值得说明的是scikit-learn是可选依赖而非强制依赖。如果缺失脚本并不会默默失败而是抛出带安装指引的ImportError——这一逻辑同时出现在 recipes/AMI/Diarization/experiment.py 和 speechbrain/integrations/alignment/diarization.py 两处分别提示pip install scikit-learn和conda install scikit-learn两种方式。2. 数据与标注下载AMI 语料库需要两份资料README 中给出了官方地址请访问 AMI 项目官网下载语料库本体amicorpus/目录包含多麦克风会议录音单麦 wav 与 8 通道麦克风阵列手动标注ami_public_manual_1.6.2/其中的segments/*.xml是逐说话人的语音区间标注也是 Oracle VAD 与参考 RTTM 的来源。三、运行方法一条命令跑通完整流程README 给出的运行命令非常简单根据所选嵌入模型二选一python experiment.py hparams/ecapa_tdnn.yaml # 或 python experiment.py hparams/xvectors.yaml入口脚本 recipes/AMI/Diarization/experiment.py 使用 SpeechBrain 标准的sb.parse_arguments()解析命令行YAML 路径 可选的--hparams覆盖再通过load_hyperpyyaml()加载超参数。整个实验自动完成以下阶段数据准备由from ami_prepare import prepare_ami引入除非skip_prep: True创建实验目录output_folder、embedding_dir、sys_rttm_dir、der_dir加载预训练嵌入模型run_on_main(params[pretrainer].collect_files)params[pretrainer].load_collected()Dev 集自动调参根据 backend/affinity 组合选择不同的调参器详见第六节Dev/Eval 最终推理用最优超参跑diarize_dataset()逐录音输出 RTTMDER 评测调用speechbrain.utils.DER.DER()计算并把逐录音的 DER 写入der_dir。运行前必须修改的两处占位符两份 YAML 中都把数据路径留作!PLACEHOLDER运行时必须替换为你的真实路径README 中提示在 AMI 官网下载data_folder: !PLACEHOLDER # 例如 /path/to/amicorpus/ manual_annot_folder: !PLACEHOLDER # 例如 /path/to/ami_public_manual_1.6.2/四、理解数据准备从 XML 标注到子段 JSON在进入模型之前数据准备是整个流程的地基。recipes/AMI/Diarization/ami_prepare.py 中的prepare_ami()负责完成原始标注 → 参考 RTTM → 子段 JSON三步转换第一步生成参考 RTTM。对 train/dev/eval 三个集合遍历每个录音从manual_annot_folder/segments/rec_id.*.segments.xml解析每位说话人A/B/C/D/E的transcriber_start/transcriber_end区间生成标准格式的SPEAKER行并写入fullref_ami_split.rttm。其中SPKR-INFO头记录录音中的说话人清单——这正是后面oracle 说话人数的依据。该函数还会过滤掉标注异常的行如 transcriber_start transcriber_end。第二步划分子段。参考 RTTM 中一个说话人可能有很长的连续语音区间直接对其提取单个嵌入无法捕捉说话人变化。因此prepare_metadata()先合并重叠区间再用get_subsegments()把长段切成固定时长的小段max_subseg_dur 3.0子段最大时长 3 秒overlap 1.5相邻子段重叠 1.5 秒即步长 shift 3.0 − 1.5 1.5 秒保证说话人切换处的子段也包含足够信息。第三步生成元数据 JSON。每个子段对应一个条目键为rec_id_start_end值为 wav 路径与起止采样点。这里有一个容易忽略的细节mic_type决定 wav 字段的写法——单麦用file而Array1多麦用files8 个通道的路径列表。这与 recipes/AMI/Diarization/experiment.py 中dataio_prep()的音频管线对应多麦时使用DelaySum_Beamformer波束成形由 YAML 中的multimic_beamformer实例化融合 8 通道信号单麦则直接read_audio()。数据准备阶段支持幂等跳过prepare_ami()会把配置序列化保存为opt_ami_prepare.mic_type.pkl若 JSON 与配置均未变化则自动跳过skip()函数方便迭代实验。五、两条基线ECAPA-TDNN 与 X-vector该 recipe 提供两套预训练说话人嵌入模型对应两份 YAML。两者在数据准备、聚类后端、DER 评测环节完全一致差异集中在特征维数与嵌入模型结构上。1. ECAPA-TDNN 配置推荐默认基线recipes/AMI/Diarization/hparams/ecapa_tdnn.yaml 是性能最优的配置其关键参数如下参数值说明pretrain_pathspeechbrain/spkrec-ecapa-voxceleb预训练模型仓库 IDHuggingFace自动下载n_mels80Fbank 滤波器组数emb_dim192嵌入向量维度emb_channels[1024, 1024, 1024, 1024, 3072]ECAPA-TDNN 各卷积层通道数emb_attention_channels128注意力模块通道数emb_lin_neurons192最终线性层神经元数batch_size512嵌入提取批大小mic_typeMix-Headset麦克风类型可选Mix-Lapel/Mix-Headset/Array1/Array1-01/BeamformItbackendSC聚类后端可选SC/kmeans/AHCaffinitycos亲和矩阵类型可选cos/nnoracle_n_spkrsTrue是否使用 ground truth 说话人数模型实例化代码也在此 YAML 中!new:speechbrain.lobes.models.ECAPA_TDNN.ECAPA_TDNN其kernel_sizes: [5, 3, 3, 3, 1]、dilations: [1, 2, 3, 4, 1]对应 ECAPA-TDNN 的 SE-Res2Net 残差块设计。同时定义了两级特征归一化mean_var_normsentence 级输入特征归一化与mean_var_norm_embglobal 级嵌入归一化。2. X-vector 配置recipes/AMI/Diarization/hparams/xvectors.yaml 使用经典的 X-vector 模型参数值说明pretrain_pathspeechbrain/spkrec-xvect-voxcelebX-vector 预训练模型n_mels24Fbank 维数比 ECAPA 低emb_dim512嵌入维度比 ECAPA 高emb_tdnn_channels[512, 512, 512, 512, 1500]TDNN 各层通道数batch_size512同前模型为speechbrain.lobes.models.Xvector.Xvector5 个 TDNN 块tdnn_kernel_sizes: [5, 3, 3, 1, 1]、tdnn_dilations: [1, 2, 3, 1, 1]激活函数为LeakyReLU。与 ECAPA 配置不同的是该 YAML 的pretrainer还额外加载了mean_var_norm_emb.ckpt嵌入级全局归一化参数也随预训练模型一起保存。3. 预训练模型加载机制两份 YAML 都通过 SpeechBrain 的Pretrainerspeechbrain/utils/parameter_transfer.py加载模型pretrainer: !new:speechbrain.utils.parameter_transfer.Pretrainer collect_in: !ref save_folder loadables: embedding_model: !ref embedding_model paths: embedding_model: !ref pretrain_path/embedding_model.ckptpretrain_path可以是指向 HuggingFace 仓库的 ID自动下载也可以替换为本地 checkpoint 目录的路径。README 明确提示这些嵌入模型源自 VoxCeleb 说话人识别任务训练方式与可用预训练模型清单见 recipes/VoxCeleb/SpeakerRec/README.md你也可以按该文件说明从头训练说话人嵌入模型后再替换到这里。六、核心流水线源码解析嵌入提取、聚类与后处理1. 嵌入提取compute_embeddings()在 recipes/AMI/Diarization/experiment.py 中每个子段的嵌入按如下链路计算全程torch.no_grad()feats paramscompute_features # Fbank 特征 feats paramsmean_var_norm # sentence 级归一化 emb paramsembedding_model # ECAPA-TDNN / X-vector emb paramsmean_var_norm_emb)embedding_computation_loop()同文件 L78-L136负责批量迭代将嵌入以speechbrain.processing.PLDA_LDA.StatObject_SB结构保存为.emb_stat.pkl文件已存在的嵌入文件会被跳过支持断点续跑其中stat1保存嵌入矩阵、segset保存子段 ID。2. 逐录音推理diarize_dataset()diarize_dataset()同文件 L162-L319是整条流水线的调度核心对每个录音从全量元数据中筛出该录音的子段 JSONprepare_subset_json()构建 DataLoaderdataio_prep()提取嵌入embedding_computation_loop()确定说话人数oracle_n_spkrsTrue时从参考 RTTM 的SPKR-INFO中读取diar.get_oracle_num_spkrs()否则对cos亲和矩阵用最大特征间隙max eigen gap自动估计按backend分发聚类SC走谱聚类、kmeans走 k-means、AHC走层次聚类输出该录音的 RTTM最后把所有单录音 RTTM 拼接成sys_output.rttm。3. 谱聚类的实现细节Spec_Clust_unorm聚类后端实现在 speechbrain/integrations/alignment/diarization.py。当affinitycos时使用Spec_Clust_unorm类其do_spec_clust()严格遵循 Von Luxburg 的谱聚类教程流程共五步相似度矩阵get_sim_mat()计算嵌入两两间的余弦相似度p-pruningp_pruning()对每行仅保留相似度最高的(1-pval)比例的值其余置零——pval是唯一需要在 Dev 集上调的超参数对称化0.5 * (A A.T)拉普拉斯矩阵get_laplacian()非归一化拉普拉斯L D - M特征分解与聚类get_spec_embs()cluster_embs()scipy.linalg.eigh求特征向量取前num_of_spk个特征向量作为谱嵌入再对谱嵌入做 k-means 得到标签。get_spec_embs()中还实现了说话人数的自动估计当k_oracleNone时计算特征值序列的相邻间隙eigen gap取前max_num_spkrs默认 10个间隙中的最大值位置作为估计的说话人数并用min_num_spkrs默认 2做下限保护。当affinitynn时则走Spec_Cluster类继承自 sklearn 的SpectralClustering用 k 近邻图构造亲和矩阵n_neighbors默认 10。4. RTTM 后处理聚类得到的只是子段 → 说话人标签的映射需要转换成时间边界。do_spec_clustering()中的后处理分两步merge_ssegs_same_speaker与distribute_overlap合并相邻且同标签的子段直接合并成连续片段分配重叠不同标签的相邻子段若重叠把重叠时长均分给两段取中点作为分界。最终由write_rttm()写出标准 NIST RTTM 行SPEAKER rec_id 0 start dur NA NA spkr_id NA NA其中子段 ID 本身编码了起止时间rec_id_start_end因此可以直接解析回时间戳。七、Dev 集自动调参与评估指标1. DER 评测标准README 明确说明评测时使用 0.25 秒的 forgiveness collar且忽略重叠语音ignore_overlap: True, forgiveness_collar: 0.25这与论文及 AMI 评测协议一致。DER 由 speechbrain/utils/DER.py 的DER()函数计算返回 Missed SpeechMS、False AlarmFA、Speaker Error RateSER与综合 DER。在 experiment.py 的最终评测段还会调用write_ders_file()输出逐录音 DER 文件含OVERALL汇总行日志会打印Final Diarization Error Rate (%) on AMI corpus: Dev X % | Eval Y %2. 三套自动调参器由于谱聚类等后端对超参数敏感脚本内置了在 Dev 集上以 DER 最小化为目标的自动调参逻辑experiment.py L322-L459调参器触发条件搜索内容dev_pval_tuneraffinitycos且backend为SC/kmeanspval在np.arange(0.002, 0.015, 0.001)网格搜索取 Dev DER 最小者dev_nn_tuneraffinitynnn_neighbors在 514 搜索假设 oracle 说话人数dev_ahc_threshold_tunerbackendAHC阈值在np.arange(0.0, 1.0, 0.1)搜索主程序__main__中根据 YAML 的affinity/backend组合自动选择合适的调参器experiment.py L567-L599随后用最优超参对 Dev 与 Eval 集执行最终推理与评测。需要说明的是dev_tuner()针对未知说话人数的 nn 亲和在源码中被标注为work in progress属于预留实验路径。3. 其他可切换的聚类后端backend参数还支持kmeans与AHC对应 diarization.py 中的do_kmeans_clustering()与do_AHC()kmeans直接对深度嵌入做 k-means说话人数未知时复用Spec_Clust_unorm的特征间隙估计逻辑源码注释指出这是实验性用法推荐谱聚类AHC聚合层次聚类AgglomerativeClusteringcosine 距离 ward 连接此时pval即距离阈值。八、基准性能AMI Dev/Eval 上的 DERREADME 公布了 ECAPA-TDNN 谱聚类SC在三种麦克风设置下的最佳性能评测条件0.25s collar、忽略重叠系统麦克风Orcl. (Dev)Orcl. (Eval)Est. (Dev)Est. (Eval)ECAPA-TDNN SCHeadsetMix2.02%1.78%2.43%4.03%ECAPA-TDNN SCLapelMix2.17%2.36%2.34%2.57%ECAPA-TDNN SCArray-12.95%2.75%3.07%3.30%其中 Orcl. 表示使用 oracle 说话人数ground truth 提供Est. 表示由算法自动估计说话人数。表中数据为仓库 README 与论文报告的实测结果可作复现基线参考更多完整分析请查阅论文原文。值得注意的是实验脚本默认oracle_n_spkrs: True若希望贴近真实应用自动估计说话人数可将该参数改为False此时cos亲和矩阵会走最大特征间隙估计路径。九、总结与扩展方向本 recipe 展示了 SpeechBrain 处理说话人日志任务的完整范式ami_prepare.py负责数据侧XML → RTTM → 子段 JSONexperiment.py负责调度侧嵌入提取、调参、推理、评测speechbrain/integrations/alignment/diarization.py负责算法侧谱聚类、k-means、AHC 及 RTTM 后处理三者通过 hyperpyyaml 配置解耦。若要在其他语料或真实场景中复用替换data_folder/manual_annot_folder为你的数据路径或参考prepare_ami()的 JSON 结构自行构造元数据目前 VAD 为 Oracle源自 ground truth 标注vad_type参数已预留但未实现接入自动 VAD 是向端到端系统演进的自然一步说话人数自动估计已内置最大特征间隙可作为无监督部署的起点。如需深入源码建议从 recipes/AMI/Diarization/experiment.py 的__main__入口读起依次追踪diarize_dataset()→do_spec_clustering()→Spec_Clust_unorm.do_spec_clust()的调用链并结合 speechbrain/integrations/alignment/diarization.py 中带 doctest 的类Spec_Clust_unorm、Spec_Cluster理解每个数学步骤的实际效果。【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考