首页
/ SpeechBrain 实战:Switchboard ASR 端到端 seq2seq 训练(CTC + Attention + Beam Search 解码)

SpeechBrain 实战:Switchboard ASR 端到端 seq2seq 训练(CTC + Attention + Beam Search 解码)

2026-09-14 12:09:51作者:明树来

本篇技术指南以 SpeechBrain 仓库中 recipes/Switchboard/ASR/seq2seq 配方(Recipe)为核心,系统讲解如何基于 CNN-RNN(CRDNN)编码器 + GRU 注意力解码器 在 Switchboard 电话对话语料上训练端到端语音识别(E2E ASR)系统,覆盖数据获取与准备、超参数配置、CTC+NLL 联合训练、Beam Search 解码以及 eval2000/Hub5 测试集上的 WER 评测。读完本文,你将掌握该配方的完整运行链路,并能依据源码级证据按需修改编码器、解码器、词表与训练策略。

配方概览:解决什么问题

该配方位于 recipes/Switchboard/ASR/seq2seq/README.md,目标是在 Switchboard 语料上训练一套 序列到序列(seq2seq)ASR 系统,其核心技术特征为:

  • 编码器:CRDNN(卷积 + 循环 + 全连接混合网络),默认配置为 2 个 CNN 块、4 层双向 LSTM、2 个 DNN 块;
  • 解码器:基于位置注意力(location-aware attention)的标准 GRU 解码器;
  • 识别单元:通过 BPE(Byte Pair Encoding)估计的 2000 个子词 token;
  • 训练目标:前若干 epoch 同时优化 CTC 与负对数似然(NLL),后续 epoch 仅优化 NLL,实现 CTC + attention 联合训练;
  • 解码方式:Beam Search(束搜索),并可选集成 CTC scorer、coverage penalty 与外部 RNN 语言模型 scorer。

仓库中该配方目录包含四个关键文件:train.py(训练/评估主脚本)、hparams/train_BPE_2000.yaml(超参数配置)、switchboard_prepare.py(数据准备)、normalize_util.py(评测归一化工具)。

数据集:需要准备哪些 LDC 语料

训练与评测依赖以下 LDC 发布的语料(均为付费数据集,需从 LDC 官方渠道获取):

用途 语料 LDC 编号
训练集 Switchboard-1 Release 2 LDC97S62
测试集语音 eval2000/Hub5 English LDC2002S09
测试集转写 eval2000/Hub5 English transcripts LDC2002T43
Fisher 语料 Part 1(仅用于 Tokenizer/LM 训练) Fisher transcripts LDC2004T19
Fisher 语料 Part 2(仅用于 Tokenizer/LM 训练) Fisher transcripts LDC2005T19

注意:Switchboard 原始音频为 sph 格式(8kHz 电话语音),eval2000 测试集按 Switchboard 会话(sw_ 前缀)与 Callhome 会话(en_ 前缀)两个子集拆分评测。Fisher 语料在本配方的声学模型训练中不参与(超参数中 add_fisher_corpus: False),它只在 Tokenizer 与语言模型训练阶段被使用,这一点在 switchboard_prepare.py 的 docstring 中有明确说明。

从源码结构可以推断,数据准备脚本要求 Switchboard 数据文件夹下存在 docsswb1_d1~swb1_d4 五个子目录(对应 4 张 DVD 内容与文档),测试集则期望 LDC2002S09/hub5e_00/english 音频目录与 LDC2002T43/.../hub5e00.english.000405.stm 转写文件存在,缺失时 check_data_folder 会直接抛出 OSError。Switchboard 的训练转写(swb_ms98_transcriptions)会由脚本自动从 OpenSLR 下载并解压,无需手动准备。

如何运行:一行命令启动训练

运行前必备条件

  1. 已训练好的 BPE Tokenizer(tokenizer.ckpt)。配方假设 tokenizer 已预先训练好,通过 pretrained_tokenizer_path 指定其本地目录,例如 recipes/Switchboard/Tokenizer 目录下产出的 checkpoint;
  2. 在超参数文件中填写两个 !PLACEHOLDER
    • pretrained_tokenizer_path:tokenizer checkpoint 所在目录;
    • data_folder:Switchboard(与 Fisher)语料存放的本地路径,如 /nfs/data/swbd

