PaddleSpeech 说话人验证实战:基于 VoxCeleb 数据集与 ECAPA-TDNN 的完整训练评测指南
PaddleSpeech 说话人验证实战:基于 VoxCeleb 数据集与 ECAPA-TDNN 的完整训练评测指南
导读
本文以 PaddleSpeech 仓库中 examples/voxceleb/README.md 为骨架,系统讲解说话人验证(Speaker Verification)在 VoxCeleb 数据集上的完整落地路径:从 VoxCeleb1/2 的数据下载、m4a 到 wav 格式转换,到 VoxCeleb 官方评测协议(trial 文件)的解读,再到 PaddleSpeech 中 sv0(ECAPA-TDNN + 纯 Python 实现)的配置、训练、测试与预训练模型推理。读完本文,你将掌握在 PaddleSpeech 中从零跑通 VoxCeleb 说话人验证全流程所需的全部命令、配置参数与原理依据。
一、VoxCeleb 数据集概览
VoxCeleb 是由牛津大学 VGG 团队发布的音视频说话人数据集,语音片段全部来自 YouTube 访谈视频的截取。它的特点非常鲜明:说话人覆盖不同种族、口音、职业与年龄段,所有语音都是"野外(in the wild)"采集,包含背景人声、笑声、重叠语音、姿态变化和光照差异等复杂干扰,每条语音片段时长至少 3 秒,同时提供音频与视频两种模态。
数据集分为两个版本:
- VoxCeleb1:规模较小,常作为评测集使用;
- VoxCeleb2:规模更大,是训练集的主力来源。
两个版本之间不存在说话人重叠(no overlap between the two versions),这意味着可以用 VoxCeleb2 训练、VoxCeleb1 评测,而不会出现说话人身份泄漏。PaddleSpeech 仓库在 dataset/voxceleb/README.md 中对数据集特性做了上述描述,对应的下载与 manifest 生成脚本分别是 dataset/voxceleb/voxceleb1.py 与 dataset/voxceleb/voxceleb2.py。
数据集规模统计
| dataset | vox1 - dev | vox1 - test | vox2 - dev | vox2 - test |
|---|---|---|---|---|
| spks(说话人数) | 1211 | 40 | 5994 | 118 |
| utts(语音条数) | 148642 | 4874 | 1092009 | 36273 |
| time(h)(总时长/小时) | 340.4 | 11.2 | 2360.2 | 79.9 |
以上数据来自 examples/voxceleb/README.md 的统计表。可以看到 VoxCeleb2 的 dev 集包含约 109 万条语音、2360 小时,是典型的"大数据量 + 数千说话人"训练场景,非常适合训练大规模说话人嵌入(speaker embedding)模型。
二、VoxCeleb 官方评测协议(trial 文件)
说话人验证的标准评测方式是构建 trial 列表:每一行是一个"注册语音-测试语音"对,并标注该对是否来自同一说话人(positive/negative)。模型对每个 trial 对计算相似度得分,再通过阈值比较得到等错误率(EER)等指标。
PaddleSpeech 仓库在 examples/voxceleb/README.md 中整理了 VoxCeleb 官方的 trial 文件统计:
| trial | filename | nums | positive | negative |
|---|---|---|---|---|
| VoxCeleb1 | veri_test.txt | 37720 | 18860 | 18860 |
| VoxCeleb1(cleaned) | veri_test2.txt | 37611 | 18802 | 18809 |
| VoxCeleb1-H | list_test_hard.txt | 552536 | 276270 | 276266 |
| VoxCeleb1-H(cleaned) | list_test_hard2.txt | 550894 | 275488 | 275406 |
| VoxCeleb1-E | list_test_all.txt | 581480 | 290743 | 290737 |
| VoxCeleb1-E(cleaned) | list_test_all2.txt | 579818 | 289921 | 289897 |
说明:
- VoxCeleb1 基础协议共 37720 对,正负样本各半(18860/18860),是社区最常用的入门评测;
- cleaned 版本剔除了官方标注为"噪声/异常"的样本对,数量略有减少;
- VoxCeleb1-H(hard)与 VoxCeleb1-E(extended)分别是更难(同国籍/同性别的困难对占比更高)与更全(覆盖全部说话人对)的扩展协议,规模都在 50 万对以上。
从 examples/voxceleb/sv0/local/data.sh 可以看到,stage 1 的数据下载阶段会通过 voxceleb1.py 自动生成 manifest.dev 与 manifest.test(注释中标注了 manifest.dev: 148642、manifest.test: 4847),并下载上述 trial 文件。sv0 的默认评测使用 cleaned 版本的 veri_test2.txt(见配置文件中的 verification_file: data/vox1/veri_test2.txt),测试脚本据此计算 EER。
三、VoxCeleb2 数据准备:m4a 转 wav
VoxCeleb2 官方发布的音频文件是 m4a 格式,而 PaddleSpeech 的后续流程要求 wav 输入,因此所有 m4a 必须先完成格式转换,并且只需执行一次(转换耗时可能长达数小时,与机器性能相关)。
原文档给出的转换命令模板为:
ffmpeg -y -i %s -ac 1 -vn -acodec pcm_s16le -ar 16000 %s
各参数含义:
-i %s:输入 m4a 文件;-ac 1:强制单声道(说话人识别通常使用单声道音频);-vn:丢弃视频流;-acodec pcm_s16le:编码为 16-bit 线性 PCM;-ar 16000:重采样到 16kHz(与配置文件中的sr: 16000一致)。
在 PaddleSpeech 中,这一步由 examples/voxceleb/sv0/local/convert.sh 自动化完成。该脚本的核心是信号量控制的并行转换:默认开 32 个并发槽位(N=32,对应 32 vCPU 环境,可按机器核数调整),遍历目录下所有 *.m4a 文件,逐个执行:
ffmpeg -loglevel panic -i "$f" -ar 16000 "${f%.*}.wav"
转换完成后,把所有 wav 文件统一放入名为 wav 的目录,目录结构形如:
voxceleb2/wav/id00012/21Uxsk56VDQ/00001.wav
即 wav/说话人ID/视频ID/片段ID.wav 的三级结构。数据脚本 examples/voxceleb/sv0/local/data.sh 的 stage 3 会调用 convert.sh 完成 m4a→wav 转换(不删除原始 m4a),stage 4 再通过 voxceleb2.py --generate 从 wav 目录生成 vox2/manifest.vox2 清单文件;由于 VoxCeleb2 全部用于训练,所有数据汇总到一个 manifest 文件。
补充:原文档提到的 VoxCeleb1 数据下载同样由
data.sh的 stage 1 完成(voxceleb1.py --manifest_prefix ... --target_dir ...),无需手动转换格式。
四、sv0 与 sv1:两条说话人验证路线
examples/voxceleb/README.md 将 VoxCeleb 示例划分为两个子目录:
- sv0:speaker verification with softmax backend,全 Python 实现,使用 ECAPA-TDNN + AAM-softmax 等损失函数端到端训练,无需 Kaldi 依赖;
- sv1:dependence on kaldi,基于 plda/sc 后端的传统路线(PLDA 概率线性判别分析 / score 归一化),依赖 Kaldi 生态。
从当前仓库结构看,examples/voxceleb 目录下只保留了 sv0,包含完整的 run.sh、配置文件与 local/ 脚本集,因此本文后续将以 sv0 为主线展开实战讲解。
五、sv0 实战:ECAPA-TDNN 全流程
sv0 的核心说明文档是 examples/voxceleb/sv0/README.md,所有脚本统一由 examples/voxceleb/sv0/run.sh 编排。
5.1 阶段划分(stage)
| Stage | 功能 |
|---|---|
| 0 | 数据准备:下载 VoxCeleb1、下载 VoxCeleb2、m4a 转 wav、生成 train/dev/test 的 manifest 文件、下载 RIR Noise 数据集并生成增强用噪声 manifest |
| 1 | 训练模型 |
| 2 | 使用 VoxCeleb trial 评测说话人验证性能 |
通过 stage 与 stop_stage 参数可以灵活控制执行范围,例如:
# 只跑训练和评测(stage 1、2)
bash run.sh --stage 1 --stop_stage 2
# 只跑数据准备(stage 0)
bash run.sh --stage 0 --stop_stage 0
5.2 环境变量与本地变量
执行任何脚本前必须先引入环境变量:
source path.sh
source ${MAIN_ROOT}/utils/parse_options.sh
其中 examples/voxceleb/sv0/path.sh 负责设置:
MAIN_ROOT:指向 PaddleSpeech 仓库根目录(realpath ${PWD}/../../../);PATH:加入仓库根目录与utils/;PYTHONPATH:加入仓库根目录,保证能 importpaddlespeech包;BIN_DIR:指向paddlespeech/vector/exps/ecapa_tdnn,即训练/测试/嵌入提取脚本所在目录。
parse_options.sh 提供了 shell 脚本中 --variable value 的参数解析能力。run.sh 中定义的本地变量包括:
| 变量 | 含义 | 默认值 |
|---|---|---|
gpus |
使用的 GPU 编号;置空则纯 CPU | 0,1,2,3 |
stage |
起始阶段 | 0 |
stop_stage |
结束阶段 | 50 |
dir |
数据信息目录 | data/ |
exp_dir |
实验输出目录 | exp/ecapa-tdnn-vox12-big/ |
conf_path |
模型配置文件路径 | conf/ecapa_tdnn.yaml |
示例:指定 GPU 启动完整流程:
bash run.sh --gpus 0,1
5.3 Stage 0:数据准备
if [ ${stage} -le 0 ] && [ ${stop_stage} -ge 0 ]; then
# prepare data
bash ./local/data.sh ${dir} ${conf_path} || exit -1
fi
也可以直接手动执行:
source path.sh
bash ./local/data.sh ./data/ conf/ecapa_tdnn.yaml
data.sh 内部又细分了 8 个子阶段(1~7),核心动作包括:
voxceleb1.py下载 VoxCeleb1 并生成manifest.dev、manifest.test;voxceleb2.py --download下载 VoxCeleb2(m4a);convert.sh将 m4a 批量转 wav;voxceleb2.py --generate生成vox2/manifest.vox2;make_vox_csv_dataset_from_json.py将 json 格式 manifest 转成训练框架直接消费的 csv(train.csv 由 vox1.dev + vox2.vox2 合并,dev/test 来自 vox1.test);rir_noise.py下载 RIR Noise 增强数据并生成噪声 manifest;make_rirs_noise_csv_dataset_from_json.py生成noise.csv、rir.csv。
处理完成后 data/ 目录结构如下(来自 examples/voxceleb/sv0/README.md):
data/
├── rir_noise
│ ├── csv
│ │ ├── noise.csv
│ │ └── rir.csv
│ ├── manifest.pointsource_noises
│ ├── manifest.real_rirs_isotropic_noises
│ └── manifest.simulated_rirs
├── vox
│ ├── csv
│ │ ├── dev.csv
│ │ ├── enroll.csv
│ │ ├── test.csv
│ │ └── train.csv
│ └── meta
│ └── label2id.txt
└── vox1
├── list_test_all2.txt
├── list_test_all.txt
├── list_test_hard2.txt
├── list_test_hard.txt
├── manifest.dev
├── manifest.test
├── veri_test2.txt
├── veri_test.txt
├── voxceleb1.dev.meta
└── voxceleb1.test.meta
其中 label2id.txt 是说话人 ID 到训练标签的映射,veri_test*.txt、list_test_*.txt 就是上一节的评测协议文件。
5.4 Stage 1:模型训练
if [ ${stage} -le 1 ] && [ ${stop_stage} -ge 1 ]; then
# train model, all `ckpt` under `exp` dir
CUDA_VISIBLE_DEVICES=${gpus} bash ./local/train.sh ${dir} ${exp_dir} ${conf_path}
fi
训练脚本 examples/voxceleb/sv0/local/train.sh 会根据 CUDA_VISIBLE_DEVICES 自动计算 GPU 数量:
- 多卡时使用 Paddle 分布式启动器:
python3 -m paddle.distributed.launch --gpus=$CUDA_VISIBLE_DEVICES ${BIN_DIR}/train.py ...; - 单卡/CPU 时直接运行
${BIN_DIR}/train.py --device {gpu|cpu} --checkpoint-dir ${exp_dir} --data-dir ${dir} --config ${conf_path}。
训练入口是 paddlespeech/vector/exps/ecapa_tdnn/train.py,模型实现位于 paddlespeech/vector/models/ecapa_tdnn.py。训练产物(model.pdparams 及中间 checkpoint)保存在 exp_dir 下。
一条常用的命令组合(纯 CPU 全流程):
source path.sh
bash ./local/data.sh ./data/ conf/ecapa_tdnn.yaml
CUDA_VISIBLE_DEVICES= ./local/train.sh ./data/ exp/ecapa-tdnn-vox12-big/ conf/ecapa_tdnn.yaml
5.5 Stage 2:模型测试(trial 评测)
if [ ${stage} -le 2 ] && [ ${stop_stage} -ge 2 ]; then
# test ckpt avg_n
CUDA_VISIBLE_DEVICES=0 bash ./local/test.sh ${dir} ${exp_dir} ${conf_path} || exit -1
fi
测试脚本 examples/voxceleb/sv0/local/test.sh 调用 ${BIN_DIR}/test.py --data-dir ${dir} --load-checkpoint ${exp_dir} --config ${conf_path} --device ${device},即 paddlespeech/vector/exps/ecapa_tdnn/test.py。它会:
- 对
verification_file指定的 trial 文件中的每个语音对提取嵌入向量; - 使用余弦相似度计算得分(
run.sh注释说明当前测试仅支持 cosine 打分); - 计算 EER 等指标输出。
六、配置文件详解:ecapa_tdnn.yaml
sv0 提供两套配置,分别是 examples/voxceleb/sv0/conf/ecapa_tdnn.yaml(VoxCeleb1+2 联合训练,7205 说话人)与 examples/voxceleb/sv0/conf/ecapa_tdnn_small.yaml(仅 VoxCeleb1,1211 说话人,通道数减半为 512,训练 100 轮)。二者结构完全一致,下面以主配置逐段解读。
6.1 数据配置
| 参数 | 默认值 | 说明 |
|---|---|---|
augment |
True | 是否启用 RIR/Noise 数据增强 |
batch_size |
32 | 训练 batch 大小 |
num_workers |
2 | DataLoader 工作进程数 |
num_speakers |
7205 | 分类任务类别数 = vox1(1211) + vox2(5994),测试说话人 41 个不参与 |
shuffle |
True | 是否打乱样本 |
skip_prep |
False | 是否跳过数据预处理 |
split_ratio |
0.9 | 训练/验证集划分比例 |
chunk_duration |
3.0 | 每条训练语音随机截取 3 秒 |
random_chunk |
True | 是否随机截取 chunk |
verification_file |
data/vox1/veri_test2.txt | 评测用 trial 文件(cleaned 版) |
从 paddlespeech/vector/io/augment.py 等源码可以确认,增强会利用 stage 0 生成的 rir.csv、noise.csv 对 3 秒 chunk 叠加真实房间冲击响应与背景噪声,提升模型在真实场景的鲁棒性。
6.2 特征提取配置
# currently, we only support fbank
sr: 16000 # sample rate
n_mels: 80
window_size: 400 #25ms, sample rate 16000, 25 * 16000 / 1000 = 400
hop_size: 160 #10ms, sample rate 16000, 10 * 16000 / 1000 = 160
当前实现仅支持 fbank(filterbank)特征:16kHz 采样率、80 维 mel 滤波器组、25ms 窗长(400 采样点)、10ms 帧移(160 采样点)。特征预处理与批处理逻辑可参考 paddlespeech/vector/io/dataset.py 与 paddlespeech/vector/io/batch.py。
6.3 模型结构配置
model:
input_size: 80
channels: [1024, 1024, 1024, 1024, 3072]
kernel_sizes: [5, 3, 3, 3, 1]
dilations: [1, 2, 3, 4, 1]
attention_channels: 128
lin_neurons: 192
这是 ECAPA-TDNN 的核心结构:
- 4 个 TDNN 卷积块,通道数 1024,卷积核 5/3/3/3,膨胀率 1/2/3/4(感受野逐层扩大);
- 第 5 层 3072 通道(SE-Res2Block 聚合层),kernel 1、dilation 1;
- 之后接 128 维注意力的 SE 模块,最终通过统计池化(mean+std)得到句级表示,再经全连接映射到 192 维说话人嵌入(
lin_neurons: 192,RESULT.md 中标注的 dim 即此值)。
完整实现见 paddlespeech/vector/models/ecapa_tdnn.py(ECAPA-TDNN 在 PaddleSpeech 中的网络主体与模块组装)。
6.4 训练超参数
seed: 1986 # according from speechbrain configuration
epochs: 10
save_interval: 10
log_interval: 10
learning_rate: 1e-8
max_lr: 1e-3
step_size: 140000
- 训练 10 个 epoch(big 配置),每 10 步保存 checkpoint、打印日志;
- 使用 循环学习率调度:
learning_rate为初始/最小学习率1e-8,max_lr为峰值1e-3,step_size: 140000为循环半周期步数。调度器实现可参考 paddlespeech/vector/training/scheduler.py。
6.5 损失函数(AAM-softmax)
margin: 0.2
scale: 30
说话人识别本质是"说话人 ID 分类 + 嵌入度量"联合训练:softmax 分类头配合 margin=0.2(角度间隔)、scale=30(特征缩放因子),即 AAM-softmax(Additive Angular Margin Softmax) 损失。实现位于 paddlespeech/vector/modules/loss.py。正是通过这种"带角度间隔的软间隔"约束,训练出的 192 维嵌入在余弦相似度度量下具备良好的区分性。
6.6 测试与打分配置
global_embedding_norm: True
embedding_mean_norm: True
embedding_std_norm: False
推理阶段对嵌入做归一化处理,相关工具函数见 paddlespeech/vector/io/embedding_norm.py。
6.7 分数归一化(s-norm)
score_norm: s-norm
cohort_size: 20000 # amount of imposter utterances in normalization cohort
n_train_snts: 400000 # used for normalization stats
测试时启用 s-norm(score normalization):用 20000 条冒名顶替(imposter)语音构成归一化群组,利用 400000 条训练语音统计归一化参数,对原始余弦得分做标准化以消除说话人/信道偏差,从而降低 EER。
七、预训练模型与嵌入提取
仓库在 docs/source/released_model.md 中发布了 VoxCeleb 预训练模型,examples/voxceleb/sv0/README.md 给出了下载、解压与直接评测的流程:
wget https://paddlespeech.cdn.bcebos.com/vector/voxceleb/sv0_ecapa_tdnn_voxceleb12_ckpt_0_2_1.tar.gz
tar -xvf sv0_ecapa_tdnn_voxceleb12_ckpt_0_2_1.tar.gz
source path.sh
# 若已完成数据准备并生成 manifest,可跳过上面的 data.sh 两步
CUDA_VISIBLE_DEVICES= bash ./local/test.sh ./data sv0_ecapa_tdnn_voxceleb12_ckpt_0_2_1/model/ conf/ecapa_tdnn.yaml
(注意:上述下载地址为仓库 README 中原始给出的 CDN 链接,实际使用请以 docs/source/released_model.md 当前发布信息为准。)
此外,examples/voxceleb/sv0/local/emb.sh 提供了单条音频嵌入提取的入口,调用 paddlespeech/vector/exps/ecapa_tdnn/extract_emb.py:
python3 ${BIN_DIR}/extract_emb.py --device cpu \
--config conf/ecapa_tdnn.yaml \
--audio-path demo/voxceleb/00001.wav --load-checkpoint exp/ecapa-tdnn-vox12-big/
该能力可直接用于构造注册/验证(enroll/test)打分流程,或作为下游声纹应用(如说话人聚类、说话人日志)的特征前端。
八、实验结果参考
仓库 examples/voxceleb/sv0/RESULT.md 记录了发布模型的评测结果:
| Model | Number of Params | Release | Config | dim | Test set | Cosine | Cosine + S-Norm |
|---|---|---|---|---|---|---|---|
| ECAPA-TDNN | 85M | 0.2.1 | conf/ecapa_tdnn.yaml | 192 | test | 0.8188 | 0.7815 |
该表给出的 EER 为 0.8188%(纯余弦打分),启用 s-norm 后降至 0.7815%(数值越小越好,EER 为等错误率百分比)。同时,RESULT.md 中对照引用了 SpeechBrain 官方 recipe 在同一任务上的参考结果(0.90% 无 s-norm / 0.80% 有 s-norm),说明 PaddleSpeech 该实现达到同类开源工具的主流水平,且 s-norm 带来的收益方向一致。注意这些对比数据来自仓库文档的转述,如需复现,请按上文流程使用同一配置与 trial 文件自行评测。
九、小结与延伸阅读
围绕 examples/voxceleb/README.md,本文完整覆盖了:VoxCeleb1/2 的数据特性与规模、VoxCeleb2 的 m4a→wav 转换、官方 trial 评测协议、sv0(ECAPA-TDNN)从数据准备到训练评测的全流程、核心配置参数逐项解读,以及预训练模型与嵌入提取方法。相关源码均可从以下位置继续深入:
- 全流程编排:examples/voxceleb/sv0/run.sh、examples/voxceleb/sv0/local/data.sh
- 模型实现:paddlespeech/vector/models/ecapa_tdnn.py
- 训练/测试/嵌入脚本:paddlespeech/vector/exps/ecapa_tdnn/train.py、paddlespeech/vector/exps/ecapa_tdnn/test.py、paddlespeech/vector/exps/ecapa_tdnn/extract_emb.py
- 损失与调度:paddlespeech/vector/modules/loss.py、paddlespeech/vector/training/scheduler.py
- 数据脚本:dataset/voxceleb/voxceleb1.py、dataset/voxceleb/voxceleb2.py
- 结果与发布:examples/voxceleb/sv0/RESULT.md、docs/source/released_model.md
如果你需要的是传统 Kaldi 路线的 PLDA/SC 后端方案,可关注仓库中 sv1 相关设计与 Kaldi 依赖的演进;若要在实际系统中落地声纹注册、验证与说话人日志,建议从 extract_emb.py 出发构建 enroll/test 打分服务。