☰
ESPnet2 中的 GRABO 数据集实战:低资源语音命令识别(ASR)端到端流程与 Conformer 训练配置详解
2026/9/25 17:56:12 网站建设 项目流程
  • 人工智能
  • 语音
  • 音频
  • 深度学习
  • NLP

【免费下载链接】espnet

End-to-End Speech Processing Toolkit

项目地址:https://gitcode.com/gh_mirrors/es/espnet
点击查看免费下载

本文以 ESPnet2 仓库中 egs2/grabo/asr1/README.md 记录的结果报告为骨架,完整还原在 ESPnet2 上跑通 GRABO(荷兰语顺序词/语音命令)数据集的端到端流程:从数据集下载与随机划分、单 token 化标注,到 Conformer 模型的训练/解码配置解析,再到分类准确率与 WER 两种指标的评测实现。读完本文,你可以直接复用 run.sh 复现该 recipe,并理解每个关键配置项的作用与底层脚本调用链。

任务与数据集背景

GRABO(GRAtuitous BOrdinals)是卡普顿大学发布的低资源语音数据集,用于“顺序词”(ordinal word,如"第一、第二……")等语音命令的识别。该 recipe 的 README 中给出了两篇数据集背景文献:

  • Renkens, V., et al. "Acquisition of ordinal words using weakly supervised NMF."2014 IEEE SLT, IEEE, 2014.
  • Renkens, V., and Van hamme, H. "Capsule networks for low resource spoken language understanding." arXiv:1805.02922 (2018).

训练/验证/测试集的划分方式则参考了第三篇文献:

  • Tian, Y., and Gorinski, P. J. "Improving end-to-end speech-to-intent classification with Reptile." arXiv:2008.01994 (2020).

这个任务的关键特性是:每条语音命令被当作一个单一 token。也就是说,每个参考句子(reference sentence)只包含一个 token——这使 ASR 退化为"语音到意图(intent)"的单标签分类问题。README 中也明确指出:由于数据集较小,划分是随机的,结果不能被严格复现;如果想得到稳定结论,更好的做法是跑多次实验并取平均准确率。这一点后文会结合数据准备脚本进一步说明。

数据下载与准备

下载:local/data.sh

数据准备入口是 local/data.sh。它从 KULeuven 的 FTP 服务器拉取约 2.1 GB 的原始包:

data_url=ftp://ftp.esat.kuleuven.be/psi/speech/vrenkens/grabo.tar.gz data_tar=grabo.tar.gz data_tar_size=2102543961

脚本会先校验本地文件字节数是否与data_tar_size一致(不一致则删除重下),下载后解包到${GRABO}/grabo目录。GRABO这一变量的取值来自 db.sh,默认值为downloads,表示"该语料可由 recipe 自动下载":

GRABO=downloads # db.sh

在 CMU TIR 集群环境中,db.sh 还会根据 hostname 自动改写为项目绝对路径(/projects/tir5/data/speech_corpora/Grabo),体现 ESPnet2 recipe 对多环境路径的适配方式。

标注与随机划分:local/data_prep.py

核心标注脚本是 local/data_prep.py,它完成三件事:

  1. 从 XML 解析出"命令 token"。原始数据中每个 utterance 对应一个帧级 XML 标注文件(framedir/<cmd>/<utt>.xml),脚本中的frametotask()函数把 XML 解析为根元素名 + 属性字典,再序列化成单行字符串:

    root = ET.parse(infile).getroot() semantic["name"] = root[0].text.strip() ... root = ET.Element(semantic["name"], attrib=semantic["args"]) return ET.tostring(root).decode("ascii")

    最终经task_str = "-".join(task_str.split())压缩空白后写入text文件——这就是"每条命令 = 一个 token"的直接实现。

  2. 按说话人 × 命令的随机 2/4/9 划分。代码对每个说话人(如pp2)的spchdatadir/<command>/下的录音排序、洗牌后切分:

    random.seed(2021) ... wav_list.sort() random.shuffle(wav_list) random.shuffle(wav_list) wav_dict = {"train": wav_list[:2], "dev": wav_list[2:6], "test": wav_list[6:]}

    即每个说话人的每条命令:2 条训练、4 条验证、剩余 9 条测试。注意 README 中"randomly split… please modify the data preparation script to make the procedure deterministic"的提醒正源于此:尽管代码里写有random.seed(2021),但os.listdir()的目录遍历顺序在不同文件系统/OS 上并不保证有序,洗牌前的样本顺序可能不一致,因此跨环境严格复现存在风险。

  3. 生成 Kaldi 风格的 data 目录(text、wav.scp、utt2spk)。两个值得注意的细节:

    • wav.scp不落地中间文件,而是直接写入 sox 管道命令,在特征提取时在线重采样到 16 kHz 单声道:

      downsampled_wav = ( f'{args.sox_path} {sample["wav_abspath"]} -t wav -r 16k -c 1 - |' )
    • utt2spk中每条 utterance 都指向自身(utt2spk_f.write(sample["wav_id"] + " " + sample["wav_id"])),从源码结构看这是把每个 utterance 当作独立"说话人"处理,规避了说话人级别信息对分类任务的干扰。

