MeloTTS中文发音人训练
MeloTTS https://github.com/myshell-ai/MeloTTS
MeloTTS是一个可以把文字转换成声音的工具,它支持英语、西班牙语、法语、中文、日语和韩语等多种语言。
它可以让你听到不同的语言和口音,比如美式英语、英式英语、印度英语、澳大利亚英语等。
它还可以调节语速,让声音快速或慢速地说出来。它的声音非常自然和流畅,就像真人在说话一样。
接下来我们开始训练自己的音频。
1: 下载准备环境,要求有GPU环境。
git clone https://github.com/myshell-ai/MeloTTS.git
cd MeloTTS
pip install -e .
python -m unidic download
2: 原始发音人数据准备(我们用xmj2002/genshin_ch_10npc)
首先下载声音模型:
git clone https://hf-mirror.com/datasets/xmj2002/genshin_ch_10npc
将声音文件及中文文本提取出来
import numpy as np
from scipy.io import wavfile
from datasets import load_dataset
import os
#https://blog.lukeewin.top/archives/melotts-zh-model-train
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'# 指定目录路径
data_dir = "g:/xxxx/xunlian/genshin_ch_10npc/train"# 获取所有 .parquet 文件路径
parquet_files = [os.path.join(data_dir, f) for f in os.listdir(data_dir) if f.endswith(".parquet")]mls = load_dataset("parquet",split="train", data_files=parquet_files)ddir = "xunlian/spk_pai"
mpInpcName = {"派蒙": 0,
}
for sample in mls:npcName = sample["npcName"] # 假设数据集中有 npcName 字段save_dir = os.path.join(ddir, npcName)os.makedirs(save_dir, exist_ok=True)print(f"文件夹已创建:{save_dir}")mpInpcName[npcName] = 0for sample in mls:npcName = sample["npcName"] # 假设数据集中有 npcName 字段lg = sample['language']if len(sample['text']) > 0:#if sample['language'] == 'CHS' and len(sample['text']) > 0:print(sample)# 获取音频数据audio_array = sample["audio"]["array"]# 将音频数据缩放到 -32768 到 32767 之间audio_array = np.int16(audio_array / np.max(np.abs(audio_array)) * 32767)_dir = os.path.join(ddir, npcName)# 保存音频数据audio_path = os.path.join(_dir, f"{mpInpcName[npcName]}_{lg}.wav")wavfile.write(audio_path, sample["audio"]["sampling_rate"], audio_array)# 保存文本内容text_path = os.path.join(_dir, f"{mpInpcName[npcName]}_{lg}.txt")with open(text_path, "w", encoding="utf-8") as f:f.write(sample["text"])mpInpcName[npcName] += 1
3:原始音频转换成:音频转码 44.1k 1 pcm_s16le
我们使用,ffmpeg 命令行转换
ffmpeg 下载只需要到github下载最新版本就行 https://ffmpeg.org/download.html
ffmpeg -i "./zjdx/zjdx.wav" -ar 44100 -ac 1 -acodec pcm_s16le "./output/zjdx.wav"
import os
import subprocess
import shutil
import redef process_convert_audio(input_path: str, output_path: str):"""音频转码 44.1k 1 pcm_s16le@param input_path: 要转码的音频所在路径@param output_path: 转码后的音频保存路径@return:"""os.makedirs(output_path, exist_ok=True)if os.path.exists(input_path) and os.path.isdir(input_path):for root, dirs, files in os.walk(input_path):for audio in files:output_file_name = os.path.basename(audio)input_file = os.path.join(root, audio)output_file = os.path.join(output_path, output_file_name)#判断是否是文本文档,如果是文本文档直接处理特殊字符并且复制到输出目录,text = re.sub(r'<.*?>|「.*?」|\{.*?\}|#|\$UNRELEASED', '', txt)if audio.endswith(".txt"):try:# 读取文本内容with open(input_file, "r", encoding="utf-8") as f:txt = f.read()# 移除特殊字符processed_text = re.sub(r'<.*?>|「.*?」|\{.*?\}|#|\$UNRELEASED', '', txt)# 保存处理后的文本到输出目录with open(output_file, "w", encoding="utf-8") as f:f.write(processed_text)print(f"文本文件已处理: {output_file}")except Exception as e:print(f"处理文本文件失败: {input_file}, 错误: {e}")else:try:command = ["ffmpeg", "-y", # 添加 -y 参数,强制覆盖输出文件"-i", input_file,"-ar", "44100", # 设置采样率为 44.1kHz"-ac", "1", # 设置单声道"-acodec", "pcm_s16le", # 设置音频编码为 PCM 16-bit little-endianoutput_file]subprocess.run(command, check=True)print(f"转换成功: {output_file}")except subprocess.CalledProcessError as e:print(f"转换失败: {input_file}, 错误: {e}")if __name__ == "__main__":odir = "xunlian/spk_pai_44.1k"ddir = "xunlian/spk_pai"# 查找ddir里面所有的文件夹名字命名为npcNamenpcNames = [f.name for f in os.scandir(ddir) if f.is_dir()]# 循环调用process_convert_audiofor npcName in npcNames:process_convert_audio(os.path.join(ddir, npcName), os.path.join(odir, npcName))
4,音频降噪(此步可省略)
可用resemble-enhance降噪
pip install resemble-enhance --upgrade
5:生成metadata文件
from pathlib import Path
import redef gen_metadata(audio_path: str, label_path: str, metadata_file: str):"""生成 metadata.list 文件@param audio_path: 音频所在路径@param label_path: 标签所在的路径@param metadata_file: 保存生成后的 metadata.list 文件@return:"""label_dict = {}# 遍历标签文件if Path(label_path).exists() and Path(label_path).is_dir():for label_file in Path(label_path).glob("*.txt"): # 遍历所有 .txt 文件label_name = label_file.stem # 获取文件名(去掉扩展名)with label_file.open('r', encoding='utf-8') as f:txt = f.read()label_dict[label_name] = txt#删除metadata_fileif Path(metadata_file).exists():Path(metadata_file).unlink()# 遍历音频文件if Path(audio_path).exists() and Path(audio_path).is_dir():for audio_file in Path(audio_path).glob("*.wav"): # 遍历所有 .wav 文件audio_name = audio_file.stem # 获取文件名(去掉扩展名)if audio_name in label_dict:label_txt = label_dict[audio_name]if len(re.sub(r'[^\u4e00-\u9fff]', '', label_txt).strip()) != 0:with open(metadata_file, 'a', encoding='utf-8') as f:f.write(f'{audio_file}|ZH-zjdx|ZH|{label_txt}\n')f.flush()if __name__ == "__main__":odir = "xunlian/spk_pai_44.1k"npcNames = [f.name for f in Path(odir).iterdir() if f.is_dir()]for npcName in npcNames:audio_path = Path(odir) / npcNamegen_metadata(audio_path, audio_path, audio_path / "metadata.list")
这里面代码:|ZH-zjdx|ZH| 中ZH-zjdx代表你的发音人名字,可自定义。
ZH是原始音频文字,如果是中英文混合,可以改成ZH_MIX_EN
6: 修改中文底膜:
中文特化模型下载:
https://openi.pcl.ac.cn/Stardust_minus/Bert-VITS2/modelmanage/show_model
修改melo/download_utils.py
对应的值
PRETRAINED_MODELS = {'G.pth': './model/basespeakers/pretrained/G.pth','D.pth': './model/basespeakers/pretrained/D.pth','DUR.pth': './model/basespeakers/pretrained/DUR.pth',
}
对应的你的下载G_0,D_0,DUR_0.可以用中英日底膜
https://openi.pcl.ac.cn/Stardust_minus/Bert-VITS2/modelmanage/model_readme_tmpl?name=Bert-VITS2%E4%B8%AD%E6%97%A5%E8%8B%B1%E5%BA%95%E6%A8%A1-fix
7:训练,生成config.json
使用如下命令:
PYTHONPATH=$PWD python /data-1T/apps/melotts/melo/preprocess_text.py --config_path /data-1T/apps/melotts/melo/configs/config.json --metadata /data-1T/apps/melotts/xunlian/output/metadata.list
把/data-1T/apps/melotts/xunlian/output/metadata.list改为自己的metadata.list文件路径。
运行上面的命令之后,会在metadata.list同级目录中生成几个文件,其中一个是config.json文件,我们需要打开这个文件,修改一些训练参数。
"train": {"log_interval": 200,"eval_interval": 1000,// 多少轮保存一个"seed": 52,"epochs": 10000, // 总共训练轮次"batch_size": 6,
......
}
根据自己的数据集大小和显卡情况修改。这里我训练的数据集为10066条数据,显卡为单张RTX4090D显卡,所以我这里设置epochs=500,batch_size=16。
8:创建目录 zh-zjdx
将 config.json 放入该目录下
这样做的目的是在训练启动的时候,会自动在源码中的melo/logs下面自动创建一个这样的目录,用于存放训练过程中的数据。
9:开始训练。
PYTHONPATH=$PWD bash ./melo/train.sh /data-1T/apps/melotts/zh-zjdx/config.json 1
上面这条命令中需要输入两个参数,一个是配置文件,一个是GPU数量,我这里只有一张显卡,所以最后一个参数是1。
10:推理
使用下面命令进行推理。
PYTHONPATH=$PWD python ./melo/infer.py --text "这个是如何改音标的?如果要训练方言的模型" -m ./logs/zh-zjdx/G_1000.pth -o ./out -l ZH
注意上面命令中并没有显示指定配置文件,其实代码内部会根据G_n.pth所在的目录中查找配置文件。
如果想要在网页端使用,那么需要修改app.py代码。
models = {'EN': TTS(language='EN', device=device),'ZH': TTS(language='ZH', device=device),'ZH-zjdx': TTS(language='ZH', device=device,config_path='./logs/zh-zjdx/config.json',ckpt_path='./logs/zh-zjdx/G_39000.pth'),
}
speaker_ids = models['EN'].hps.data.spk2iddefault_text_dict = {'ZH-zjdx': 'text-to-speech 领域近年来发展迅速','EN': 'The field of text-to-speech has seen rapid development recently.','ZH': 'text-to-speech 领域近年来发展迅速',
}.....language = gr.Radio(['EN','ZH','ZH-zjdx'], label='Language', value='EN')
......
然后启动这个app.py就可以在网页端进行语音合成了。
最后注意事项:如果你训练出来的音频是胡乱说话。根本听不出来说的什么,那么可能有两个问题导致的,
1:底膜没对,请参考6选项设置底膜
2:可能训练轮次不够或者原始音频量不够。