启动命令

python train.py hparams/train_BPE_2000.yaml

在仓库根目录下运行时,等价于:

python recipes/Switchboard/ASR/seq2seq/train.py recipes/Switchboard/ASR/seq2seq/hparams/train_BPE_2000.yaml

训练脚本还支持 SpeechBrain 标准的 CLI 参数解析(train.py 中通过 sb.parse_arguments(sys.argv[1:]) 处理),因此可追加 --device--debug--data_parallel_backend 等 run_opts,也可以直接在命令行用 hparams="..." 覆盖 YAML 中的任意超参数。数据准备、Tokenizer/LM 下载、训练、测试会在一次运行中按序自动完成。

训练流程中的关键节点

train.py 的主流程可以梳理出完整执行链:

  1. 解析 CLI 参数,初始化 DDP 组(sb.utils.distributed.ddp_init_group),加载 HyperPyYAML 超参数;
  2. 创建实验输出目录(sb.create_experiment_directory),保存超参数副本;
  3. 数据准备:run_on_main(prepare_switchboard, ...) 保证多卡环境下只在主进程执行一次;随后 run_on_main(hparams["prepare_noise_data"]) 自动从 URL 下载增强用噪声数据并生成 noise.csv 清单;
  4. 构建数据集:dataio_prepare(hparams) 返回 train/valid/test 数据集与(可选的)动态批采样器;
  5. 加载预训练 tokenizer(hparams["pretrainer"].collect_files() + load_collected());
  6. 初始化 ASR Brain 类并调用 fit(...) 训练,最后对 test_swbdtest_callhometest 三个测试子集分别执行 evaluate(...),WER 结果写入 wer_<split>.txt

超参数详解:train_BPE_2000.yaml 逐项拆解

配置文件 hparams/train_BPE_2000.yaml 是配方的核心,其声明模型为"E2E ASR with attention-based ASR:CRDNN 编码器 + GRU 解码器 + BPE unigram token + CTC+NLL 损失"。下面对照源码逐段说明。

数据与目录配置

seed: 1312
output_folder: !ref results/CRDNN_BPE_NO_LM/<seed>
pretrained_tokenizer_path: !PLACEHOLDER
tokenizer_file: !ref <pretrained_tokenizer_path>/tokenizer.ckpt
data_folder: !PLACEHOLDER

splits: ["train", "dev"]
split_ratio: [99, 1]
skip_prep: False
add_fisher_corpus: False
normalize_words: True
max_utt: 300
ckpt_interval_minutes: 15

test_csv:
   - !ref <save_folder>/test_swbd.csv
   - !ref <save_folder>/test_callhome.csv
   - !ref <save_folder>/test.csv
  • seed: 1312 通过 speechbrain.utils.seed_everything 在 YAML 顶层、任何带参数对象实例化之前设置随机种子,保证实验可复现;
  • split_ratio: [99, 1] 表示把 Switchboard 训练语料按会话数近似 99:1 切分为 train/dev;脚本要求比例之和为 1 或 100(switchboard_prepare.pysum(split_ratio) != 100 and sum(split_ratio) != 1 时抛出 ValueError);
  • max_utt: 300 用于限制相同转写文本在训练集中最多出现 300 次,源码中通过 utt2count 计数器在写 CSV 时去重控制,其目的如 docstring 所述:移除 uh-huh 这类重复率极高的填充语;
  • test_csv 含三个文件:测试集整体(test.csv)以及按 Switchboard 会话(test_swbd.csv)与 Callhome 会话(test_callhome.csv)拆分后的两个子集,由 eval2000_data_prep 在准备阶段生成(switchboard_prepare.py);
  • 噪声数据(用于训练期增强)会自动从 NOISE_DATASET_URL 下载到 data_folder_noise,并通过 prepare_noise_data 生成清单文件 noise.csv

训练超参数

number_of_epochs: 20
number_of_ctc_epochs: 5
batch_size: 10
lr: 1.0
ctc_weight: 0.5
sorting: ascending
dynamic_batching: False

# Feature parameters
sample_rate: 8000
n_fft: 400
n_mels: 40

