news 2026/9/15 13:58:17

SpeechBrain 实战:在 LibriSpeech 上训练 CRDNN + Attention 的 seq2seq 端到端语音识别系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpeechBrain 实战:在 LibriSpeech 上训练 CRDNN + Attention 的 seq2seq 端到端语音识别系统

SpeechBrain 实战:在 LibriSpeech 上训练 CRDNN + Attention 的 seq2seq 端到端语音识别系统

【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain

本指南以 recipes/LibriSpeech/ASR/seq2seq 目录下的完整实验配方为对象,系统讲解如何在 SpeechBrain 中从零训练一个基于CTC + Attention 联合训练的端到端(E2E)ASR 系统:编码器采用 CRDNN(CNN + RNN + DNN),解码器采用带注意力机制的 GRU,推理阶段使用束搜索(beam search)并融合神经语言模型(RNNLM 或 TransformerLM)进行打分重排。读完本文,你将掌握该配方的完整运行流程、三个超参数文件的核心差异、每个关键配置项的底层含义,以及如何替换编码器、解码器、词元(token)与数据划分来搭建自己的 ASR 实验。

配方概览:目录结构与技术栈

该配方位于 recipes/LibriSpeech/ASR/seq2seq,包含以下核心文件:

文件作用
train.py定义ASR(sb.Brain)训练流程:前向计算、CTC+NLL 损失、验证/测试解码、checkpoint 与日志管理
librispeech_prepare.py将 LibriSpeech 原始数据集解析为 SpeechBrain 使用的 CSV 清单文件
hparams/train_BPE_1000.yaml默认配置:1000 个 BPE 词元,GRU 解码器 + RNNLM,束搜索
hparams/train_BPE_1000_sligru.yaml变体:把编码器 RNN 换成 SLiGRU(基于简化 LSTM 门控)
hparams/train_BPE_5000.yaml变体:5000 个 BPE 词元,解码端融合 TransformerLM 与 CTC scorer

从 train.py 的模块文档字符串可知,该系统采用"编码器—解码器—注意力"三段式结构,解码用beam search 耦合神经网络语言模型。模型同时优化 CTC 目标与负对数似然(NLL)目标,识别基本单元是通过 Byte Pairwise Encoding(BPE)得到的子词。训练在完整的 LibriSpeech 960 小时数据上进行。该实验文件足够灵活,可以通过修改参数文件尝试不同的编码器、解码器、词元(例如用字符代替 BPE)、训练划分(例如只用 train-clean-100)等多种变化。

整个配方假设 tokenizer 和语言模型已经训练好:为避免词元不匹配,声学模型使用的 tokenizer 必须与 LM 相同,配方默认直接从预训练仓库下载这两者(见下文"依赖准备"一节)。

快速开始:运行命令与前置条件

第一步:下载 LibriSpeech 数据集

