SpeechBrain 实战:Switchboard ASR 端到端 seq2seq 训练(CTC + Attention + Beam Search 解码)
本篇技术指南以 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 数据文件夹下存在 docs、swb1_d1~swb1_d4 五个子目录(对应 4 张 DVD 内容与文档),测试集则期望 LDC2002S09/hub5e_00/english 音频目录与 LDC2002T43/.../hub5e00.english.000405.stm 转写文件存在,缺失时 check_data_folder 会直接抛出 OSError。Switchboard 的训练转写(swb_ms98_transcriptions)会由脚本自动从 OpenSLR 下载并解压,无需手动准备。
如何运行:一行命令启动训练
运行前必备条件
- 已训练好的 BPE Tokenizer(
tokenizer.ckpt)。配方假设 tokenizer 已预先训练好,通过pretrained_tokenizer_path指定其本地目录,例如recipes/Switchboard/Tokenizer目录下产出的 checkpoint; - 在超参数文件中填写两个
!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 的主流程可以梳理出完整执行链:
- 解析 CLI 参数,初始化 DDP 组(
sb.utils.distributed.ddp_init_group),加载 HyperPyYAML 超参数; - 创建实验输出目录(
sb.create_experiment_directory),保存超参数副本; - 数据准备:
run_on_main(prepare_switchboard, ...)保证多卡环境下只在主进程执行一次;随后run_on_main(hparams["prepare_noise_data"])自动从 URL 下载增强用噪声数据并生成noise.csv清单; - 构建数据集:
dataio_prepare(hparams)返回 train/valid/test 数据集与(可选的)动态批采样器; - 加载预训练 tokenizer(
hparams["pretrainer"].collect_files()+load_collected()); - 初始化
ASRBrain 类并调用fit(...)训练,最后对test_swbd、test_callhome、test三个测试子集分别执行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.py 中sum(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: 5与ctc_weight: 0.5控制联合训练策略:前 5 个 epoch 损失为0.5 * loss_ctc + 0.5 * loss_seq,之后仅优化loss_seq。该逻辑体现在 train.py 的compute_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: True且time_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: 100、dropout: 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.py 的 compute_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_search 与 test_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 清单,其核心处理包括:
- Switchboard 训练数据:按
swb1_all.dvd.tbl映射表把每个会话定位到对应 DVD 目录(swb1_d1~swb1_d4),从转写文件逐行解析出ID/duration/start/stop/channel/wav/words/spk_id八列;起始/结束时间以 8kHz 采样率换算为采样点,供后续在 train.py 的audio_pipeline中按frame_offset/num_frames精确切片;多声道sph文件按channel(A/B)选取对应声道; - 文本清洗:
filter_text按数据集类型(train/eval2000/fisher)应用不同正则规则——Switchboard 训练文本会去除[SILENCE]、笑声标记等标签(如[LAUGHTER-STORY]→STORY)、拆分部分词(-[AN]Y→-Y)、去除花括号非标准词标记;eval2000 测试文本则删除<B_ASIDE>之类的旁白标记与(...)可选词; - 缩写统一:
make_acronym_map结合 Switchboard 词典与 MSU 单字母表构建缩写映射,把IBM→i._b._m.这类写法转换为 Fisher 语料约定(该逻辑复刻自 Kaldi 的format_acronyms_dict.py); - eval2000 测试集:解析
2000_hub5_eng_eval_tr/reference/hub5e00.english.000405.stm,按文件名前缀sw_/en_区分 Switchboard 与 Callhome 会话,分别生成test_swbd.csv、test_callhome.csv并合并为test.csv;同时解析en20000405_hub5.glm文件生成glm.csv,供评测期归一化使用; - Fisher 语料:当
add_fisher_corpus: True时,把 Fisher 两份转写与 Switchboard 训练文本合并为train_lm.csv,专供 Tokenizer/LM 训练(此时max_utt被放大到极大值以保留全部语句)。
训练与评估脚本:Brain 类如何组织前向、损失与评测
train.py 定义了一个继承 sb.Brain 的 ASR 类,三个核心钩子函数与配置严格对应:
- 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't→WILL NOT、can't→CAN NOT、I'v→I HAVE等),拆分连写词(如pseudo-rebel→pseudo 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 中
enc为speechbrain.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 利用率。
参考与延伸阅读
- 配方入口:recipes/Switchboard/ASR/seq2seq/README.md
- 训练脚本:recipes/Switchboard/ASR/seq2seq/train.py
- 超参数配置:recipes/Switchboard/ASR/seq2seq/hparams/train_BPE_2000.yaml
- 数据准备:recipes/Switchboard/ASR/seq2seq/switchboard_prepare.py
- 评测归一化:recipes/Switchboard/ASR/seq2seq/normalize_util.py
- CRDNN 编码器实现:speechbrain/lobes/models/CRDNN.py
- 注意力解码器实现:speechbrain/nnet/RNN.py
- 束搜索解码器实现:speechbrain/decoders/seq2seq.py
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.24 K638- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python670
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#230
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python52874
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go22545
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java36351