news 2026/9/24 15:11:41

ESPnet2 Recipe Template 实战指南:用自有语料搭建 ASR/TTS 训练流程与 Kaldi 风格数据准备

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESPnet2 Recipe Template 实战指南:用自有语料搭建 ASR/TTS 训练流程与 Kaldi 风格数据准备
  • 人工智能
  • 语音
  • 音频
  • 深度学习
  • NLP

【免费下载链接】espnet

End-to-End Speech Processing Toolkit

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

导读

ESPnet2 采用「任务级统一 Recipe」的设计思路:不再像 ESPnet1 那样为每个语料单独编写全套脚本,而是为 ASR、TTS、增强、翻译等每个任务维护一份通用模板脚本(如asr.sh),通过命令行参数吸收不同语料的差异。本文以仓库中的 Recipe Template 文档 为主体,结合 egs2/TEMPLATE/asr1/ 下的setup.shasr.shdb.sh等真实源码,系统讲解如何用模板在自有语料上快速跑通端到端训练、如何手工构造 Kaldi 风格的data/目录,以及开发者如何把 ESPnet1/Kaldi 的老 Recipe 移植为 ESPnet2 新 Recipe。读完本文,你将掌握从「一份原始音频 + 标注」到「可训练、可解码、可评分」的完整落地路径。


一、Recipe Template 是什么

Recipe(配方/流程脚本)是 ESPnet 中把「数据准备 → 特征提取 → 词表构建 → 语言模型训练 → ASR 模型训练 → 解码 → 评分 → 打包 → 上传」等步骤串联起来的可执行流水线。egs2/TEMPLATE/README.md指出,模板被设计为支持每个任务常见且通用的功能与需求,从而让用户无需修改 Recipe 主体,只需补充语料相关的一小段数据准备脚本即可。