LibriSpeech 需要从 OpenSLR 下载(README 中标注的地址为 http://www.openslr.org/12)。下载完成后,把数据集路径填入 YAML 中的data_folder: !PLACEHOLDER位置(如/path/to/LibriSpeech)。

第二步:启动训练

在 recipes/LibriSpeech/ASR/seq2seq 目录下执行:

python train.py hparams/train_BPE_1000.yaml

train.py会依次完成以下流程(对应 train.py 的__main__部分):

  1. 通过sb.parse_arguments解析命令行参数(含--device--seed等运行选项与--hparams覆盖项);
  2. 调用sb.utils.distributed.ddp_init_group初始化多卡通信组(支持 DDP 多 GPU 训练);
  3. load_hyperpyyaml加载 YAML,实例化其中声明的全部对象;
  4. 创建实验目录并把超参数文件存档;
  5. 调用prepare_librispeech生成 CSV 清单(由run_on_main保证只在主进程执行一次);
  6. 下载噪声数据用于增强(prepare_noise_data);
  7. 通过dataio_prepare构建DynamicItemDataset与数据处理管线;
  8. Pretrainer下载并加载预训练 LM 与 tokenizer;
  9. 实例化ASRBrain,执行fit()训练与evaluate()测试(test-clean / test-other 分开评估并各自写出 WER 文件)。

依赖准备:tokenizer 与语言模型必须匹配

本配方不训练tokenizer 与 LM,而是直接复用预训练产物。原因在 train.py 中有明确说明:如果声学模型与 LM 使用不同 tokenizer 会产生词元不匹配,因此必须保证两者一致。

在 train_BPE_1000.yaml 中,通过如下配置指定预训练来源:

# NB: To avoid mismatch, the speech recognizer must be trained with the same # tokenizer used for LM training. Here, we download everything from the # speechbrain HuggingFace repository. However, a local path pointing to a # directory containing the lm.ckpt and tokenizer.ckpt may also be specified # instead. pretrained_lm_tokenizer_path: speechbrain/asr-crdnn-rnnlm-librispeech

该路径既可以是 HuggingFace 仓库名,也可以指向本地目录(需包含lm.ckpttokenizer.ckpt)。加载动作由 YAML 末尾的Pretrainer完成:

pretrainer: !new:speechbrain.utils.parameter_transfer.Pretrainer collect_in: !ref <save_folder> loadables: lm: !ref <lm_model> tokenizer: !ref <tokenizer> paths: lm: !ref <pretrained_lm_tokenizer_path>/lm.ckpt tokenizer: !ref <pretrained_lm_tokenizer_path>/tokenizer.ckpt

lm指向下面声明的lm_model模块,tokenizer指向sentencepiece.SentencePieceProcessor(仅占位,真正权重由 checkpoint 恢复)。在 train.py 中通过hparams["pretrainer"].collect_files()load_collected()完成下载与加载;加载后 tokenizer 会被动态绑定到 Brain 实例上(asr_brain.tokenizer = hparams["tokenizer"],见 train.py),供验证/测试阶段把预测 token 解码成词来计算 WER/CER。

如果你希望从零训练完整系统,README 中给出三步流程(详见"从零开始的三阶段训练"一节):

  1. 训练 tokenizer(见 recipes/LibriSpeech/Tokenizer);
  2. 训练语言模型(见 recipes/LibriSpeech/LM);
  3. 用本目录代码训练声学模型。

架构详解:CRDNN 编码器 + 注意力 GRU 解码器

整体数据流(对应 compute_forward)

ASR.compute_forward 展示了完整前向链路:

  1. 训练阶段若配置了wav_augment,先对波形做增强(加噪、变速、频带/时间块丢弃),并用replicate_labels同步扩展标签;
  2. compute_features提取 40 维 FBank 特征(Fbank 实现,对应配置n_fft: 400, n_mels: 40, sample_rate: 16000);
  3. normalize做全局输入归一化(InputNormalization);
  4. enc(CRDNN)把特征编码为隐状态序列;
  5. emb把带bos_index前缀的目标 token 序列嵌入,送入dec(AttentionalRNNDecoder)得到解码器隐状态h
  6. seq_lin+log_softmax得到 seq2seq 输出概率p_seq
  7. number_of_ctc_epochs个 epoch 内,额外用ctc_lin计算 CTC 概率p_ctc(训练早期用 CTC 辅助对齐,帮助注意力机制收敛);
  8. 验证/测试阶段用valid_search/test_searchS2SRNNBeamSearcher)执行束搜索解码得到预测 token。

CRDNN 编码器

CRDNN 定义在 speechbrain/lobes/models/CRDNN.py,继承sb.nnet.containers.Sequential,结构为"卷积块 → 时间维池化 → RNN → DNN":

  • CNN 部分cnn_blocks个 VGG 风格卷积块(CNN_Block),每个块包含两层 Conv2d(核大小cnn_kernelsize)+ LayerNorm + LeakyReLU + 池化 + Dropout2d(见 CRDNN.py);
  • 时间池化time_pooling_size: 4在时间轴做 4 倍下采样,压缩序列长度、降低计算量;
  • RNN 部分:默认 4 层双向 LSTM(rnn_neurons: 1024),rnn_re_init: True表示对循环权重做正交初始化;use_rnnp为 True 时在每层 RNN 后追加线性投影层(用于压缩参数);
  • DNN 部分dnn_blocks个全连接块(Linear + BatchNorm1d + LeakyReLU + Dropout),输出维度dnn_neurons