data.sh 在 stage 2 调用该脚本后,还会对train/dev/test三个目录依次执行utt2spk_to_spk2utt.pl、fix_data_dir.sh与validate_data_dir.sh --no-feats完成目录规整与校验。

训练入口:run.sh 的完整参数

整个 recipe 由 run.sh 驱动,它调用 ESPnet2 的通用 asr.sh。完整命令如下(保留原文件全部参数):

train_set=train valid_set=dev test_sets="dev test" asr_tag=conformer_mono16k_warmup800_lr2e-4_accum2 inference_tag=infer asr_config=conf/train_asr.yaml inference_config=conf/decode_asr.yaml # speed perturbation related # (train_set will be "${train_set}_sp" if speed_perturb_factors is specified) speed_perturb_factors="0.9 0.95 1.0 1.05 1.1" ./asr.sh \ --skip_data_prep false \ --skip_train false \ --skip_eval false \ --ngpu 1 \ --nj 8 \ --inference_nj 8 \ --speed_perturb_factors "${speed_perturb_factors}" \ --feats_type fbank_pitch \ --audio_format wav \ --fs 16000 \ --token_type word \ --use_lm false \ --asr_tag "${asr_tag}" \ --asr_config "${asr_config}" \ --inference_tag "${inference_tag}" \ --inference_config "${inference_config}" \ --inference_asr_model valid.acc.ave.pth \ --train_set "${train_set}" \ --valid_set "${valid_set}" \ --test_sets "${test_sets}" \ --local_score_opts "--inference_tag ${inference_tag}" \ --lm_train_text "data/${train_set}/text" "$@"

关键参数的含义(结合 asr.sh 中的注释与实现):

