Files
2026-10-05 16:14:53 -04:00

227 lines
9.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
import argparse
import torch
# load coqui-ai/trainer libraries
from trainer import Trainer, TrainerArgs
# load coqui-ai/TTS libraries
from TTS.tts.configs.shared_configs import BaseDatasetConfig
from TTS.tts.configs.vits_config import VitsConfig
from TTS.tts.datasets import load_tts_samples
from TTS.tts.models.vits import Vits, VitsArgs, VitsAudioConfig
#
# parse command line arguments
#
def parse_cmdline_args():
parser = argparse.ArgumentParser(
description = "Code to clone a voice from a given set of voice samples and a multi-speaker baseline model")
parser.add_argument("--baseline_model_path", type = str, required = True,
help = "Path to multi-speaker baseline model (VITS model)")
parser.add_argument("--speaker_dataset_path", type = str, required = True,
help = "Path to voice cloning dataset")
parser.add_argument("--speaker_embeddings_path", type = str, required = True,
help = "Path to speaker's embeddings file")
parser.add_argument("--output_path", type = str, default = "results/cloned-voices",
help = "Path to store trained / generated assets")
parser.add_argument("--batch_size", type = int, default = 96, # 96 is suitable for AWS g5 instances
help = "Batch size for training run")
parser.add_argument("--max_epochs", type = int, default = 200, # 200 for batch_size 96 (with the 22.050 sampling rate multi-speaker model
help = "Maximum number of epochs for training run") # 2000 for batch_size 64 and 1500 for batch_size 96 (with the initial 16k sampling rate VCTK 0.80 model)
parser.add_argument("--use_cpu", default = False, action = "store_true", # untested!!!
help = "Signal that CPU should be used even if a CUDA-device is available")
parser.add_argument("--output_format", type = str, choices = ["txt", "json"], default = "txt",
help = "Output format; available choices include 'txt' for human readible text and 'json' for JSON formatting")
return parser.parse_args()
#
# main training method (voice cloning)
#
def main(args):
if args.output_format == "txt":
print("Commencing training of a new multi-speaker potion-voice baseline model:")
print("")
print(" + Baseline multi-speaker model path: {}" . format(args.baseline_model_path))
print(" + Voice training dataset path : {}" . format(args.speaker_dataset_path))
print(" + Speaker embeddings path : {}" . format(args.speaker_embeddings_path))
print(" + Output path : {}" . format(args.output_path))
print(" + Batch size : {}" . format(args.batch_size))
print(" + Training runs (max epochs) : {}" . format(args.max_epochs))
print("")
# determine whether CUDA support is available and set device parameters accordingly
use_cuda = torch.cuda.is_available()
if args.output_format == "txt":
print(" + CUDA availability : {}" . format(use_cuda))
if args.use_cpu:
device = "cpu"
device_torch = False
elif use_cuda:
device = "cuda"
device_torch = torch.device("cuda")
else:
device = "cpu"
device_torch = False
if args.output_format == "txt":
print(" + Compute device used : {}" . format(device))
print("")
# define training data set
dataset_config = BaseDatasetConfig(formatter = "vctk_old", language = "en-us", path = args.speaker_dataset_path)
# set VITS training parameters
audio_config = VitsAudioConfig(
sample_rate = 22050,
win_length = 1024,
hop_length = 256,
num_mels = 80,
mel_fmin = 0,
mel_fmax = None,
)
vitsArgs = VitsArgs(
use_speaker_embedding = False,
use_d_vector_file = True,
d_vector_file = [args.speaker_embeddings_path],
d_vector_dim = 512,
num_layers_text_encoder = 10
)
config = VitsConfig(
model_args = vitsArgs,
audio = audio_config,
run_name = "vits_potion_clone",
use_speaker_embedding = False,
use_d_vector_file = True,
d_vector_file = [args.speaker_embeddings_path],
d_vector_dim = 512,
batch_size = args.batch_size,
eval_batch_size = 8,
batch_group_size = 0, # changing this to 5 (VITS training default) slows training down, but doesn't have any positive training effects
num_loader_workers = 4,
num_eval_loader_workers = 4,
run_eval = True,
eval_split_size = 2, # fix size of eval dataset (default 1% approach requires at least 100 voice samples!)
test_delay_epochs = -1,
epochs = args.max_epochs,
text_cleaner = "english_cleaners",
use_phonemes = False,
phoneme_language = "en-us",
phoneme_cache_path = os.path.join(args.output_path, "phoneme_cache"),
compute_input_seq_cache = True,
print_step = 50,
print_eval = True,
mixed_precision = True,
max_text_len = 325,
output_path = args.output_path,
save_checkpoints = True,
save_step = 200,
datasets = [dataset_config],
cudnn_benchmark = False,
#characters = {
# "pad": "_",
# "eos": "&",
# "bos": "*",
# "characters": "!¡'(),-.:;¿?abcdefghijklmnopqrstuvwxyz «°±µ»$%&‘’‚“`”„",
# "punctuations": "!¡'(),-.:;¿? ",
# "phonemes": None,
# "unique": True
#},
test_sentences = [
["It took me quite a long time to develop a voice, and now that I have it I'm not going to be silent."],
["Be a voice, not an echo."],
["I'm sorry Dave. I'm afraid I can't do that."],
["This cake is great. It's so delicious and moist."],
["Prior to November 22, 1963."],
["Hey! Sandra."],
["Hey! Andrew."],
["Hey, Michelle."],
["Hey! George."],
["Hey there, Rachel."]
]
)
# load training samples
train_samples, eval_samples = load_tts_samples(config.datasets, eval_split = True, eval_split_max_size = config.eval_split_max_size, eval_split_size = config.eval_split_size)
# init VITS model
model = Vits.init_from_config(config)
# init voice cloning
trainer = Trainer(
TrainerArgs(restore_path = args.baseline_model_path, use_ddp = False),
config,
args.output_path,
model = model,
train_samples = train_samples,
eval_samples = eval_samples
)
# trigger voice cloning (aka single speaker training)
try:
trainer.fit()
except (KeyboardInterrupt, SystemExit):
print("Training stopped manually (via keyboard interrupt)! Bye.")
exit(0)
# determine required adjustment for speech synthesizing (i.e., the scaling factor for the duration predictor)
# take the duration of the test sentence and calculate the difference to corresponding reference samples
# set config.model_args["length_scale"] accordingly and save the updated config asset
# exit gracefully
if args.output_format == "txt":
print("")
print("Completed voice cloning. The resulting model(s) can be found at:")
print(" --> {}" . format(args.output_path))
print("")
print("Done; bye.")
print("")
if __name__ == "__main__":
# parse command line arguments
args = parse_cmdline_args()
# clear command line arguments to avoid triggering argparse features part of Trainer / coqpit imports
# Traceback (most recent call last):
# File "train_multispeaker_baseline_model.py", line 208, in <module>
# main(args)
# File "train_multispeaker_baseline_model.py", line 177, in main
# trainer = Trainer(
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/trainer/trainer.py", line 360, in __init__
# config, new_fields = self.init_training(args, coqpit_overrides, config)
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/trainer/trainer.py", line 594, in init_training
# config.parse_known_args(coqpit_overrides, relaxed_parser=True)
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/coqpit/coqpit.py", line 843, in parse_known_args
# parser = self.init_argparse(arg_prefix=arg_prefix, relaxed_parser=relaxed_parser)
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/coqpit/coqpit.py", line 881, in init_argparse
# _init_argparse(
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/coqpit/coqpit.py", line 529, in _init_argparse
# parser = _init_argparse(
# File "/home/ubuntu/dev/potion-voice_venv/lib/python3.8/site-packages/coqpit/coqpit.py", line 550, in _init_argparse
# return default.init_argparse(
# AttributeError: 'str' object has no attribute 'init_argparse'
sys.argv = [sys.argv[0]]
# ensure the output path exists
os.makedirs(args.output_path, exist_ok = True)
main(args)
### USAGE:
### $ python3 TTS/TTS/bin/resample.py --input_dir voice_dataset_path/person_82/wav48/1 --output_sr 16000
### $ python3 clone_voice.py [with argument]