三个 YAML 在编码器上的差异值得对比:

配置项train_BPE_1000train_BPE_1000_sligrutrain_BPE_5000
cnn_channels(128, 256)(128, 256)(64, 128)
dnn_blocks / dnn_neurons2 / 5122 / 5121 / 1024
rnn_classLSTMSLiGRULSTM
use_rnnpFalseFalseTrue
emb_size1281281024

在 train_BPE_5000 中dnn_neurons提升到 1024,同时开启use_rnnp: True,说明模型在更大的 5000 词元词表下增加了表示容量。

注意力 GRU 解码器

解码器使用 AttentionalRNNDecoder,关键配置(以 train_BPE_1000 为例):

dec: !new:speechbrain.nnet.RNN.AttentionalRNNDecoder enc_dim: !ref <dnn_neurons> # 512,编码器输出维度 input_size: !ref <emb_size> # 128,token 嵌入维度 rnn_type: gru attn_type: location # location-sensitive 注意力 hidden_size: !ref <dec_neurons> # 1024 attn_dim: 1024 num_layers: 1 channels: 10 # 位置注意力卷积通道数 kernel_size: 100 # 位置注意力卷积核宽度 re_init: True dropout: !ref <dropout> # 0.15

attn_type: location表示使用带位置信息的注意力(location-sensitive attention),通过channelskernel_size控制对历史对齐信息的卷积平滑范围,可有效抑制注意力漂移与重复解码问题。eos_threshold: 1.5max_attn_shift: 240等解码约束参数进一步保证对齐的单调性与终止判断(详见"解码与打分"一节)。

输出层与模型组装

两个线性输出层分别服务 CTC 与 seq2seq 路径:

ctc_lin: !new:speechbrain.nnet.linear.Linear # 输入 dnn_neurons,输出 output_neurons seq_lin: !new:speechbrain.nnet.linear.Linear # 输入 dec_neurons,输出 output_neurons

所有模块通过modules:字典与model: !new:torch.nn.ModuleList汇总,交由Checkpointer统一管理(train_BPE_1000.yaml 中的recoverables包含modelschedulernormalizercounter)。

训练目标:CTC 与 NLL 联合优化

训练损失由 compute_objectives 计算:

loss_seq = self.hparams.seq_cost(p_seq, tokens_eos, length=tokens_eos_lens) if ( stage == sb.Stage.TRAIN and current_epoch <= self.hparams.number_of_ctc_epochs ): loss_ctc = self.hparams.ctc_cost(p_ctc, tokens, wav_lens, tokens_lens) loss = self.hparams.ctc_weight * loss_ctc loss += (1 - self.hparams.ctc_weight) * loss_seq else: loss = loss_seq

要点:

  • seq_cost为带label_smoothing: 0.1的 NLL 损失(nll_loss),标签从tokens_eos读取(目标后追加 eos);
  • ctc_cost为 ctc_loss,blank_index: 0
  • number_of_ctc_epochs控制 CTC 辅助训练持续多少个 epoch:train_BPE_1000 为 5,而 train_BPE_5000 与 sligru 变体为 25(整个训练期都带 CTC);
  • ctc_weight: 0.5控制 CTC 与 seq2seq 损失的加权比例;
  • 验证/测试阶段会把预测 token 用self.tokenizer.decode_ids解码成词,与batch.wrd对比,分别累计 WER(error_rate_computer)与 CER(cer_computersplit_tokens: True按字符拆分)。

验证阶段结束后,on_stage_end 会根据验证 WER 用NewBobScheduler做学习率退火(improvement_threshold: 0.0025annealing_factor: 0.8),并通过checkpointer.save_and_keep_only(meta={"WER": ...}, min_keys=["WER"])只保留验证 WER 最优的 checkpoint。

数据准备与数据增强

数据清单生成

