首页
/ SpeechBrain 实战:基于 SLURP 数据集训练 Spoken Language Understanding(SLU)模型

SpeechBrain 实战:基于 SLURP 数据集训练 Spoken Language Understanding(SLU)模型

2026-09-14 17:24:46作者:伍霜盼Ellen

导读

本文聚焦 SpeechBrain 仓库中 recipes/SLURP 目录下的三套 SLU(口语语言理解,Spoken Language Understanding)recipe:Direct(语音直接映射语义)、Tokenizer(语义子词分词器训练)与 NLU(文本转写映射语义)。SLURP 是一个包含真实与合成语音的智能家居语义理解基准数据集,本文将从环境安装、数据准备、超参数配置、模型架构、训练命令到评测(SLU-F1)完整展开,并深入到源码与 YAML 配置层,帮助你理解 SpeechBrain 如何用一套 sb.Brain + HyperPyYAML 的范式把"语音 → 意图/动作/实体槽位"端到端跑通。

阅读完本文,你将掌握:如何下载并解析 SLURP 数据集、如何训练三套 SLU recipe、如何解读 predictions.jsonl 并计算官方 SLU 指标、以及如何在两套不同前端(预训练 ASR 编码器 / wav2vec2-HuBERT)之间切换。


一、SLURP 数据集与三类 SLU 任务概览

SLURP 是一个面向智能家居场景的英文口语理解数据集,由 Zenodo 发布(slurp_synth 合成语音与 slurp_real 真实语音两个压缩包),标注格式为 JSONL,每条记录包含 scenario(场景)、action(动作)、sentence_annotation(带实体槽位标注的句子,如 Play [song: the beatles])以及对应的音频 recordings

recipes/SLURP 目录结构如下:

recipes/SLURP/
├── README.md                    # 本 recipe 的总览文档
├── extra_requirements.txt       # 额外依赖(jsonlines)
├── prepare.py                   # 数据集下载与 CSV 清单生成
├── direct/                      # 语音 → 语义 端到端(seq2seq)
│   ├── prepare.py
│   ├── train.py                 # 基于预训练 ASR 编码器
│   ├── train_with_wav2vec2.py   # 基于 wav2vec2/HuBERT
│   └── hparams/{train.yaml, train_with_wav2vec2.yaml}
├── Tokenizer/                   # 语义子词分词器训练
│   ├── prepare.py
│   ├── train.py
│   └── hparams/tokenizer_bpe58.yaml
└── NLU/                         # 文本转写 → 语义(纯文本 NLU)
    ├── prepare.py
    ├── train.py
    └── hparams/train.yaml

三套 recipe 对应三种不同的输入输出形态(见 prepare.py 中的 slu_type 注释):

slu_type 输入 输出 说明
direct 音频波形 语义字典 端到端语音语义理解
decoupled 真实转写文本 语义字典 文本 NLU 上限
multistage 音频 语义字典 中间经过 ASR 转写(本文未使用)

其中 Direct 与 NLU 两种模式在 README 中有明确的结果记录,也是本 recipe 的核心;Tokenizer 是两者的前置可选步骤(Direct/NLU recipe 都会自动下载现成 tokenizer,通常无需手动训练)。


二、环境准备:安装额外依赖

三个 recipe 的脚本都依赖 jsonlines 库来读写 SLURP 的 JSONL 标注与测试阶段的 predictions.jsonl 输出。仓库通过 extra_requirements.txt 声明该依赖,内容只有一行 jsonlines

安装命令(README 第 6-10 行):

pip install -r extra_requirements.txt

此外,从源码可以看出还隐含以下依赖:

  • pandas:用于把 JSONL 转换为训练所需的 CSV 清单(prepare.py 中显式 import pandas as pd,缺失时会抛出 ImportError 提示);
  • sentencepiece:训练/加载 unigram 子词分词器(三个 YAML 中都实例化了 sentencepiece.SentencePieceProcessor);
  • speechbrain 本体及其依赖(torch、hyperpyyaml 等)。

三、数据准备:自动下载与语义字典编码

所有训练脚本在 main 中都会通过 run_on_main(prepare_SLURP, ...) 调用 prepare.py 中的 prepare_SLURP(),它完成三件事:

  1. 下载与解压:若 data_folder 下不存在 slurp_synth / slurp_real 目录,则自动下载 Zenodo 上的 slurp_synth.tar.gzslurp_real.tar.gz 并解压;
  2. 拉取标注:若本地没有 train.jsonl / devel.jsonl / test.jsonl,会从 slurp 数据集仓库下载对应 JSONL(train_real 分片对应 train.jsonl);
  3. 生成 CSV 清单:遍历 JSONL 中每条录音,为每个 split 生成 {split}-type={slu_type}.csv,最终把 train_synthetictrain_real 合并为 train-type={slu_type}.csv