opt_class: !name:torch.optim.Adadelta
   lr: !ref <lr>
   rho: 0.95
   eps: 1.e-8
  • number_of_ctc_epochs: 5ctc_weight: 0.5 控制联合训练策略:前 5 个 epoch 损失为 0.5 * loss_ctc + 0.5 * loss_seq,之后仅优化 loss_seq。该逻辑体现在 train.pycompute_forward/compute_objectives 中——前 5 个 epoch 额外计算 ctc_lin 输出,之后便不再产生 CTC 分支;
  • 特征为 8kHz 采样率下的 40 维 FBank(n_fft=400, n_mels=40),由 speechbrain.lobes.features.Fbank 计算,训练时以 speechbrain.processing.features.InputNormalization 做全局归一化;
  • 优化器采用 Adadelta(lr=1.0, rho=0.95, eps=1e-8),配合 NewBob 调度器(improvement_threshold=0.0025, annealing_factor=0.8)在验证 WER 无明显改善时将学习率乘以 0.8;
  • sorting: ascending 表示训练数据按 duration 升序排列以加速训练并稳定收敛,同时 dataloader 的 shuffle 会被置为 False(否则排序无意义),见 dataio_prepare 中的实现。

模型结构配置

cnn_blocks: 2
cnn_channels: (128, 256)
inter_layer_pooling_size: (2, 2)
cnn_kernelsize: (3, 3)
time_pooling_size: 4
rnn_class: !name:speechbrain.nnet.RNN.LSTM
rnn_layers: 4
rnn_neurons: 1024
rnn_bidirectional: True
dnn_blocks: 2
dnn_neurons: 512
emb_size: 128
dec_neurons: 1024
output_neurons: 2000  # Number of tokens used for tokenizer
blank_index: 0
bos_index: 1
eos_index: 2

编码器为 speechbrain.lobes.models.CRDNN.CRDNN(实现见 speechbrain/lobes/models/CRDNN.py),其构造逻辑为"CNN 块 → 时间维池化 → 多层双向循环网络 → DNN 块"的顺序容器:

  • 2 个 CNN 块(128、256 通道,3×3 卷积核),块间 (2, 2) 池化;随后 time_pooling: Truetime_pooling_size: 4,在进入 RNN 前对时间轴做 4 倍下采样(源码 docstring 明确注明该模式下输出时间维会被降采样);
  • 4 层双向 LSTM、每层 1024 神经元,rnn_re_init: True 表示对循环权重做正交初始化;
  • 2 个 DNN 块、每层 512 神经元,编码器输出维度即 dnn_neurons = 512
  • 词嵌入维度 emb_size=128,解码器隐藏层 dec_neurons=1024,输出层 output_neurons=2000 与 BPE 词表大小严格对应;
  • blank_index/bos_index/eos_index 分别为 0/1/2,用于 CTC 空白符与序列起止符。

解码器使用 speechbrain.nnet.RNN.AttentionalRNNDecoder(实现见 speechbrain/nnet/RNN.py),配置为 1 层 GRU、attn_type: location 位置注意力、attn_dim: 1024、注意力卷积核 channels: 10, kernel_size: 100dropout: 0.15

数据增强配置

add_noise: !new:speechbrain.augment.time_domain.AddNoise
   snr_low: 0
   snr_high: 15
speed_perturb: !new:speechbrain.augment.time_domain.SpeedPerturb
   speeds: [95, 100, 105]
drop_freq: ...
drop_chunk: ...
wav_augment: !new:speechbrain.augment.augmenter.Augmenter
   concat_original: True
   min_augmentations: 4
   max_augmentations: 4
   augment_prob: 1.0

增强发生在波形域:加噪(SNR 0~15dB)、速度扰动(95%/100%/105%)、频带随机置零(DropFreq)与时间块随机置零(DropChunk)。Augmenter 每步从 4 种增强中随机组合 4 种(concat_original: True 表示原信号与增强信号拼接,增加训练样本时长)。在 train.pycompute_forward 中,增强仅在 sb.Stage.TRAIN 阶段应用,并通过 wav_augment.replicate_labels / replicate_multiple_labels 同步复制标签,保证拼接/并行增强后标签序列与音频时间维一致。