train.py 调用 prepare_librispeech 生成 CSV:

  • 默认训练划分train_splits: ["train-clean-100", "train-clean-360", "train-other-500"](共 960 小时),开发集dev_splits: ["dev-clean"],测试集test_splits: ["test-clean", "test-other"]
  • merge_lst指定把多个训练划分合并为train.csv
  • 每个划分的 CSV 包含ID, duration, wav, spk_id, wrd五列,wav 路径用$data_root/占位符替换,便于在不同机器间迁移(librispeech_prepare.py);
  • 脚本通过parallel_map并行读取 FLAC 元数据计算时长,并通过OPT_FILE = "opt_librispeech_prepare.pkl"记录配置,若已生成则自动跳过(skip_prep可直接跳过准备阶段)。

数据处理管线

dataio_prepare 定义了两条动态管线:

  • audio_pipelineread_audio读取 wav 得到sig
  • text_pipeline:用 tokenizer 把wrd编码为 token 序列,并生成三种变体——tokens_bos(前插 bos)、tokens_eos(后接 eos)、tokens(原始序列),分别供解码器输入、NLL 损失与 CTC 损失使用。

排序策略sorting支持ascending/descending/random:升序按duration排序可加速训练并改善效果,但此时 DataLoader 中必须关闭 shuffle(代码中会强制shuffle=False)。此外配置预留了dynamic_batching开关,开启后改用DynamicBatchSamplerfeats_hop_size折算的帧数动态组批(max_batch_length: 20000帧、num_buckets: 20)。

波形级数据增强

YAML 中通过Augmenter组合四种增强(train_BPE_1000.yaml):

增强实现类关键参数
加噪AddNoiseSNR 0–15 dB,噪声由prepare_noise_data从 URL 自动下载并生成noise.csv
变速SpeedPerturb速度[95, 100, 105],即 0.95x / 1.0x / 1.05x
频带丢弃DropFreq随机丢弃 1–3 个频带,宽度 0.05
时间块丢弃DropChunk随机丢弃 1–5 个 1000–2000 采样点的时间块

Augmenter配置为concat_original: True(增强样本与原样本拼接)、每次固定施加 4 种增强、augment_prob: 1.0。增强对标签的影响由 train.py 中的replicate_labels/replicate_multiple_labels处理(拼接增强会使时间维翻倍,需同步复制标签)。

解码与打分:束搜索 + 神经语言模型

解码器配置

推理使用 S2SRNNBeamSearcher,验证与测试使用独立实例以便采用不同束宽:

参数train_BPE_1000train_BPE_5000含义
valid_beam_size8020验证束宽
test_beam_size8040测试束宽
eos_threshold1.51.5终止阈值(eos 分数需超过该值)
using_max_attn_shiftTrueTrue限制注意力偏移范围
max_attn_shift240300最大注意力偏移(对应时间池化后的帧数)
min/max_decode_ratio0.0 / 1.00.0 / 1.0解码长度约束比例
temperature1.251.0解码温度

打分器(Scorer)体系

打分逻辑由 speechbrain/decoders/scorer.py 中的ScorerBuilder组装。三个 YAML 的打分策略差异体现了 RNNLM 与 TransformerLM 两条技术路线:

train_BPE_1000(RNNLM 路线)

rnnlm_scorer: !new:speechbrain.decoders.scorer.RNNLMScorer language_model: !ref <lm_model> # RNNLM: 2 层 LSTM, 2048 神经元 temperature: !ref <temperature_lm> # 1.25 scorer: !new:speechbrain.decoders.scorer.ScorerBuilder full_scorers: [!ref <rnnlm_scorer>, !ref <coverage_scorer>] weights: rnnlm: !ref <lm_weight> # 0.5 coverage: !ref <coverage_penalty> # 1.5

其中lm_model为 speechbrain.lobes.models.RNNLM.RNNLM(2 层、2048 神经元、return_hidden: True供推理),必须与预训练 RNNLM 结构一致。

train_BPE_5000(TransformerLM + CTC 路线):引入三个 scorer:

