文本转语音
文本转语音
这是ESPnet2的TTS(文本转语音)配方模板。
目录
- 文本转语音
- 目录
- 配方流程
- 如何运行
- 支持的文本前端
- 支持的文本清理工具
- 支持的模型
- 常见问题
- ESPnet1 模型与 ESPnet2 兼容吗?
- 如何在训练中调整小批量(minibatch)大小?
- 如何为我的数据集创建新配方?
- 如何添加一个新的
g2p模块? - 如何添加一个新的
cleaner模块? - 如何在Python中使用训练好的模型?
- 如何获取预训练模型?
- 如何加载预训练参数?
- 如何微调预训练模型?
- 如何添加新模型?
- 如何使用任意给定文本测试我的模型?
- 如何训练声码器?
- 如何使用text2mel GTA输出训练声码器?
- 如何处理
validate_data_dir.sh中的错误? - 为什么模型在最后生成了无意义的语音?
- 为什么使用我自己的数据集时模型无法训练好?
- 为什么结合神经声码器时输出会含有金属噪音?
- FastSpeech2的时长是如何生成的?
- 为什么Tacotron2或Transformer的输出是非确定性的?
配方流程
TTS配方包含9个阶段。
- 数据准备
数据准备阶段。您有两种方法可以生成数据:
ESPnet格式:
它会调用local/data.sh在data/目录下创建Kaldi风格的数据目录,用于训练集、验证集和评估集。
另请参阅:
(新) MFA对齐生成
你可以使用Montreal-Forced-Aligner工具生成对齐数据。使用脚本scripts/mfa.sh来生成所需的mfa对齐数据,并训练一个使用这些对齐数据的模型。
由于脚本scripts/mfa.sh已准备好数据,因此无需事先执行local/data.sh。不过,您需要设置一些额外的标志,例如--split_sets、--samplerate或--acoustic_model:
./scripts/mfa.sh --split_sets "train_set dev_set test_set" \
--stage 1 \
--stop-stage 2 \
--train true --nj 36 --g2p_model espeak_ng_english_vits你可以在egs2/ljspeech/tts1/local/run_mfa.sh中找到参考。
脚本scripts/mfa.sh将使用给定的g2p_model和acoustic_model生成对齐数据,并将其存储在<split_sets>_phn目录中。该脚本会下载预训练模型(当--train false时)或训练mfa的g2p和声学模型(当--train true时),然后生成对齐数据。
然后,您可以在主脚本上继续训练:
./run.sh --train-set train_set_phn \
--dev-set dev_set_phn \
--test_sets "dev_set_phn test_set_phn" \
--stage 2 \
--g2p none \
--cleaner none \
--teacher_dumpdir "data"- 音频转储/嵌入准备
音频转储阶段。此阶段会重新格式化数据目录中的wav.scp文件。
此外,我们在此阶段支持说话人嵌入提取,您可以在ESPnet1中使用该功能。如果指定--use_spk_embed true(默认值:use_spk_embed=false),我们将提取说话人嵌入。您可以通过指定--spk_embed_tool <option>(默认值:spk_embed_tool=espnet)来选择使用的工具类型(kaldi、speechbrain或espnet)。如果选择kaldi,我们还会额外提取MFCC特征和VAD决策。在这种情况下,spk_embed_tag将自动设置为xvector。请注意,此处理需要已编译的kaldi。
此外,如果您指定了--use_sid true和--use_lid true选项,本阶段还将执行说话人ID嵌入和语言ID嵌入的准备工作。请注意,该处理假设utt2spk或utt2lang已在阶段1正确创建,请务必注意。
- 提取说话人嵌入特征
提取说话人嵌入特征。
- 移除长/短数据
处理阶段,用于从训练和验证数据中移除过长或过短的语音片段。您可以通过--min_wav_duration和--max_wav_duration参数调整阈值。
- 令牌列表生成
词元列表生成阶段。该阶段从srctexts生成词元列表(字典)。您可以通过--token_type选项更改分词类型。支持token_type=char和token_type=phn两种类型。若指定--cleaner选项,输入文本将通过指定的清理器进行净化处理。当选择token_type=phn时,输入文本将通过--g2p选项指定的G2P模块进行转换。
另请参阅:
- 文本转语音(TTS)统计收集
统计计算阶段。该阶段收集输入和输出的形状信息,并计算用于特征归一化的统计量(训练数据的均值和方差)。
- 文本转语音(TTS)训练
TTS模型训练阶段。您可以通过--train_config和--train_args选项更改训练设置。
另请参阅:
- 文本转语音解码
TTS模型解码阶段。您可以通过--inference_config和--inference_args修改解码设置。
另请参阅:
- 使用versa进行TTS评估
使用versa进行TTS模型评估阶段。
默认指标如下,位于conf/versa.yaml文件中:
| 1 | 语音质量感知评估 (PESQ) | pesq | pesq | | 2 | 短时客观可懂度 (STOI) | stoi | stoi | | 3 | 梅尔倒谱失真 (MCD) | mcd_f0 | mcd | | 4 | F0相关性 | mcd_f0 | f0_corr | | 5 | F0均方根误差 | mcd_f0 | f0_rmse | | 6 | 东京大学猿实验室2022年语音MOS挑战系统 (UTMOS) | pseudo_mos | utmos | | 7 | P.835深度噪声抑制MOS评分 (DNSMOS) | pseudo_mos | dnsmos_overall | | 8 | P.808深度噪声抑制MOS评分 (DNSMOS) | pseudo_mos | dnsmos_p808 | | 9 | 丢包隐藏相关MOS评分 (PLCMOS) | pseudo_mos | plcmos | | 10 | 说话人嵌入相似度 | speaker | spk_similarity |
您可以在VERSA README中找到更多详细信息和更多指标。
- (可选) 打包结果以上传
打包阶段。将训练好的模型文件打包,为上传至Hugging Face做准备。
- (可选) 将模型上传至Hugging Face
将训练好的模型上传至Hugging Face以便分享。更多信息请参阅Docs。
如何运行
这里,我们展示使用egs2/ljspeech/tts1运行该配方的流程。
进入配方目录。
$ cd egs2/ljspeech/tts1如果想更改下载目录,请修改db.sh中的LJSPEECH变量。
$ vim db.sh如需使用作业调度器,请修改cmd.sh和conf/*.conf文件。详情请参阅using job scheduling system。
$ vim cmd.sh运行run.sh脚本,该脚本会执行上述所有阶段的操作。
$ ./run.sh默认情况下,我们使用conf/train.yaml配置训练Tacotron2,其中feats_type=raw + token_type=phn。
然后,您可以在配方目录中看到以下目录。
├── data/ # Kaldi-style data directory
│ ├── dev/ # validation set
│ ├── eval1/ # evaluation set
│ └── tr_no_dev/ # training set
├── dump/ # feature dump directory
│ ├── token_list/ # token list (dictionary)
│ └── raw/
│ ├── org/
│ │ ├── tr_no_dev/ # training set before filtering
│ │ └── dev/ # validation set before filtering
│ ├── srctexts # text to create token list
│ ├── eval1/ # evaluation set
│ ├── dev/ # validation set after filtering
│ └── tr_no_dev/ # training set after filtering
└── exp/ # experiment directory
├── tts_stats_raw_phn_tacotron_g2p_en_no_space # statistics
└── tts_train_raw_phn_tacotron_g2p_en_no_space # model
├── att_ws/ # attention plot during training
├── tensorboard/ # tensorboard log
├── images/ # plot of training curves
├── decode_train.loss.ave/ # decoded results
│ ├── dev/ # validation set
│ └── eval1/ # evaluation set
│ ├── att_ws/ # attention plot in decoding
│ ├── probs/ # stop probability plot in decoding
│ ├── norm/ # generated features
│ ├── denorm/ # generated denormalized features
│ ├── wav/ # generated wav via Griffin-Lim
│ ├── log/ # log directory
│ ├── durations # duration of each input tokens
│ ├── feats_type # feature type
│ ├── focus_rates # focus rate
│ └── speech_shape # shape info of generated features
├── config.yaml # config used for the training
├── train.log # training log
├── *epoch.pth # model parameter file
├── checkpoint.pth # model + optimizer + scheduler parameter file
├── latest.pth # symlink to latest model parameter
├── *.ave_5best.pth # model averaged parameters
└── *.best.pth # symlink to the best model parameter loss在解码阶段,默认使用Griffin-Lim算法进行波形生成(端到端文本到波形模型如VITS和联合训练模型可直接生成波形)。如需结合神经声码器,可与kan-bayashi/ParallelWaveGAN配合使用。
# Make sure you already install parallel_wavegan repo
$ . ./path.sh && pip install -U parallel_wavegan
# Use parallel_wavegan provided pretrained ljspeech style melgan as a vocoder
$ ./run.sh --stage 8 --inference_args "--vocoder_tag parallel_wavegan/ljspeech_style_melgan.v1" --inference_tag decode_with_ljspeech_style_melgan.v1
# Use the vocoder trained by `parallel_wavegan` repo manually
$ ./run.sh --stage 8 --vocoder_file /path/to/checkpoint-xxxxxxsteps.pkl --inference_tag decode_with_my_vocoder如果想从转储的特征生成波形,请查看使用ESPnet-TTS模型特征进行解码。
首次使用时,建议通过--stage和--stop-stage选项逐步执行每个阶段。
$ ./run.sh --stage 1 --stop-stage 1
$ ./run.sh --stage 2 --stop-stage 2
...
$ ./run.sh --stage 8 --stop-stage 8这可能有助于您理解每个阶段的处理流程和目录结构。
FastSpeech 训练
如需训练FastSpeech,需要额外执行教师模型的相关步骤。请确保您已完成教师模型(Tacotron2或Transformer-TTS)的训练。
首先,解码所有数据,包括训练集、验证集和评估集。
# specify teacher model directory via --tts_exp option
$ ./run.sh --stage 8 \
--tts_exp exp/tts_train_raw_phn_tacotron_g2p_en_no_space \
--test_sets "tr_no_dev dev eval1"这将为训练集、验证集和评估集在exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_train.loss.ave中生成durations。
然后,您可以通过--teacher_dumpdir选项指定包含durations的目录来训练FastSpeech。
$ ./run.sh --stage 7 \
--train_config conf/tuning/train_fastspeech.yaml \
--teacher_dumpdir exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_train.loss.ave在上述示例中,我们使用生成的梅尔频谱图作为目标,这被称为知识蒸馏训练。如果您想使用真实梅尔频谱图作为目标,则需要在解码过程中使用教师强制方法。
$ ./run.sh --stage 8 \
--tts_exp exp/tts_train_raw_phn_tacotron_g2p_en_no_space \
--inference_args "--use_teacher_forcing true" \
--test_sets "tr_no_dev dev eval1"您可以在exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_use_teacher_forcingtrue_train.loss.ave中获取真实对齐的持续时间。
这样,你就可以无需知识蒸馏训练FastSpeech。
$ ./run.sh --stage 7 \
--train_config conf/tuning/train_fastspeech.yaml \
--teacher_dumpdir exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_use_teacher_forcingtrue_train.loss.aveFastSpeech2 训练
该流程与FastSpeech几乎相同,但我们在解码时必须使用教师强制机制。
$ ./run.sh --stage 8 \
--tts_exp exp/tts_train_raw_phn_tacotron_g2p_en_no_space \
--inference_args "--use_teacher_forcing true" \
--test_sets "tr_no_dev dev eval1"要训练FastSpeech2,我们需要使用额外的特征(F0和能量)。因此,我们需要从stage 5开始计算额外的统计信息。
$ ./run.sh --stage 6 \
--train_config conf/tuning/train_fastspeech2.yaml \
--teacher_dumpdir exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_use_teacher_forcingtrue_train.loss.ave \
--tts_stats_dir exp/tts_train_raw_phn_tacotron_g2p_en_no_space/decode_use_teacher_forcingtrue_train.loss.ave/stats \
--write_collected_feats true其中--tts_stats_dir是用于指定统计信息输出目录的选项,--write_collected_feats是用于在统计计算时输出特征的选项。使用--write_collected_feats是可选的,但它有助于加速训练过程。
带说话人嵌入训练的多说话人模型
首先,你需要从第2和第3阶段运行,使用--use_spk_embed true来提取说话人嵌入特征。
$ ./run.sh --stage 3 --stop-stage 4 --use_spk_embed true您可以在dump/${spk_embed_tag}/*/${spk_embed_tag}.{ark,scp}中找到提取的说话人嵌入向量。然后,您可以使用在tts_conf中包含spk_embed_dim: 512配置的配置文件来运行训练。
# e.g.
tts_conf:
spk_embed_dim: 512 # dimension of speaker embedding
spk_embed_integration_type: add # how to integrate speaker embedding(可选) 基于说话人平均声纹嵌入进行训练
使用说话人平均说话人嵌入训练的模型,相比于使用从单个训练语句中提取的嵌入训练的模型,在说话人嵌入未知的推理任务中可能具有更好的泛化能力。完成上述提取步骤后,若想使用说话人平均说话人嵌入进行训练和评估,可通过以下命令将语句级说话人嵌入替换为说话人平均值。请确保事先设置好train_set、dev_set和test_set变量:
for dset in "${train_set}" "${dev_set}" "${test_set}"
do
./pyscripts/utils/convert_to_avg_spk_embed.py \
--utt-embed-path dump/${spk_embed_tag}/${dset}/${spk_embed_tag}.scp \
--utt2spk data/${dset}/utt2spk \
--spk-embed-path dump/${spk_embed_tag}/${dset}/spk_${spk_embed_tag}.scp
done原始的${spk_embed_tag}.scp文件被重命名为xvector.scp.bak,以便您需要时可以还原更改。
完成提取步骤后,如果执行了说话人平均替换步骤,请从第6阶段开始运行训练。
$ ./run.sh --stage 7 --use_xvector true --train_config /path/to/your_xvector_config.yaml你可以在egs2/vctk/tts1/conf/tuning中找到示例配置文件。
带说话人ID嵌入训练的多说话人模型
首先,你需要从第2和第3阶段运行,使用--use_sid true来提取说话人ID。
$ ./run.sh --stage 3 --stop-stage 4 --use_sid true您可以在dump/raw/*/utt2sid中找到说话人ID文件。请注意,您需要在数据准备阶段正确创建utt2spk才能生成utt2sid。然后,您可以使用在tts_conf中包含spks: #spks的配置来运行训练。
# e.g.
tts_conf:
spks: 128 # Number of speakers请从第6阶段开始运行训练。
$ ./run.sh --stage 7 --use_sid true --train_config /path/to/your_multi_spk_config.yaml带语言ID嵌入训练的多语言模型
首先,你需要从第2和第3阶段运行,使用--use_lid true来提取说话人ID。
$ ./run.sh --stage 3 --stop-stage 4 --use_lid true你可以在dump/raw/*/utt2lid中找到说话人ID文件。注意:在数据准备阶段,你需要额外创建utt2lang文件来生成utt2lid。然后,你可以使用配置文件中tts_conf包含langs: #langs的配置来运行训练。
# e.g.
tts_conf:
langs: 4 # Number of languages请从第6阶段开始运行训练。
$ ./run.sh --stage 7 --use_lid true --train_config /path/to/your_multi_lang_config.yaml当然,您还可以进一步结合x-vector或说话人ID嵌入。如果想同时使用说话人ID(sid)和语言ID(lid),处理流程应如下所示:
$ ./run.sh --stage 3 --stop-stage 4 --use_lid true --use_sid true配置你的设置。
# e.g.
tts_conf:
langs: 4 # Number of languages
spks: 128 # Number of speakers请从第6阶段开始运行训练。
$ ./run.sh --stage 7 --use_lid true --use_sid true --train_config /path/to/your_multi_spk_multi_lang_config.yamlVITS训练
首先,VITS配置硬编码为22.05 kHz或44.1 kHz,并使用不同的特征提取方法。(请注意,您可以使用任何特征提取方法,但默认方法是linear_spectrogram。)如果想在24 kHz或16 kHz数据集上使用,请特别注意这些要点。
# Assume that data prep stage (stage 1) is finished
$ ./run.sh --stage 1 --stop-stage 1
# Single speaker 22.05 khz case
$ ./run.sh \
--stage 2 \
--ngpu 4 \
--fs 22050 \
--n_fft 1024 \
--n_shift 256 \
--win_length null \
--dumpdir dump/22k \
--expdir exp/22k \
--tts_task gan_tts \
--feats_extract linear_spectrogram \
--feats_normalize none \
--train_config ./conf/tuning/train_vits.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth
# Single speaker 44.1 khz case
$ ./run.sh \
--stage 2 \
--ngpu 4 \
--fs 44100 \
--n_fft 2048 \
--n_shift 512 \
--win_length null \
--dumpdir dump/44k \
--expdir exp/44k \
--tts_task gan_tts \
--feats_extract linear_spectrogram \
--feats_normalize none \
--train_config ./conf/tuning/train_full_band_vits.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth
# Multi speaker with SID 22.05 khz case
$ ./run.sh \
--stage 2 \
--use_sid true \
--ngpu 4 \
--fs 22050 \
--n_fft 1024 \
--n_shift 256 \
--win_length null \
--dumpdir dump/22k \
--expdir exp/22k \
--tts_task gan_tts \
--feats_extract linear_spectrogram \
--feats_normalize none \
--train_config ./conf/tuning/train_multi_spk_vits.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth
# Multi speaker with SID 44.1 khz case
$ ./run.sh \
--stage 2 \
--use_sid true \
--ngpu 4 \
--fs 44100 \
--n_fft 2048 \
--n_shift 512 \
--win_length null \
--dumpdir dump/44k \
--expdir exp/44k \
--tts_task gan_tts \
--feats_extract linear_spectrogram \
--feats_normalize none \
--train_config ./conf/tuning/train_full_band_multi_spk_vits.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth
# Multi speaker with speaker embedding 22.05 khz case (need compiled kaldi to run if use Kaldi toolkit)
$ ./run.sh \
--stage 2 \
--use_spk_embed true \
--ngpu 4 \
--fs 22050 \
--n_fft 1024 \
--n_shift 256 \
--win_length null \
--dumpdir dump/22k \
--expdir exp/22k \
--tts_task gan_tts \
--feats_extract linear_spectrogram \
--feats_normalize none \
--train_config ./conf/tuning/train_xvector_vits.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth训练时间需要较长时间(大约几周),但大约10万个样本可以生成合理的声音。
你可以在以下位置找到示例配置文件:
egs2/ljspeech/tts1/conf/tuning/train_vits.yaml: 单说话人22.05千赫兹配置.egs2/jsut/tts1/conf/tuning/train_full_band_vits.yaml: 单说话人44.1千赫兹配置.egs2/vctk/tts1/conf/tuning/train_multi_spk_vits.yaml: 支持说话人ID(SID)的多说话人22.05 kHz配置文件.egs2/vctk/tts1/conf/tuning/train_full_band_multi_spk_vits.yaml: 带说话人ID的44.1kHz多说话人配置.egs2/libritts/tts1/conf/tuning/train_xvector_vits.yaml: 使用X-vector的22.05千赫兹多说话人配置.
在VITS和JETS训练过程中,您可以监控由MOS预测模型生成的伪MOS值。您可以通过在训练配置中设置tts_conf.plot_pred_mos: true来启用此功能。请参考egs2/ljspeech/tts1/conf/tuning/train_vits.yaml查看如何设置该标志。
联合文本到语音训练
联合训练使我们能够通过基于GAN的训练同时训练text2mel和声码器模型。目前,我们仅在非自回归text2mel模型上使用ljspeech数据集进行了测试,但以下模型和声码器已获得支持。
文本转语音
- Tacotron2
- Transformer
- FastSpeech
- FastSpeech2
声码器
- ParallelWaveGAN 生成器/判别器
- (多频段) MelGAN G / D
- HiFiGAN 生成器/判别器
- StyleMelGAN 生成器/判别器
这里,我们展示使用两种训练策略(从头开始训练以及预训练文本转梅尔和声码器的微调)联合训练conformer fastspeech2 + hifigan的示例流程。
# Make sure you are ready to train fastspeech2 (already prepared durations file with teacher model)
$ ...
# Case 1: Train conformer fastspeech2 + hifigan G + hifigan D from scratch
$ ./run.sh \
--stage 7 \
--tts_task gan_tts \
--train_config ./conf/tuning/train_joint_conformer_fastspeech2_hifigan.yaml
# Case 2: Fine-tuning of pretrained conformer fastspeech2 + hifigan G + hifigan D
# (a) Prepare pretrained models as follows
$ tree -L 2 exp
exp
...
├── ljspeech_hifigan.v1 # pretrained vocoder
│ ├── checkpoint-2500000steps.pkl
│ ├── config.yml
│ └── stats.h5
├── tts_train_conformer_fastspeech2_raw_phn_tacotron_g2p_en_no_space # pretrained text2mel
│ ├── config.yaml
│ ├── images
│ └── train.loss.ave_5best.pth
...
# If you want to use the same files of this example
$ ipython
# Download text2mel model
[ins] In [1]: from espnet_model_zoo.downloader import ModelDownloader
[ins] In [2]: d = ModelDownloader("./downloads")
[ins] In [3]: d.download_and_unpack("kan-bayashi/ljspeech_conformer_fastspeech2")
# Download vocoder
[ins] In [4]: from parallel_wavegan.utils import download_pretrained_model
[ins] In [5]: download_pretrained_model("ljspeech_hifigan.v1", "downloads")
# Move them to exp directory
$ mv download/59c43ac0d40b121060bd71dd418f5ece/exp/tts_train_conformer_fastspeech2_raw_phn_tacotron_g2p_en_no_space exp
$ mv downloads/ljspeech_hifigan.v1 exp
# (b) Convert .pkl checkpoint to espnet loadable format
$ ipython
[ins] In [1]: import torch
[ins] In [2]: d = torch.load("./exp/ljspeech_hifigan.v1/checkpoint-2500000steps.pkl")
[ins] In [3]: torch.save(d["model"]["generator"], "generator.pth")
[ins] In [4]: torch.save(d["model"]["discriminator"], "discriminator.pth")
# (c) Prepare configuration
$ vim conf/tuning/finetune_joint_conformer_fastspeech2_hifigan.yaml
# edit text2mel_params / generator_params / discriminator_params to be the same as the pretrained model
# edit init_param part to specify the correct path of the pretrained model
# (d) Run training
$ ./run.sh \
--stage 7 \
--tts_task gan_tts \
--train_config ./conf/tuning/finetune_joint_conformer_fastspeech2_hifigan.yaml你可以在以下位置找到示例配置文件:
egs2/ljspeech/tts1/conf/tuning/train_joint_conformer_fastspeech2_hifigan.yaml: Conformer FastSpeech2与HiFi-GAN的联合训练.egs2/ljspeech/tts1/conf/tuning/finetune_joint_conformer_fastspeech2_hifigan.yaml: Conformer FastSpeech2与HiFi-GAN联合微调配置.
评估
我们提供五种客观评估指标:
- 梅尔倒谱失真(MCD)
- 对数基频均方根误差 (log-F0 RMSE)
- 字符错误率 (CER)
- 条件弗雷谢语音距离 (CFSD)
- 说话人嵌入余弦相似度(SECS)
- 离散语音指标
MCD和log-F0 RMSE反映了说话人、韵律和语音内容的相似性,而CER可以反映可懂度。对于MCD和log-F0 RMSE,我们应用动态时间规整(DTW)来匹配真实语音和生成语音之间的长度差异。离散语音指标比MCD更能与人类主观判断相关联。
这里我们展示计算客观指标的示例命令:
cd egs2/<recipe_name>/tts1
. ./path.sh
# Evaluate MCD
./pyscripts/utils/evaluate_mcd.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate log-F0 RMSE
./pyscripts/utils/evaluate_f0.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# If you want to calculate more precisely, limit the F0 range
./pyscripts/utils/evaluate_f0.py \
--f0min xxx \
--f0max yyy \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate with automatic MOS prediction models.
./pyscripts/utils/evaluate_pseudomos.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate CER
./scripts/utils/evaluate_asr.sh \
--model_tag <asr_model_tag> \
--nj 1 \
--inference_args "--beam_size 10 --ctc_weight 0.4 --lm_weight 0.0" \
--gt_text "dump/raw/eval1/text" \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
exp/<model_dir_name>/<decode_dir_name>/asr_results
# You can also use openai whisper for evaluation
./scripts/utils/evaluate_asr.sh \
--whisper_tag base \
--nj 1 \
--gt_text "dump/raw/eval1/text" \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
exp/<model_dir_name>/<decode_dir_name>/asr_results
# Since ASR model does not use punctuation, it is better to remove punctuations if it contains
./scripts/utils/remove_punctuation.pl < dump/raw/eval1/text > dump/raw/eval1/text.no_punc
./scripts/utils/evaluate_asr.sh \
--model_tag <asr_model_tag> \
--nj 1 \
--inference_args "--beam_size 10 --ctc_weight 0.4 --lm_weight 0.0" \
--gt_text "dump/raw/eval1/text.no_punc" \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
exp/<model_dir_name>/<decode_dir_name>/asr_results
# Some ASR models assume the existence of silence at the beginning and the end of audio
# Then, you can perform silence padding with sox to get more reasonable ASR results
awk < "exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp" \
'{print $1 " sox " $2 " -t wav - pad 0.25 0.25 |"}' \
> exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav_pad.scp
./scripts/utils/evaluate_asr.sh \
--model_tag <asr_model_tag> \
--nj 1 \
--inference_args "--beam_size 10 --ctc_weight 0.4 --lm_weight 0.0" \
--gt_text "dump/raw/eval1/text.no_punc" \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav_pad.scp \
exp/<model_dir_name>/<decode_dir_name>/asr_results
# Evaluate CFSD
./pyscripts/utils/evaluate_cfsd.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate SECS
./pyscripts/utils/evaluate_secs.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate SpeechBERTScore
./pyscripts/utils/evaluate_speechbertscore.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp
# Evaluate SpeechBLEU
./pyscripts/utils/evaluate_speechbleu.py \
exp/<model_dir_name>/<decode_dir_name>/eval1/wav/wav.scp \
dump/raw/eval1/wav.scp虽然这些客观指标可以评估合成语音的质量,但仅凭这些数值仍难以全面判断人类的感知质量,尤其是对于高保真生成的语音。因此,如果您想详细检查感知质量,我们建议进行主观评估。
您可以参考此页面使用webMUSHRA启动基于网络的主观评价系统。
支持的文本前端
你可以通过tts.sh中的--g2p选项来更改。
none: Just separate by space- 例如:
HH AH0 L OW1 <space> W ER1 L D->[HH, AH0, L, OW1, <space>, W, ER1, L D]
- 例如:
g2p_en: Kyubyong/g2p- 例如
Hello World->[HH, AH0, L, OW1,, W, ER1, L D]
- 例如
g2p_en_no_space: Kyubyong/g2p- 相同的G2P但不使用单词分隔符
- 例如
Hello World->[HH, AH0, L, OW1, W, ER1, L, D]
pyopenjtalk: r9y9/pyopenjtalk- 例如
こ、こんにちは->[k, o, pau, k, o, N, n, i, ch, i, w, a]
- 例如
pyopenjtalk_kana: r9y9/pyopenjtalk- 使用假名而非音素
- 例如
こ、こんにちは->[コ, 、, コ, ン, ニ, チ, ワ]
pyopenjtalk_accent: r9y9/pyopenjtalk- 除了音素标签外,还添加重音标签
- 基于开发考虑重音短语的日语端到端语音合成服务器
- 例如
こ、こんにちは->[k, 1, 0, o, 1, 0, k, 5, -4, o, 5, -4, N, 5, -3, n, 5, -2, i, 5, -2, ch, 5, -1, i, 5, -1, w, 5, 0, a, 5, 0]
pyopenjtalk_accent_with_pause: r9y9/pyopenjtalk- 在音素和重音标签之外添加暂停标签
- 基于开发考虑重音短语的日语端到端语音合成服务器
- 例如
こ、こんにちは->[k, 1, 0, o, 1, 0, pau, k, 5, -4, o, 5, -4, N, 5, -3, n, 5, -2, i, 5, -2, ch, 5, -1, i, 5, -1, w, 5, 0, a, 5, 0]
pyopenjtalk_prosody: r9y9/pyopenjtalk- 使用特殊符号进行韵律控制
- 基于通过符号作为序列到序列声学建模的输入来控制韵律特征,用于神经TTS
- 例如
こ、こんにちは->[^, k, #, o, _, k, o, [, N, n, i, ch, i, w, a, $]
pypinyin: mozillanzg/python-pinyin- e.g.
卡尔普陪外孙玩滑梯。->[ka3, er3, pu3, pei2, wai4, sun1, wan2, hua2, ti1, 。]
- e.g.
pypinyin_phone: mozillanzg/python-pinyin- 分割为前部和后部
- e.g.
卡尔普陪外孙玩滑梯。->[k, a3, er3, p, u3, p, ei2, wai4, s, un1, uan2, h, ua2, t, i1, 。]
espeak_ng_arabic: espeak-ng/espeak-ng- 例如
السلام عليكم->[ʔ, a, s, s, ˈa, l, aː, m, ʕ, l, ˈiː, k, m] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_german: espeak-ng/espeak-ng- 例如
Das hört sich gut an.->[d, a, s, h, ˈœ, ɾ, t, z, ɪ, ç, ɡ, ˈuː, t, ˈa, n, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_french: espeak-ng/espeak-ng- 例如
Bonjour le monde.->[b, ɔ̃, ʒ, ˈu, ʁ, l, ə-, m, ˈɔ̃, d, .] - 该结果由封装库 bootphon/phonemizer 提供
- 例如
espeak_ng_spanish: espeak-ng/espeak-ng- 例如
Hola Mundo.->[ˈo, l, a, m, ˈu, n, d, o, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_russian: espeak-ng/espeak-ng- 例如
Привет мир.->[p, rʲ, i, vʲ, ˈe, t, mʲ, ˈi, r, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_greek: espeak-ng/espeak-ng- 例如
Γειά σου Κόσμε.->[j, ˈa, s, u, k, ˈo, s, m, e, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_finnish: espeak-ng/espeak-ng- 例如
Hei maailma.->[h, ˈei, m, ˈaː, ɪ, l, m, a, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_hungarian: espeak-ng/espeak-ng- 例如
Helló Világ.->[h, ˈɛ, l, l, oː, v, ˈi, l, aː, ɡ, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_dutch: espeak-ng/espeak-ng- 例如
Hallo Wereld.->[h, ˈɑ, l, oː, ʋ, ˈɪː, r, ə, l, t, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_hindi: espeak-ng/espeak-ng- 例如
नमस्ते दुनिया->[n, ə, m, ˈʌ, s, t, eː, d, ˈʊ, n, ɪ, j, ˌaː] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_italian: espeak-ng/espeak-ng- 例如
Ciao mondo.->[tʃ, ˈa, o, m, ˈo, n, d, o, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_polish: espeak-ng/espeak-ng- 例如
Witaj świecie.->[v, ˈi, t, a, j, ɕ, fʲ, ˈɛ, tɕ, ɛ, .] - 该结果由封装库bootphon/phonemizer提供
- 例如
espeak_ng_english_us_vits: espeak-ng/espeak-ng- 类似VITS官方实现的处理方式 (https://github.com/jaywalnut310/vits)
- 例如
Hello World.->[h, ə, l, ˈ, o, ʊ, , <space>, w, ˈ, ɜ, ː, l, d, .] - 该结果由封装库bootphon/phonemizer提供
g2pk: Kyubyong/g2pK- 例如
안녕하세요 세계입니다.->[ᄋ, ᅡ, ᆫ, ᄂ, ᅧ, �, ᄒ, ᅡ, ᄊ, ᅦ, ᄋ, ᅭ, , ᄊ, ᅦ, ᄀ, ᅨ, ᄋ, ᅵ, �, ᄂ, ᅵ, ᄃ, ᅡ, .]
- 例如
g2pk_no_space: Kyubyong/g2pK- 相同的G2P但不使用单词分隔符
- 例如
안녕하세요 세계입니다.->[ᄋ, ᅡ, ᆫ, ᄂ, ᅧ, ᆼ, ᄒ, ᅡ, ᄉ, ᅦ, ᄋ, ᅭ, ᄰ, ᅦ, ᄀ, ᅨ, ᄋ, ᅵ, ᆷ, ᄂ, ᅵ, ᄃ, ᅡ, .]
g2pk_explicit_space: Kyubyong/g2pK- 相同的G2P但使用显式词分隔符
- 例如
안녕하세요 세계입니다.->[ᄋ, ᅡ, �, ᄂ, ᅧ, �, ᄒ, ᅡ, ᄉ, ᅦ, ᄋ, ᅭ, <space>, ᄰ, ᅦ, ᄀ, ᅨ, ᄋ, ᅵ, ᆷ, ᄂ, ᅵ, ᄃ, ᅡ, .]
korean_jaso: jdongian/python-jamo- 例如
나는 학교에 갑니다.->[ᄂ, ᅡ, ᄂ, ᅳ, ᆫ, <space>, ᄒ, ᅡ, ᆨ, ᄀ, ᅭ, ᄋ, ᅦ, <space>, ᄀ, ᅡ, ᆸ, ᄂ, ᅵ, ᄃ, ᅡ, .]
- 例如
korean_jaso_no_space: jdongian/python-jamo- 例如
나는 학교에 갑니다.->[ᄂ, ᅡ, ᄂ, ᅳ, ᆫ, ᄒ, ᅡ, ᆨ, ᄀ, ᅭ, ᄋ, ᅦ, ᄀ, ᅡ, ᆸ, ᄂ, ᅵ, ᄃ, ᅡ, .]
- 例如
你可以从这里查看代码示例。
支持的文本清理器
你可以通过tts.sh中的--cleaner选项来更改。
none: 无文本清理器。tacotron: keithito/tacotron- 例如
"(Hello-World); & jr. & dr."->HELLO WORLD, AND JUNIOR AND DOCTOR
- 例如
jaconv: kazuhikoarase/jaconv- e.g.
”あらゆる” 現実を 〜 ’すべて’ 自分の ほうへ ねじ曲げたのだ。"->"あらゆる" 現実を ー \'すべて\' 自分の ほうへ ねじ曲げたのだ。
- e.g.
你可以查看这里的代码示例。
支持的模型
您可以通过修改tts.sh中--train_config选项的*.yaml配置文件来训练以下模型。
单说话人模型
- Tacotron 2
- Transformer-TTS
- FastSpeech
- FastSpeech2 (FastPitch)
- Conformer-基础的FastSpeech / FastSpeech2
- VITS
- JETS
你可以在egs2/ljspeech/tts1/conf/tuning中找到上述模型的示例配置文件。
多说话人模型扩展
您可以使用/组合以下嵌入来构建多说话人模型:
X-Vector由kaldi提供,并使用VoxCeleb语料库进行预训练。您可以在以下位置找到上述模型的示例配置:
现在我们支持其他工具包的说话人嵌入特征:请查看以下选项。
https://github.com/espnet/espnet/blob/df053b8c13c26fe289fc882751801fd781e9d43e/egs2/TEMPLATE/tts1/tts.sh#L69-L71
常见问题
ESPnet1模型与ESPnet2兼容吗?
不,我们无法在ESPnet2中使用ESPnet1模型。
如何在训练中更改小批量(minibatch)大小?
参见更改小批量类型。默认情况下,我们使用batch_type=numel和batch_bins而非batch_size来实现动态批量大小功能。以下配置示例可供参考:https://github.com/espnet/espnet/blob/96b2fd08d4fd9276aabd7ad41ec5e02a88b30958/egs2/ljspeech/tts1/conf/tuning/train_tacotron2.yaml#L61-L62
如何为我的数据集创建新的配方?
参见如何制作/移植新配方。
如何添加一个新的g2p模块?
在espnet2/text/phoneme_tokenizer.py中添加一个新模块,并将其添加到espnet2/text/phoneme_tokenizer.py中的g2p_choices里。
我们提供了bootphon/phonemizer的封装模块。您可以在espnet2/text/phoneme_tokenizer.py中找到该模块。如果您需要的g2p功能已在bootphon/phonemizer中实现,我们可以像这样轻松添加它(请注意,您需要按照我上面提到的方式更新选项)。
以下示例PR可能对您有所帮助:
如何添加一个新的cleaner模块?
更新espnet2/text/cleaner.py以添加新模块。然后在espnet2/bin/tokenize_text.py和espnet2/tasks/tts.py的参数解析器中添加新的选项。
如何在Python中使用训练好的模型?
from espnet2.bin.tts_inference import Text2Speech
# without vocoder
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth")
wav = tts("Hello, world")["wav"]
# with local vocoder
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth", vocoder_file="/path/to/vocoder.pkl")
wav = tts("Hello, world")["wav"]
# with pretrained vocoder (use ljseepch style melgan as an example)
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth", vocoder_tag="parallel_wavegan/ljspeech_style_melgan.v1")
wav = tts("Hello, world")["wav"]参见使用预训练模型进行推理。
如何获取预训练模型?
使用ESPnet模型库。您可以从这里找到所有预训练模型列表,或在Hugging Face上搜索预训练模型。
如果想使用egs2/hogehoge/tts1/README.md中预训练的模型,请访问Zenodo网址并复制页面底部的下载链接。然后,您可以按如下方式使用:
from espnet2.bin.tts_inference import Text2Speech
# provide copied URL directly
tts = Text2Speech.from_pretrained(
"https://zenodo.org/record/5414980/files/tts_train_vits_raw_phn_jaconv_pyopenjtalk_accent_with_pause_train.total_count.ave.zip?download=1",
)
wav = tts("こんにちは、世界。")["wav"]如何加载预训练参数?
请使用--init_param选项或在训练配置(*.yaml)中添加该参数。
# Usage
--init_param <file_path>:<src_key>:<dst_key>:<exclude_keys>
# Load all parameters
python -m espnet2.bin.tts_train --init_param model.pth
# Load only the parameters starting with "decoder"
python -m espnet2.bin.tts_train --init_param model.pth:tts.dec
# Load only the parameters starting with "decoder" and set it to model.tts.dec
python -m espnet2.bin.tts_train --init_param model.pth:decoder:tts.dec
# Set parameters to model.tts.dec
python -m espnet2.bin.tts_train --init_param decoder.pth::tts.dec
# Load all parameters excluding "tts.enc.embed"
python -m espnet2.bin.tts_train --init_param model.pth:::tts.enc.embed
# Load all parameters excluding "tts.enc.embed" and "tts.dec"
python -m espnet2.bin.tts_train --init_param model.pth:::tts.enc.embed,tts.dec如何微调预训练模型?
如何添加新模型?
正在建设中。
如何使用任意给定文本测试我的模型?
如果你想在本地尝试:
from espnet2.bin.tts_inference import Text2Speech
# with local model
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth")
wav = tts("Hello, world")["wav"]
# with local model and local vocoder
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth", vocoder_file="/path/to/vocoder.pkl")
wav = tts("Hello, world")["wav"]
# with local model and pretrained vocoder (use ljseepch as an example)
tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth", vocoder_tag="parallel_wavegan/ljspeech_style_melgan.v1")
wav = tts("Hello, world")["wav"]
# with pretrained model and pretrained vocoder (use ljseepch as an example)
tts = Text2Speech.from_pretrained(model_tag="kan-bayashi/ljspeech_conformer_fastspeech2", vocoder_tag="parallel_wavegan/ljspeech_style_melgan.v1")
wav = tts("Hello, world")["wav"]如何训练声码器?
请使用kan-bayashi/ParallelWaveGAN,该工具提供了训练各种基于GAN的声码器的配方。如果尚未准备配方,您可以通过espnet2 TTS配方快速开始训练。详见Run training using ESPnet2-TTS recipe within 5 minutes。
或者您可以尝试文本转梅尔谱与声码器的联合训练。
训练好的声码器可以按如下方式使用:
使用Python
from espnet2.bin.tts_inference import Text2Speech tts = Text2Speech.from_pretrained(model_file="/path/to/model.pth", vocoder_file="/path/to/your_trained_vocoder_checkpoint.pkl") wav = tts("Hello, world")["wav"]使用TTS配方
$ ./run.sh --stage 8 --vocoder_file /path/to/your_trained_vocoder_checkpoint.pkl --inference_tag decode_with_my_vocoder
如何使用文本转梅尔谱GTA输出来训练声码器?
有时,我们希望使用文本转梅尔谱的真实对齐(GTA)输出来微调声码器。详见使用ESPnet2-TTS GTA输出进行微调。
如何处理validate_data_dir.sh中的错误?
utils/validate_data_dir.sh: 文本包含N行不可打印字符,出现在此行
这是由于kaldi最近的变更导致的。我们建议将utils/validate_data_dir.sh中的以下部分修改为non_print=true。
https://github.com/kaldi-asr/kaldi/blob/40c71c5ee3ee5dffa1ad2c53b1b089e16d967bb5/egs/wsj/s5/utils/validate_data_dir.sh#L9
utils/validate_text.pl: 语句xxx的行包含不允许的Unicode空白字符>utils/validate_text.pl: 错误: 文本文件'data/xxx'包含不允许的UTF-8空白字符
不允许在text中使用全角空格。请将其改为半角空格或其他符号。
为什么模型在最后生成了无意义的语音?
这是因为模型未能预测到停止标记。有几种解决方案可以解决此问题:
- 在推理过程中使用注意力约束(仅在Tacotron 2中,在推理配置中设置
use_attention_constraint=True)。 - 使用较大的
bce_pos_weight(例如bce_pos_weight=10.0)训练模型。 - 使用非自回归模型(FastSpeech或FastSpeech2)
为什么模型无法在我的数据集上训练良好?
大多数问题都是由数据集清理不当引起的。请仔细检查以下事项:
- Check the attention plot during the training. Loss value is not so meaningful in TTS.
- 你可以参考这个PR作为示例。
- Remove the silence at the beginning and end of the speech.
- 你可以使用这个示例中的静音修剪脚本。
- 如果语音中间包含长时间静默,则进行语音分离。
- 如果G2P可用,则使用音素而非字符。
- 尽可能清理文本(如缩写、数字等)
- 如果语音包含静音部分,在文本中添加姿势符号。
- 如果数据集较小,请考虑使用预训练模型进行适配。
- 如果数据集较小,建议使用较大的缩减因子(reduction factor),这有助于注意力机制的学习。
为什么在结合神经声码器时输出会包含金属噪声?
这种情况尤其容易发生在神经声码器未使用噪声作为输入时(例如MelGAN、HiFiGAN),这些模型对声学特征的失配鲁棒性较差。通过使用文本转梅尔频谱的GTA输出进行声码器微调,或对文本转梅尔和声码器进行联合训练/微调,可以减轻金属音问题。
FastSpeech2的时长是如何生成的?
我们使用教师模型的注意力权重来计算持续时间,方法与FastSpeech相同。更多信息请参阅FastSpeech论文。
为什么Tacotron2或Transformer的输出是非确定性的?
这是因为我们在解码器中使用了prenet,它始终应用dropout。更多信息请参阅Tacotron2论文。
如果想固定结果,可以使用--always_fix_seed选项。