解码与语言模型配置

min_decode_ratio: 0.0
max_decode_ratio: 1.0
valid_beam_size: 60
test_beam_size: 80
eos_threshold: 1.5
using_max_attn_shift: True
max_attn_shift: 240
ctc_weight_decode: 0.3
coverage_penalty: 1.8
temperature: 1.25
scorer_beam_scale: 0.1

解码器为 speechbrain.decoders.S2SRNNBeamSearcher(实现见 speechbrain/decoders/seq2seq.py),验证集束宽 60、测试集束宽 80,eos_threshold: 1.5 用于在束搜索中对提前终止施加阈值,max_attn_shift: 240 限制注意力偏移范围(电话语音解码的常用约束)。valid_searchtest_search 是同一解码器的两个实例,仅束宽不同。

YAML 中声明了一个 RNN 语言模型(speechbrain.lobes.models.RNNLM.RNNLM,2 层、每层 2048 神经元、return_hidden: True)与两个 scorer:

  • CTCScorer:利用 ctc_lin 输出在解码时注入 CTC 分数,权重 ctc_weight_decode: 0.3
  • CoverageScorer:惩罚注意力重复覆盖已生成内容,权重 coverage_penalty: 1.8
  • 二者经 ScorerBuilder 组合,scorer_beam_scale: 0.1 调整 scorer 随束搜索推进的缩放。

需要说明的是:该 YAML 名称为 CRDNN_BPE_NO_LM,即默认不加载外部 LM 权重(LM 结构与 scorer 已声明,但 pretrainer 仅加载 tokenizer);超参数文件注释亦提示"LM 必须与预训练的 RNNLM 匹配",若需启用 LM 可参照仓库中 transformer 配方的 transformer_finetuned_LM.yaml 自行接入预训练 LM checkpoint。

数据准备脚本:从 LDC 原始语料到 CSV 清单

switchboard_prepare.py 负责把原始语料转成 SpeechBrain 的 DynamicItemDataset 可直接读取的 CSV 清单,其核心处理包括:

  1. Switchboard 训练数据:按 swb1_all.dvd.tbl 映射表把每个会话定位到对应 DVD 目录(swb1_d1~swb1_d4),从转写文件逐行解析出 ID/duration/start/stop/channel/wav/words/spk_id 八列;起始/结束时间以 8kHz 采样率换算为采样点,供后续在 train.pyaudio_pipeline 中按 frame_offset/num_frames 精确切片;多声道 sph 文件按 channel(A/B)选取对应声道;
  2. 文本清洗filter_text 按数据集类型(train/eval2000/fisher)应用不同正则规则——Switchboard 训练文本会去除 [SILENCE]、笑声标记等标签(如 [LAUGHTER-STORY]STORY)、拆分部分词(-[AN]Y-Y)、去除花括号非标准词标记;eval2000 测试文本则删除 <B_ASIDE> 之类的旁白标记与 (...) 可选词;
  3. 缩写统一make_acronym_map 结合 Switchboard 词典与 MSU 单字母表构建缩写映射,把 IBMi._b._m. 这类写法转换为 Fisher 语料约定(该逻辑复刻自 Kaldi 的 format_acronyms_dict.py);
  4. eval2000 测试集:解析 2000_hub5_eng_eval_tr/reference/hub5e00.english.000405.stm,按文件名前缀 sw_/en_ 区分 Switchboard 与 Callhome 会话,分别生成 test_swbd.csvtest_callhome.csv 并合并为 test.csv;同时解析 en20000405_hub5.glm 文件生成 glm.csv,供评测期归一化使用;
  5. Fisher 语料:当 add_fisher_corpus: True 时,把 Fisher 两份转写与 Switchboard 训练文本合并为 train_lm.csv,专供 Tokenizer/LM 训练(此时 max_utt 被放大到极大值以保留全部语句)。

训练与评估脚本:Brain 类如何组织前向、损失与评测