ctc_scorer: !new:speechbrain.decoders.scorer.CTCScorer eos_index: !ref <eos_index> blank_index: !ref <blank_index> ctc_fc: !ref <ctc_lin> ctc_window_size: !ref <ctc_window_size> # 200 transformerlm_scorer: !new:speechbrain.decoders.scorer.TransformerLMScorer language_model: !ref <lm_model> test_scorer: !new:speechbrain.decoders.scorer.ScorerBuilder full_scorers: [!ref <transformerlm_scorer>, !ref <coverage_scorer>] partial_scorers: [!ref <ctc_scorer>] weights: transformerlm: !ref <lm_weight> # 0.8 coverage: !ref <coverage_penalty> # 1.5 ctc: !ref <ctc_weight_decode> # 0.40

要点:验证阶段只用coverage_scorer(更快),测试阶段才叠加 TransformerLM 与 CTC scorer。partial_scorers表示 CTC 打分只在部分解码步骤(束内前缀对齐窗口内)生效,ctc_window_size: 200控制该窗口大小;transformerlm_scorer对应的lm_model是 TransformerLM(d_model: 768nhead: 12、12 层编码器、d_ffn: 3072、GELU 激活)。CoverageScorer依据已消耗的注意力覆盖度施加惩罚,用于缓解注意力欠覆盖导致的漏词问题。

优化器与学习率调度

优化器统一使用 Adadelta(lr: 1.0, rho: 0.95, eps: 1e-8),配合NewBobScheduler:当验证 WER 相对历史最佳提升不足 0.0025 时,学习率乘以annealing_factor: 0.8patient: 0表示无耐心期)。Adadelta 天然不需要手动调节学习率,适合本配方直接以lr: 1.0起步。

评测指标与实验结果

README 记录了该配方在 1xV100 32GB 上的官方复现结果(以 WER 计,越低越好):

Release超参数文件Test Clean WER预训练模型(HuggingFace)GPUs
01-03-21train_BPE_1000.yaml3.16asr-crdnn-rnnlm-librispeech1xV100 32GB
01-03-21train_BPE_5000.yaml2.89asr-crdnn-transformerlm-librispeech1xV100 32GB

训练开销参考:在 NVIDIA V100 32GB 上,每个 epoch 约需 5 小时。

评测过程在 train.py 中实现:遍历test_datasets(keys 为test_cleantest_other),对每个测试集调用asr_brain.evaluate(..., min_key="WER")(按验证 WER 最优 checkpoint 恢复),并把 WER 明细写入output_wer_folder/wer_{k}.txt。测试时test_dataloader_opts使用batch_size: 1,避免批内长度填充影响对齐评估。

预训练模型可直接通过 HuggingFace 使用 easy-inference 接口调用,对应的完整实验文件夹(checkpoints、logs 等)可从 Dropbox 链接下载,其中还包括第三个模型asr-transformer-transformerlm-librispeech(纯 Transformer 编码器的变体,对应仓库中的 recipes/LibriSpeech/ASR/transformer 配方)。

从零开始的三阶段训练流程

如果不想复用预训练 tokenizer/LM,README 给出完整流程:

  1. 训练 tokenizer:进入 recipes/LibriSpeech/Tokenizer,用train.py hparams/xxxx.yaml训练 SentencePiece 模型(BPE/unigram),得到tokenizer.ckpt
  2. 训练语言模型:进入 recipes/LibriSpeech/LM 训练 RNNLM 或 TransformerLM,得到lm.ckpt,并确保其使用的 tokenizer 与第 1 步一致;
  3. 训练声学模型:回到本目录,把pretrained_lm_tokenizer_path改为包含lm.ckpttokenizer.ckpt的本地目录,再运行python train.py hparams/train_BPE_1000.yaml

集成测试与最小可运行示例

仓库在 tests/integration/ASR_seq2seq 提供了端到端的集成测试,用于验证 seq2seq 训练管线的正确性:

  • example_asr_seq2seq_experiment.py 在迷你数据集(tests/samples/ASR 下的少量音频)上训练一个 CRDNN 编码器 + GRU 解码器 + 贪心搜索的简化版系统,其compute_forward/compute_objectives结构与正式配方一致,并断言训练损失 < 1.0(即能过拟合小数据集,验证前向与反向传播正确);
  • 配套的 hyperparams.yaml 给出了最小化的模块与优化器配置。

