语音增强
语音增强
这是ESPnet2语音增强前端的通用配置方案。
目录
enh.sh 简介
在egs2/TEMPLATE/enh1/enh.sh中,包含13个阶段。
阶段1:数据准备
此阶段与asr.sh中的阶段1类似。
阶段2:语音扰动
语音扰动在自动语音识别(ASR)任务中被广泛使用,但在语音增强领域却很少应用。我们的一些初步实验表明,语音扰动在wsj0_2mix数据集上有效。我们正在进行更多实验来验证其效果。语音扰动的处理流程与ASR中的几乎相同,我们已将scripts/utils/perturb_data_dir_speed.sh复制为scripts/utils/perturb_enh_data_dir_speed.sh,并做了一些小修改以支持对多个scp文件(而不仅仅是wav.scp)进行语音扰动处理。
阶段3:格式化wav.scp
格式化scp文件,例如wav.scp。这些scp文件包括:
wav.scp: 混合/含噪输入信号的音频文件列表。spk{}.scp: 语音参考信号的wav文件列表。{}可以是1、2等,取决于wav.scp中输入信号中的说话人数量。noise{}.scp(可选): 噪声参考信号的音频文件列表。{}可以是1、2等数字,取决于wav.scp中输入信号包含的噪声类型数量。当指定--use_noise_ref true参数时需要提供该文件。该参数还与变量noise_type_num相关。dereverb{}.scp(可选): 去混响参考信号的wav文件列表(用于训练去混响模型)。当指定--use_dereverb_ref true时需要此文件。同时也与变量dereverb_ref_num相关。utt2category: (可选) 每个语音片段的类别信息。该文件可帮助批次采样器在每个批次中加载相同类别的语音片段。一个使用场景是用户希望在不同批次中分别加载仿真数据和真实数据。
阶段4:移除短数据
此阶段与ASR配方中的阶段相同。
阶段5:收集增强任务的统计数据。
与ASR任务相同,我们在训练前收集数据统计信息。相关的新Python文件包括:
espnet2/
├── bin
│ └── enh_train.py
└── tasks
└── enh.py在espnet2/bin/enh_train.py中调用了espnet2/tasks/enh.py里定义的EnhancementTask。在collect_stats模式下,EnhancementTask的行为与ABSTask相同。
阶段6:增强任务训练
我们在espnet2/tasks/enh.py中创建了EnhancementTask,用于训练定义在espnet2/enh/espnet_model.py中的ESPnetEnhancementModel(AbsESPnetModel)。在EnhancementTask中,语音增强或分离模型遵循encoder-separator-decoder架构,并实现了多种编码器、解码器和分离器。虽然目前它被定义为一个独立任务,但其他任务可以轻松调用EnhancementTask中的模型,甚至可以与其他任务联合训练(参见egs2/TEMPLATE/enh_asr1/、egs2/TEMPLATE/enh_st1/)。
现在我们支持通过在配置中指定
preprocessor来实时添加噪声、混响和干扰语音。例如,要使用EnhPreprocessor,可以在配置中指定preprocessor: "enh",并在run.sh中指定--extra_wav_list。更多详情请查看PR #4321。我们还支持与其他语音增强/分离工具包(如Asteroid)的可能集成,这样使用其他语音增强/分离工具包训练的模型可以在ESPnet上重复使用/评估,用于ASR等下游任务。
enh.sh中的相关参数包括:
- --ref_num
- --enh_args
- --enh_config
- --enh_exp
- --ngpu
- --num_nodes
- --init_param
- --use_dereverb_ref
- --use_noise_ref
- --extra_wav_list
相关的Python文件:
espnet2/
├── bin
│ ├── enh_inference.py
│ ├── enh_scoring.py
│ └── enh_train.py
├── enh
│ ├── abs_enh.py
│ ├── decoder
│ │ ├── abs_decoder.py
│ │ ├── conv_decoder.py
│ │ ├── null_decoder.py
│ │ └── stft_decoder.py
│ ├── encoder
│ │ ├── abs_encoder.py
│ │ ├── conv_encoder.py
│ │ ├── null_encoder.py
│ │ └── stft_encoder.py
│ ├── espnet_model.py
│ ├── layers
│ │ ├── beamformer.py
│ │ ├── complex_utils.py
│ │ ├── complexnn.py
│ │ ├── conv_utils.py
│ │ ├── dc_crn.py
│ │ ├── dnn_beamformer.py
│ │ ├── dnn_wpe.py
│ │ ├── dpmulcat.py
│ │ ├── dprnn.py
│ │ ├── dptnet.py
│ │ ├── fasnet.py
│ │ ├── ifasnet.py
│ │ ├── mask_estimator.py
│ │ ├── skim.py
│ │ ├── tcn.py
│ │ └── wpe.py
│ ├── loss
│ │ ├── criterions
│ │ │ ├── abs_loss.py
│ │ │ ├── tf_domain.py
│ │ │ └── time_domain.py
│ │ └── wrappers
│ │ ├── abs_wrapper.py
│ │ ├── dpcl_solver.py
│ │ ├── fixed_order.py
│ │ ├── multilayer_pit_solver.py
│ │ └── pit_solver.py
│ └── separator
│ ├── abs_separator.py
│ ├── asteroid_models.py
│ ├── conformer_separator.py
│ ├── dan_separator.py
│ ├── dc_crn_separator.py
│ ├── dccrn_separator.py
│ ├── dpcl_separator.py
│ ├── dpcl_e2e_separator.py
│ ├── dprnn_separator.py
│ ├── dptnet_separator.py
│ ├── fasnet_separator.py
│ ├── neural_beamformer.py
│ ├── rnn_separator.py
│ ├── skim_separator.py
│ ├── svoice_separator.py
│ ├── tcn_separator.py
│ └── transformer_separator.py
└── tasks
└── enh.py阶段7:语音增强推理
此阶段使用训练好的模型生成增强或分离的语音。生成的音频文件将存放在${expdir}/${eval_set}/logdir目录下,同时会在${expdir}/${eval_set}目录中创建对应的scp文件。
enh.sh中的相关参数包括:
- --ref_num
- --fs
- --gpu_inference
- --inference_args
- --inference_model
- --inference_nj
- --inference_enh_config
现在,我们支持在此阶段通过指定
--inference_enh_config来更改模型属性(例如NeuralBeamformer中的beamformer_type)。详情请查看PR #4251。
相关的新Python文件:
espnet2/
└── bin
└── enh_inference.py阶段8:评分
此阶段用于进行语音增强的评分。每个${eval_set}的评分结果将被汇总到${expdir}/RESULTS.TXT中。
enh.sh 中的相关参数包括:
- --scoring_protocol
- --参考通道
相关的新Python文件:
egs2
└── TEMPLATE
└── enh1
└── scripts
└── utils
└── show_enh_score.sh
espnet2/
└── bin
└── enh_scoring.py阶段9:使用预训练的ASR模型进行解码
与ASR任务中的阶段11相同。阶段7中增强的语音被输入到一个预训练的ASR模型(指定为"${asr_exp}"/"${decode_asr_model}")进行解码。
enh.sh中的相关参数包括:
- --score_with_asr
- --asr_exp
- --decode_args
- --decode_asr_model
- --gpu_inference
- --inference_nj
相关的Python文件:
espnet2/
└── bin
└── asr_inference.py阶段10:使用预训练的ASR模型进行评分
与ASR任务中的阶段12相同。阶段11的解码结果将被评分以计算平均CER/WER/TER。
阶段11:打包模型
与其他任务相同。在espnet2/bin/pack.py中添加了一个新的条目用于打包语音增强模型。
阶段12:将模型上传至Zenodo (已弃用)
将训练好的语音增强/分离模型上传至Zenodo平台以便共享。
阶段13:将模型上传至Hugging Face
将训练好的语音增强/分离模型上传至Hugging Face平台进行共享。更多信息请参阅文档。
(开发者指南) 创建新配方的说明
步骤1 创建配方目录
首先,运行以下命令从我们的模板中为新配方创建目录:
egs2/TEMPLATE/enh1/setup.sh egs2/<your_recipe_name>/enh1请遵循其他配方中的命名约定。
在以下步骤中,我们假设操作都在目录
egs2/下进行。/enh1/
步骤2 编写数据准备脚本
准备local/data.sh文件,该文件将在enh.sh的第一阶段使用。它可以接收一些参数作为输入,具体可参考egs2/wsj0_2mix/enh1/local/data.sh示例。
脚本 local/data.sh 最终应在 <recipe_dir>/data/ 目录下生成Kaldi风格的数据目录。每个子集目录至少应包含4个文件:
<recipe-dir>/data/<subset-name>/
├── spk1.scp (clean speech references)
├── spk2utt
├── utt2spk
└── wav.scp (noisy speech)可选地,它还可以包含noise{}.scp和dereverb{}.scp,分别指向相应的噪声和去混响参考文件。{}可以是1、2、...,具体取决于wav.scp中输入信号包含的噪声类型(去混响信号)数量。
确保按照Kaldi的方式对scp及其他相关文件进行排序。同时,记得在排序前在local/data.sh中运行. ./path.sh,因为这会强制按字节顺序排序,即export LC_ALL=C。
请尽可能遵循其他示例中的风格。参考egs2/chime4/enh1/local/data.sh。
请记得使用
shellcheck检查你的新脚本,否则它们可能无法通过ci/test_shell.sh中的测试。
步骤3 准备训练配置
在conf/目录下准备训练配置文件(例如train.yaml)。
如果您有多个配置文件,建议将它们放在
conf/tuning/目录下,并创建一个符号链接conf/tuning/train.yaml指向性能最佳的配置文件。请删除每行末尾的空格。
步骤4 准备run.sh
编写run.sh作为模板入口脚本,这样用户可以通过./run.sh轻松运行您的配方。请参考egs2/wsj0_2mix/enh1/run.sh作为示例。
请确保
run.sh中的参数--ref_num与上一步创建的训练配置文件中separator_conf下的num_spk保持一致(MixIT训练除外)。在MixIT训练中,run.sh中的参数--inf_num应与separator_conf下的num_spk保持一致。如果您的配方提供了噪声和/或去混响的参考,您可以在
run.sh中设置参数--use_noise_ref true和/或--use_dereverb_ref true。
创建新模型的指南
当前的ESPnet-SE工具为所有模型采用了编码器-分离器-解码器架构,例如。
对于时频掩蔽模型,编码器和解码器分别是stft_encoder.py和stft_decoder.py,分离器可以是dprnn_separator.py、rnn_separator.py、tcn_separator.py、transformer_separator.py等。对于TasNet,编码器和解码器分别是conv_encoder.py和conv_decoder.py,分离器是tcn_separator.py。
步骤1 创建模型脚本
对于编码器、分离器和解码器模型,请分别在espnet2/enh/encoder/、espnet2/enh/separator/和espnet2/enh/decoder/目录下创建新的脚本。
对于分离模型,请确保其实现了num_spk属性。此外,建议同时支持predict_noise以估计噪声信号。可参考espnet2/enh/separator/tcn_separator.py。
请遵循CONTRIBUTING.md中提到的编码风格。
请确保将新脚本格式化为符合
black、flake8和isort的样式规范,否则它们可能无法通过ci/test_python.sh中的测试。
步骤2 将新模型添加到相关脚本中
在espnet2/tasks/enh.py中,将您的新模型添加到对应的ClassChoices中,例如
- 对于编码器,将
<key>=<your-model>添加到encoder_choices中。 - 对于解码器,将
<key>=<your-model>添加到decoder_choices中。 - 对于分隔符,将
<key>=<your-model>添加到separator_choices中。
步骤3 [可选] 创建新的损失函数
如果想为模型使用新的损失函数,可以将其作为模块添加到espnet2/enh/loss/criterions/目录下。
参考espnet2/enh/loss/criterions/tf_domain.py中的
FrequencyDomainMSE。
然后将您的损失函数名称添加到espnet2/tasks/enh.py中的criterion_choices,这样您就可以直接在yaml文件中配置它。
步骤4 为新模型创建单元测试
最后,建议在test/espnet2/enh/test_espnet_model.py和test/espnet2/enh/encoder / test/espnet2/enh/decoder / test/espnet2/enh/separator下为你的新模型编写一些单元测试。
