ported changes form original branch

This commit is contained in:
bmalaski committed 2024-06-20 22:54:57 -04:00
1 parent 062483df1a
commit a97ad658c1
4 files changed
+407 -63

No files matched your search

+392 -60
View File
@@ -1,3 +1,4 @@
import math
import os
import gc
import re
@@ -6,6 +7,8 @@ import time
import glob
import json
import site
import datetime
import torch
import signal
import random
@@ -25,16 +28,22 @@ from pathlib import Path
from faster_whisper import WhisperModel
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
from metrics_logger import MetricsLogger
# Use a local Tokenizer to resolve Japanese support
# from TTS.tts.layers.xtts.tokenizer import multilingual_cleaners
from system.ft_tokenizer.tokenizer import multilingual_cleaners
import importlib.metadata as metadata
from packaging import version
from tokenizers import ByteLevelBPETokenizer
from tokenizers.pre_tokenizers import Whitespace
# STARTUP VARIABLES
this_dir = Path(__file__).parent.resolve()
audio_folder = this_dir / "finetune" / "put-voice-samples-in-here"
out_path = this_dir / "finetune" / "tmp-trn"
default_path = this_dir / "finetune" / "tmp-trn"
out_path = default_path
progress = 0
theme = gr.themes.Default()
refresh_symbol = '🔄'
@@ -310,13 +319,17 @@ def create_temporary_file(folder, suffix=".wav"):
def format_audio_list(target_language, whisper_model, out_path, max_sample_length, eval_split_number, speaker_name_input, gradio_progress=gr.Progress()):
def format_audio_list(target_language, whisper_model, max_sample_length, eval_split_number, speaker_name_input, create_bpe_tokenizer, gradio_progress=gr.Progress()):
global validate_train_metadata_path, validate_eval_metadata_path, validate_audio_folder, validate_whisper_model, validate_target_language
buffer = 0.3
max_duration = float(max_sample_length) # Ensure max_duration is a float
eval_percentage = eval_split_number / 100.0
speaker_name = speaker_name_input
audio_total_size = 0
global out_path
if speaker_name and speaker_name != 'personsname':
out_path = this_dir / "finetune" / speaker_name
os.makedirs(out_path, exist_ok=True)
temp_folder = os.path.join(out_path, "temp")
os.makedirs(temp_folder, exist_ok=True)
@@ -367,8 +380,13 @@ def format_audio_list(target_language, whisper_model, out_path, max_sample_lengt
print(f"[FINETUNE] No audio files found in {original_samples_folder}. Skipping processing.")
return None, None, 0
whisper_words = []
audio_steps = (0, len(original_audio_files))
gradio_progress_duration = 0
gradio_progress(audio_steps, desc="Processing Audio Files", unit="files")
for audio_path in tqdm(original_audio_files): # No start argument for tqdm
gradio_progress((2, 4), desc="Processing Audio Files")
start = datetime.datetime.now()
audio_file_name_without_ext, _ = os.path.splitext(os.path.basename(audio_path))
temp_audio_path = os.path.join(temp_folder, f"{audio_file_name_without_ext}.wav")
@@ -415,6 +433,9 @@ def format_audio_list(target_language, whisper_model, out_path, max_sample_lengt
words_list.extend(words)
for word_idx, word in enumerate(words_list):
if create_bpe_tokenizer:
whisper_words.append(word.word)
if first_word:
sentence_start = word.start
if word_idx == 0:
@@ -481,6 +502,14 @@ def format_audio_list(target_language, whisper_model, out_path, max_sample_lengt
os.remove(temp_audio_path)
end = datetime.datetime.now()
gradio_progress_duration += (end - start).total_seconds()
audio_steps = (audio_steps[0] + 1, audio_steps[1])
additional_data_points_needed = audio_steps[1] - audio_steps[0]
avg_duration = gradio_progress_duration / audio_steps[0]
gradio_estimated_duration = (avg_duration * additional_data_points_needed)
gradio_progress(audio_steps, desc=f"Processing. Estimated Completion: {c_logger.format_duration(gradio_estimated_duration)}", unit="files")
# Check if the WAV files folder contains files after processing
audio_files = [os.path.join(audio_folder, file) for file in os.listdir(audio_folder) if file.endswith('.wav')]
@@ -532,6 +561,17 @@ def format_audio_list(target_language, whisper_model, out_path, max_sample_lengt
final_training_set.sort_values('audio_file').to_csv(train_metadata_path, sep='|', index=False)
final_eval_set.sort_values('audio_file').to_csv(eval_metadata_path, sep='|', index=False)
if create_bpe_tokenizer:
print(f"Training BPE Tokenizer")
tokenizer = ByteLevelBPETokenizer(str(this_dir / base_path / "xttsv2_2.0.3" / "vocab.json"))
tokenizer.pre_tokenizer = Whitespace()
tokenizer.add_tokens(["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]", "[STOP]", "[SAPCE]"])
tokenizer.train_from_iterator(whisper_words, vocab_size=30000, show_progress=True, min_frequency=2, special_tokens=[
"[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]", "[STOP]", "[SPACE]",
])
# Save the tokenizer model
tokenizer.save(path=str(out_path / "bpe_tokenizer-vocab.json"), pretty=True)
gradio_progress((4,4), "Finalizing")
del asr_model, final_eval_set, final_training_set, new_data_df, existing_metadata
@@ -648,7 +688,8 @@ def load_and_display_mismatches():
#### STEP 2 BITS #####
######################
from trainer import Trainer, TrainerArgs
from trainer import TrainerArgs, Trainer
from trainer_alltalk.trainer import Trainer
from TTS.config.shared_configs import BaseDatasetConfig
from TTS.tts.datasets import load_tts_samples
@@ -663,10 +704,10 @@ def basemodel_or_finetunedmodel_choice(value):
elif value == "Existing finetuned model":
basemodel_or_finetunedmodel = False
def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, model_to_train, output_path, max_audio_length=255995, progress=gr.Progress()):
def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, model_to_train, continue_run, disable_shared_memory, learning_rate_scheduler, optimizer, max_audio_length=255995, progress=gr.Progress()):
pfc_check_fail()
if "No Models Available" in eval_csv:
if "No Models Available" in model_to_train:
print(f"[FINETUNE] \033[91mError\033[0m: Cannot train. You have not selected a model. Please download a model.")
print(f"[FINETUNE] \033[91mError\033[0m: into a sub folder within the correct models folder.")
return
@@ -678,7 +719,7 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
model_path = this_dir / "models" / "xtts" / model_to_train
# Set here the path that the checkpoints will be saved. Default: ./training/
OUT_PATH = os.path.join(output_path, "training")
OUT_PATH = os.path.join(out_path, "training")
print("[FINETUNE] \033[94mStarting Step 2\033[0m - Fine-tuning the XTTS Encoder")
print(f"[FINETUNE] \033[94mLanguage: \033[92m{language} \033[94mEpochs: \033[92m{num_epochs} \033[94mBatch size: \033[92m{batch_size}\033[0m \033[94mGrad accumulation steps: \033[92m{grad_acumm}\033[0m")
print(f"[FINETUNE] \033[94mTraining : \033[92m{train_csv}\033[0m")
@@ -731,8 +772,30 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
MEL_NORM_FILE = model_path / "mel_stats.pth"
SPEAKERS_FILE = model_path / "speakers_xtts.pth"
training_assets = None
if (out_path / "bpe_tokenizer-vocab.json").exists():
print("[FINETUNE] Using custom BPE tokenizer")
training_assets = {
'Tokenizer': str(out_path / "bpe_tokenizer-vocab.json")
}
continue_path = None
if continue_run:
folders = glob.glob(os.path.join(OUT_PATH, '*/'))
if folders:
last_run = max(folders, key=os.path.getmtime)
if last_run:
checkpoints = glob.glob(os.path.join(last_run, "best_model_*.pth"))
if checkpoints:
latest_checkpoint = max(checkpoints, key=os.path.getmtime)
if latest_checkpoint:
XTTS_CHECKPOINT = None
continue_path = last_run
print(f"[FINETUNE] Continuing previous fine tuning {latest_checkpoint}")
# Copy the supporting files
destination_dir = this_dir / "finetune" / "tmp-trn" / "chkptandnorm"
destination_dir = out_path / "chkptandnorm"
destination_dir.mkdir(parents=True, exist_ok=True)
shutil.copy2(DVAE_CHECKPOINT, destination_dir / DVAE_CHECKPOINT.name)
shutil.copy2(MEL_NORM_FILE, destination_dir / MEL_NORM_FILE.name)
@@ -763,6 +826,95 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
if language == "ja":
number_of_workers = 0
lr_scheduler = None
lr_scheduler_params = {}
if learning_rate_scheduler and learning_rate_scheduler != "None":
lr_gamma_mapping = {
1e-6: 0.9,
5e-6: 0.8,
1e-5: 0.3,
5e-5: 0.3,
1e-4: 0.3,
5e-4: 0.1,
1e-3: 0.1
}
lr_scheduler = learning_rate_scheduler
if lr_scheduler == "StepLR":
lr_scheduler_params = {'step_size': 30, 'gamma': 0.1, 'last_epoch': -1}
elif lr_scheduler == "MultiStepLR":
exponent = 3 - int(math.log2(num_epochs) / 2)
base = 2
num_milestones = min(num_epochs, int(math.pow(base, exponent)))
milestone_interval = num_epochs // (num_milestones + 1)
milestones = [milestone_interval * (i + 1) for i in range(num_milestones)]
lr_scheduler_params = {'milestones': milestones, 'gamma': lr_gamma_mapping[learning_rate], 'last_epoch': -1}
elif lr_scheduler == "ExponentialLR":
lr_scheduler_params = {'gamma': 0.5, 'last_epoch': -1}
elif lr_scheduler == "CosineAnnealingLR":
lr_scheduler_params = {'T_max': num_epochs, 'eta_min': 1e-6, 'last_epoch': -1}
elif lr_scheduler == "ReduceLROnPlateau":
lr_scheduler_params = {'mode': 'min', 'factor': 0.8, 'patience': 1, 'threshold': 0.0001,
'threshold_mode': 'rel', 'cooldown': 0, 'min_lr': 1e-8, 'eps': 1e-08,}
elif lr_scheduler == "CyclicLR":
lr_scheduler_params = {'base_lr': learning_rate, 'max_lr': 0.1, 'step_size_up': 2000, 'step_size_down': None,
'mode': 'triangular', 'gamma': 1.0, 'scale_fn': None, 'scale_mode': 'cycle',
'cycle_momentum': True, 'base_momentum': 0.8, 'max_momentum': 0.9, 'last_epoch': -1}
elif lr_scheduler == "OneCycleLR":
#TODO: Determine total steps / epoch for this
lr_scheduler_params = {'max_lr': learning_rate, 'total_steps': None, 'epochs_up': None, 'steps_per_epoch': None,
'anneal_strategy': 'cos', 'cycle_momentum': True, 'base_momentum': 0.85,
'max_momentum': 0.95, 'div_factor': 25.0, 'final_div_factor': 10000.0,
'last_epoch': -1}
elif lr_scheduler == "CosineAnnealingWarmRestarts":
#Set 4 learning rate restarts
lr_scheduler_params = {'T_0': int(num_epochs / 4), 'T_mult': 1, 'eta_min': 1e-6, 'last_epoch': -1}
optimizer_params = None
optimizer_wd_only_on_weight = OPTIMIZER_WD_ONLY_ON_WEIGHTS
if optimizer == "AdamW":
optimizer_params = {
"betas": [0.9, 0.96],
"eps": 1e-8,
"weight_decay": 1e-2
}
elif optimizer == "RMSprop":
optimizer_params = {
"alpha": 0.99,
"eps": 1e-8,
"weight_decay": 1e-4
}
elif optimizer == "SGD":
optimizer_params = {
"momentum": 0.9,
"weight_decay": 1e-4
}
elif optimizer == "Adam":
optimizer_params = {
"betas": [0.9, 0.999],
"eps": 1e-8,
"weight_decay": 1e-4
}
elif optimizer == "Adagrad":
optimizer_params = {
"lr_decay": 0,
"weight_decay": 1e-4,
"eps": 1e-10
}
elif optimizer == "RAdam":
optimizer_params = {
"betas": [0.9, 0.999],
"eps": 1e-8,
"weight_decay": 1e-2
}
elif optimizer == "stepwisegraduallr" or optimizer == "noamlr":
optimizer_params = { }
# These are Hardcoded learning rate schedulers
print(f"[FINETUNE] Learning Scheduler {lr_scheduler}, params {lr_scheduler_params}")
# training parameters config
config = GPTTrainerConfig(
epochs=num_epochs,
@@ -788,13 +940,13 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
# target_loss="loss",
print_eval=False,
# Optimizer values like tortoise, pytorch implementation with modifications to not apply WD to non-weight parameters.
optimizer="AdamW",
optimizer=optimizer,
optimizer_wd_only_on_weights=OPTIMIZER_WD_ONLY_ON_WEIGHTS,
optimizer_params={"betas": [0.9, 0.96], "eps": 1e-8, "weight_decay": 1e-2},
optimizer_params=optimizer_params,
lr=learning_rate, # learning rate
lr_scheduler="MultiStepLR",
lr_scheduler=lr_scheduler,
# it was adjusted accordly for the new step scheme
lr_scheduler_params={"milestones": [50000 * 18, 150000 * 18, 300000 * 18], "gamma": 0.5, "last_epoch": -1},
lr_scheduler_params=lr_scheduler_params,
test_sentences=[],
)
progress(0, desc="Model is currently training. See console for more information")
@@ -808,11 +960,15 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
eval_split_size=config.eval_split_size,
)
global c_logger
c_logger = MetricsLogger()
# init the trainer
trainer = Trainer(
TrainerArgs(
restore_path=None, # xtts checkpoint is restored via xtts_checkpoint key so no need of restore it using Trainer restore_path parameter
skip_train_epoch=False,
continue_path=continue_path,
start_with_eval=START_WITH_EVAL,
grad_accum_steps=GRAD_ACUMM_STEPS,
),
@@ -821,7 +977,15 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
model=model,
train_samples=train_samples,
eval_samples=eval_samples,
training_assets=training_assets,
c_logger=c_logger,
)
if(disable_shared_memory):
# Limit training to GPU memory instead of shared memory
print("[FINETUNE] Limiting GPU memory to 95% to prevent spillover")
torch.cuda.set_per_process_memory_fraction(0.95)
trainer.fit()
# get the longest text audio file to use as speaker reference
@@ -884,7 +1048,13 @@ def run_tts(lang, tts_text, speaker_audio_file):
if XTTS_MODEL is None or not speaker_audio_file:
return "You need to run the previous step to load the model !!", None, None
speaker_audio_file = str(speaker_audio_file)
gpt_cond_latent, speaker_embedding = XTTS_MODEL.get_conditioning_latents(audio_path=speaker_audio_file, gpt_cond_len=XTTS_MODEL.config.gpt_cond_len, max_ref_length=XTTS_MODEL.config.max_ref_len, sound_norm_refs=XTTS_MODEL.config.sound_norm_refs)
if os.path.isdir(speaker_audio_file):
wavs_files = glob.glob(os.path.join(speaker_audio_file, "*.wav"))
speaker_audio_file = wavs_files[0]
else:
wavs_files = [speaker_audio_file]
gpt_cond_latent, speaker_embedding = XTTS_MODEL.get_conditioning_latents(audio_path=wavs_files, gpt_cond_len=XTTS_MODEL.config.gpt_cond_len, max_ref_length=XTTS_MODEL.config.max_ref_len, sound_norm_refs=XTTS_MODEL.config.sound_norm_refs)
out = XTTS_MODEL.inference(
text=tts_text,
language=lang,
@@ -904,33 +1074,45 @@ def run_tts(lang, tts_text, speaker_audio_file):
return "Speech generated !", out_path, speaker_audio_file
def get_available_voices(minimum_size_kb=1200):
def get_available_voices(minimum_size_kb=1200, speaker_name=None):
directory = out_path
if speaker_name and speaker_name != 'personsname':
directory = this_dir / "finetune" / speaker_name
voice_files = [
voice for voice in Path(f"{this_dir}/finetune/tmp-trn/wavs").glob("*.wav")
voice for voice in Path(f"{directory}/wavs").glob("*.wav")
if voice.stat().st_size > minimum_size_kb * 1200 # Convert KB to bytes
]
return sorted([str(file) for file in voice_files]) # Return full path as string
def find_best_models(directory):
def find_best_models(directory, speaker_name=None):
if speaker_name and speaker_name != 'personsname':
directory = this_dir / "finetune" / speaker_name
"""Find files named 'best_model.pth' in the given directory."""
return [str(file) for file in Path(directory).rglob("best_model.pth")]
def find_models(directory, extension):
def find_models(directory, extension, speaker_name=None):
if speaker_name and speaker_name != 'personsname':
directory = this_dir / "finetune" / speaker_name
"""Find files with a specific extension in the given directory."""
return [str(file) for file in Path(directory).rglob(f"*.{extension}")]
def find_jsons(directory, filename):
def find_jsons(directory, filename, speaker_name=None):
if speaker_name and speaker_name != 'personsname':
directory = this_dir / "finetune" / speaker_name
"""Find files with a specific filename in the given directory."""
return [str(file) for file in Path(directory).rglob(filename)]
# Your main directory
main_directory = Path(this_dir) / "finetune" / "tmp-trn"
# XTTS checkpoint files (best_model.pth)
xtts_checkpoint_files = find_best_models(main_directory)
xtts_checkpoint_files = find_best_models(out_path)
# XTTS config files (config.json)
xtts_config_files = find_jsons(main_directory, "config.json")
xtts_config_files = find_jsons(out_path, "config.json")
# XTTS vocab files (vocab.json)
xtts_vocab_files = find_jsons(main_directory, "vocab.json")
xtts_vocab_files = find_jsons(out_path, "vocab.json")
##########################
#### STEP 4 AND OTHER ####
@@ -976,11 +1158,11 @@ def compact_custom_model(xtts_checkpoint_copy, folder_path, overwrite_existing):
files_to_copy = ["vocab.json", "config.json"]
copy_files(folder_path_new, target_dir, files_to_copy)
# Copy second set of files
chkptandnorm_path = this_dir / "finetune" / "tmp-trn" / "chkptandnorm"
chkptandnorm_path = out_path / "chkptandnorm"
files_to_copy2 = ["speakers_xtts.pth", "mel_stats.pth", "dvae.pth"]
copy_files(chkptandnorm_path, target_dir, files_to_copy2)
# Copy large wav files
source_wavs_dir = this_dir / "finetune" / "tmp-trn" / "wavs"
source_wavs_dir = out_path / "wavs"
target_wavs_dir = target_dir / "wavs"
target_wavs_dir.mkdir(parents=True, exist_ok=True)
for file_path in source_wavs_dir.iterdir():
@@ -991,7 +1173,7 @@ def compact_custom_model(xtts_checkpoint_copy, folder_path, overwrite_existing):
def delete_training_data():
# Define the folder to be deleted
folder_to_delete = Path(this_dir / "finetune" / "tmp-trn")
folder_to_delete = Path(out_path)
# Check if the folder exists before deleting
if folder_to_delete.exists():
@@ -1077,6 +1259,9 @@ logging.basicConfig(
]
)
c_logger = MetricsLogger()
def load_metrics():
return c_logger.plot_metrics(), f"Running Time: {c_logger.format_duration(c_logger.total_duration)} - Estimated Completion: {c_logger.format_duration(c_logger.estimated_duration)}"
def read_logs():
sys.stdout.flush()
with open(sys.stdout.log_file, "r") as f:
@@ -1088,10 +1273,10 @@ def cleanup_before_exit(signum, frame):
sys.exit(0)
def create_refresh_button(refresh_components, refresh_methods, elem_class, interactive=True):
def refresh():
def refresh(speaker_name):
updates = {}
for component, method in zip(refresh_components, refresh_methods):
args = method() if callable(method) else method
args = method(speaker_name=speaker_name) if callable(method) else method
if args and 'choices' in args:
# Select the most recent file (last in the sorted list)
args['value'] = args['choices'][-1] if args['choices'] else ""
@@ -1103,12 +1288,33 @@ def create_refresh_button(refresh_components, refresh_methods, elem_class, inter
refresh_button = gr.Button("Refresh Dropdowns", elem_classes=elem_class, interactive=interactive)
refresh_button.click(
fn=refresh,
inputs=[],
inputs=[speaker_name_input_testing],
outputs=refresh_components
)
return refresh_button
def create_refresh_button_next(refresh_components, refresh_methods, elem_class, interactive=True):
def refresh_export(speaker_name):
updates = {}
for component, method in zip(refresh_components, refresh_methods):
args = method(speaker_name=speaker_name) if callable(method) else method
if args and 'choices' in args:
# Select the most recent file (last in the sorted list)
args['value'] = args['choices'][-1] if args['choices'] else ""
for k, v in args.items():
setattr(component, k, v)
updates[component] = gr.update(**(args or {}))
return updates
refresh_button = gr.Button("Refresh Dropdowns", elem_classes=elem_class, interactive=interactive)
refresh_button.click(
fn=refresh_export,
inputs=[speaker_name_input_export],
outputs=refresh_components
)
return refresh_button
pfc_markdown = f"""
@@ -1315,11 +1521,11 @@ if __name__ == "__main__":
#######################
with gr.Tab("📁 Step 1 - Generating the dataset"):
with gr.Tab("Generate Dataset"):
out_path = gr.Textbox(
label="Output path (where data and checkpoints will be saved):",
value=out_path,
visible=False,
)
# out_path = gr.Textbox(
# label="Output path (where data and checkpoints will be saved):",
# value=out_path,
# visible=False,
# )
# Define directories
this_dir = Path(__file__).parent.resolve()
voice_samples_dir = this_dir / "finetune" / "put-voice-samples-in-here"
@@ -1381,6 +1587,13 @@ if __name__ == "__main__":
delete_dataset_button.click(delete_existing_training_data, outputs=output_text)
with gr.Row():
speaker_name_input = gr.Textbox(
label="Training Project Name",
value="personsname",
visible=True,
scale=2,
info='Create Unique Training Folder',
)
whisper_model = gr.Dropdown(
label="Whisper Model",
value="large-v2",
@@ -1431,12 +1644,11 @@ if __name__ == "__main__":
step=1, # Increment step
scale=2,
)
speaker_name_input = gr.Textbox(
label="The name of the speaker/person you are training",
value="personsname",
visible=True,
scale=2,
)
create_bpe_tokenizer = gr.Checkbox(
label='BPE Tokenizer',
value=False,
info='Custom Tokenizer for training'
)
progress_data = gr.Label(
label="Progress:"
)
@@ -1449,7 +1661,7 @@ if __name__ == "__main__":
prompt_compute_btn = gr.Button(value="Step 1 - Create dataset")
def preprocess_dataset(language, whisper_model, out_path, max_sample_length, eval_split_number, speaker_name_input, progress=gr.Progress()):
def preprocess_dataset(language, whisper_model, max_sample_length, eval_split_number, speaker_name_input, create_bpe_tokenizer, progress=gr.Progress()):
clear_gpu_cache()
test_for_audio_files = [file for file in os.listdir(audio_folder) if any(file.lower().endswith(ext) for ext in ['.wav', '.mp3', '.flac'])]
if not test_for_audio_files:
@@ -1457,7 +1669,7 @@ if __name__ == "__main__":
else:
try:
train_meta, eval_meta, audio_total_size = format_audio_list(target_language=language, whisper_model=whisper_model, out_path=out_path, max_sample_length=max_sample_length, eval_split_number=eval_split_number, speaker_name_input=speaker_name_input, gradio_progress=progress)
train_meta, eval_meta, audio_total_size = format_audio_list(target_language=language, whisper_model=whisper_model, max_sample_length=max_sample_length, eval_split_number=eval_split_number, speaker_name_input=speaker_name_input, create_bpe_tokenizer=create_bpe_tokenizer, gradio_progress=progress)
except:
traceback.print_exc()
error = traceback.format_exc()
@@ -1472,7 +1684,7 @@ if __name__ == "__main__":
return message, "", ""
print("[FINETUNE] Dataset Generated. Either run Dataset Validation or move to Step 2")
return "Dataset Generated. Either run Dataset Validation or move to Step 2", train_meta, eval_meta
return "Dataset Generated. Either run Dataset Validation or move to Step 2", train_meta, eval_meta, speaker_name_input
def play_selected_audio(selected_file):
if selected_file and selected_file != "Please Generate Your Dataset":
@@ -1534,8 +1746,26 @@ if __name__ == "__main__":
#### GRADIO STEP 2 ####
#######################
with gr.Tab("💻 Step 2 - Training"):
with gr.Tab("Training the model"):
with gr.Tab("Training the model"):
with gr.Row():
speaker_name_input_training = gr.Textbox(
label="Project Name",
value="personsname",
visible=True,
info='Create Unique Training Folder',
scale=1,
)
with gr.Group():
continue_run = gr.Checkbox(
value=False,
label="Continue Previous",
scale=1,
)
disable_shared_memory = gr.Checkbox(
value=False,
label="Disable Shared Memory",
scale=1,
)
model_to_train_choices = list(available_models.keys())
model_to_train = gr.Dropdown(
choices=model_to_train_choices,
@@ -1550,6 +1780,7 @@ if __name__ == "__main__":
label="Eval CSV:",
scale=2,
)
with gr.Row():
learning_rates = gr.Dropdown(
value=5e-6,
label="Learning Rate",
@@ -1564,7 +1795,42 @@ if __name__ == "__main__":
],
type="value",
allow_custom_value=True,
scale=0,
scale=1,
)
learning_rate_scheduler = gr.Dropdown(
value="None",
label="Learning Rate Scheduler(s)",
choices=[
("None", "None"),
("Cosine Annealing", "CosineAnnealingLR"),
("Cosine Annealing Warm Restarts", "CosineAnnealingWarmRestarts"),
("Cyclic", "CyclicLR"),
("Exponential", "ExponentialLR"),
("Multi Step", "MultiStepLR"),
("Reduce on Plateau", "ReduceLROnPlateau"),
("Step", "StepLR")
#("OneCycleLR", "OneCycleLR"),
],
type="value",
allow_custom_value=False,
scale=2,
)
optimizer = gr.Dropdown(
value="AdamW",
label="Optimizer",
choices=[
("AdamW", "AdamW"),
("SGD with Momentum", "SGD"),
("RMSprop", "RMSprop"),
("Adagrad", "Adagrad"),
("Adam", "Adam"),
("Rectified Adam", "RAdam"),
("Step Wise", "stepwisegraduallr"),
("Noam", "noamlr")
],
type="value",
allow_custom_value=False,
scale=2,
)
with gr.Row():
num_epochs = gr.Slider(
@@ -1599,24 +1865,41 @@ if __name__ == "__main__":
progress_train = gr.Label(
label="Progress:"
)
with gr.Row():
train_time = gr.Label("Estimated Total Training Time", show_label=False)
with gr.Row():
model_data = gr.Image(c_logger.plot_metrics(), show_label=False)
logs_tts_train = gr.Textbox(
label="Logs:",
interactive=False,
lines=10,
)
demo.load(load_metrics, None, [model_data, train_time], every=1)
demo.load(read_logs, None, logs_tts_train, every=1)
train_btn = gr.Button(value="Step 2 - Run the training")
def train_model(language, train_csv, eval_csv, learning_rates, model_to_train, num_epochs, batch_size, grad_acumm, output_path, max_audio_length, progress=gr.Progress()):
def train_model(language, train_csv, eval_csv, learning_rates, model_to_train, num_epochs, batch_size, grad_acumm, max_audio_length, speaker_name_input_training, continue_run, disable_shared_memory, learning_rate_scheduler, optimizer, progress=gr.Progress()):
clear_gpu_cache()
global out_path
if speaker_name_input_training and speaker_name_input_training != 'personsname':
out_path = this_dir / "finetune" / speaker_name_input_training
if not train_csv or not eval_csv:
return "You need to run the data processing step or manually set `Train CSV` and `Eval CSV` fields !", "", "", "", ""
if (out_path / "metadata_eval.csv").exists() and (out_path / "metadata_train.csv").exists():
train_csv = out_path / "metadata_eval.csv"
eval_csv = out_path / "metadata_train.csv"
print("Using existing metadata and training csv.")
else:
return "You need to run the data processing step or manually set `Train CSV` and `Eval CSV` fields !", "", "", "", ""
try:
# convert seconds to waveform frames
max_audio_length = int(max_audio_length * 22050)
learning_rate = float(learning_rates) # Convert the learning rate value to a float
progress(0, "Initializing training...")
config_path, original_xtts_checkpoint, vocab_file, exp_path, speaker_wav = train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, model_to_train, output_path=str(output_path), max_audio_length=max_audio_length, progress=gr.Progress())
config_path, original_xtts_checkpoint, vocab_file, exp_path, speaker_wav = train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, model_to_train, continue_run, disable_shared_memory, learning_rate_scheduler, optimizer, max_audio_length=max_audio_length, progress=gr.Progress())
except:
traceback.print_exc()
error = traceback.format_exc()
@@ -1629,7 +1912,7 @@ if __name__ == "__main__":
ft_xtts_checkpoint = os.path.join(exp_path, "best_model.pth")
print("[FINETUNE] Model training done. Move to Step 3")
clear_gpu_cache()
return "Model training done. Move to Step 3", config_path, vocab_file, ft_xtts_checkpoint, speaker_wav
return "Model training done. Move to Step 3", config_path, vocab_file, ft_xtts_checkpoint, speaker_wav, speaker_name_input_training
with gr.Tab("Training the model Instructions"):
gr.Markdown(
@@ -1699,6 +1982,35 @@ if __name__ == "__main__":
The optimal learning rate depends on the model architecture, dataset, and other hyperparameters.
"""
)
with gr.Tab("Info - Learning Rate Schedulers"):
gr.Markdown(
f"""
### 🟩 <u>Learning Rate Schedulers</u><br>
◽ None: Learning rate remains unchanged throughout the training. <br>
◽ Step: Decreases the learning rate by a factor at specified intervals, offering a straightforward method for controlling learning rate decay.<br>
◽ MultiStep: Decreases the learning rate by a factor at specified milestones, useful for gradually reducing the learning rate during training.<br>
◽ Exponential: Decreases the learning rate exponentially over epochs, providing a simple decay mechanism for training.<br>
◽ CosineAnnealing: Adjusts the learning rate following a cosine annealing schedule, facilitating smoother optimization and potentially better convergence.<br>
◽ Reduce On Plateau: Reduces the learning rate when a monitored metric has stopped improving, helping to fine-tune learning rates during training.<br>
◽ Cyclic: Cycles the learning rate between two boundary values, allowing for dynamic learning rate schedules during training.<br>
◽ One Cycle (disabled): Sets the learning rate using a cyclical learning rate policy and varies momentum inversely, potentially accelerating training and improving performance.<br>
◽ Cosine Annealing Warm Restarts: Adjusts the learning rate following a cosine annealing schedule with warm restarts, offering a balance between exploration and exploitation during optimization.<br><br>
The choice of learning rate scheduler depends on the specific characteristics of the model, dataset, and training objectives.
"""
)
with gr.Tab("Info - Optimizers"):
gr.Markdown(
f"""
### 🟩 <u>Optimizers</u><br>
◽ SGD with Momentum: Good for convergence, requires careful tuning of the learning rate and momentum.
◽ RMSprop: Adaptively adjusts learning rates, good for varying gradient magnitudes.
◽ Adam: Widely used, performs well with default parameters.
◽ Adagrad: Good for sparse data, adapts learning rates based on historical gradients.
◽ AdamW: Similar to Adam but with decoupled weight decay for better regularization.
When picking an optimizer, you need to experiment with different options to find the one that achieves the best performance and convergence for your task.
AdamW has been set to default as its a sensible pick that will handle most needs.
"""
)
with gr.Tab("Info - Epochs"):
gr.Markdown(
f"""
@@ -1778,6 +2090,13 @@ if __name__ == "__main__":
allow_custom_value=True, # Allow custom values
scale=1,
)
speaker_name_input_testing = gr.Textbox(
label="Project Name",
value="personsname",
visible=True,
info='Create Unique Training Folder',
scale=1,
)
with gr.Row():
tts_language = gr.Dropdown(
@@ -1804,12 +2123,13 @@ if __name__ == "__main__":
)
# Create refresh button
refresh_button = create_refresh_button(
[xtts_checkpoint, xtts_config, xtts_vocab, speaker_reference_audio],
[xtts_checkpoint, xtts_config, xtts_vocab, speaker_reference_audio,
speaker_name_input_testing],
[
lambda: {"choices": find_best_models(main_directory), "value": ""},
lambda: {"choices": find_jsons(main_directory, "config.json"), "value": ""},
lambda: {"choices": find_jsons(main_directory, "vocab.json"), "value": ""},
lambda: {"choices": get_available_voices(), "value": ""},
lambda speaker_name: {"choices": find_best_models(out_path, speaker_name=speaker_name), "value": ""},
lambda speaker_name: {"choices": find_jsons(out_path, "config.json", speaker_name=speaker_name), "value": ""},
lambda speaker_name: {"choices": find_jsons(out_path, "vocab.json", speaker_name=speaker_name),"value": ""},
lambda speaker_name: {"choices": get_available_voices(speaker_name=speaker_name),"value": ""},
],
elem_class="refresh-button-class"
)
@@ -1878,11 +2198,18 @@ if __name__ == "__main__":
allow_custom_value=True,
scale=2,
)
speaker_name_input_export = gr.Textbox(
label="Project Name",
value="personsname",
visible=True,
info='Unique Training Folder',
scale=1,
)
# Create refresh button
refresh_button = create_refresh_button(
[xtts_checkpoint_copy,],
refresh_button = create_refresh_button_next(
[xtts_checkpoint_copy,speaker_name_input_export],
[
lambda: {"choices": find_best_models(main_directory), "value": ""},
lambda speaker_name: {"choices": find_best_models(out_path, speaker_name), "value": ""},
],
elem_class="refresh-button-class")
with gr.Row():
@@ -1901,15 +2228,16 @@ if __name__ == "__main__":
inputs=[
lang,
whisper_model,
out_path,
max_sample_length,
eval_split_number,
speaker_name_input,
create_bpe_tokenizer
],
outputs=[
progress_data,
train_csv,
eval_csv,
speaker_name_input_training
],
)
@@ -1925,10 +2253,14 @@ if __name__ == "__main__":
num_epochs,
batch_size,
grad_acumm,
out_path,
max_audio_length,
speaker_name_input_training,
continue_run,
disable_shared_memory,
learning_rate_scheduler,
optimizer
],
outputs=[progress_train, xtts_config, xtts_vocab, xtts_checkpoint, speaker_reference_audio],
outputs=[progress_train, xtts_config, xtts_vocab, xtts_checkpoint, speaker_reference_audio, speaker_name_input_testing],
)
load_btn.click(
+6
View File
@@ -0,0 +1,6 @@
@echo off
cd /D "C:\AI\text-generation-webui\extensions\alltalk_tts\"
set CONDA_ROOT_PREFIX=C:\AI\text-generation-webui\extensions\alltalk_tts\alltalk_environment\conda
set INSTALL_ENV_DIR=C:\AI\text-generation-webui\extensions\alltalk_tts\alltalk_environment\env
call "C:\AI\text-generation-webui\extensions\alltalk_tts\alltalk_environment\conda\condabin\conda.bat" activate "C:\AI\text-generation-webui\extensions\alltalk_tts\alltalk_environment\env"
call python diagnostics.py
+8 -2
View File
@@ -1,6 +1,7 @@
###############################################
# DONT CHANGE # These are base imports needed #
###############################################
import glob
import os
import sys
import json
@@ -471,10 +472,15 @@ class tts_class:
print(f"[{self.branding}Debug] Deciding if streaming or not") if self.debug_tts else None
print(f"[{self.branding}Debug] self.current_model_loaded", self.current_model_loaded) if self.debug_tts else None
self.current_model_loaded
if os.path.isdir(voice):
wavs_files = glob.glob(os.path.join(voice, "*.wav"))
else:
wavs_files = [f"{self.main_dir}/voices/{voice}"]
if self.current_model_loaded.startswith ("xtts"):
print(f"[{self.branding}Debug] Text arriving at engine {text}") if self.debug_tts else None
gpt_cond_latent, speaker_embedding = self.model.get_conditioning_latents(
audio_path=[f"{self.main_dir}/voices/{voice}"],
audio_path=wavs_files,
gpt_cond_len=self.model.config.gpt_cond_len,
max_ref_length=self.model.config.max_ref_len,
sound_norm_refs=self.model.config.sound_norm_refs,
@@ -551,7 +557,7 @@ class tts_class:
self.model.tts_to_file(
text=text,
file_path=output_file,
speaker_wav=[f"{self.main_dir}/voices/{voice}"],
speaker_wav=wavs_files,
language=language,
temperature=temperature,
length_penalty=self.model.config.length_penalty,
+1 -1
View File
@@ -1089,7 +1089,7 @@ def process_rvc_narrator(part_type, voice_gen, pitch, default_model_file, output
class JSONInput(BaseModel):
text_input: str = Field(..., max_length=int(api_defaults["api_max_characters"]), description=f"text_input needs to be {api_defaults['api_max_characters']} characters or less.")
text_filtering: str = Field(..., pattern="^(none|standard|html)$", description="text_filtering needs to be 'none', 'standard' or 'html'.")
character_voice_gen: str = Field(..., pattern=r'^[\(\)a-zA-Z0-9\_\-./\s]+$', description="character_voice_gen needs to be the name of a valid voice for the loaded TTS engine.")
#character_voice_gen: str = Field(..., pattern=r'^[\(\)a-zA-Z0-9\_\-./\s]+$', description="character_voice_gen needs to be the name of a valid voice for the loaded TTS engine.")
rvccharacter_voice_gen: str = Field(..., description="rvccharacter_voice_gen needs to be the name of a valid pth file in the 'folder\\file.pth' format or the word 'Disabled'.")
rvccharacter_pitch: float = Field(..., description="RVC Character pitch needs to be a number between -24 and 24")
narrator_enabled: str = Field(..., pattern="^(true|false|silent)$", description="narrator_enabled needs to be 'true', 'false' or 'silent'.")