SpeechBrain 波形映射语音增强实战:基于 VoiceBank 数据集与全卷积网络(FCN)的训练全流程
【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain
本文基于 SpeechBrain 仓库中 recipes/Voicebank/enhance/waveform_map/README.md 对应的配方(recipe)展开:它实现了一套直接在时域波形上进行映射的语音增强(waveform mapping based speech enhancement)系统,使用 VoiceBank 数据集训练,以端到端方式从带噪波形直接回归出干净波形。读完本文,你将掌握该配方的数据集准备、FCN 全卷积模型结构、超参数文件含义、训练脚本的数据流与评估逻辑,并能直接复现训练与推理流程。
技术背景:为什么选择波形映射
语音增强的目标是从带噪语音中恢复出干净语音。主流做法有两类:
- 频域方法(spectral mapping / masking):先对带噪信号做 STFT 得到频谱,在频域学习掩码或频谱映射,再用 ISTFT 重建波形。仓库中 recipes/Voicebank/enhance/spectral_mask 即是此类代表。
- 时域波形映射(waveform mapping):本配方所采用的方式,直接以原始带噪波形为输入、干净波形为训练目标,通过神经网络完成
noisy_wav → clean_wav的端到端回归。
波形映射的显著优势是不需要任何手工设计的特征提取与信号重建模块,整个"前端处理"由网络自行学习,训练与推理流程因此变得非常简洁。在 hparams/train.yaml 中可以看到,STFT/ISTFT 相关配置全部被注释掉(# compute_STFT、# compute_ISTFT等),这正是时域方法无需频谱变换的直接证据。
数据集准备:VoiceBank(noisy-vctk-16k)
数据集的目录结构
VoiceBank 是语音增强领域广泛使用的标准评测数据集。本配方通过 voicebank_prepare.py 完成数据准备,它期望data_folder下存在以下目录(均为 16kHz 重采样后的版本):
| 目录 | 用途 |
|---|---|
clean_trainset_28spk_wav_16k | 训练集干净语音(28 位说话人) |
noisy_trainset_28spk_wav_16k | 训练集带噪语音(与干净语音一一对应) |
trainset_28spk_txt | 训练集文本标注 |
clean_testset_wav_16k | 测试集干净语音 |
noisy_testset_wav_16k | 测试集带噪语音 |
testset_txt | 测试集文本标注 |
下载与重采样
脚本中提供了download_vctk(destination, tmp_dir=None, device="cpu")辅助函数:它会下载 VoiceBank 的 6 个 zip 压缩包(clean/noisy 的训练与测试集及对应文本),解压后使用torchaudio.transforms.Resample(orig_freq=48000, new_freq=16000)将原始 48kHz 采样率降采样为 16kHz,并重新打包为noisy-vctk-16k.zip。重采样可以指定运行设备(device参数),方便在 GPU 上加速。
训练/验证集划分
脚本通过valid_speaker_count参数(默认 2)控制验证集规模:从 28 位训练说话人(TRAIN_SPEAKERS列表,如p226、p287、p227等)中取出前 N 位作为验证集说话人,其余作为训练集:
valid_speakers = TRAIN_SPEAKERS[:valid_speaker_count] wav_lst_train = get_all_files(train_noisy_folder, match_and=extension, exclude_or=valid_speakers) wav_lst_valid = get_all_files(train_noisy_folder, match_and=extension, match_or=valid_speakers)测试集则使用全部测试目录下的 wav 文件。这样划分保证了验证集说话人与训练集完全不相交,评估结果更能反映模型的泛化能力。
JSON 标注文件生成
create_json()为每条语音生成一条 JSON 记录,包含:
noisy_wav/clean_wav:带噪与干净音频的相对路径(以{data_root}占位符开头,运行时由replacements={"data_root": hparams["data_folder"]}替换);length:语音时长(秒),由read_audio读取信号后按signal.shape[0] / 16000计算;words/phones:词级与音素级标注。音素由 LibriSpeech 词表(create_lexicon自动下载解析)查得,并对连续重复音素做了去重处理,缺失词条由脚本内置的MISSING_LEXICON字典补齐。
最终生成train.json、valid.json、test.json三个文件,存放在save_folder(即 hparams 中的output_folder)下。prepare_voicebank支持skip_prep=True跳过已完成的准备(通过检查三个 JSON 文件是否已存在判断)。
模型结构:全卷积网络 FCN
模型定义在 hparams/models/FCN.yaml,对应论文"End-to-End Waveform Utterance Enhancement for Direct Evaluation Metrics Optimization by Fully Convolutional Neural Networks"(TASLP, 2018)。它是一套纯 1D 卷积的时域映射网络:
| 层 | 类型 | 关键参数 |
|---|---|---|
IN0 | InstanceNorm1d | input_size=1,affine=True |
conv1~conv7 | Conv1d | out_channels=base_channels(80),kernel_size=55 |
IN1~IN7 | InstanceNorm1d | input_size=80 |
act1~act7 | LeakyReLU | negative_slope=0.3 |
conv_out | Conv1d | out_channels=1,kernel_size=55 |
要点说明:
- 感受野设计:卷积核
kernel_size: 55(约 3.4ms @16kHz),7 层堆叠后网络能感知足够长的上下文窗口,对语音帧进行局部建模; - 通道数:
base_channels: 80,配置注释提示"Use 30 for small version",即减小到 30 可得到轻量版本; - 归一化:全部使用
InstanceNorm1d且track_running_stats: False,避免 batch 统计引入训练/推理不一致,适合 batch_size 较小的场景; - 输出层:
conv_out输出 1 个通道,对应单声道干净波形,与输入input_shape: [null, null, 1]呼应; - 可替换性:配置注释提示卷积层"Can be replaced with SincConv"——SpeechBrain 在 speechbrain/nnet/CNN.py 中同时实现了
SincConv(可学习的 sinc 带通滤波器组,SincConv定义于该文件第 30 行)与Conv1d(第 314 行),你可以用!name:speechbrain.nnet.CNN.SincConv直接替换以获得可解释的前端滤波器。
模型通过Sequential容器(speechbrain/nnet/containers.py)按命名顺序组装,超参kernel_size、base_channels通过!ref引用复用,便于统一调参。
超参数文件解析:train.yaml
hparams/train.yaml 是配方的核心配置,基于 SpeechBrain 的 HyperPyYAML 机制编写(!ref引用、!new实例化、!name函数引用、!apply执行)。逐段拆解如下。
基础路径与随机种子
seed: 3234 __set_seed: !apply:speechbrain.utils.seed_everything [!ref <seed>] data_folder: /localscratch/noisy-vctk-16k output_folder: !ref ./results/waveform_map/<seed> save_folder: !ref <output_folder>/save train_log: !ref <output_folder>/train_log.txt enhanced_folder: !ref <output_folder>/enhanced- 种子必须在 YAML 顶部、任何带参数的模块实例化之前设置,以保证全程可复现;
data_folder指向下载并解压好的 VoiceBank 数据目录;output_folder按<seed>隔离实验目录,其中包含save/(检查点)、enhanced/(增强后音频)、train_log.txt(训练日志)。
数据文件与训练参数
train_annotation: !ref <output_folder>/train.json valid_annotation: !ref <output_folder>/valid.json test_annotation: !ref <output_folder>/test.json skip_prep: False number_of_epochs: 150 N_batch: 1 lr: 0.001 device: 'cuda:0' sorting: ascending dataloader_options: batch_size: !ref <N_batch>- 三个标注文件由
prepare_voicebank生成; - 默认训练150 个 epoch、
batch_size=1(逐条样本更新)、学习率0.001(Adam 优化器); sorting: ascending表示按音频时长升序排列训练集,同时自动关闭 dataloader 的shuffle(见 train.py 中dataio_prep的逻辑:ascending/descending 时filtered_sorted+shuffle=False,random 时保持打乱,其他取值直接抛NotImplementedError);device默认cuda:0,可在命令行通过 run_opts 覆盖。
模型、优化器与检查点
models: !include:models/FCN.yaml epoch_counter: !new:speechbrain.utils.epoch_loop.EpochCounter limit: !ref <number_of_epochs> modules: model: !ref <models[model]> opt_class: !name:torch.optim.Adam lr: !ref <lr> checkpointer: !new:speechbrain.utils.checkpoints.Checkpointer checkpoints_dir: !ref <save_folder> recoverables: model: !ref <models[model]> counter: !ref <epoch_counter>- 通过
!include:models/FCN.yaml一行切换模型——把该语句指向其他模型文件即可替换网络; modules把 FCN 模型注册到 Brain 类的self.modules.model;EpochCounter(定义于 speechbrain/utils/epoch_loop.py)配合Checkpointer实现断点续训,recoverables同时保存模型与 epoch 计数。
损失函数与日志
compute_cost: !name:speechbrain.nnet.losses.mse_loss # compute_cost: !name:speechbrain.nnet.loss.stoi_loss.stoi_loss train_logger: !new:speechbrain.utils.train_logger.FileTrainLogger save_file: !ref <train_log> tensorboard_train_logger: !new:speechbrain.utils.train_logger.TensorboardLogger save_dir: !ref <tensorboard_logs>默认损失为均方误差(MSE),实现于 speechbrain/nnet/losses.py 第 320 行的mse_loss——它会对预测与目标做长度截断(容忍allowed_len_diff=3的差异)并用长度掩码计算真实误差,支持mean/batch/sum等 reduction 方式。注释中还给出了第二个选项stoi_loss(实现于 speechbrain/nnet/loss/stoi_loss.py 第 135 行),其返回值为-1 * STOI以便用 SGD 直接优化该可微指标。更换损失函数只需取消注释、切换!name即可,这正是 SpeechBrain 配方高度模块化的体现。
训练脚本核心逻辑
train.py 是整个配方的执行入口,核心是继承自sb.Brain的SEBrain类。
前向计算:compute_forward
noisy_wavs, lens = batch.noisy_sig noisy_wavs = torch.unsqueeze(noisy_wavs, -1) predict_wavs = self.modules.model(noisy_wavs)[:, :, 0]从 batch 中取出带噪波形,unsqueeze(-1)增加通道维度以匹配 FCN 的input_shape [null, null, 1],前向得到预测波形后取[:, :, 0]去掉输出通道维。
损失与评估:compute_objectives
loss = self.hparams.compute_cost(predict_wavs, clean_wavs, lens)训练阶段只计算损失;验证/测试阶段还会:
- 用
stoi_loss计算STOI(注意它返回负值,故统计时-self.stoi_metric.summarize("average")转回正值,值越大越好); - 用第三方
pesq库以宽频带模式计算PESQ:pesq(fs=16000, ref=..., deg=..., mode="wb"); - 在
sb.Stage.TEST阶段将增强波形归一化后写入磁盘:
pred_wav = pred_wav / torch.max(torch.abs(pred_wav)) * 0.99 audio_io.save(enhance_path, torch.unsqueeze(pred_wav[: int(length)].cpu(), 0), 16000)增强音频保存在enhanced_folder下,命名与输入样本一致(追加.wav),length由相对长度乘上样本长度还原为绝对采样点数,用于裁剪尾部填充。
数据管道:dataio_prep
通过@sb.utils.data_pipeline.takes/provides装饰器定义两条动态管道:
@sb.utils.data_pipeline.takes("noisy_wav") @sb.utils.data_pipeline.provides("noisy_sig") def noisy_pipeline(noisy_wav): return sb.dataio.dataio.read_audio(noisy_wav) @sb.utils.data_pipeline.takes("clean_wav") @sb.utils.data_pipeline.provides("clean_sig") def clean_pipeline(clean_wav): return sb.dataio.dataio.read_audio(clean_wav)即从 JSON 记录的noisy_wav/clean_wav路径实时读取音频,输出键为id、noisy_sig、clean_sig。三个数据集(train/valid/test)均由DynamicItemDataset.from_json构建,共享同一条管道定义。
主流程
hparams_file, run_opts, overrides = sb.parse_arguments(sys.argv[1:]) ... sb.utils.distributed.ddp_init_group(run_opts) run_on_main(prepare_voicebank, ...) datasets = dataio_prep(hparams) sb.create_experiment_directory(...) se_brain.fit(epoch_counter=..., train_set=..., valid_set=...) test_stats = se_brain.evaluate(test_set=..., max_key="pesq", ...)sb.parse_arguments支持命令行传入超参数覆盖(如python train.py hparams/train.yaml --number_of_epochs 50);- 数据准备通过
run_on_main仅在主进程中执行一次(兼容多卡 DDP); - 训练完成后用
max_key="pesq"加载 PESQ 最优的检查点进行测试集评估; - 检查点保存策略为
save_and_keep_only(meta=stats, max_keys=["pesq"]),即只保留验证集 PESQ 最好的模型。
运行方法
进入配方目录后执行(注意:实际配置文件位于hparams/子目录):
cd recipes/Voicebank/enhance/waveform_map python train.py hparams/train.yaml前提条件:
- 安装 SpeechBrain 及其依赖(
pesq库用于评测); - 将 VoiceBank 数据放置于
data_folder(或用download_vctk下载重采样),或在命令行通过--data_folder /path/to/data覆盖默认路径; - 有可用 GPU 时保持默认
device: 'cuda:0'。
训练产出物:
results/waveform_map/<seed>/save/:模型检查点(按 PESQ 择优保留);results/waveform_map/<seed>/enhanced/:测试集增强后的 wav 音频;results/waveform_map/<seed>/train_log.txt及logs/:文本/TensorBoard 训练日志(use_tensorboard: True时启用)。
原 README 还提到官方提供了完整实验目录(checkpoints、logs 等)的预训练结果下载,用于直接对比或推理验证;当前仓库中同一目录下还提供了 spectral_mask(频域掩码)、MetricGAN、MetricGAN-U、SEGAN、SGMSE 等对照配方,便于横向比较不同增强范式的效果。
小结
本配方用不足 250 行训练脚本 + 两个 YAML 配置,完整实现了一套可复现的时域波形映射语音增强系统:voicebank_prepare.py负责数据下载、重采样与 JSON 标注生成,FCN.yaml定义 7 层全卷积映射网络,train.yaml通过 HyperPyYAML 将数据、模型、损失、优化器、检查点与日志全部参数化,SEBrain统一承载前向、损失、评估与增强音频导出逻辑。对研究者而言,这套结构是快速验证"新模型 + 新损失"组合的高效起点——只需修改!include指向的模型文件或compute_cost指向的损失函数即可。
【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考