参数取值说明
--feats_typefbank_pitch特征类型,asr.sh支持raw、raw_copy、fbank_pitch等;fbank_pitch分支会调用steps/make_fbank_pitch.sh提取 Fbank+基频特征并在dumpdir/fbank_pitch中写入feats_type标记文件
--speed_perturb_factors0.9 0.95 1.0 1.05 1.1语速扰动因子,指定后训练集实际会变为train_sp(见 asr.sh 与#L580-L601的扰动数据构造),对小数据量任务等效于 5 倍数据增强
--token_typeword本任务中每条命令即一个"词"级 token
--use_lmfalse不训练独立语言模型,解码仅依赖内部 LM 权重(见后文decode_asr.yaml)
--inference_asr_modelvalid.acc.ave.pth推理时加载验证集上准确率平均的最优 checkpoint
--local_score_opts--inference_tag infer透传给 local/score.sh 的评分脚本
--ngpu/--nj/--inference_nj1/8/8单卡训练,CPU 并行度 8

需要说明的是,README 中记录了feats_type: raw与feats_type: fbank_pitch两组实验,但当前 run.sh 只保留了fbank_pitch这一组的入口;raw组是通过把--feats_type改为raw复用的同一套模型配置得到的(asr.sh在feats_type=raw时把数据放在dumpdir/raw,跳过 Kaldi 特征提取,直接对 16 kHz 波形建模)。

训练配置解析:conf/train_asr.yaml

训练超参见 conf/train_asr.yaml(conf/tuning/train_asr_conformer_adam.yaml 为内容相同的历史存档)。逐段说明:

批处理与优化策略

batch_type: numel # 按元素个数凑批 batch_bins: 2000000 # 每个 batch 的总元素数上限(特征元素数量) accum_grad: 2 # 梯度累积 2 步,等效 batch 翻倍 max_epoch: 250 patience: none # 不做早停 best_model_criterion: - - valid - acc - max # 以验证集准确率为模型选择准则 keep_nbest_models: 10 # 保留 10 个最优 checkpoint

best_model_criterion直接决定了run.sh中--inference_asr_model valid.acc.ave.pth的选取逻辑:训练结束后取验证集准确率最高的若干模型做平均。对于"单 token 分类"任务,用 accuracy 而非 loss 选模型是更贴合最终指标的做法。

编码器:12 层 Conformer

encoder: conformer encoder_conf: output_size: 256 attention_heads: 4 linear_units: 2048 num_blocks: 12 dropout_rate: 0.1 positional_dropout_rate: 0.1 attention_dropout_rate: 0.0 input_layer: conv2d # Conv2d 前端,把 mel 特征图下采样为序列 normalize_before: true # Pre-LN macaron_style: true # Macaron 风格双 FFN 结构 rel_pos_type: legacy pos_enc_layer_type: rel_pos # 相对位置编码 selfattention_layer_type: rel_selfattn activation_type: swish use_cnn_module: true # Conformer 标志性的卷积模块 cnn_module_kernel: 15

input_layer: conv2d意味着该配置同时兼容fbank_pitch与raw两种输入:对 raw 波形,ESPnet2 会在 Conformer 前接 STFT 前端(对应 tag 中的mono16k);对 fbank_pitch,则把 Fbank 与基频拼接后送入 Conv2d。这与 README 中两组实验共享同一 tag 名asr_conformer_mono16k_warmup800_lr2e-4_accum2相吻合。

解码器与损失

decoder: transformer decoder_conf: attention_heads: 4 linear_units: 2048 num_blocks: 6 dropout_rate: 0.1 positional_dropout_rate: 0.1 self_attention_dropout_rate: 0.0 src_attention_dropout_rate: 0.0 optim: adam optim_conf: lr: 0.0002 scheduler: warmuplr # pytorch v1.1.0+ required scheduler_conf: warmup_steps: 800 model_conf: ctc_weight: 0.0 # 纯 Attention 模型,不用 CTC lsm_weight: 0.0 length_normalized_loss: false

模型名中的三个数字都能在这里找到出处:warmup800对应warmup_steps: 800,lr2e-4对应lr: 0.0002,accum2对应accum_grad: 2。ctc_weight: 0.0说明模型完全依赖 Attention 解码路径,这在解码配置中会再次得到印证。

SpecAugment 数据增强

specaug: specaug specaug_conf: apply_time_warp: true time_warp_window: 5 time_warp_mode: bicubic apply_freq_mask: true freq_mask_width_range: [0, 30] num_freq_mask: 2 apply_time_mask: true time_mask_width_range: [0, 40] num_time_mask: 2

配合run.sh中的语速扰动,训练侧共有两层增强:时间维度的 SpecAugment(时变/频带掩蔽 + 时间扭曲)与语速 5 倍扰动,这是对"数据集小"这一根本约束的主要应对手段。

特征参数

fbank_pitch模式下,Fbank 与基频分别由 conf/fbank.conf 与 conf/pitch.conf 控制:

# fbank.conf --sample-frequency=16000 --num-mel-bins=80
# pitch.conf --sample-frequency=16000

均为 16 kHz 采样率下的 80 维 mel 滤波组,与data_prep.py中 sox 在线重采样到-r 16k的约定一致。

解码配置:conf/decode_asr.yaml

lm_weight: 0.0 ctc_weight: 0.0 beam_size: 1 maxlenratio: -1

这份极简配置与训练侧ctc_weight: 0.0形成闭环:解码时既无外部 LM(lm_weight: 0.0)也无 CTC 分支(ctc_weight: 0.0),beam_size: 1即等价于 greedy 解码。由于每条假设只有一个 token,beam search 的宽度本来就无意义,beam_size: 1使解码退化为对单一 token 的 argmax,进一步印证了"单 token 分类"的任务本质。maxlenratio: -1则关闭输出长度比约束,允许任意长度假设参与排序。

评测:分类准确率与 WER

评测由asr.sh的推理阶段统一调度,WER 分数由通用评分流程产出,而分类准确率则由 recipe 自带的两个脚本计算:

  • local/score.sh:遍历exp/<asr_tag>/<inference_tag>/下各测试集子目录,对每个目录调用score.py,并把逐测试集结果追加汇总为accuracy.csv;

  • local/score.py:读取推理输出的hyp.trn与ref.trn,逐 utterance 做整句字符串精确匹配来统计准确率:

    for sample_id in ref_dict: n_samples += 1 if ref_dict[sample_id] == hyp_dict[sample_id]: n_correct += 1

    结果写入accuracy.csv(表头total,correct,accuracy)。

由于每条参考只有一个 token,二者是互补的视角:分类准确率统计"整句命中",WER 的 Snt 列则给出替换率。两者满足近似关系accuracy ≈ 1 - Sub/100(本任务 Del、Ins 均为 0),README 中的结果表也验证了这一点(如 fbank_pitch 组 dev:accuracy 0.965 对应 Sub 3.5%)。

实验结果(README 原文记录)

feats_type: raw

  • 环境:Python 3.9.7 / espnet 0.10.5a1 / PyTorch 1.9.0 / Git hash09ddefe8fd5b6394338b0c653c3f6ec50063a843(2021-11-20)
  • 模型:asr_conformer_mono16k_warmup800_lr2e-4_accum2(Zenodo 记录 5716386)
datasetSntWrdCorrSubDelInsErrS.Err
infer/dev1584158497.92.10.00.02.12.1
infer/test3631363197.62.40.00.02.42.4

feats_type: fbank_pitch

  • 环境:Python 3.8.12 (conda-forge) / espnet 0.10.3a3 / PyTorch 1.9.0 / Git hash3d17c072348a1a9a4a3f179ad642c0d9f07f4406(2021-10-01)
  • 模型:asr_conformer_mono16k_warmup800_lr2e-4_accum2(Zenodo 记录 5637566)

分类准确率:

datasettotalcorrectaccuracy
dev158415290.965
test363135280.972

WER:

datasetSntWrdCorrSubDelInsErrS.Err
infer/dev1584158496.53.50.00.03.53.5
infer/test3631363197.22.80.00.02.82.8

从表中可以读出几点:

  1. 全部错误都是替换(Sub),没有删除/插入——与单 token 输出结构必然导致的 Del=Ins=0 一致;
  2. raw 特征组略优于 fbank_pitch 组(test 2.4% vs 2.8%),差距很小;
  3. test 集(3631 条)明显大于 dev(1584 条),符合 2/4/9 的划分比例(约 1:2:4.5)。

复现注意事项与适用边界

  • 随机划分的不可复现性:如 README 所述,划分依赖os.listdir()顺序与洗牌,跨环境结果可能有微小波动。README 建议"跑多次实验取平均准确率"而非追求单点可复现;若要确定性划分,需自行修改 local/data_prep.py 中的排序/切分逻辑(仓库只读,请在本地副本中修改)。
  • 运行前提:需要 sox(data_prep.py通过command -v sox定位);fbank_pitch特征提取依赖 Kaldi 工具链(asr.sh 中fbank_pitch分支注释为[Require Kaldi]);数据约 2.1 GB 且来自 FTP 源,下载环境需放行 FTP。
  • 适用场景:该 recipe 展示了 ESPnet2 如何用"通用 ASR 流程 + 单 token 文本"实现低资源语音命令/意图识别。其设计(单 token 参考、accuracy 选模、greedy 解码、语速扰动 + SpecAugment)可以直接迁移到其他小型命令词表任务,但训练/测试集规模(1584/3631 条)决定了它只能作为流程与配置参考,不构成大规模识别系统的性能基线。

相关文件索引

文件作用
egs2/grabo/asr1/README.md数据集文献、随机划分说明与两组实验结果报告
egs2/grabo/asr1/run.sh一键训练/推理入口,定义全部命令行参数
egs2/grabo/asr1/local/data.sh数据下载、解包与 data 目录构建
egs2/grabo/asr1/local/data_prep.pyXML 命令解析、2/4/9 随机划分、sox 重采样管道
egs2/grabo/asr1/conf/train_asr.yamlConformer 训练超参、SpecAugment 配置
egs2/grabo/asr1/conf/decode_asr.yaml纯 Attention greedy 解码配置
egs2/grabo/asr1/conf/fbank.conf / conf/pitch.conf16 kHz 下 80 维 Fbank 与基频参数
egs2/grabo/asr1/local/score.sh / local/score.py分类准确率统计与汇总
egs2/grabo/asr1/db.sh语料路径变量(GRABO=downloads)
  • 人工智能
  • 语音
  • 音频
  • 深度学习
  • NLP

【免费下载链接】espnet

End-to-End Speech Processing Toolkit

项目地址:https://gitcode.com/gh_mirrors/es/espnet
点击查看免费下载

相关推荐

上一篇:pytest 6.2.5 发布解析:bug-fix 版本、Python 3.10 支持与 pluggy 1.0 兼容性升级指南
下一篇:unity-mcp 中 `unity_reflect` 工具全解析:用实时反射校验 Unity C API,告别过时的训练数据

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询