- 人工智能
- 语音
- 音频
- NLP
- 媒体生成
【免费下载链接】PaddleSpeech
Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.
本篇技术指南以 PaddleSpeech 仓库中 examples/aishell3/vc1 的官方示例为主线,系统讲解如何基于FastSpeech2 声学模型 + AISHELL-3 中文多说话人数据集搭建一套可复现的语音克隆(Voice Cloning)流水线。读完本文,你将掌握 GE2E 说话人编码器、FastSpeech2 合成器、Parallel WaveGAN 声码器三阶段级联的完整技术原理,能够独立完成数据集准备、说话人 Embedding 生成、预处理、模型训练、批量合成与零样本(zero-shot)语音克隆推理的全部命令行操作,并深入理解conf/default.yaml中每个关键配置项的物理含义。
任务背景:从说话人验证迁移到多说话人 TTS
语音克隆的目标是:仅凭一段参考音频(Reference Audio),即可让 TTS 系统"模仿"该说话人的音色生成任意文本的语音。vc1 示例采用了 《Transfer Learning from Speaker Verification to Multispeaker Text-To-Speech Synthesis》 论文提出的经典方案,将任务拆解为三个独立训练、再级联推理的模块:
- Speaker Encoder(说话人编码器):以说话人验证(Speaker Verification)为训练任务,用 GE2E(Generalized End-to-End Loss)损失训练一个编码器,把任意一段语音映射为一个固定维度的说话人 Embedding。训练该编码器不需要文本标注,因此可以无限制地堆叠大量多说话人数据集,例如 examples/other/ge2e 示例中使用的 Librispeech-other-500、VoxCeleb、VoxCeleb2、aidatatang-200zh、magicdata 等。
- Synthesizer(合成器):使用训练好的说话人编码器为 AISHELL-3 中的每个句子生成说话人 Embedding,并将其作为 FastSpeech2 的额外输入,与编码器输出拼接(concat)后送入解码器,从而让声学模型感知"是谁在说话"。
- Vocoder(声码器):使用 Parallel WaveGAN(PWG)将 FastSpeech2 输出的梅尔频谱还原为可听的波形,其训练与推理流程见 examples/aishell3/voc1。
三阶段中,说话人编码器与声码器均使用预训练权重(可直接下载),本示例真正需要训练的部分只有 FastSpeech2 合成器本身。
环境与数据集准备
AISHELL-3 数据集下载与解压
从 AISHELL-3 官网下载数据集后解压到~/datasets,得到目录~/datasets/data_aishell3,其中包含train、dev、test三个子集的 wav 音频与对应文本标注。AISHELL-3 是一个约 880 名中文说话人、88000+ 条话语的多说话人中文语料库,是本示例的训练数据来源。
MFA 强制对齐结果
FastSpeech2 属于时长预测型(duration-based)非自回归 TTS,训练需要每个音素(phone)的时长标注。vc1 使用 Montreal Forced Aligner(MFA)完成音素级别的强制对齐,两种获取方式任选其一:
- 直接下载官方提供的对齐结果
aishell3_alignment_tone.tar.gz(MFA 2.x 产物,含声调信息),解压后目录名为aishell3_alignment_tone; - 参考 examples/other/mfa 示例自行训练 MFA 模型(当前使用 MFA 1.x)生成对齐结果。
注意:
run.sh、preprocess.sh中均硬编码了./aishell3_alignment_tone这一相对路径,请将对齐结果解压到 vc1 示例目录下,保持与脚本约定一致。
预训练 GE2E 说话人编码器
下载官方预训练的 GE2E 模型压缩包ge2e_ckpt_0.3.zip并解压。该模型即 examples/other/ge2e 示例的训练产物。在 run.sh 中默认约定:
# 不含 ".pdparams" 后缀,用于生成 speaker embedding ge2e_ckpt_path=./ge2e_ckpt_0.3/step-3000000 # 包含 ".pdparams" 后缀,用于 voice cloning 推理 ge2e_params_path=${ge2e_ckpt_path}.pdparams从 ge2e 推理源码 可以看到,paddle.load(weights_fpath + ".pdparams")加载权重后,使用LSTMSpeakerEncoder对每个 utterance 输出256 维说话人 Embedding(模型配置为n_mels=40、num_layers=3、hidden_size=256、output_size=256)。
预训练 Parallel WaveGAN 声码器
下载pwg_aishell3_ckpt_0.5.zip并解压,得到以下文件:
pwg_aishell3_ckpt_0.5 ├── default.yaml # 训练 parallel wavegan 时的默认配置 ├── feats_stats.npy # 训练时用于归一化频谱的统计量 └── snapshot_iter_1000000.pdz # parallel wavegan 生成器参数该声码器用于最终的波形合成阶段,全程无需再训练。
快速开始:一条命令跑通全流程
run.sh是 vc1 示例的总入口,它通过source ${MAIN_ROOT}/utils/parse_options.sh支持--stage/--stop-stage参数控制执行阶段。四个阶段的划分与默认参数如下(见 run.sh):
| 阶段 | 内容 | 默认命令 |
|---|---|---|
| stage 0 | 数据预处理(生成说话人 Embedding、时长、特征、归一化) | ./local/preprocess.sh ${conf_path} ${ge2e_ckpt_path} |
| stage 1 | 训练 FastSpeech2 | ./local/train.sh ${conf_path} ${train_output_path} |
| stage 2 | 基于metadata.jsonl批量合成测试集波形 | ./local/synthesize.sh ${conf_path} ${train_output_path} ${ckpt_name} |
| stage 3 | 语音克隆推理(输入参考音频目录,输出克隆语音) | ./local/voice_cloning.sh ${conf_path} ${train_output_path} ${ckpt_name} ${ge2e_params_path} ${ref_audio_dir} |
整体运行:
./run.sh只跑数据预处理阶段:
./run.sh --stage 0 --stop-stage 0脚本默认变量还包括:gpus=0,1(双卡)、conf_path=conf/default.yaml、train_output_path=exp/default、ckpt_name=snapshot_iter_482.pdz(在 stage 2/3 前请替换为你实际训练得到的 checkpoint 文件名)。
数据预处理详解:五步流水线与 dump 目录
./local/preprocess.sh(见 preprocess.sh)内部实际由 5 个子阶段组成,比普通 TTS(如 tts3)多了一步 GE2E 说话人 Embedding 生成,这也是语音克隆任务的核心差异所在:
① 生成说话人 Embedding(stage 0)
python3 ${MAIN_ROOT}/paddlespeech/vector/exps/ge2e/inference.py \ --input=~/datasets/data_aishell3/train/wav/ \ --output=dump/embed \ --checkpoint_path=${ge2e_ckpt_path}该命令遍历输入目录下所有.wav(--pattern默认*.wav),对每个音频先做 VAD 切分、梅尔频谱提取(40 维 mel),再经LSTMSpeakerEncoder.embed_utterance输出 256 维向量,以.npy格式保存到dump/embed,目录层级与 wav 文件完全一致。官方示例中仅对train集生成 embedding,dev/test集在后续训练与合成阶段会实时从前端提取。
② 由 MFA 结果生成音素时长(stage 1)
python3 ${MAIN_ROOT}/utils/gen_duration_from_textgrid.py \ --inputdir=./aishell3_alignment_tone \ --output durations.txt \ --config=${config_path}将 MFA 的 TextGrid 对齐结果转换为durations.txt,作为 FastSpeech2 时长预测器的监督信号。
③ 特征提取(stage 2)
python3 ${BIN_DIR}/preprocess.py \ --dataset=aishell3 \ --rootdir=~/datasets/data_aishell3/ \ --dumpdir=dump \ --dur-file=durations.txt \ --config=${config_path} \ --num-cpu=20 \ --cut-sil=True \ --spk_emb_dir=dump/embed关键参数:--cut-sil=True会切除句首句尾静音;--spk_emb_dir=dump/embed指定上一步生成的说话人 Embedding 目录,使其与频谱、基频(pitch)、能量(energy)特征一同进入数据管线。
④ 统计量计算(stage 3)
python3 ${MAIN_ROOT}/utils/compute_statistics.py \ --metadata=dump/train/raw/metadata.jsonl \ --field-name="speech" # 同理对 "pitch"、"energy" 字段各执行一次仅基于train集计算三个特征的均值/标准差,分别输出dump/train/speech_stats.npy、pitch_stats.npy、energy_stats.npy,供后续归一化使用(dev/test集也复用 train 的统计量,避免信息泄漏)。
⑤ 归一化与词典构建(stage 4)
对train、dev、test三个子集分别调用${BIN_DIR}/normalize.py,使用 train 的统计量完成特征归一化,同时生成dump/phone_id_map.txt(音素词典)与dump/speaker_id_map.txt(说话人词典)。
预处理完成后,当前目录下生成完整的dump文件夹,结构如下:
dump ├── dev │ ├── norm # 归一化后的特征 │ └── raw # 原始 speech/pitch/energy 特征 ├── embed # 每个句子的说话人 embedding(.npy) │ ├── SSB0005 │ ├── SSB0009 │ └── ... ├── phone_id_map.txt # 音素 -> id 映射 ├── speaker_id_map.txt# 说话人 -> id 映射 ├── test │ ├── norm │ └── raw └── train ├── energy_stats.npy ├── norm ├── pitch_stats.npy ├── raw └── speech_stats.npy每个子目录(train/dev/test)下都有一份表格式文件metadata.jsonl,每一行记录一条话语的:phones(音素序列)、text_lengths、speech_lengths、durations(时长)、speech 特征路径、pitch 特征路径、energy 特征路径、speaker、id,是后续训练与合成的数据索引。
FastSpeech2 模型训练
训练入口与语音克隆开关
./local/train.sh调用${BIN_DIR}/train.py(即paddlespeech/t2s/exps/fastspeech2/train.py):
CUDA_VISIBLE_DEVICES=${gpus} ./local/train.sh ${conf_path} ${train_output_path}脚本内容(见 train.sh)关键在最后两个参数:
python3 ${BIN_DIR}/train.py \ --train-metadata=dump/train/norm/metadata.jsonl \ --dev-metadata=dump/dev/norm/metadata.jsonl \ --config=${config_path} \ --output-dir=${train_output_path} \ --ngpu=2 \ --phones-dict=dump/phone_id_map.txt \ --voice-cloning=True与普通多说话人 TTS(tts3)相比,唯一区别是设置--voice-cloning=True。从 train.py 源码 可以看到:开启该开关后,collate 函数切换为fastspeech2_multi_spk_batch_fn,模型将把说话人 Embedding(而非稀疏的 speaker id)作为条件输入,这正是"零样本/任意新说话人"能力的来源——推理时任何人的 Embedding 都可以直接送入模型。
配置详解:conf/default.yaml
训练与特征提取共用一份配置(见 conf/default.yaml),各区块含义如下:
特征提取设置(FEATURE EXTRACTION SETTING)
| 参数 | 默认值 | 含义 |
|---|---|---|
fs | 24000 | 采样率(Hz),PWG 声码器输出也以此为采样率 |
n_fft | 2048 | FFT 大小(采样点数) |
n_shift | 300 | 帧移(采样点数),约 12.5ms |
win_length | 1200 | 窗长(采样点数),约 50ms |
window | "hann" | 窗函数类型 |
fmin/fmax | 80 / 7600 | Mel 滤波器组最小/最大频率(Hz) |
n_mels | 80 | Mel 滤波器组个数 |
f0min/f0max | 80 / 400 | 基频(pitch)提取的上下限(Hz) |
数据与模型设置
- 数据:
batch_size: 64、num_workers: 2。 - 模型(
model区块)核心结构参数:adim: 384(注意力维度)、aheads: 2(注意力头数)、elayers: 4 / eunits: 1536(编码器层数与 FFN 单元数)、dlayers: 4 / dunits: 1536(解码器层数与 FFN 单元数)、positionwise_layer_type: conv1d(位置前馈层为卷积)及positionwise_conv_kernel_size: 3。 - 预测器参数:时长预测器
duration_predictor_layers: 2 / chans: 256 / kernel_size: 3;基频预测器pitch_predictor_layers: 5 / chans: 256 / kernel_size: 5 / dropout: 0.5;能量预测器energy_predictor_layers: 2 / chans: 256 / kernel_size: 3 / dropout: 0.5。FastSpeech2 通过这三个预测器在非自回归解码时对齐文本与频谱长度。 - PostNet:
postnet_layers: 5、postnet_filts: 5、postnet_chans: 256,对解码输出做残差精修。 - 语音克隆关键参数:
spk_embed_dim: 256(与 GE2E 输出维度一致)、spk_embed_integration_type: concat(说话人 Embedding 以拼接方式与编码器输出融合),对应源码中voice_cloning.py将 256 维spk_emb直接传入声学模型推理。 - 正则化:Transformer 编码器/解码器的 dropout 均为 0.2;
encoder_normalize_before/decoder_normalize_before为 True(层归一化置于子层之前);初始化方式init_type: xavier_uniform。
优化器与训练设置
optimizer: optim: adam, learning_rate: 0.001;updater.use_masking: True(损失计算对 padding 部分做掩码);max_epoch: 200、num_snapshots: 5(最多保留 5 个快照);seed: 10086(随机种子,保证可复现)。
训练完成后,checkpoint 保存在${train_output_path}/checkpoints/目录下,run.sh中的ckpt_name即指向其中的某个snapshot_iter_*.pdz文件。
批量合成:从 metadata.jsonl 到波形
./local/synthesize.sh调用${BIN_DIR}/../synthesize.py(即paddlespeech/t2s/exps/synthesize.py),作用是根据测试集的metadata.jsonl批量合成语音:
CUDA_VISIBLE_DEVICES=${gpus} ./local/synthesize.sh ${conf_path} ${train_output_path} ${ckpt_name}脚本内容(见 synthesize.sh):
python3 ${BIN_DIR}/../synthesize.py \ --am=fastspeech2_aishell3 \ --am_config=${config_path} \ --am_ckpt=${train_output_path}/checkpoints/${ckpt_name} \ --am_stat=dump/train/speech_stats.npy \ --voc=pwgan_aishell3 \ --voc_config=pwg_aishell3_ckpt_0.5/default.yaml \ --voc_ckpt=pwg_aishell3_ckpt_0.5/snapshot_iter_1000000.pdz \ --voc_stat=pwg_aishell3_ckpt_0.5/feats_stats.npy \ --test_metadata=dump/test/norm/metadata.jsonl \ --output_dir=${train_output_path}/test \ --phones_dict=dump/phone_id_map.txt \ --speaker_dict=dump/speaker_id_map.txt \ --voice-cloning=True该步骤同样需要--voice-cloning=True:合成时模型从测试集音频实时提取说话人 Embedding 作为条件,输出波形保存到${train_output_path}/test/。--am_ckpt指向训练得到的 FastSpeech2 快照,--voc_*指向预训练的 PWG 声码器及其频谱归一化统计量。
语音克隆推理:用参考音频克隆任意说话人
推理入口
语音克隆是 vc1 的最终目的:给定若干参考音频(./ref_audio),模型提取其中的音色并合成指定文本。假设参考音频目录结构为:
ref_audio ├── 001238.wav ├── LJ015-0254.wav └── audio_self_test.mp3执行:
CUDA_VISIBLE_DEVICES=${gpus} ./local/voice_cloning.sh ${conf_path} ${train_output_path} ${ckpt_name} ${ge2e_params_path} ${ref_audio_dir}脚本内容(见 voice_cloning.sh)默认合成文本为"凯莫瑞安联合体的经济崩溃迫在眉睫。",输出到${train_output_path}/vc_syn/,每个参考音频对应一个${utt_id}.wav。
底层实现剖析
voice_cloning.sh调用paddlespeech/t2s/exps/voice_cloning.py(见 voice_cloning.py),其推理链路可拆解为四步:
- 说话人 Embedding 提取:默认使用 GE2E 编码器——
SpeakerVerificationPreprocessor对参考音频做 16kHz 重采样、VAD 预处理与 40 维 mel 部分片段提取,再由LSTMSpeakerEncoder.embed_utterance得到 256 维spk_emb(对应--ge2e_params_path加载的参数)。源码同时预留了--use_ecapa开关,可切换为 ECAPA-TDNN 说话人编码器(经VectorExecutor调用)。 - 文本前端:
Frontend(phone_vocab_path=args.phones_dict)将输入文本转为音素 id 序列(phone_ids),支持中文 g2p。 - 声学模型推理:
get_am_inference(am="fastspeech2_aishell3", ...)加载 FastSpeech2,am_inference(phone_ids, spk_emb=spk_emb)以"音素序列 + 说话人 Embedding"为输入,输出梅尔频谱。 - 声码器推理:
voc_inference(...)用 PWG 将频谱还原为波形,以am_config.fs(24000Hz)为采样率写出 wav。
推理时声学模型对 Embedding 完全开放——无论参考音频来自哪个说话人(甚至训练集之外的说话人),模型都能依据该 Embedding 合成对应音色的语音,这正是零样本语音克隆的核心机制。
预训练模型与效果
官方提供了 vc1 任务的预训练 FastSpeech2 模型fastspeech2_nosil_aishell3_vc1_ckpt_0.5.zip,解压后包含:
fastspeech2_nosil_aishell3_ckpt_vc1_0.5 ├── default.yaml # 训练 fastspeech2 的默认配置 ├── phone_id_map.txt # 音素词典(训练时生成) ├── snapshot_iter_96400.pdz # 模型参数与优化器状态 └── speech_stats.npy # 频谱归一化统计量注意:语音克隆模式下不需要speaker_id_map.txt(说话人词典),因为说话人信息由 GE2E 的连续 Embedding 承载,而非离散 id。
官方给出的参考训练结果如下(来自 README):
| Model | Step | eval/loss | eval/l1_loss | eval/duration_loss | eval/pitch_loss | eval/energy_loss |
|---|---|---|---|---|---|---|
| default | 2(gpu) x 96400 | 0.99699 | 0.62013 | 0.053057 | 0.11954 | 0.20426 |
其中duration_loss、pitch_loss、energy_loss分别对应 FastSpeech2 三个预测器的监督损失,l1_loss为频谱重构损失,可在训练日志中对照监控训练是否收敛。
小结与延伸
vc1 示例完整呈现了"说话人验证迁移到 TTS"的工程化实现:GE2E 提供可迁移的说话人表征,FastSpeech2 借助spk_embed条件输入实现多说话人乃至零样本合成,PWG 负责高质量波形还原。若需继续深入,可依次阅读:
- 说话人编码器训练:examples/other/ge2e
- 标准多说话人 TTS(无语音克隆):examples/aishell3/tts3
- 声码器训练:examples/aishell3/voc1
- 语音克隆推理核心代码:voice_cloning.py
- 人工智能
- 语音
- 音频
- NLP
- 媒体生成
【免费下载链接】PaddleSpeech
Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.
相关推荐
PaddleSpeech 音色克隆(Voice Cloning)实战指南:从说话人编码到语音合成
PaddleSpeech 音色克隆(Voice Cloning)实战指南:从说话人编码到语音合成 本文围绕 PaddleSpeech 仓库中 paddlespe
人工智能语音音频NLP媒体生成实时语音克隆:CorentinJ/Real-Time-Voice-Cloning
实时语音克隆:CorentinJ/Real Time Voice Cloning 在深入探讨此项目之前,先给出它的官方链接:。这是一个由CorentinJ开发的
人工智能语音深度学习Real-Time Voice Cloning Web界面开发:基于Flask的语音克隆应用
Real Time Voice Cloning Web界面开发:基于Flask的语音克隆应用 引言:语音克隆技术的Web化挑战 你是否曾想过将实时语音克隆技术集
人工智能语音深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考