值得注意的细节:CSV 中 semantics 列存放的是序列化后的 Python 字典字符串 {'scenario': ..., 'action': ..., 'entities': <a href="https://link.gitcode.com/i/1285c9414f765ef392c2f6ac43eb9b7f" target="_blank">...]},为了不被 CSV 的逗号分隔符干扰,[prepare.py 把字典中的逗号统一替换为管道符 |(例如 'scenario': 'inquiry'| 'action': 'query')。这个细节与后面评测阶段 predictions.jsonl 的解析逻辑(replace("|", ","))一一对应。

此外,Direct 的 train.yaml 还通过 prepare_noise_data / prepare_rir_data 两个 !name:speechbrain.augment.preparation.prepare_dataset_from_URL 节点自动下载噪声与房间冲激响应(RIR)数据集用于数据增强,无需手动干预。


四、Direct recipe:语音 → 语义 端到端建模

4.1 训练命令

cd recipes/SLURP/direct
python train.py hparams/train.yaml

该 recipe 的核心思想(见 direct/train.py 的 docstring):用 LibriSpeech 上预训练的 ASR 编码器把波形编码为特征,再喂给一个 seq2seq 模型映射到语义序列。预训练 ASR 编码器在训练时被完全冻结(with torch.no_grad() 包裹),因此整个训练只更新 SLU 部分,训练代价很低(README 记录为约 1 小时/epoch)。

4.2 模型架构与关键超参数

direct/hparams/train.yaml 中模型部分由四块组成(对应 model: !new:torch.nn.ModuleList 中的四个模块):

组件 配置 说明
asr_model_source speechbrain/asr-crdnn-rnnlm-librispeech 冻结的预训练 ASR 编码器,通过 EncoderDecoderASR.from_hparams 加载(train.py
slu_enc 双向 2 层 LSTM(encoder_dim=256)+ Linear 对 ASR 特征做序列建模,input_shape=[null, null, 512],即输入维度为 ASR_encoder_dim=512
output_emb + dec 128 维 Embedding + 3 层 GRU 注意力解码器(AttentionalRNNDecoderattn_type: keyvalue seq2seq 解码器
seq_lin Linear(512 → 58) 输出层,output_neurons=58 对应 58 个 unigram 语义子词(索引 0 为 eos/bos)

解码使用 S2SRNNBeamSearcherbeam_size=80eos_threshold=1.5temperature=1.25min/max_decode_ratio 控制生成长度)。训练损失为带 0.1 标签平滑的 NLL(seq_cost: nll_loss),优化器为 Adam(lr=0.0003),并用 NewBobScheduler 依据验证集 WER 做学习率退火(improvement_threshold=0.0025annealing_factor=0.8)。

其他核心训练参数:

参数 说明
number_of_epochs 20 总训练轮数
batch_size 16 批次大小
sorting random 训练集排序策略(可选 ascending/descending/random,见 train.py
ckpt_interval_minutes 15 每 15 分钟保存一次 checkpoint
train_splits ["train_synthetic", "train_real"] 训练集由合成+真实语音拼接
sample_rate 16000 音频采样率
skip_prep False 置 True 可跳过数据准备步骤

4.3 数据增强管线

train.yaml 里定义了一套组合式时域增强(wav_augment: !new:speechbrain.augment.augmenter.Augmenter):

  • AddReverb:基于自动下载的 RIR 数据集加混响;
  • AddNoise:加噪声,snr_low=0snr_high=15(dB);
  • SpeedPerturb:速度扰动,speeds: [90, 95, 105, 110]
  • DropFreq:随机丢弃 1~3 个频带;
  • DropChunk:随机丢弃 1~3 段时域片段(1000~2000 样本)。

Augmenter 配置为 concat_original=True(保留原始样本)、每次随机挑选 1~4 个增强(min_augmentations=1max_augmentations=4)。在 train.pycompute_forward 中,增强只作用于训练阶段,且标签通过 replicate_labels 与增强后的样本对齐(因为 concat_original=True 会复制 batch)。

4.4 训练流程与输出

训练主流程遵循 SpeechBrain 标准范式(train.py):

  1. sb.parse_arguments + load_hyperpyyaml 加载超参数并允许命令行覆盖;
  2. sb.create_experiment_directory 创建实验目录并保存超参数副本;
  3. prepare_SLURP 准备数据(DDP 下仅主进程执行 run_on_main);
  4. dataio_prepare 定义数据管线:audio_pipeline 读取波形(read_audio),text_pipeline 用 tokenizer 把语义字典字符串编码为 tokens_bos / tokens_eos / tokenstrain.py);
  5. Pretrainer.collect_files()/load_collected() 下载并加载 58 词 unigram tokenizer;
  6. slu_brain.fit(...) 训练 + slu_brain.evaluate(test_set) 测试。

测试阶段会在 output_folder 下生成 predictions.jsonl,每行是解析后的语义字典并附上 file 字段(见 train.py),供官方评测工具计算 SLU-F1。


五、Direct(wav2vec2/HuBERT 前端)recipe

5.1 训练命令

python train_with_wav2vec2.py hparams/train_with_wav2vec2.yaml

对应脚本 direct/train_with_wav2vec2.py,把前端替换为大规模自监督预训练模型,是当前 README 中效果最好的 Direct 方案(SLU-F1 75.10)。

5.2 与 ASR 前端版本的核心差异

hparams/train_with_wav2vec2.yamltrain.yaml 的差异集中在以下几点:

维度 ASR 前端 wav2vec2/HuBERT 前端
编码器 asr-crdnn-rnnlm-librispeech(冻结) wav2vec2_hub: "facebook/hubert-base-ls960"(可微调)
特征维度 ASR_encoder_dim=512 encoder_dim=768(HuBERT 输出维度)
解码器 GRU + keyvalue 注意力 LSTM + content 注意力
训练轮数 20 35
batch_size 16 6
优化器 单一 Adam 双优化器:模型 lr=0.0003,wav2vec2 lr_wav2vec2=0.00001
增强 噪声+混响+速度+DropFreq+DropChunk 仅速度+DropFreq+DropChunk(固定 4 个增强)

关键控制开关:

  • freeze_wav2vec2: False:整体冻结/解冻 wav2vec2 的开关;
  • freeze_wav2vec2_conv: True:当整体解冻时,仍冻结 HuBERT 的 feature_extractor 卷积部分(train_with_wav2vec2.py 调用 feature_extractor._freeze_parameters()),只微调 transformer 层。

代码层面最大的不同是 init_optimizers 的重写(train_with_wav2vec2.py):单独为 modules.wav2vec2.parameters() 创建 wav2vec2_optimizer,两个优化器分别受 lr_annealinglr_annealing_wav2vec2(退火因子 0.9)调度,并作为独立 recoverable 注册进 Checkpointer。compute_forwardwav2vec2_out = self.modules.wav2vec2(wavs, wav_lens) 直接走可训练的编码器前向,而不再有 torch.no_grad() 包裹。

说明:README 中提到该变体参考了论文 SLURP 上的 wav2vec2/HuBERT 实验结果arxiv.org/pdf/2111.02735),仅作为背景信息,训练脚本本身不依赖该论文代码。


六、Tokenizer recipe:训练语义子词分词器

cd recipes/SLURP/Tokenizer
python train.py hparams/tokenizer_bpe58.yaml

README 明确指出:Direct recipe 会自动下载现成 tokenizer,因此这一步通常不需要运行,仅当你希望为 SLURP 重新训练一个分词器时才需要。

hparams/tokenizer_bpe58.yaml 的关键配置:

参数 说明
token_type unigram 可选 unigram / bpe / char
token_output 58 词表大小(0 为 blank/eos/bos/unk 索引)
character_coverage 1.0 字符覆盖率
csv_read semantics 从 CSV 的 semantics 列学习子词

实现上,tokenizer: !name:speechbrain.tokenizers.SentencePiece.SentencePiece 调用 SpeechBrain 的 SentencePiece 封装,训练数据来自 prepare_SLURP(..., slu_type="direct") 生成的 train-type=direct.csv,并用 annotation_list_to_check 在训练/验证两个 CSV 上做词表覆盖检查。训练脚本 Tokenizer/train.py 本身很轻量:数据准备后直接执行 hparams["tokenizer"]() 触发训练。

版本提示:README 中写的是 hparams/tokenizer_bpe51.yaml,而仓库实际文件名为 hparams/tokenizer_bpe58.yaml(词表 58),以仓库实际文件为准。下游 Direct/NLU 配置中的 output_neurons: 58 与该 tokenizer 一致。


七、NLU recipe:文本转写 → 语义

7.1 训练命令

cd recipes/SLURP/NLU
python train.py hparams/train.yaml

与 Direct 不同,NLU recipe 以真实转写文本(gold transcript)为输入,而不是语音,用来训练一个"转写 → 语义"的 seq2seq 模型。它衡量的是在 ASR 完美情况下的语义理解上限,README 中记录的准确率也最高(scenario 90.81 / action 88.29 / intent 87.28)。

7.2 双 tokenizer 与模型结构

hparams/NLU/train.yaml 使用两个 tokenizer

  • asr_tokenizer:1000 词 unigram,把输入转写文本编码为 token 序列(num_asr_tokens=1000);
  • slu_tokenizer:58 词 unigram,把输出语义编码为序列(output_neurons=58)。

模型结构与 Direct 几乎相同,区别是输入侧多了一个 input_emb(Embedding(1000 → 128))把转写 token 转成向量,再进入 slu_enc(双向 2 层 LSTM + Linear)。数据管线(NLU/train.py)定义了 transcript_pipeline(ASR tokenizer 编码)与 semantics_pipeline(SLU tokenizer 编码,含 bos/eos 拼接),prepare_SLURPslu_type="decoupled" 调用(对应 CSV 命名 *-type=decoupled.csv)。注意 decoupled 模式下转写文本会被大写化(prepare.py),这与 tokenizer 的字符覆盖设置相关。


八、评测:predictions.jsonl 与 SLU-F1

三套 recipe 的测试逻辑一致(见各 train.pycompute_objectiveson_stage_end):

  1. sb.Stage.TEST 阶段,对每个测试样本用 beam searcher 解码出语义 token 序列,tokenizer.decode_ids 还原为语义字符串;
  2. ast.literal_eval 把字符串解析为字典(direct/train.py);解析失败(输出不是合法字典)时回退为 {"scenario": "none", "action": "none", "entities": []}
  3. 写入 output_folder/predictions.jsonl,每行包含解析后的 scenario / action / entities 以及 file(音频文件名,来自测试 CSV 的 ID→文件映射);
  4. 同时用 ErrorRateStats 统计 WER 与 CER(基于语义 token 序列)。

README 说明:SLURP 官方提供 SLU-F1 等指标的评测工具,把 results 文件夹下的 predictions.jsonl 喂给该工具即可计算。评测时 "|" 会按 , 还原(与数据准备时的编码互逆)。

性能汇总(来自 README,48GB RTX 8000 上测得,recipe 亦在 12GB Tesla K80 上验证通过)

Direct(语音 → 语义):

模型 scenario 准确率 action 准确率 intent 准确率 Word-F1 Char-F1 SLU-F1 训练时间
Direct 81.73 77.11 75.05 61.24 65.42 63.26 约 1 小时/epoch
Direct (HuBert) 91.24 88.47 87.54 72.93 77.40 75.10 约 4 小时/epoch

NLU(文本 → 语义):

模型 scenario 准确率 action 准确率 intent 准确率 训练时间
NLU 90.81 88.29 87.28 约 40 分钟/epoch

可见 HuBERT 前端的端到端方案已大幅逼近"真实转写 + NLU"的上限(SLU-F1 75.10 vs NLU 组合),这也是 README 将 Direct (HuBert) 模型作为推荐推理模型的原因。预训练模型的下载链接见 README 中性能表的 "Model link" 列。


九、推理:HuggingFace 上的开箱即用接口

README 指出 Direct (HuBert) 模型已发布到 HuggingFace,模型标识为 speechbrain/SLU-direct-SLURP-hubert-enc,并配套了易于使用的推理接口(对应 SpeechBrain 的 inference 模块与 Pretrained 体系)。你可以直接通过该模型标识加载并使用,无需本地训练即可对语音做语义理解推理。

仓库内也有通用的推理范式可以参考:speechbrain/inference/ 目录提供了各类任务的推理接口封装(ASR、SLU、ST、TTS 等),其统一入口基于 speechbrain/utils/pretrained.pyPretrained.from_hparams 机制——这与训练脚本中加载 ASR 模型(EncoderDecoderASR.from_hparams)用的是同一套参数迁移与加载逻辑。


十、实战要点小结

  1. 先装依赖再跑pip install -r recipes/SLURP/extra_requirements.txt,并确保 pandassentencepiece 可用;
  2. 占位符必须替换:三个 YAML 的 data_folder: !PLACEHOLDER 需要替换为你的本地数据目录(如 /localscratch/SLURP),首次运行会自动下载约数 GB 的数据集、噪声与 RIR 包;
  3. Tokenizer 无需重复训练:Direct/NLU 通过 Pretrainer 自动下载 58 词 tokenizer;只有想自定义词表时才跑 Tokenizer/train.py
  4. 三套 recipe 共用数据准备prepare_SLURPslu_type 生成不同 CSV,训练/验证/测试清单的命名规则为 {split}-type={slu_type}.csv
  5. 评测入口统一:测试结果统一落盘为 results/.../predictions.jsonl,配合 SLURP 官方评测脚本计算 scenario/action/intent 准确率、Word/Char-F1 与 SLU-F1;
  6. 想快速复现最高指标:优先选择 Direct (HuBert)(train_with_wav2vec2.yaml),注意其 batch_size 更小、训练轮数更多、且默认微调 HuBERT transformer 层(冻结卷积层),需要显存更大的 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