仓库中模板按任务划分在 egs2/TEMPLATE/ 下,每个任务一个目录,例如:

  • asr1:语音识别(核心示例,脚本名为asr.sh
  • enh1:语音增强(enh.sh
  • tts1/tts2:语音合成(tts.sh
  • mt1:机器翻译(mt.sh
  • st1:语音翻译(st.sh
  • s2t1:语音到文本(s2t.sh
  • enh_asr1enh_diar1sds1ssl1spk1等更多任务

asr1为例,模板目录包含以下关键内容(见 egs2/TEMPLATE/asr1/):

文件/目录作用
setup.sh一键把模板复制到新语料目录的引导脚本
asr.sh通用 ASR 主流水线(15 个 stage,约 1800 行)
path.sh设置 PATH、Python 环境、工具路径
cmd.sh选择本地执行 / SLURM / PBS 等运行队列方式
db.sh集中登记各语料的下载路径或本地路径
local/语料相关的数据准备与评分脚本(每个 Recipe 需要自定义的部分)
conf/特征、训练、解码、队列配置(fbank.conftrain_asr_*.yamldecode_asr_*.yaml等)
scripts/pyscripts/steps/utils/公共工具脚本(通常以符号链接指向模板)

从源码看,setup.sh(见 egs2/TEMPLATE/asr1/setup.sh)做的事非常明确:

  • 复制cmd.shconflocal三个部分到目标目录(这些是需要按语料定制的内容);
  • 符号链接asr.shpath.shdb.shscriptspyscriptsstepsutils到模板(这些是公共内容,软链可保证升级模板时自动同步)。

这样设计的好处是:同一份asr.sh被所有语料共享,任何 bug 修复或新特性只需改模板一处。


二、用自有语料运行 ESPnet:三步上手

原文档给出了最核心的三步流程,下面结合源码逐一展开。

第 1 步:复制模板到自己的语料目录

% task=asr1 # enh1, tts1, mt1, st1 % egs2/TEMPLATE/${task}/setup.sh egs2/foo/${task}

执行后,egs2/foo/${task}下会生成可直接修改的工作目录:cmd.shconf/local/被真实复制,其余公共文件以符号链接形式存在。setup.sh还做了两项校验(源码第 18-31 行):必须恰好传入 1 个目标目录参数,且目标目录的上级须存在TEMPLATE目录,否则报错退出。

第 2 步:创建egs2/foo/${task}/data目录

data/是训练、验证、测试数据的存放处,采用 Kaldi 风格结构(下一节详解)。如果你的语料在egs2下已有对应 Recipe(如mini_an4librispeech),也可以直接参考其local/data.sh的实现。

第 3 步:运行主脚本

原文档以 ASR 为例给出了最小启动命令:

cd egs2/foo/${task} # 官方约定:所有脚本都在该目录下执行 # 假设 Stage1 已经生成 data,可直接从 Stage2 开始 ./asr.sh \ --stage 2 \ --ngpu 1 \ --train_set train \ --valid_set valid \ --test_sets "test" \ --lm_train_text "data/train/text"

要点说明:

  • 必须cd到 Recipe 目录执行asr.sh依赖相对路径(如./path.shutils/parse_options.shconf/*.yaml),离开该目录会导致解析失败。
  • --stage 2表示跳过 Stage 1(数据准备,假定data/已就绪),从速度扰动阶段开始。
  • --ngpu 1指定使用 1 块 GPU;设为0则纯 CPU 运行(见 asr.sh 中ngpu=1 # The number of gpus ("0" uses cpu, otherwise use gpu))。
  • --train_set/--valid_set/--test_sets必填项asr.sh在进入主流程前会做严格校验(源码第 301-331 行),未指定--train_set/--valid_set直接报错退出,train_setvalid_set不允许相同,test_sets中不允许出现train_set
  • 若遇到 CUDA 显存不足(OOM),应调小batch_bins(或batch_size),而不是盲目增大nj
  • 指定 GPU 设备号可使用环境变量CUDA_VISIBLE_DEVICES

下面这张表汇总了asr.sh中最常用的参数(默认值均来自 asr.sh 的源码),便于按需调整:

参数默认值说明
--stage/--stop_stage1/10000流水线起止阶段
--skip_stages指定要跳过的阶段
--ngpu1GPU 数量,0表示 CPU
--num_nodes1分布式训练的节点数
--nj32数据/特征阶段的并行任务数
--inference_nj32解码阶段的并行任务数
--gpu_inferencefalse是否用 GPU 解码
--dumpdir/--expdirdump/exp特征与实验输出目录
--feats_typerawrawraw_copyfbank_pitchfbankextracted
--audio_formatflacwavflacwav.arkflac.ark(仅raw系列有效)
--fs16k采样率(需与语料实际采样率匹配)
--min_wav_duration/--max_wav_duration0.1/20训练音频时长过滤区间(秒)
--token_typebpebpecharwordwhisper_enwhisper_multilingualhugging_face
--nbpe30BPE 词表大小
--bpemodeunigramunigrambpe
--use_lmtrue解码时是否使用语言模型
--lm_config/--lm_argsLM 训练配置与覆盖参数
--asr_config/--asr_argsASR 训练配置与覆盖参数
--inference_config/--inference_args解码配置与覆盖参数
--inference_asr_modelvalid.acc.ave.pth解码所用模型快照,也常用valid.loss.ave.pth
--download_model直接从 Model Zoo 下载预训练模型用于解码
--use_ngramfalse是否训练并使用 n-gram 语言模型
--feats_normalizeglobal_mvn特征归一化方式
--use_streaming/--use_maskctcfalse流式解码 / Mask-CTC 解码
--train_set/--valid_set/--test_sets空(必填)训练/验证/测试数据目录名

几个值得注意的源码细节:

  • --lm_train_text未指定时,会自动复用训练集文本(源码第 374 行Use the same text as ASR for lm training if not specified),所以最小启动命令里即使不写它也通常没问题。
  • 指定--feats_type raw时,特征目录为${dumpdir}/raw(源码第 334-349 行);选择不支持的feats_type会直接报错退出。
  • --token_type支持多种词表策略:bpe(SentencePiece)、charwordwhisper_*(复用 Whisper 词表)以及hugging_face(需配合--hugging_face_model_name_or_path),源码第 402-424 行逐一分支处理。
  • 通过--lm_args/--asr_args/--inference_args传入的覆盖参数会自动拼进实验目录的 tag 中(源码第 452-455 行),保证不同参数组合的产物目录不互相覆盖,这也是推荐「先用 yaml 配置,再在命令行覆盖」的原因。

三、Kaldi 风格 data 目录:格式与构造详解

训练集、开发集、测试集各自拥有完全相同的目录结构(详见原文档「About Kaldi style data directory」一节)。data/traindata/devdata/test三者并列,内部文件格式完全一致。

3.1 目录结构总览

data/ train/ - text # 转写文本 - wav.scp # 音频文件路径 - utt2spk # 发音单元(utterance) id 到说话人(spk) id 的映射 - spk2utt # 说话人 id 到发音单元 id 列表的映射 - segments # [可选] 指定每条发音的起止时间 dev/ ... test/ ...

3.2 各文件格式

text:转写文件

uttidA <transcription> uttidB <transcription> ...

wav.scp:音频路径文件

uttidA /path/to/uttidA.wav uttidB /path/to/uttidB.wav ...

utt2spk:发音单元 → 说话人

uttidA speakerA uttidB speakerB uttidC speakerA uttidD speakerB ...

spk2utt:说话人 → 发音单元列表

speakerA uttidA uttidC ... speakerB uttidB uttidD ... ...

原文档特别强调:spk2utt可由utt2spk生成,反之亦然,因此手工创建时二选一即可,用模板自带的 Perl 工具互转:

utils/utt2spk_to_spk2utt.pl data/train/utt2spk > data/train/spk2utt utils/spk2utt_to_utt2spk.pl data/train/spk2utt > data/train/utt2spk

没有说话人信息怎么办?原文档给出两种兜底方案:把说话人 id 设成与发音单元 id 相同,或全部填同一个dummyid。它同时指出:「(对于 asr recipe)我们实际上并不使用说话人信息」,所以这种占位做法不会影响 ASR 训练。

uttidA uttidA uttidB uttidB ...

uttidA dummy uttidB dummy ...

3.3 可选文件segments:长音频切分

如果原始录音很长(约 > 1 小时),且一个音频文件里包含多段语音,就需要segments指定每条发音的起止时间,格式为<utterance_id> <wav_id> <start_time> <end_time>

sw02001-A_000098-001156 sw02001-A 0.98 11.56 ...

关键约定:使用segments时,wav.scp中的键是<wav_id>而非<utterance_id>sw02001-A对应整段录音,而sw02001-A_000098-001156是切出的某条发音):

sw02001-A /path/to/sw02001-A.wav ...

asr.shStage 3 的注释(源码第 643-649 行)可以印证这条约定:segments用于把wav.scp中的整段录音按<segment_id> <record_id> <start_time> <end_time>切分为发音单元,时间单位为秒。

3.4 目录校验:validate_data_dir.sh

手工构造完data/后,强烈建议用官方校验脚本检查格式是否合法:

utils/validate_data_dir.sh --no-feats data/train utils/validate_data_dir.sh --no-feats data/dev utils/validate_data_dir.sh --no-feats data/test

--no-feats表示跳过特征文件检查(因为我们只有wav.scp,没有预提取的feats.scp)。该校验脚本能及时发现wav.scp键不一致、text缺失、时间戳格式错误等常见问题。

3.5 推荐实践:先跑 mini_an4 感受数据结构

原文档建议通过mini_an4亲自检查data/的实际内容:

cd egs2/mini_an4/asr1 ./run.sh

egs2/mini_an4/asr1/run.sh 展示了run.sh的真实形态——它只是asr.sh的一层薄封装,把数据集名称与配置文件集中传递:

./asr.sh \ --nj 2 \ --inference_nj 2 \ --lang en \ --asr_config conf/train_asr_rnn_debug.yaml \ --lm_config conf/train_lm_rnn_debug.yaml \ --inference_config conf/decode_asr_debug.yaml \ --train_set train_nodev \ --valid_set train_dev \ --test_sets "train_dev test test_seg" \ --lm_train_text "data/train_nodev/text" "$@"

跑完run.sh后查看data/train_nodev/下的textwav.scputt2spkspk2utt,就能直观理解上述格式约定。


四、(开发者向)如何制作 / 移植一个新的 Recipe

原文档第三部分是给开发者看的「How to make/port new recipe」。ESPnet2 与 ESPnet1 最大的不同在于:不为每个语料准备不同 Recipe,而是每个任务一套通用 Recipeasr.shenh.shtts.sh等)。这些通用脚本被精心设计为适用于任意语料,理想情况下你几乎不需要修改 Recipe 主体,唯一必须自己做的是local/data.sh

4.1 建立目录与 run.sh

% task=asr1 # enh1, tts1, mt1, st1 % egs2/TEMPLATE/${task}/setup.sh egs2/foo/${task} % cd egs2/foo/${task} % cp ../../mini_an4/${task}/run.sh . % vi run.sh

run.sh是通用 Recipe 的薄封装,内容形如:

# The contents of run.sh ./asr.sh \ --train_set train \ --valid_set dev \ --test_sets "dev test1 test2" \ --lm_train_text "data/train/text" "$@"

注意末尾的"$@":它把用户在命令行额外传入的--xxx参数原样转发给asr.sh,这是run.sh保持灵活性的关键设计。

4.2 local/data.sh:唯一的必写脚本

原文档给出四条核心原则:

  1. 语料差异必须通过asr.sh的命令行参数吸收,不要擅自修改公共脚本。
  2. local/data.sh负责生成 Kaldi 风格的data/train(训练)、data/dev(验证)以及多个data/test1data/test2(测试)目录(对应asr.sh的 Stage 1,源码第 572-576 行直接调用local/data.sh ${local_data_opts})。
  3. 特征提取、速度扰动(Speed Perturbation)、长短句过滤都由公共阶段完成local/data.sh中不需要(也不应该)重复实现这些步骤(见原文档对「Feature extraction, Speed Perturbation, Removing long/short utterances」的说明)。
  4. 从 ESPnet1 或 Kaldi 移植时,只需把原 Recipe 的数据准备部分嵌入local/data.sh

4.3 关于验证集与测试集的几条约定

  • 验证集必须只有一份:训练期间的验证集(--valid_set)必须是单个数据目录。如果有多个验证目录,用utils/combine_data.sh合并。
  • 测试集可以有多份:推理阶段(--test_sets)接受多个测试目录,因此可以顺手把验证集也纳入评估。
  • 部分语料没有官方 dev 集:此时可以从训练数据中切出一部分作为验证集,其余仍作训练集(原文档举例egs2/csj/asr1/local/data.sh就是这么做的)。
  • asr.sh的自动去重逻辑(源码第 316-331 行):若test_sets里包含valid_set,脚本会自动把eval_valid_set置为true,并把验证集从test_sets中剔除,避免重复评估;test_sets内部的重复项也会被自动去重。

4.4 在 db.sh 登记语料路径

如果 Recipe 依赖的语料尚未在 egs2/TEMPLATE/asr1/db.sh 中登记,则需要补充一行:

... YOUR_CORPUS= ...

db.sh中已登记了大量语料,值有三种形态,从文件头注释「"downloads"means the corpus can be downloaded by the recipe automatically」可以确认:

  • downloads:表示 Recipe 能自动下载该语料(如LIBRISPEECH=downloadsAN4=downloads);
  • 空值:等待用户填写本地路径;
  • 绝对路径:某些机构环境预置的路径(db.sh底部还有针对 CMU TIR、JHU CLSP 等特定环境的hostname分支,自动覆盖对应语料路径)。

因此新增语料时,把YOUR_CORPUS=改成你的本地路径或downloads即可。

4.5 特殊工具依赖写入 local/path.sh

如果 Recipe 依赖某些特殊命令行工具,在local/path.sh中声明:

# e.g. flac command is required if ! which flac &> /dev/null; then echo "Error: flac is not installed" return 1 fi

path.sh会在主流程加载时执行(asr.sh源码第 296 行. ./path.sh),工具缺失会提前暴露,避免跑到一半才失败。注意path.sh面向的是「配方所需的外部命令」,而 Python 解释器等公共环境由模板级path.sh统一处理。


五、asr.sh 的 15 阶段流水线:一次跑通全流程

结合源码(asr.sh 中各log "Stage N: ..."语句),asr.sh从数据到部署的完整流水线如下,这也是理解模板设计的最直观方式:

Stage内容源码位置
1数据准备:调用local/data.sh生成data/{train,valid,test}asr.sh#L572-L576
2速度扰动:对train_setspeed_perturb_factors生成train_spasr.sh#L579-L599
3特征处理:raw模式下格式化wav.scp(统一格式与采样率);fbank_pitch等模式下提取特征asr.sh#L615-L760
4长短句过滤:按min/max_wav_duration清洗dump/数据asr.sh#L801
5词表构建:按token_type生成 BPE / char / word / whisper / hugging_face 的token_listasr.sh#L879-L1008
6LM 统计量收集(collect stats)asr.sh#L1009
7LM 训练asr.sh#L1086
8LM 困惑度(perplexity)评估asr.sh#L1162
9n-gram 训练(需use_ngram=trueasr.sh#L1181
10ASR 统计量收集(collect stats)asr.sh#L1191
11ASR 模型训练asr.sh#L1310
12解码(含 k2 / streaming / Mask-CTC / n-best 重打分等可选路径)asr.sh#L1513
13评分(默认走sclite,可通过score_opts/local_score_opts定制)asr.sh#L1654
14打包模型(pack)asr.sh#L1760
15上传模型到 HuggingFace(hf_repo指定仓库)asr.sh#L1793

几个与阶段编排直接相关的参数组合:

  • --skip_data_prep true:自动跳过 Stage 1-5(源码第 544-546 行)。
  • --skip_train true:跳过训练相关阶段(2/4/5/6/7/8/9/10/11),只做数据与解码评估。
  • --use_lm false:跳过 LM 相关阶段 6/7/8。
  • --use_ngram false(默认):跳过 Stage 9。
  • --skip_eval true:跳过 Stage 12/13。
  • --skip_packing(默认true)与--skip_upload_hf(默认true):默认不执行打包与上传,需要时手动打开。

特征配置方面,conf/fbank.conf 提供了 fbank 提取的默认参数(--sample-frequency=16000--num-mel-bins=80),conf/pitch.conf 提供 pitch 特征参数;队列配置则在 conf/ 下的queue.conf(本地/多机)、slurm.conf(SLURM 集群)、pbs.conf(PBS 集群)中选择,并由cmd.sh切换。实际语料通常在 egs2/librispeech/asr1/conf/ 这类目录中提供更完整的train_asr_*.yamldecode_asr_*.yaml参考配置。


六、总结:模板使用的完整心智模型

把原文档与仓库源码合起来看,Recipe Template 的使用可以归结为三层:

  1. 普通用户(跑通自有语料)setup.sh复制模板 → 构造 Kaldi 风格data/text+wav.scp+utt2spk/spk2utt,长音频加segments)→utils/validate_data_dir.sh校验 → 从--stage 2起跑asr.sh,必要时调整batch_bins解决显存问题。
  2. 格式细节:四个核心文件(text/wav.scp/utt2spk/spk2utt)的键值约定、segments<utterance_id> <wav_id> <start> <end>四列格式、以及「用segmentswav.scpwav_id为键」的隐含规则。
  3. 开发者(制作/移植 Recipe):只写local/data.sh(生成train/dev/test的 Kaldi 目录)、在run.sh中传参、在db.sh登记语料路径、在local/path.sh声明特殊工具依赖;特征提取、速度扰动、长短句过滤、训练与解码全部复用公共流水线。验证集只能有一份(多个则combine_data.sh合并),测试集可以有多个,asr.sh会自动处理test_setsvalid_set重叠的情况。

掌握这套模板机制后,无论是跑通一个新的开源语料,还是把 ESPnet1/Kaldi 时代的老 Recipe 迁移到 ESPnet2,都能以最小的代码量完成——这也是 ESPnet2 以「通用 Recipe + 参数化差异」替代「每语料一套脚本」的核心设计意图。

  • 人工智能
  • 语音
  • 音频
  • 深度学习
  • NLP

【免费下载链接】espnet

End-to-End Speech Processing Toolkit

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

相关推荐

上一篇:May协程库CLS机制:为什么应该用CLS替代TLS
下一篇:ProcMon-for-Linux安装指南:支持Ubuntu、Debian、Fedora等主流发行版

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

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

Flowable 监听器使用指南

Flowable 监听器使用指南 在 Flowable 流程引擎中&#xff0c;监听器&#xff08;Listener&#xff09;是扩展流程行为的核心机制之一。它允许开发者在流程执行的特定时刻插入自定义逻辑&#xff0c;而无需修改 BPMN 流程图本身。Flowable 主要提供两种监听器&#xff1a;执行监…

作者头像 李华