歌声合成
歌声合成
这是ESPnet2的SVS配方模板。
目录
配方流程
SVS配方包含9个阶段。
- 依赖数据库的数据准备
数据准备阶段。
它会调用local/data.sh来创建Kaldi风格的数据目录,但会在data/目录下为训练集、验证集和评估集额外生成score.scp和label文件。
另请参阅:
- 音频转储/嵌入准备
如果指定--feats_type raw选项,这是一个wav转储阶段,用于重新格式化数据目录中的wav.scp文件。
否则,如果您指定--feats_type fbank选项或--feats_type stft选项,这将是一个特征提取阶段(待更新)。
此外,如果您指定了--use_sid true和--use_lid true选项,本阶段还将执行说话人ID嵌入和语言ID嵌入的准备工作。请注意,该处理过程假设utt2spk或utt2lang已在阶段1正确创建,请务必注意。
- 过滤
过滤阶段。
处理阶段,用于从训练集和验证集中移除过长或过短的语音片段。您可以通过--min_wav_duration和--max_wav_duration参数调整阈值。
空文本也将被移除。
- 词元列表生成
词元列表生成阶段。该阶段从srctexts生成词元列表(字典)。您可以通过--token_type选项更改分词类型。支持token_type=phn。若指定--cleaner选项,输入文本将使用指定的清理器进行处理。当token_type=phn时,输入文本将通过--g2p选项指定的G2P模块进行转换。
另请参阅:
数据准备将在第4阶段结束。您可以通过--skip_data_prep选项跳过数据准备阶段(阶段1至阶段4)。
- 语音合成统计信息收集
统计计算阶段。该阶段收集输入和输出的形状信息,并计算用于特征归一化的统计量(训练集和验证集上的均值和方差)。
在此阶段,您可以设置--write_collected_feats true来存储音高和特征的统计信息。
- 语音合成训练
SVS模型训练阶段。您可以通过--train_config和--train_args选项来更改训练设置。
另请参阅:
训练过程将在第6阶段结束。您可以通过--skip_train选项跳过训练过程(第5阶段至第6阶段)。
- 歌声合成推理
SVS模型解码阶段。您可以通过--inference_config和--inference_args修改解码设置。兼容的声码器可以被训练和加载。
另请参阅:
- 客观评估
评估阶段。该阶段执行四项客观评估。另请参阅:
- 模型打包
打包阶段。将训练好的模型文件进行打包。
如何运行
这里,我们展示使用egs2/ofuton_p_utagoe_db/svs1运行配方的步骤。
进入配方目录。
$ cd egs2/ofuton_p_utagoe_db/svs1如需更改下载目录,请修改db.sh中的OFUTON变量。
$ vim db.sh如需使用作业调度器,请修改cmd.sh和conf/*.conf文件。详情请参阅using job scheduling system。
$ vim cmd.sh运行run.sh脚本,该脚本会执行上述所有阶段的操作。
$ ./run.sh默认情况下,我们使用conf/train.yaml配置,以feats_type=raw和token_type=phn参数训练Naive_RNN模型。
然后,您可以在配方目录中看到以下目录。
├── data/ # Kaldi-style data directory
│ ├── dev/ # validation set
│ ├── eval/ # evaluation set
│ ├── tr_no_dev/ # training set
│ └── token_list/ # token list (directory)
│ └── phn_none_jp/ # token list
├── dump/ # feature dump directory
│ └── raw/
│ ├── org/
│ │ ├── tr_no_dev/ # training set before filtering
│ │ └── dev/ # validation set before filtering
│ ├── srctexts # text to create token list
│ ├── eval/ # evaluation set
│ ├── dev/ # validation set after filtering
│ └── tr_no_dev/ # training set after filtering
└── exp/ # experiment directory
├── svs_stats_raw_phn_none_jp # statistics
│ ├── logdir/ # statistics calculation log directory
│ ├── train/ # train statistics
│ ├── valid/ # valid statistics
└── svs_train_raw_phn_none_jp # model
├── tensorboard/ # tensorboard log
├── images/ # plot of training curves
├── valid/ # valid results
├── decode_train.loss.best/ # decoded results
│ ├── dev/ # validation set
│ └── eval/ # evaluation set
│ ├── norm/ # generated features
│ ├── denorm/ # generated denormalized features
│ ├── MCD_res/ # mel-cepstral distortion
│ ├── VUV_res/ # voiced/unvoiced error rate
│ ├── SEMITONE_res/ # semitone accuracy
│ ├── F0_res/ # log-F0 RMSE
│ ├── wav/ # generated wav via vocoder
│ ├── log/ # log directory
│ ├── feats_type # feature type
│ └── 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_2best.pth # model averaged parameters
└── *.best.pth # symlink to the best model parameter loss在解码过程中,您可以查看声码器训练来设置声码器。
首次使用时,建议通过--stage和--stop_stage选项逐步执行每个阶段。
$ ./run.sh --stage 1 --stop_stage 1
$ ./run.sh --stage 2 --stop_stage 2
...
$ ./run.sh --stage 7 --stop_stage 7这可能帮助您理解每个阶段的处理流程和目录结构。
Naive_RNN 训练
首先,完成数据准备工作:
$ ./run.sh \
--stage 1 \
--stop_stage 4 \
# for sample_rate 24000 hz
$ ./run.sh \
--fs 24000 \
--n_shift 300 \
--win_length 1200 \
--stage 1 \
--stop_stage 4 \警告:请注意不同模型中fs、n_shift和win_length的设置有所不同。窗口偏移量n_shift和窗口长度win_lenght会根据采样率fs进行调整。
其次,检查"train_config"(默认为conf/train.yaml)、"score_feats_extract"(RNN中的帧级别)并将"vocoder_file"修改为您自己的声码器路径。
$ ./run.sh --stage 5 \
--train_config conf/tuning/train_naive_rnn.yaml \
--score_feats_extract frame_score_feats \
--pitch_extract dio \
--vocoder_file ${your vocoder path} \Naive_RNN_DP 训练
首先,完成数据准备工作:
$ ./run.sh \
--stage 1 \
--stop_stage 4 \
# for sample_rate 24000 hz
$ ./run.sh \
--fs 24000 \
--n_shift 300 \
--win_length 1200 \
--stage 1 \
--stop_stage 4 \警告:请注意不同模型中fs、n_shift和win_length的设置有所不同。窗口偏移量n_shift和窗口长度win_lenght会根据采样率fs进行调整。
其次,检查"train_config"(默认为conf/train.yaml)、"score_feats_extract"(RNN_DP中的音节级别)并将"vocoder_file"修改为您自己的声码器路径。
$ ./run.sh --stage 5 \
--train_config conf/tuning/train_naive_rnn.yaml \
--score_feats_extract syllable_score_feats \
--pitch_extract dio \
--vocoder_file ${your vocoder path} \XiaoiceSing 训练
首先,完成数据准备工作:
$ ./run.sh \
--stage 1 \
--stop_stage 4 \
# for sample_rate 24000 hz
$ ./run.sh \
--fs 24000 \
--n_shift 300 \
--win_length 1200 \
--stage 1 \
--stop_stage 4 \警告:请注意不同模型中fs、n_shift和win_length的设置有所不同。窗口偏移量n_shift和窗口长度win_lenght会根据采样率fs进行调整。
其次,检查"train_config"(默认conf/train.yaml)、"score_feats_extract"(XiaoiceSing中的音节级别)并修改"vocoder_file"为您自己的声码器路径。
$ ./run.sh --stage 5 \
--train_config conf/tuning/train_naive_rnn.yaml \
--score_feats_extract syllable_score_feats \
--pitch_extract dio \
--vocoder_file ${your vocoder path} \Diffsinger 训练
首先,完成数据准备工作:
$ ./run.sh \
--stage 1 \
--stop_stage 4 \
# for sample_rate 24000 hz
$ ./run.sh \
--fs 24000 \
--n_shift 300 \
--win_length 1200 \
--stage 1 \
--stop_stage 4 \要训练Diffsinger,您需要先训练一个XiaoiceSing模型(参见XiaoiceSing训练)。然后将XiaoiceSing的预训练模型作为Diffsinger:FFTSinger加载。您可以在此处查看--pretrained_model的详细信息。
$ ./run.sh \
--stage 5 \
--train_config conf/tuning/train_diffsinger.yaml \
--inference_config conf/tuning/decode_diffsinger.yaml \
--score_feats_extract syllable_score_feats \
--pitch_extract dio \
--expdir exp/diffsinger \
--inference_model latest.pth \
--vocoder_file ${your vocoder path} \
--pretrained_model ${your pretrained model path} \
--use_feats_minmax true \
# for example
$ --pretrained_model /exp/xiaoice-2-24-250k/500epoch.pth:svs:svs.fftsinger \VISinger (1+2) 训练
VISinger / VISinger 2 的配置默认为22.05 kHz或44.1 kHz,并使用不同的特征提取方法。(请注意,您可以使用任何特征提取方法,但默认方法是fbank。)如果您想将其用于24 kHz或16 kHz数据集,请注意这些要点。
首先,检查fs(采样率)并完成数据准备工作:
$ ./run.sh \
--fs 44100 \
--n_shift 512 \
--win_length 2048 \
--stage 1 \
--stop_stage 4 \其次,检查"train_config"(默认为conf/train.yaml,你也可以使用--train_config ./conf/tuning/train_visinger2.yaml来训练VISinger 2)、"score_feats_extract"(VISinger中使用音节级别)、"svs_task"(VISinger中使用gan_svs)。
# Single speaker 44100 hz case
./run.sh \
--stage 5 \
--fs 44100 \
--n_fft 2048 \
--n_shift 512 \
--win_length 2048 \
--svs_task gan_svs \
--pitch_extract dio \
--feats_extract fbank \
--feats_normalize none \
--score_feats_extract syllable_score_feats \
--train_config ./conf/tuning/train_visinger.yaml \
--inference_config conf/tuning/decode_vits.yaml \
--inference_model latest.pth \
--write_collected_feats trueVISinger 2 Plus 训练
VISinger 2 Plus 已被 SLT 2024 会议收录。如需使用预训练模型,您可以从此处下载。该模型使用OpenCpop数据集进行训练。您也可以选择多歌手版本的ACE-OpenCpop数据集,通过此处下载对应模型。
要训练模型,您可以选择在ACESinger或Opencpop数据集上进行多种配置训练。train_visinger2_plus_hubert.yaml中的默认设置使用hubert_large_ll60k作为预训练的SSL模型。如需切换SSL模型,您可以通过取消MERT或Chinese Hubert相关行的注释来修改配置。
./run.sh \
--stage 1 \
--stop_stage 8 \
--ngpu 1 \
--fs 24000 \
--n_fft 2048 \
--n_shift 480 \
--win_length 2048 \
--dumpdir dump/24k \
--expdir exp/24k \
--svs_task gan_svs \
--feats_extract fbank \
--feats_normalize none \
--train_config ./conf/tuning/train_visinger2_plus_hubert.yaml \
--inference_config ./conf/tuning/decode_vits.yaml \
--inference_model latest.pth \
--write_collected_feats false歌唱Tacotron训练
首先,完成数据准备工作:
$ ./run.sh \
--stage 1 \
--stop_stage 4 \
# for sample_rate 24000 hz
$ ./run.sh \
--fs 24000 \
--n_shift 300 \
--win_length 1200 \
--stage 1 \
--stop_stage 4 \警告:请注意不同模型中fs、n_shift和win_length的设置有所不同。窗口偏移量n_shift和窗口长度win_lenght会根据采样率fs进行调整。
其次,检查"train_config"(默认为conf/train.yaml)、"score_feats_extract"(在Singing Tacotron中为音节级别)并将"vocoder_file"修改为您自己的声码器路径。
$ ./run.sh --stage 5 \
--train_config conf/tuning/train_singing_tacotron.yaml \
--inference_config conf/tuning/decode_singing_tacotron.yaml \
--score_feats_extract syllable_score_feats \
--vocoder_file ${your vocoder path} \带说话人ID嵌入训练的多说话人模型
首先,你需要从第2和第3阶段运行,使用--use_sid true来提取说话人ID。
$ ./run.sh --stage 2 --stop_stage 3 --use_sid true你可以在dump/raw/*/utt2sid中找到说话人ID文件。请注意,你需要在数据准备阶段正确创建utt2spk才能生成utt2sid。然后,你可以使用在svs_conf中包含spks: #spks的配置来运行训练。
# e.g.
svs_conf:
spks: 5 # Number of speakers请从第6阶段开始运行训练。
$ ./run.sh --stage 6 --use_sid true --train_config /path/to/your_multi_spk_config.yaml带语言ID嵌入训练的多语言模型
首先,你需要从第2和第3阶段运行,使用--use_lid true来提取说话人ID。
$ ./run.sh --stage 2 --stop_stage 3 --use_lid true你可以在dump/raw/*/utt2lid中找到说话人ID文件。注意:你需要在阶段1额外创建utt2lang文件才能生成utt2lid。然后,你就可以使用svs_conf中包含langs: #langs的配置来运行训练。
# e.g.
svs_conf:
langs: 4 # Number of languages请从第6阶段开始运行训练。
$ ./run.sh --stage 6 --use_lid true --train_config /path/to/your_multi_lang_config.yaml当然,您还可以进一步结合说话人ID嵌入。如果想同时使用说话人ID(sid)和语言ID(lid),处理流程应如下所示:
$ ./run.sh --stage 2 --stop_stage 3 --use_lid true --use_sid true配置你的设置。
# e.g.
svs_conf:
langs: 4 # Number of languages
spks: 5 # Number of speakers请从第6阶段开始运行训练。
$ ./run.sh --stage 6 --use_lid true --use_sid true --train_config /path/to/your_multi_spk_multi_lang_config.yaml声码器训练
如果您的--vocoder_file设置为none,将使用Griffin-Lim算法。您也可以使用kan-bayashi/ParallelWaveGAN训练相应的声码器。
预训练的声码器如下所示:
*_hifigan.v1
├── checkpoint-xxxxxxsteps.pkl
├── config.yml
└── stats.h5# Use the vocoder trained by `parallel_wavegan` repo manually
$ ./run.sh --stage 7 --vocoder_file /path/to/checkpoint-xxxxxxsteps.pkl --inference_tag decode_with_my_vocoder评估
我们提供四种客观评估指标:
- 梅尔倒谱失真(MCD)
- 基频的对数均方根误差(log-F0 RMSE)
- 半音准确率(Semitone ACC)
- 浊音/清音错误率 (VUV_E)
- 词/字符错误率 (WER/CER,用户可选执行)
对于MCD(梅尔倒谱失真),我们应用动态时间规整(DTW)来匹配真实歌声与生成歌声之间的时长差异。
这里我们展示计算客观指标的示例命令:
cd egs2/<recipe_name>/svs1
. ./path.sh
# Evaluate MCD & log-F0 RMSE & Semitone ACC & VUV Error Rate
./pyscripts/utils/evaluate_*.py \
exp/<model_dir_name>/<decode_dir_name>/eval/wav/gen_wavdir_or_wavscp.scp \
dump/raw/eval/gt_wavdir_or_wavscp.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
# 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
# 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虽然这些客观指标可以评估合成歌声的质量,但仅凭这些数值仍难以全面判断人类的感知质量,尤其是对于高保真生成的歌声。因此,如果您想详细检查感知质量,我们建议进行主观评估(例如平均意见得分MOS)。
您可以参考此页面使用webMUSHRA启动基于网络的主观评价系统。
关于数据目录
训练集、开发集和评估集的每个目录都具有相同的目录结构。另请参阅 https://github.com/espnet/espnet/tree/master/egs2/TEMPLATE#about-kaldi-style-data-directory 了解Kaldi数据结构。我们建议您运行ofuton_p_utagoe_db配方并自行检查data/目录的内容。
cd egs/ofuton_p_utagoe_db/svs1
./run.sh目录结构
data/ ├── tr_no_dev/ # 训练集目录 │ ├── text # 文本转录 │ ├── label # 指定转录的开始和结束时间 │ ├── score.scp # 评分文件路径 │ ├── wav.scp # 音频文件路径 │ ├── utt2spk # 将话语ID映射到说话者ID的文件 │ ├── spk2utt # 将说话者ID映射到话语ID的文件 │ ├── segments # [可选] 指定每个话语的开始和结束时间 │ └── (utt2lang) # 将话语ID映射到语言类型的文件(仅用于多语言场景) ├── dev/ │ ... ├── eval/ │ ... └── token_list/ # 标记列表目录 ...text格式uttidA <转写文本> uttidB <转写文本> ...label格式uttidA (startA1, endA1, phA1) (startA2, endA2, phA1) ... uttidB (startB1, endB1, phB1) (startB2, endB2, phB2) ... ...score.scp格式key1 /some/path/score.json key2 /some/path/score.json ...请注意,对于没有明确乐谱或MusicXML的数据库,我们将在未来提供基于规则的自动音乐转录来提取相关音乐信息。
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生成,而utt2spk也可以通过spk2utt生成,因此只需创建其中任意一个即可。utils/utt2spk_to_spk2utt.pl data/tr_no_dev/utt2spk > data/tr_no_dev/spk2utt utils/spk2utt_to_utt2spk.pl data/tr_no_dev/spk2utt > data/tr_no_dev/utt2spk如果您的语料库不包含说话人信息,可以将说话人ID设置为与话语ID相同以满足目录格式要求,或者为所有话语赋予相同的说话人ID(实际上当前ASR配方中我们并未使用说话人信息)。
uttidA uttidA uttidB uttidB ...或
uttidA dummy uttidB dummy ...[可选]
segments格式如果音频数据原本是较长的录音(约大于1小时),且每个音频文件包含多个片段的语音内容,则需要创建
segments文件来指定每个语音片段的开始和结束时间。格式为<utterance_id> <wav_id> <start_time> <end_time>。ofuton_0000000000000000hato_0007 ofuton_0000000000000000hato 33.470 38.013 ...请注意,如果使用
segments,则wav.scp中的<wav_id>需要与segments中的对应,而不是utterance_id。ofuton_0000000000000000hato /path/to/ofuton_0000000000000000hato.wav ...utt2lang格式uttidA languageA uttidB languageB uttidC languageA uttidD lagnuageB ...请注意
utt2lang文件仅针对多语言数据集生成(参见配方egs/multilingual_four)。
完成数据目录创建后,最好通过utils/validate_data_dir.sh进行检查。
utils/validate_data_dir.sh --no-feats data/tr_no_dev
utils/validate_data_dir.sh --no-feats data/dev
utils/validate_data_dir.sh --no-feats data/test分数准备
要准备一个新的配方,如果没有官方提供的分段,我们首先通过--silence选项将歌曲分割成片段。
然后,我们将原始数据转换为score.json,根据标注情况可分为两种情形:
案例1:音素标注与标准化评分
如果音素和音符在时域上对齐,可直接转换原始数据。(例如:Opencpop)
如果音素标注在时域上与音符未对齐,则通过g2p将音素(来自
label)与音符-歌词对(来自musicXML)进行对齐。(例如:Ofuton)我们还针对数据集中缺失静音片段提供了一些自动修复方案。在stage1阶段,当您遇到诸如"歌词长度超过音素"或"音素长度超过歌词"等错误时,脚本会自动生成修复代码。您可能需要将这些代码放入
egs2/[数据集名称]/svs1/local/prep_segments.py文件中的get_error_dict方法内。请注意,根据建议的input_type类型,您可能需要将其复制到hts或xml的error_dict中。(更多信息请查看namine或natsume)
特别地,如果音符时长存在问题(例如Natsume),可以通过其他旋律文件(如MIDI)重建音符-歌词对。
案例2:仅包含音素标注
待更新。
您可能会遇到的问题
在第一阶段,即数据准备阶段,您可能会遇到ValueError问题,这些问题通常表明标注存在错误。为了解决这些问题,需要手动检查对应部分的原始数据并进行必要的修正。虽然其他工具包和开源代码库可能没有此类要求或检查,但我们发现投入时间解决这些错误能显著提升歌声合成器的质量。
请注意,可以在本地或通过阶段1的数据处理流程对原始数据进行修改。为了方便开源,我们推荐使用后者。
- 如需修改原始数据,可以使用music21、miditoolkit或MuseScore等工具包。
- 要在数据流中进行处理,您可以使用提供的读取器和写入器。示例可以在
egs2/{natsume, ameboshi, pjs}/svs1/local/{prep_segments.py, prep_segments_from_xml.py}/中的make_segment函数中找到。
以下是一些需要注意的常见错误:
- 错误的分割点
- 在相邻歌词之间添加停顿或直接分割。
- 移除停顿并将时长分配给正确的音素。
- 歌词/MIDI标注错误
- 替换为正确的选项。
- 添加缺失项并重新分配相邻时长。
- 移除冗余项并重新分配相邻时长。
- 针对给定g2p的不同歌词-音素对
- 使用如下音节-音素对的
customed_dic:# 例如 # 在日语数据集ofuton中,"ヴぁ"通过pyopenjtalk的输出与原始数据"v a"不同 > pyopenjtalk.g2p("ヴぁ") v u a # 将以下歌词-音素对添加到customed_dic中 ヴぁ v_a - Specify
--g2p noneand store the lyric-phoneme pairs intoscore.json, especially for polyphone problem in Mandarin.# e.g. # In Mandarin dataset Opencpop, the pronounce the second "重" should be "chong". > pypinyin.pinyin("情意深重爱恨两重", style=Style.NORMAL) [['qing'], ['shen'], ['yi'], ['zhong'], ['ai'], ['hen'], ['liang'], ['zhong']]
- MusicXML中的特殊标记
- Breath:
breath mark在 note.articulations 中:通常出现在句子末尾。在某些情况下,breath mark不会在其所属音符上生效。请在 local/ 目录下处理它们。br在 note.lyric 中。(已在 XMLReader 中解决)- 带有固定特殊音高的特殊注释。(已在XMLReader中解决)
- 断奏(Staccato): 在某些情况下,当音符的articulations中出现
staccato标记时会出现停顿。我们允许用户在local/目录下自行决定是否执行分段操作。
支持的文本清理器
你可以通过svs.sh中的--cleaner选项来更改。
none: 无文本清理器。
你可以查看这里的代码示例。
支持的文本前端
你可以通过svs.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]
- 例如:
pyopenjtalk: r9y9/pyopenjtalk- 例如
こ、こんにちは->[k, o, pau, k, o, N, n, i, ch, i, w, a]
- 例如
你可以查看这里的代码示例。
支持的模型
你可以通过修改run.sh中--train_config选项对应的*.yaml配置文件来训练以下模型。
你可以在egs/ofuton_p_utagoe_db/svs1/conf/tuning中找到上述模型的示例配置文件。
