SpeechBrain 实战:基于 SLURP 数据集训练 Spoken Language Understanding(SLU)模型
导读
本文聚焦 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(),它完成三件事:
- 下载与解压:若
data_folder下不存在slurp_synth/slurp_real目录,则自动下载 Zenodo 上的slurp_synth.tar.gz、slurp_real.tar.gz并解压; - 拉取标注:若本地没有
train.jsonl/devel.jsonl/test.jsonl,会从 slurp 数据集仓库下载对应 JSONL(train_real分片对应train.jsonl); - 生成 CSV 清单:遍历 JSONL 中每条录音,为每个 split 生成
{split}-type={slu_type}.csv,最终把train_synthetic与train_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 注意力解码器(AttentionalRNNDecoder,attn_type: keyvalue) |
seq2seq 解码器 |
seq_lin |
Linear(512 → 58) | 输出层,output_neurons=58 对应 58 个 unigram 语义子词(索引 0 为 eos/bos) |
解码使用 S2SRNNBeamSearcher(beam_size=80、eos_threshold=1.5、temperature=1.25、min/max_decode_ratio 控制生成长度)。训练损失为带 0.1 标签平滑的 NLL(seq_cost: nll_loss),优化器为 Adam(lr=0.0003),并用 NewBobScheduler 依据验证集 WER 做学习率退火(improvement_threshold=0.0025、annealing_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=0、snr_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=1、max_augmentations=4)。在 train.py 的 compute_forward 中,增强只作用于训练阶段,且标签通过 replicate_labels 与增强后的样本对齐(因为 concat_original=True 会复制 batch)。
4.4 训练流程与输出
训练主流程遵循 SpeechBrain 标准范式(train.py):
sb.parse_arguments+load_hyperpyyaml加载超参数并允许命令行覆盖;sb.create_experiment_directory创建实验目录并保存超参数副本;prepare_SLURP准备数据(DDP 下仅主进程执行run_on_main);dataio_prepare定义数据管线:audio_pipeline读取波形(read_audio),text_pipeline用 tokenizer 把语义字典字符串编码为tokens_bos/tokens_eos/tokens(train.py);Pretrainer.collect_files()/load_collected()下载并加载 58 词 unigram tokenizer;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.yaml 与 train.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_annealing 与 lr_annealing_wav2vec2(退火因子 0.9)调度,并作为独立 recoverable 注册进 Checkpointer。compute_forward 中 wav2vec2_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_SLURP 以 slu_type="decoupled" 调用(对应 CSV 命名 *-type=decoupled.csv)。注意 decoupled 模式下转写文本会被大写化(prepare.py),这与 tokenizer 的字符覆盖设置相关。
八、评测:predictions.jsonl 与 SLU-F1
三套 recipe 的测试逻辑一致(见各 train.py 的 compute_objectives 与 on_stage_end):
- 在
sb.Stage.TEST阶段,对每个测试样本用 beam searcher 解码出语义 token 序列,tokenizer.decode_ids还原为语义字符串; - 用
ast.literal_eval把字符串解析为字典(direct/train.py);解析失败(输出不是合法字典)时回退为{"scenario": "none", "action": "none", "entities": []}; - 写入
output_folder/predictions.jsonl,每行包含解析后的scenario/action/entities以及file(音频文件名,来自测试 CSV 的 ID→文件映射); - 同时用
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.py 的 Pretrained.from_hparams 机制——这与训练脚本中加载 ASR 模型(EncoderDecoderASR.from_hparams)用的是同一套参数迁移与加载逻辑。
十、实战要点小结
- 先装依赖再跑:
pip install -r recipes/SLURP/extra_requirements.txt,并确保pandas、sentencepiece可用; - 占位符必须替换:三个 YAML 的
data_folder: !PLACEHOLDER需要替换为你的本地数据目录(如/localscratch/SLURP),首次运行会自动下载约数 GB 的数据集、噪声与 RIR 包; - Tokenizer 无需重复训练:Direct/NLU 通过
Pretrainer自动下载 58 词 tokenizer;只有想自定义词表时才跑Tokenizer/train.py; - 三套 recipe 共用数据准备:
prepare_SLURP按slu_type生成不同 CSV,训练/验证/测试清单的命名规则为{split}-type={slu_type}.csv; - 评测入口统一:测试结果统一落盘为
results/.../predictions.jsonl,配合 SLURP 官方评测脚本计算 scenario/action/intent 准确率、Word/Char-F1 与 SLU-F1; - 想快速复现最高指标:优先选择 Direct (HuBert)(
train_with_wav2vec2.yaml),注意其 batch_size 更小、训练轮数更多、且默认微调 HuBERT transformer 层(冻结卷积层),需要显存更大的 GPU。
附:延伸阅读(仓库内路径)
- 数据集准备逻辑:recipes/SLURP/prepare.py
- Direct 训练脚本与配置:recipes/SLURP/direct/train.py、recipes/SLURP/direct/hparams/train.yaml
- wav2vec2/HuBERT 版本:recipes/SLURP/direct/train_with_wav2vec2.py、recipes/SLURP/direct/hparams/train_with_wav2vec2.yaml
- NLU 版本:recipes/SLURP/NLU/train.py、recipes/SLURP/NLU/hparams/train.yaml
- Tokenizer 训练:recipes/SLURP/Tokenizer/train.py、recipes/SLURP/Tokenizer/hparams/tokenizer_bpe58.yaml
- 相关基类与工具:speechbrain/core.py(
sb.Brain)、speechbrain/decoders/seq2seq.py(S2SRNNBeamSearcher)、speechbrain/augment/augmenter.py(Augmenter)
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