train.py 定义了一个继承 sb.BrainASR 类,三个核心钩子函数与配置严格对应:

  • compute_forward:波形 → FBank → 归一化 → CRDNN 编码器(x = self.modules.enc(feats.detach()),注意编码器输入对特征做了 detach(),避免增强模块的梯度反向传播进特征提取)→ 嵌入层 + 注意力解码器 → seq_lin 输出 seq2seq 对数概率。训练前 number_of_ctc_epochs 个 epoch 额外返回 ctc_lin 的 CTC 对数概率;验证/测试阶段则调用 valid_search/test_search 做束搜索,返回解码出的 token 序列 p_tokens
  • compute_objectives:训练阶段按 ctc_weight 加权 CTC 损失与 NLL 损失(nll_loss 启用 label_smoothing: 0.1);验证/测试阶段把预测 token 用 tokenizer.decode_ids 还原为词序列,与 batch.words 目标词对比,累加进 WER/CER 指标(speechbrain.utils.metric_stats.ErrorRateStats),并在测试阶段通过 normalize_fn(即 normalize_util.normalize_words)先做归一化再计分;
  • on_stage_end:验证阶段以 WER 为监控指标做 NewBob 学习率退火,并调用 checkpointer.save_and_keep_only(meta={"WER": ...}, min_keys=["WER"]) 只保留 WER 最优的 checkpoint;测试阶段将 WER 明细写入 wer_<split>.txt

评测归一化:为什么 Switchboard 的 WER 需要"特殊处理"

normalize_util.py 实现了评测期文本归一化,其作用被源码 docstring 概括为"移除所有在参考转写中属于可选/可删除(括号内)的内容——删掉它们没有损失,识别对它们也没有收益",具体策略包括:

  • 展开常见缩略(won'tWILL NOTcan'tCAN NOTI'vI HAVE 等),拆分连写词(如 pseudo-rebelpseudo rebel);
  • 剔除 - 开头的残缺词、过滤 <UNK>/UH/UM/MM 等排除词;
  • 依据准备阶段解析出的 glm.csv(来自 en20000405_hub5.glm 的 Hub4-E/Hub5-E 替代表)把预测词映射到参考中的合法替身。

这套流程复刻自 Kaldi 的 local/score.sh,是 Switchboard/eval2000 评测中保证 WER 可比的必要环节,与 YAML 中 normalize_words: True 相对应。

实验结果与硬件需求

该配方在仓库记录的公开结果如下(训练于单张 40GB A100 GPU):

Release hyperparams file Swbd WER Callhome WER Eval2000 WER GPUs
17-09-22 train_BPE_2000.yaml 16.01 25.12 20.71 1×A100 40GB

三个数字分别对应 eval2000 测试集的 Switchboard 部分、Callhome 部分与整体 WER。实验输出(日志、checkpoint、WER 报告)默认落在 results/CRDNN_BPE_NO_LM/<seed>/ 目录下,其中 wer_<split>.txt 为逐句 WER 明细,可用于错误分析。

常见改动路径与进阶方向

train.py 的 docstring("The experiment file is flexible enough to support a large variety of different systems")可知,该配方刻意设计为高度可定制:

  • 更换编码器:替换 YAML 中 encspeechbrain.lobes.models.transformer.TransformerEncoder 等,即可向 Transformer 编码器迁移(仓库 recipes/Switchboard/ASR/transformer 目录即为同数据集的 Transformer 版本,可对照参考);
  • 更换识别单元:把 output_neurons 与 tokenizer 换成字符级(character)词表即可训练字符模型;
  • 启用语言模型:为 pretrainer 增加 lm_model 的 loadable/path 映射,并保持 YAML 中 RNNLM 结构与预训练 LM 一致(详见配置中关于 LM 的注释);
  • 训练数据规模扩展:设置 add_fisher_corpus: True 并把 Fisher 转写放入 data_folder,可扩大语言模型/词表训练语料;
  • 批处理策略:将 dynamic_batching: True 后,dataio_prepare 会改用 speechbrain.dataio.sampler.DynamicBatchSampler,按 feats_hop_size(0.01s)换算的帧数为单位限制 max_batch_length: 20000,在长语音数据上显著提升 GPU 利用率。

参考与延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.81 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
945
1.86 K
docsdocs
暂无描述
Markdown
906
5.84 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
537
607
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.28 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.39 K
1.48 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
401
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.19 K
347