- 人工智能
- 语音
- 音频
- 深度学习
- NLP
【免费下载链接】espnet
End-to-End Speech Processing Toolkit
本文以 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,它完成三件事:
从 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/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 上并不保证有序,洗牌前的样本顺序可能不一致,因此跨环境严格复现存在风险。生成 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_type | fbank_pitch | 特征类型,asr.sh支持raw、raw_copy、fbank_pitch等;fbank_pitch分支会调用steps/make_fbank_pitch.sh提取 Fbank+基频特征并在dumpdir/fbank_pitch中写入feats_type标记文件 |
--speed_perturb_factors | 0.9 0.95 1.0 1.05 1.1 | 语速扰动因子,指定后训练集实际会变为train_sp(见 asr.sh 与#L580-L601的扰动数据构造),对小数据量任务等效于 5 倍数据增强 |
--token_type | word | 本任务中每条命令即一个"词"级 token |
--use_lm | false | 不训练独立语言模型,解码仅依赖内部 LM 权重(见后文decode_asr.yaml) |
--inference_asr_model | valid.acc.ave.pth | 推理时加载验证集上准确率平均的最优 checkpoint |
--local_score_opts | --inference_tag infer | 透传给 local/score.sh 的评分脚本 |
--ngpu/--nj/--inference_nj | 1/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 个最优 checkpointbest_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: 15input_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 hash
09ddefe8fd5b6394338b0c653c3f6ec50063a843(2021-11-20) - 模型:
asr_conformer_mono16k_warmup800_lr2e-4_accum2(Zenodo 记录 5716386)
| dataset | Snt | Wrd | Corr | Sub | Del | Ins | Err | S.Err |
|---|---|---|---|---|---|---|---|---|
| infer/dev | 1584 | 1584 | 97.9 | 2.1 | 0.0 | 0.0 | 2.1 | 2.1 |
| infer/test | 3631 | 3631 | 97.6 | 2.4 | 0.0 | 0.0 | 2.4 | 2.4 |
feats_type: fbank_pitch
- 环境:Python 3.8.12 (conda-forge) / espnet 0.10.3a3 / PyTorch 1.9.0 / Git hash
3d17c072348a1a9a4a3f179ad642c0d9f07f4406(2021-10-01) - 模型:
asr_conformer_mono16k_warmup800_lr2e-4_accum2(Zenodo 记录 5637566)
分类准确率:
| dataset | total | correct | accuracy |
|---|---|---|---|
| dev | 1584 | 1529 | 0.965 |
| test | 3631 | 3528 | 0.972 |
WER:
| dataset | Snt | Wrd | Corr | Sub | Del | Ins | Err | S.Err |
|---|---|---|---|---|---|---|---|---|
| infer/dev | 1584 | 1584 | 96.5 | 3.5 | 0.0 | 0.0 | 3.5 | 3.5 |
| infer/test | 3631 | 3631 | 97.2 | 2.8 | 0.0 | 0.0 | 2.8 | 2.8 |
从表中可以读出几点:
- 全部错误都是替换(Sub),没有删除/插入——与单 token 输出结构必然导致的 Del=Ins=0 一致;
- raw 特征组略优于 fbank_pitch 组(test 2.4% vs 2.8%),差距很小;
- 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.py | XML 命令解析、2/4/9 随机划分、sox 重采样管道 |
| egs2/grabo/asr1/conf/train_asr.yaml | Conformer 训练超参、SpecAugment 配置 |
| egs2/grabo/asr1/conf/decode_asr.yaml | 纯 Attention greedy 解码配置 |
| egs2/grabo/asr1/conf/fbank.conf / conf/pitch.conf | 16 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
相关推荐
ESPnet2 SPGispeech ASR Recipe 详解:Conformer + RNN-LM 混合解码的端到端语音识别训练与结果解读
ESPnet2 SPGispeech ASR Recipe 详解:Conformer + RNN LM 混合解码的端到端语音识别训练与结果解读 本文基于 ESP
人工智能语音音频深度学习NLPESPnet2 瑞士法语多音词语料 ASR 实战:Conformer 端到端语音识别 Recipe 与结果复盘
ESPnet2 瑞士法语多音词语料 ASR 实战:Conformer 端到端语音识别 Recipe 与结果复盘 本篇基于 ESPnet 仓库中 egs2/pol
人工智能语音音频深度学习NLPESPnet2 端到端语音处理实战教程:Recipe 体系、训练配置、流式 ASR 与 Transducer 模型全解析
ESPnet2 端到端语音处理实战教程:Recipe 体系、训练配置、流式 ASR 与 Transducer 模型全解析 导读 本文是基于 ESPnet 仓库
人工智能语音音频深度学习NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考