对照测试代码与正式配方,可以清晰看到二者在数据管线(takes/provides装饰器)、损失计算(NLL)与评估(PER/WER 统计)上的对应关系,是理解正式配方内部机制的最好入门材料。

配置定制建议:如何改造为自己的实验

结合源码结构,从 train.py 与 YAML 可以推断以下常见的实验变体改法:

  • 换编码器:修改enc指向其他 lobe 模型,如 speechbrain/lobes/models/transformer/TransformerEncoder.py(对应transformer配方)或换成纯 CNN/LiGRU;只需保证输出维度与ctc_lin/dec.enc_dim匹配;
  • 换词元:把 BPE 换成字符,需同步修改output_neurons(词表大小)与 tokenizer 训练方式(见 Tokenizer 配方);
  • 换数据划分:如只训练train-clean-100,修改train_splits并相应调整 epoch 数;
  • 调整 CTC 辅助强度:通过number_of_ctc_epochsctc_weight控制;
  • 多 GPU 训练:运行python train.py hparams/train_BPE_1000.yaml --device cuda:0,1,...并使用sb.utils.distributed.ddp_init_group初始化(代码已内置支持,详见 speechbrain/utils/distributed.py)。

无论做何种改动,务必牢记本配方的黄金约束:声学模型、LM 与 tokenizer 三者的词元表必须一致,否则解码端会出现 token 错位,WER 将严重劣化。

【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 13:58:04

良品铺子网站规划和建设到底多少钱?域名服务器避坑指南

良品铺子网站规划和建设到底多少钱?域名服务器避坑指南 域名服务器搞不懂,报价单看两眼就头晕,这是很多项目经理在做【良品铺子网站规划和建设】时最头疼的事。别急,今天不聊虚的,直接拆解这笔账。很多人问【良品铺子网站规划和建设】多少钱,其实钱花在哪里,决定了你的网站是“能用”还是“好用”。…

作者头像 李华
网站建设 2026/9/15 13:57:03

文件上传漏洞从攻击到防御:绕过手法、代码审计与加固实践

做安全的这些年&#xff0c;如果说哪个漏洞让我觉得“看似不起眼、实际特别致命”&#xff0c;文件上传漏洞绝对排得上前三名。很多开发同学觉得上传功能不过就是“接收文件、存到服务器”&#xff0c;能有什么风险&#xff1f;可真出了问题&#xff0c;往往就是服务器直接被拿…

作者头像 李华
网站建设 2026/9/15 13:56:23

网页数据一键变可编辑Excel:Skill开发全流程拆解与避坑指南

最近整理了一个新的 Skill&#xff0c;名字很直白&#xff1a;把网页数据直接变成一张能编辑的表格。给 Claude Code、Codex 这类编程代理丢一个链接&#xff0c;它就能自动把页面里的数据抓下来&#xff0c;整理成 Excel 或 CSV&#xff0c;打开就能改。听起来就是“网页采集”…

作者头像 李华
网站建设 2026/9/15 13:56:15

iOS审核3.2(f)条款深度解析:Flutter与UniApp合规避坑指南

1. 项目概述&#xff1a;这不是一次“封号通知”&#xff0c;而是一次iOS生态规则的现场教学App Store 3.2(f)条款&#xff0c;过去三年里被开发者私下称为“沉默绞索”——它不发警告邮件&#xff0c;不显示具体违规代码行&#xff0c;不提供复审通道&#xff0c;只在审核通过…

作者头像 李华
网站建设 2026/9/15 13:55:28

C++会员系统源码实战:从编译运行到二次开发

简介&#xff1a;这是一份面向C初学者与课程设计人群的会员管理系统资源&#xff0c;基于C控制台实现&#xff0c;涵盖登录、查看会员、添加和修改会员信息等核心功能。压缩包共4个文件&#xff0c;约18KB&#xff0c;包含可直接运行的exe程序、完整cpp源代码、编译生成的o中间…

作者头像 李华