- 人工智能
- 语音
- 音频
【免费下载链接】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.
WaveFlow 是一种基于归一化流(Normalizing Flow)的自回归式神经声码器,能够把梅尔频谱(mel spectrogram)转换为高保真原始波形。本指南以 PaddleSpeech 仓库中examples/ljspeech/voc0示例为核心,完整讲解数据准备、特征预处理、模型训练(含单卡与多卡分布式训练)、从梅尔频谱合成波形以及预训练模型使用的全流程。读完本文,你将掌握在 PaddleSpeech 中训练 WaveFlow 声码器并将其接入 Tacotron2 等声学模型输出进行端到端语音合成的完整实战方案。
WaveFlow 示例在 PaddleSpeech 中的定位
在文本转语音(TTS)流水线中,声学模型(如 Tacotron2、FastSpeech2)负责把文本映射为梅尔频谱,而**声码器(vocoder)**则负责把梅尔频谱还原为可听的波形。examples/ljspeech/voc0就是 PaddleSpeech 提供的 WaveFlow 声码器示例,它以 LJSpeech-1.1 英文单说话人数据集为训练语料。
该示例与examples/ljspeech/tts0(Tacotron2 示例)天然衔接:Tacotron2 生成的梅尔频谱(默认位于../tts0/output/test,即examples/ljspeech/tts0/output/test)可以直接作为 WaveFlow 合成阶段的输入。示例的目录结构如下:
examples/ljspeech/voc0/ ├── README.md ├── path.sh # 设置环境变量与 BIN_DIR ├── run.sh # 一键流水线(stage 0/1/2) └── local/ ├── preprocess.sh # 数据预处理 ├── train.sh # 模型训练 └── synthesize.sh # 从梅尔频谱合成波形path.sh定义了脚本所需的环境:它将仓库根目录设为MAIN_ROOT,并声明MODEL=waveflow、BIN_DIR=${MAIN_ROOT}/paddlespeech/t2s/exps/${MODEL},即所有local/*.sh脚本最终都会调用 paddlespeech/t2s/exps/waveflow 目录下的 Python 入口(preprocess.py、train.py、synthesize.py)。
数据集准备:下载与解压 LJSpeech-1.1
WaveFlow 训练使用 LJSpeech-1.1 数据集(约 13,100 条英文语音片段,总时长约 24 小时,22050 Hz 采样率)。从 LJSpeech 官方网站下载LJSpeech-1.1压缩包后,将其解压到~/datasets目录,最终数据集目录为~/datasets/LJSpeech-1.1。
解压后的数据集内每个语音文件以LJ###-####.wav命名,配套一个metadata.csv文件记录每条语音的文本标注。WaveFlow 的训练只需要音频本身,文本标注用于其他任务(如 Tacotron2),本示例的预处理仅提取音频与梅尔频谱。
说明:在 preprocess.py 中,数据集元信息通过
LJSpeechMetaData读取,预处理阶段会对每条音频执行librosa.load并校验采样率必须为 22050 Hz(源码中assert loaded_sr == sr,若不一致会提示需要重采样)。
快速开始:一键跑通预处理、训练与合成
进入示例目录后,直接运行 run.sh 即可依次完成环境初始化、数据预处理、模型训练和波形合成四个步骤:
cd examples/ljspeech/voc0 ./run.shrun.sh内部通过source ${MAIN_ROOT}/utils/parse_options.sh解析命令行参数,支持用--stage与--stop-stage控制要执行的阶段区间。例如,只运行数据预处理阶段:
./run.sh --stage 0 --stop-stage 0run.sh中各阶段的默认行为如下(变量可在脚本头部修改):
| 阶段 | 默认操作 |
|---|---|
| stage 0 | 调用./local/preprocess.sh ${preprocess_path},其中preprocess_path=preprocessed_ljspeech |
| stage 1 | 调用CUDA_VISIBLE_DEVICES=${gpus} ./local/train.sh ${preprocess_path} ${train_output_path},其中gpus=0,1、train_output_path=output |
| stage 2 | 从preprocessed_ljspeech/mel中复制LJ050-001*.npy到preprocessed_ljspeech/mel_test,再调用./local/synthesize.sh ${input_mel_path} ${train_output_path} ${ckpt_name}合成波形,其中ckpt_name=step-10000 |
也就是说,默认流程会:预处理原始数据集 → 用 2 张 GPU 训练 → 取step-10000检查点,对LJ050-001开头的几条测试梅尔频谱合成波形。这也说明run.sh默认假定你已经在step-10000或更早的迭代中得到了检查点;若从头训练,应先将ckpt_name改为实际存在的检查点名称。
数据预处理:从 WAV 到梅尔频谱
预处理脚本 local/preprocess.sh 只接收一个参数——预处理输出路径:
./local/preprocess.sh ${preprocess_path}它等价于调用:
python3 ${BIN_DIR}/preprocess.py \ --input=~/datasets/LJSpeech-1.1 \ --output=${preprocess_path}特征计算细节
preprocess.py 中的Transform类实现了完整的特征流水线,默认参数来自配置(详见后文"配置参数详解"):
- 加载音频:
librosa.load(wav_path, sr=None)保持原始采样率,随后断言采样率等于配置值 22050 Hz; - 反射填充(reflect padding):为使帧数与
hop_length对齐,计算frames = ceil(wav.size / hop_length),并将音频两端以reflect模式填充到目标长度; - 幅度归一化:
wav = wav / np.abs(wav).max() * 0.999,将峰值幅度归一化到 0.999,避免削波; - STFT:
librosa.core.stft,参数为n_fft=1024、win_length=1024、hop_length=256、center=False(关闭内部填充); - 梅尔滤波:用
librosa.filters.mel(sr, n_fft, n_mels=80, fmin=0, fmax=8000)构造滤波组,与 STFT 幅度谱做矩阵乘法得到 80 维梅尔频谱; - 对数幅度:通过
LogMagnitude(min=1e-5)对梅尔频谱取对数,得到对数幅度梅尔频谱(log magnitude mel); - 裁剪对齐:
audio = wav[fft_padding:-fft_padding]去除 STFT 填充部分,保证mel.shape[1] * hop_length == audio.size。
预处理输出结构
预处理完成后,${preprocess_path}目录下会生成三个组成部分:
preprocessed_ljspeech/ ├── metadata.csv # 每行:文件名、梅尔帧数、音频采样数(Tab 分隔) ├── wav/ # 每条语音的原始波形 .npy(float 数组) └── mel/ # 每条语音的对数梅尔频谱 .npy(80 维)其中metadata.csv由pd.DataFrame.to_csv(..., sep="\t", header=None)写出,是后续训练阶段定位mel/*.npy与wav/*.npy的索引。
模型训练:单卡与分布式
训练脚本 local/train.sh 接收预处理路径与输出路径两个参数:
CUDA_VISIBLE_DEVICES=${gpus} ./local/train.sh ${preprocess_path} ${train_output_path}它等价于调用:
python3 ${BIN_DIR}/train.py \ --data=${preprocess_path} \ --output=${train_output_path} \ --ngpu=1train.py 命令行参数
train.py 基于 PaddleSpeech 的ExperimentBase训练框架,支持以下参数:
| 参数 | 含义 | 说明 |
|---|---|---|
--data | 训练数据集路径 | 即预处理生成的preprocessed_ljspeech目录 |
--output | 输出目录 | 训练日志、可视化与检查点保存在该目录下 |
--ngpu | 使用的 GPU 数量 | ngpu == 0时使用 CPU;ngpu > 1时启用分布式训练 |
--config | 额外配置文件 | 可选,用于覆盖默认配置(yaml 格式) |
--opts | 键值对覆盖项 | 可选,以KEY VALUE对的形式覆盖配置 |
分布式训练说明:将--ngpu设为大于 1 的值(如 4)即可启用多卡训练。源码中if args.ngpu > 1: dist.spawn(main_sp, args=(config, args), nprocs=args.ngpu),即通过 Paddle 的dist.spawn启动多个进程;训练脚本中同时使用DistributedBatchSampler按world_size与rank切分数据。需要特别注意的是,分布式训练目前不支持 CPU(ngpu == 1时走单进程路径)。
训练循环与数据组织
训练阶段的数据流在 train.py 的setup_dataloader中定义:
- 使用 ljspeech.py 中的
LJSpeech数据集类读取metadata.csv,按记录加载mel/*.npy与wav/*.npy; - 前
config.data.valid_size(默认 16)条样本被dataset.split保留为验证集,其余为训练集; - 训练集使用
LJSpeechClipCollector(config.data.clip_frames, config.data.hop_length)做随机裁剪:从每条样本的梅尔频谱中随机截取clip_frames=65帧(对应65 × 256 = 16640个波形采样点),保证每个 batch 内数据长度一致且充分利用长音频; - 验证集使用
LJSpeechCollector对整段音频做 padding 后组成 batch。
每步训练中,模型前向计算得到隐变量z与对数雅可比行列式log_det_jacobian,再经WaveFlowLoss计算负对数似然损失并反向传播更新参数(优化器为 Adam,学习率默认2e-4)。训练日志包含每个 step 的耗时与 loss 值,并通过 VisualDL 记录train/loss与valid/loss标量曲线。
波形合成:从梅尔频谱到 WAV
合成脚本 local/synthesize.sh 接收三个参数:
CUDA_VISIBLE_DEVICES=${gpus} ./local/synthesize.sh ${input_mel_path} ${train_output_path} ${ckpt_name}它等价于调用:
python ${BIN_DIR}/synthesize.py \ --input=${input_mel_path} \ --output=${train_output_path}/wavs/ \ --checkpoint_path=${train_output_path}/checkpoints/${ckpt_name} \ --ngpu=1synthesize.py 的合成逻辑要点如下:
- 输入:
--input必须是一个目录,内含若干.npy格式的对数幅度梅尔频谱(80 维);脚本遍历目录下所有*.npy文件逐个合成; - 加载模型:通过
ConditionalWaveFlow.from_pretrained(config, args.checkpoint_path)加载检查点;--checkpoint_path指向参数文件(.pdparams),注意该路径不含.pdparams扩展名(例如output/checkpoints/step-10000); - 推理模式:加载后调用
layer_tools.recursively_remove_weight_norm(model)移除权重归一化(weight norm)以加速推理,随后model.eval(); - 精度与加速:合成在
paddle.amp.auto_cast()(混合精度)上下文中执行model.predict(mel); - 输出:每个输入
.npy对应生成一个同名.wav文件,保存到--output目录,采样率为config.data.sample_rate(22050 Hz),写入使用soundfile(sf.write); - 设备选择:
--ngpu=0时使用 CPU,--ngpu>0时使用 GPU。
一个典型的合成示例是:先用examples/ljspeech/tts0的 Tacotron2 模型生成测试梅尔频谱到../tts0/output/test,再将其作为--input交给本示例合成语音,从而串联起完整的"文本 → 梅尔频谱 → 波形"链路。
配置参数详解
WaveFlow 的默认配置定义在 paddlespeech/t2s/exps/waveflow/config.py,通过 yacsCfgNode管理,train.py/synthesize.py/preprocess.py均以它为基准,并可用--config或--opts覆盖。
数据相关参数(config.data)
| 参数 | 默认值 | 含义 |
|---|---|---|
batch_size | 8 | 每个 batch 的样本数(裁剪后的短片段) |
valid_size | 16 | 数据集前 16 条样本保留作为验证集 |
sample_rate | 22050 | 音频采样率(Hz) |
n_fft | 1024 | STFT 帧长 |
win_length | 1024 | 窗长 |
hop_length | 256 | 相邻帧的帧移 |
fmin | 0 | 梅尔滤波最低频率(Hz) |
fmax | 8000 | 梅尔滤波最高频率(Hz) |
n_mels | 80 | 梅尔频带数 |
clip_frames | 65 | 训练时随机裁剪的梅尔帧数 |
模型结构参数(config.model)
| 参数 | 默认值 | 含义 |
|---|---|---|
upsample_factors | [16, 16] | 两层 Conv2DTranspose 的时间上采样因子,乘积 256 恰好等于hop_length |
n_flows | 8 | WaveFlow 中 flow 的数量 |
n_layers | 8 | 每个 flow 中卷积块的数量 |
n_group | 16 | 音频与频谱的折叠(fold)因子 |
channels | 128 | 每个 flow 中的残差通道数(residual channel) |
kernel_size | [3, 3] | 每个卷积块的卷积核大小 |
sigma | 1.0 | 随机噪声的标准差(用于损失中的噪声项) |
训练参数(config.training)
| 参数 | 默认值 | 含义 |
|---|---|---|
lr | 2e-4 | Adam 优化器学习率 |
valid_interval | 1000 | 每 1000 步执行一次验证 |
save_interval | 10000 | 每 10000 步保存一次检查点 |
max_iteration | 3000000 | 最大训练迭代步数 |
upsample_factors与预处理参数存在强约束关系:模型实现 waveflow.py 中的UpsampleNet注释明确指出,np.prod(upsample_factors)必须等于 STFT 的hop_length——16 × 16 = 256,恰好与预处理阶段的hop_length=256匹配,保证上采样后的梅尔频谱与波形在时间轴上对齐。修改其中任何一组参数时,务必保持这一等式成立。
WaveFlow 模型结构源码解析
WaveFlow 的完整实现位于 paddlespeech/t2s/models/waveflow.py,核心组件包括:
UpsampleNet:由多个Conv2DTranspose(带 weight norm 初始化)组成,把梅尔频谱在时间维度上按upsample_factors逐步上采样到与波形相同的时间分辨率,每层后接leaky_relu(0.4)激活,并可选择trim_conv_artifact裁剪反卷积边界伪影;ResidualBlock:WaveFlow 的基本卷积单元,在高度维度使用因果填充(causal padding)、宽度维度使用 same padding,并带有条件(condition)投影与输出投影,通过receptive_field = 1 + (k - 1) * d计算感受野以确定填充量;fold:将音频/频谱的时间维按n_group=16折叠为(time_steps // n_group, n_group)的分组形状,这是 WaveFlow 并行化的关键操作;ConditionalWaveFlow:以梅尔频谱为条件的主模型,支持from_pretrained加载检查点与predict推理;WaveFlowLoss:基于归一化流的负对数似然损失,以sigma作为噪声标准差。
结合 train.py 中setup_model的调用可知,模型实例化参数(upsample_factors、n_flows、n_layers、n_group、channels、n_mels、kernel_size)全部取自上述配置,体现了"配置驱动模型结构"的设计模式。
使用预训练模型
仓库为 LJSpeech 数据集提供了 WaveFlow 预训练模型(residual channel 为 128,即对应默认配置channels=128),压缩包名为waveflow_ljspeech_ckpt_0.3.zip,可从 PaddleSpeech 官方模型下载地址(paddlespeech.cdn.bcebos.com 的 Parakeet released_models 目录)获取并解压。解压后得到检查点文件,可直接将其路径作为synthesize.py的--checkpoint_path使用(同样不含.pdparams扩展名),省去自行训练的耗时。
小结
examples/ljspeech/voc0给出了一个完整、可复现的 WaveFlow 声码器训练与推理流程:从 LJSpeech-1.1 原始音频出发,通过 preprocess.py 得到对齐的对数梅尔频谱与波形对;用 train.py 以归一化流目标训练ConditionalWaveFlow,支持随机裁剪、验证集划分与多卡分布式扩展;再用 synthesize.py 从任意.npy梅尔频谱批量合成 22050 Hz 的 WAV 音频。该示例与examples/ljspeech/tts0(Tacotron2)可以无缝衔接,构成 PaddleSpeech 中英文单说话人 TTS 的完整闭环。
- 人工智能
- 语音
- 音频
【免费下载链接】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 实战:基于 LJSpeech-1.1 训练 HiFiGAN 声码器全流程指南
PaddleSpeech 实战:基于 LJSpeech 1.1 训练 HiFiGAN 声码器全流程指南 本文是一篇面向语音合成开发者的实战指南,围绕 Paddl
人工智能语音音频NLP媒体生成MooTool二维码生成与解析:自定义尺寸、Logo与高级纠错功能
MooTool二维码生成与解析:自定义尺寸、Logo与高级纠错功能 在数字化时代,二维码已成为我们生活中不可或缺的一部分。无论是支付、分享链接还是身份验证,二维
人工智能语音音频PaddleSpeech WaveFlow 声码器波形合成指南:synthesize.py 全流程解析与实战
PaddleSpeech WaveFlow 声码器波形合成指南:synthesize.py 全流程解析与实战 WaveFlow 是基于流的生成式声码器(voco
人工智能语音音频NLP媒体生成
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考