diff --git a/app.py b/app.py index 2947e5e..714ebec 100644 --- a/app.py +++ b/app.py @@ -4,9 +4,8 @@ import os import sys import gradio as gr -import numpy as np import torch -from gradio.processing_utils import convert_to_16_bit_wav +import yaml from common.constants import ( DEFAULT_ASSIST_TEXT_WEIGHT, @@ -22,7 +21,12 @@ from common.constants import ( ) from common.log import logger from common.tts_model import ModelHolder -from config import config + +# Get path settings +with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: + path_config: dict[str, str] = yaml.safe_load(f.read()) + # dataset_root = path_config["dataset_root"] + assets_root = path_config["assets_root"] languages = [l.value for l in Languages] @@ -182,7 +186,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--cpu", action="store_true", help="Use CPU instead of GPU") parser.add_argument( - "--dir", "-d", type=str, help="Model directory", default=config.out_dir + "--dir", "-d", type=str, help="Model directory", default=assets_root ) parser.add_argument( "--share", action="store_true", help="Share this app publicly", default=False diff --git a/colab.ipynb b/colab.ipynb index 468a918..13596d6 100644 --- a/colab.ipynb +++ b/colab.ipynb @@ -6,9 +6,10 @@ "source": [ "# Style-Bert-VITS2 (ver 1.2) のGoogle Colabでの学習\n", "\n", - "Google driveと連携することで、Google Colab上でStyle-Bert-VITS2の学習を行うことができます。\n", + "Google Colab上でStyle-Bert-VITS2の学習を行うことができます。\n", "\n", - "このnotebookでは、あなたのGoogle Driveにフォルダ`Style-Bert-VITS2`を作り、その内部での作業を行います。他のフォルダには触れません。\n", + "このnotebookでは、通常使用ではあなたのGoogle Driveにフォルダ`Style-Bert-VITS2`を作り、その内部での作業を行います。他のフォルダには触れません。\n", + "Google Driveを使わない場合は、初期設定のところで適切なパスを指定してください。\n", "\n", "## 流れ\n", "\n", @@ -16,16 +17,75 @@ "上から順に実行していけばいいです。音声合成に必要なファイルはGoogle Driveの`Style-Bert-VITS2/model_assets/`に保存されます。また、途中経過も`Style-Bert-VITS2/Data/`に保存されるので、学習を中断したり、途中から再開することもできます。\n", "\n", "### 学習を途中から再開したいとき\n", - "1と2を行い、3の前処理は飛ばして、4から始めてください。\n" + "0と1と2を行い、3の前処理は飛ばして、4から始めてください。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Google Driveとの連携とデータの配置\n", + "## 0. 環境構築\n", "\n", - "次のセルを実行して、Google driveと連携します。" + "Style-Bert-VITS2の環境をcolab上に構築します。グラボモードが有効になっていることを確認し、以下のセルを順に実行してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#@title このセルを実行して環境構築してください。\n", + "#@markdown 最後に赤文字でエラーや警告が出ても何故かうまくいくみたいです。\n", + "\n", + "!git clone https://github.com/litagin02/Style-Bert-VITS2.git\n", + "%cd Style-Bert-VITS2/\n", + "!pip install -r requirements.txt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#@title 必要なモデルのダウンロード\n", + "\n", + "!python initialize.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. 初期設定\n", + "\n", + "学習とその結果を保存するディレクトリ名を指定します。\n", + "Google driveの場合はそのまま実行、カスタマイズしたい方は変更して実行してください。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "dataset_root = \"/content/drive/MyDrive/Style-Bert-VITS2/Data\"\n", + "assets_root = \"/content/drive/MyDrive/Style-Bert-VITS2/model_assets\"\n", + "\n", + "import yaml\n", + "\n", + "with open(\"configs/paths.yml\", \"w\", encoding=\"utf-8\") as f:\n", + " yaml.dump({\"dataset_root\": dataset_root, \"assets_root\": assets_root}, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Google Driveとの連携とデータの配置\n", + "\n", + "Google driveにデータを保存する方は、次のセルを実行して、Google driveと連携します。" ] }, { @@ -48,7 +108,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "次のセルを実行して、Google driveの中にStyle-Bert-VITS2フォルダと、学習データをいれるDataフォルダを作成します。" + "次のセルを実行して、学習データをいれるフォルダ(1で設定した`dataset_root`)を作成します。" ] }, { @@ -61,7 +121,7 @@ "source": [ "import os\n", "\n", - "os.makedirs((\"/content/drive/MyDrive/Style-Bert-VITS2/Data/\"), exist_ok=True)" + "os.makedirs(dataset_root, exist_ok=True)" ] }, { @@ -104,55 +164,6 @@ "```" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. 環境構築\n", - "\n", - "Style-Bert-VITS2の環境をcolab上に構築します。グラボモードが有効になっていることを確認し、以下のセルを順に実行してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 - }, - "id": "0CH9aI_MOmTA", - "outputId": "34cf1e8f-23b9-4d2b-e040-ea5a3ec44a82" - }, - "outputs": [], - "source": [ - "#@title このセルを実行して環境構築してください。\n", - "#@markdown 最後に赤文字でエラーや警告が出ても何故かうまくいくみたいです。\n", - "\n", - "!git clone https://github.com/litagin02/Style-Bert-VITS2.git\n", - "%cd Style-Bert-VITS2/\n", - "!pip install -r requirements.txt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "CpLs5YoNPe4Z", - "outputId": "0edd3309-08d1-4372-e47a-6a805a74b620" - }, - "outputs": [], - "source": [ - "#@title 必要なモデルのダウンロード\n", - "\n", - "!python initialize.py" - ] - }, { "cell_type": "markdown", "metadata": { @@ -178,7 +189,8 @@ "# 学習のバッチサイズ。4ぐらいが最適か。VRAMのはみ出具合に応じて調整してください。\n", "batch_size = 4\n", "\n", - "# 学習のエポック数(データセットを合計何周するか)。100ぐらいで十分だと思われます。\n", + "# 学習のエポック数(データセットを合計何周するか)。\n", + "# 100ぐらいで十分かもしれませんが、もっと多くやると質が上がるのかもしれません。\n", "epochs = 100\n", "\n", "# 保存頻度。何ステップごとにモデルを保存するか。分からなければデフォルトのままで。\n", @@ -256,15 +268,15 @@ "from webui_train import get_path\n", "\n", "dataset_path, _, _, _, config_path = get_path(model_name)\n", + "\n", "with open(\"default_config.yml\", \"r\", encoding=\"utf-8\") as f:\n", " yml_data = yaml.safe_load(f)\n", "yml_data[\"model_name\"] = model_name\n", - "yml_data[\"dataset_path\"] = dataset_path\n", "with open(\"config.yml\", \"w\", encoding=\"utf-8\") as f:\n", " yaml.dump(yml_data, f, allow_unicode=True)\n", "\n", "\n", - "!python train_ms.py --config {config_path} --model {dataset_path}" + "!python train_ms.py --config {config_path} --model {dataset_path} --asset_root {assets_root}" ] }, { @@ -280,7 +292,7 @@ "outputs": [], "source": [ "#@title 学習結果を試すならここから\n", - "!python app.py --share --dir \"/content/drive/MyDrive/Style-Bert-VITS2/model_assets/\"" + "!python app.py --share --dir {assets_root}" ] } ], @@ -295,7 +307,16 @@ "name": "python3" }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" } }, "nbformat": 4, diff --git a/config.py b/config.py index 0acfe76..db42372 100644 --- a/config.py +++ b/config.py @@ -205,7 +205,7 @@ class Translate_config: class Config: - def __init__(self, config_path: str): + def __init__(self, config_path: str, path_config: dict[str, str]): if not os.path.isfile(config_path) and os.path.isfile("default_config.yml"): shutil.copy(src="default_config.yml", dst=config_path) print( @@ -222,12 +222,10 @@ class Config: if "dataset_path" in yaml_config: dataset_path = yaml_config["dataset_path"] else: - dataset_path = f"Data/{model_name}" - self.out_dir = yaml_config["out_dir"] - # openi_token: str = yaml_config["openi_token"] + dataset_path = os.path.join(path_config["dataset_root"], model_name) self.dataset_path: str = dataset_path - # self.mirror: str = yaml_config["mirror"] - # self.openi_token: str = openi_token + self.assets_root: str = path_config["assets_root"] + self.out_dir = os.path.join(self.assets_root, model_name) self.resample_config: Resample_config = Resample_config.from_dict( dataset_path, yaml_config["resample"] ) @@ -256,14 +254,27 @@ class Config: # ) +with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: + path_config: dict[str, str] = yaml.safe_load(f.read()) + # Should contain the following keys: + # - dataset_root: the root directory of the dataset, default to "Data" + # - assets_root: the root directory of the assets, default to "model_assets" + parser = argparse.ArgumentParser() # 为避免与以前的config.json起冲突,将其更名如下 -parser.add_argument("-y", "--yml_config", type=str, default="config.yml") -args, _ = parser.parse_known_args() +parser.add_argument( + "-y", + "--yml_config", + type=str, + default="config.yml", + help="Path to setting yaml file.", +) + +args = parser.parse_args() try: - config = Config(args.yml_config) + config = Config(args.yml_config, path_config) except TypeError: logger.warning("Old config.yml found. Replace it with default_config.yml.") shutil.copy(src="default_config.yml", dst="config.yml") - config = Config("config.yml") + config = Config("config.yml", path_config) diff --git a/configs/paths.yml b/configs/paths.yml new file mode 100644 index 0000000..d743748 --- /dev/null +++ b/configs/paths.yml @@ -0,0 +1,8 @@ +# Root directory of the training dataset. +# The training dataset of {model_name} should be placed in {dataset_root}/{model_name}. +dataset_root: Data + +# Root directory of the model assets (for inference). +# In training, the model assets will be saved to {assets_root}/{model_name}, +# and in inference, we load all the models from {assets_root}. +assets_root: model_assets diff --git a/default_config.yml b/default_config.yml index f7fc9c5..ab5bebc 100644 --- a/default_config.yml +++ b/default_config.yml @@ -1,24 +1,20 @@ -# Global configuration file for Bert-VITS2 - model_name: "model_name" -out_dir: "model_assets" - # If you want to use a specific dataset path, uncomment the following line. -# Otherwise, the dataset path is `Data/{model_name}`. +# Otherwise, the dataset path is `{dataset_root}/{model_name}`. # dataset_path: "your/dataset/path" resample: sampling_rate: 44100 - in_dir: "audios/raw" - out_dir: "audios/wavs" + in_dir: "raw" + out_dir: "wavs" preprocess_text: - transcription_path: "filelists/esd.list" + transcription_path: "esd.list" cleaned_path: "" - train_path: "filelists/train.list" - val_path: "filelists/val.list" + train_path: "train.list" + val_path: "val.list" config_path: "config.json" val_per_lang: 4 max_val_total: 12 @@ -42,13 +38,13 @@ train_ms: WORLD_SIZE: 1 LOCAL_RANK: 0 RANK: 0 - model: "models" + model_dir: "models" # The directory to save the model (for training), relative to `{dataset_root}/{model_name}`. config_path: "config.json" num_workers: 16 spec_cache: True keep_ckpts: 1 # Set this to 0 to keep all checkpoints -webui: +webui: # For `webui.py`, which is not supported yet in Style-Bert-VITS2. # 推理设备 device: "cuda" # 模型路径 diff --git a/default_style.py b/default_style.py index ced093c..9198ca8 100644 --- a/default_style.py +++ b/default_style.py @@ -12,7 +12,7 @@ def set_style_config(json_path, output_path): json_dict["data"]["num_styles"] = 1 json_dict["data"]["style2id"] = {DEFAULT_STYLE: 0} with open(output_path, "w", encoding="utf-8") as f: - json.dump(json_dict, f, indent=2) + json.dump(json_dict, f, indent=2, ensure_ascii=False) logger.info(f"Save style config (only {DEFAULT_STYLE}) to {output_path}") diff --git a/server_fastapi.py b/server_fastapi.py index b0cecd5..40877fd 100644 --- a/server_fastapi.py +++ b/server_fastapi.py @@ -67,7 +67,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--cpu", action="store_true", help="Use CPU instead of GPU") parser.add_argument( - "--dir", "-d", type=str, help="Model directory", default=config.out_dir + "--dir", "-d", type=str, help="Model directory", default=config.assets_root ) args = parser.parse_args() diff --git a/train_ms.py b/train_ms.py index e494bf2..f508f77 100644 --- a/train_ms.py +++ b/train_ms.py @@ -19,6 +19,8 @@ from tqdm import tqdm import commons import default_style import utils +from common.log import logger +from common.stdout_wrapper import SAFE_STDOUT from config import config from data_utils import ( DistributedBucketSampler, @@ -29,8 +31,6 @@ from losses import discriminator_loss, feature_loss, generator_loss, kl_loss from mel_processing import mel_spectrogram_torch, spec_to_mel_torch from models import DurationDiscriminator, MultiPeriodDiscriminator, SynthesizerTrn from text.symbols import symbols -from common.log import logger -from common.stdout_wrapper import SAFE_STDOUT torch.backends.cuda.matmul.allow_tf32 = True torch.backends.cudnn.allow_tf32 = ( @@ -44,12 +44,6 @@ torch.backends.cuda.enable_mem_efficient_sdp( ) # Not available if torch version is lower than 2.0 torch.backends.cuda.enable_math_sdp(True) -try: - import google.colab - - IS_COLAB = True -except ImportError: - IS_COLAB = False global_step = 0 @@ -86,7 +80,7 @@ def run(): # 命令行/config.yml配置解析 # hps = utils.get_hparams() parser = argparse.ArgumentParser() - # 非必要不建议使用命令行配置,请使用config.yml文件 + # Command line configuration is not recommended unless necessary, use config.yml parser.add_argument( "-c", "--config", @@ -102,12 +96,26 @@ def run(): help="数据集文件夹路径,请注意,数据不再默认放在/logs文件夹下。如果需要用命令行配置,请声明相对于根目录的路径", default=config.dataset_path, ) + parser.add_argument( + "--assets_root", + type=str, + help="Root directory of model assets needed for inference.", + default=config.assets_root, + ) + parser.add_argument( + "--skip_default_style", + action="store_true", + help="Skip saving default style config and mean vector.", + ) args = parser.parse_args() model_dir = os.path.join(args.model, config.train_ms_config.model) if not os.path.exists(model_dir): os.makedirs(model_dir) hps = utils.get_hparams_from_file(args.config) + + # This is needed because we have to pass `model_dir` to `train_and_evaluate()` hps.model_dir = model_dir + # 比较路径是否相同 if os.path.realpath(args.config) != os.path.realpath( config.train_ms_config.config_path @@ -125,31 +133,27 @@ def run(): args.model: For saving all info needed for training. default: `Data/{model_name}`. - hps.model_dir = model_dir: For saving checkpoints (for resuming training). + hps.model_dir := model_dir: For saving checkpoints (for resuming training). default: `Data/{model_name}/models`. + (Use `hps` since we have to pass `model_dir` to `train_and_evaluate()`. - config.out_dir: Root directory of model assets needed for inference. - default: `model_assets`. + args.assets_root: The root directory of model assets needed for inference. + default: config.assets_root == `model_assets`. - out_dir: For saving resulting models (for inference). - default: `model_assets/{model_name}`, which is used for inference. + config.out_dir: The directory for model assets of this model (for inference). + default: `model_assets/{model_name}`. """ - if IS_COLAB: - config.out_dir = "/content/drive/MyDrive/Style-Bert-VITS2/model_assets" - logger.info( - "Colab detected, so use mounted Google Drive as directory for saving resulting models:" - ) - logger.info(config.out_dir) - os.makedirs(config.out_dir, exist_ok=True) - out_dir = os.path.join(config.out_dir, config.model_name) - os.makedirs(out_dir, exist_ok=True) + os.makedirs(config.out_dir, exist_ok=True) - # Save default style to out_dir - default_style.set_style_config(args.config, os.path.join(out_dir, "config.json")) - default_style.save_mean_vector( - os.path.join(args.model, "wavs"), - os.path.join(out_dir, "style_vectors.npy"), - ) + if not args.skip_default_style: + # Save default style to out_dir + default_style.set_style_config( + args.config, os.path.join(config.out_dir, "config.json") + ) + default_style.save_mean_vector( + os.path.join(args.model, "wavs"), + os.path.join(config.out_dir, "style_vectors.npy"), + ) torch.manual_seed(hps.train.seed) torch.cuda.set_device(local_rank) @@ -158,10 +162,10 @@ def run(): if rank == 0: # logger = utils.get_logger(hps.model_dir) # logger.info(hps) - logger.add(os.path.join(hps.model_dir, "train.log")) - utils.check_git_hash(hps.model_dir) - writer = SummaryWriter(log_dir=hps.model_dir) - writer_eval = SummaryWriter(log_dir=os.path.join(hps.model_dir, "eval")) + logger.add(os.path.join(model_dir, "train.log")) + utils.check_git_hash(model_dir) + writer = SummaryWriter(log_dir=model_dir) + writer_eval = SummaryWriter(log_dir=os.path.join(model_dir, "eval")) train_dataset = TextAudioSpeakerLoader(hps.data.training_files, hps.data) train_sampler = DistributedBucketSampler( train_dataset, @@ -284,10 +288,10 @@ def run(): net_dur_disc, device_ids=[local_rank], find_unused_parameters=True ) - if utils.is_resuming(hps.model_dir): + if utils.is_resuming(model_dir): if net_dur_disc is not None: _, _, dur_resume_lr, epoch_str = utils.load_checkpoint( - utils.latest_checkpoint_path(hps.model_dir, "DUR_*.pth"), + utils.latest_checkpoint_path(model_dir, "DUR_*.pth"), net_dur_disc, optim_dur_disc, skip_optimizer=hps.train.skip_optimizer @@ -297,7 +301,7 @@ def run(): if not optim_dur_disc.param_groups[0].get("initial_lr"): optim_dur_disc.param_groups[0]["initial_lr"] = dur_resume_lr _, optim_g, g_resume_lr, epoch_str = utils.load_checkpoint( - utils.latest_checkpoint_path(hps.model_dir, "G_*.pth"), + utils.latest_checkpoint_path(model_dir, "G_*.pth"), net_g, optim_g, skip_optimizer=hps.train.skip_optimizer @@ -305,7 +309,7 @@ def run(): else True, ) _, optim_d, d_resume_lr, epoch_str = utils.load_checkpoint( - utils.latest_checkpoint_path(hps.model_dir, "D_*.pth"), + utils.latest_checkpoint_path(model_dir, "D_*.pth"), net_d, optim_d, skip_optimizer=hps.train.skip_optimizer @@ -320,7 +324,7 @@ def run(): epoch_str = max(epoch_str, 1) # global_step = (epoch_str - 1) * len(train_loader) global_step = int( - utils.get_steps(utils.latest_checkpoint_path(hps.model_dir, "G_*.pth")) + utils.get_steps(utils.latest_checkpoint_path(model_dir, "G_*.pth")) ) logger.info( f"******************检测到模型存在,epoch为 {epoch_str},gloabl step为 {global_step}*********************" @@ -328,14 +332,14 @@ def run(): else: try: _ = utils.load_safetensors( - os.path.join(hps.model_dir, "G_0.safetensors"), net_g + os.path.join(model_dir, "G_0.safetensors"), net_g ) _ = utils.load_safetensors( - os.path.join(hps.model_dir, "D_0.safetensors"), net_d + os.path.join(model_dir, "D_0.safetensors"), net_d ) if net_dur_disc is not None: _ = utils.load_safetensors( - os.path.join(hps.model_dir, "DUR_0.safetensors"), net_dur_disc + os.path.join(model_dir, "DUR_0.safetensors"), net_dur_disc ) logger.info("Loaded the pretrained models.") except Exception as e: @@ -402,14 +406,14 @@ def run(): optim_g, hps.train.learning_rate, epoch, - os.path.join(hps.model_dir, "G_{}.pth".format(global_step)), + os.path.join(model_dir, "G_{}.pth".format(global_step)), ) utils.save_checkpoint( net_d, optim_d, hps.train.learning_rate, epoch, - os.path.join(hps.model_dir, "D_{}.pth".format(global_step)), + os.path.join(model_dir, "D_{}.pth".format(global_step)), ) if net_dur_disc is not None: utils.save_checkpoint( @@ -417,13 +421,13 @@ def run(): optim_dur_disc, hps.train.learning_rate, epoch, - os.path.join(hps.model_dir, "DUR_{}.pth".format(global_step)), + os.path.join(model_dir, "DUR_{}.pth".format(global_step)), ) utils.save_safetensors( net_g, epoch, os.path.join( - out_dir, + config.out_dir, f"{config.model_name}_e{epoch}_s{global_step}.safetensors", ), for_infer=True, @@ -690,12 +694,12 @@ def train_and_evaluate( n_ckpts_to_keep=keep_ckpts, sort_by_time=True, ) + # Save safetensors (for inference) to `model_assets/{model_name}` utils.save_safetensors( net_g, epoch, os.path.join( config.out_dir, - config.model_name, f"{config.model_name}_e{epoch}_s{global_step}.safetensors", ), for_infer=True, diff --git a/webui_dataset.py b/webui_dataset.py index 3aeb765..29bfddf 100644 --- a/webui_dataset.py +++ b/webui_dataset.py @@ -1,14 +1,22 @@ import os import gradio as gr +import yaml from common.log import logger -from common.subprocess_utils import run_script_with_log, second_elem_of +from common.subprocess_utils import run_script_with_log + +# Get path settings +with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: + path_config: dict[str, str] = yaml.safe_load(f.read()) + dataset_root = path_config["dataset_root"] + # assets_root = path_config["assets_root"] def do_slice(model_name: str, min_sec: float, max_sec: float, input_dir="inputs"): logger.info("Start slicing...") - output_dir = os.path.join("Data", model_name, "raw") + input_dir = "inputs" + output_dir = os.path.join(dataset_root, model_name, "raw") cmd = [ "slice.py", "--input_dir", @@ -30,8 +38,8 @@ def do_transcribe(model_name, whisper_model, compute_type, language, initial_pro if initial_prompt == "": initial_prompt = "こんにちは。元気、ですかー?私は……ちゃんと元気だよ!" logger.debug(f"initial_prompt: {initial_prompt}") - input_dir = os.path.join("Data", model_name, "raw") - output_file = os.path.join("Data", model_name, "esd.list") + input_dir = os.path.join(dataset_root, model_name, "raw") + output_file = os.path.join(dataset_root, model_name, "esd.list") result = run_script_with_log( [ "transcribe.py", diff --git a/webui_merge.py b/webui_merge.py index 91adc01..2635c5a 100644 --- a/webui_merge.py +++ b/webui_merge.py @@ -5,14 +5,13 @@ import sys import gradio as gr import numpy as np import torch +import yaml from safetensors import safe_open from safetensors.torch import save_file - -from config import config -from common.tts_model import Model, ModelHolder -from common.log import logger from common.constants import DEFAULT_STYLE +from common.log import logger +from common.tts_model import Model, ModelHolder voice_keys = ["dec", "flow"] speech_style_keys = ["enc_p"] @@ -20,8 +19,13 @@ tempo_keys = ["sdp", "dp"] device = "cuda" if torch.cuda.is_available() else "cpu" -model_dir = config.out_dir -model_holder = ModelHolder(model_dir, device) +# Get path settings +with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: + path_config: dict[str, str] = yaml.safe_load(f.read()) + # dataset_root = path_config["dataset_root"] + assets_root = path_config["assets_root"] + +model_holder = ModelHolder(assets_root, device) def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_list): @@ -37,17 +41,17 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li raise ValueError("No element with {DEFAULT_STYLE} output style name found.") style_vectors_a = np.load( - os.path.join(model_dir, model_name_a, "style_vectors.npy") + os.path.join(assets_root, model_name_a, "style_vectors.npy") ) # (style_num_a, 256) style_vectors_b = np.load( - os.path.join(model_dir, model_name_b, "style_vectors.npy") + os.path.join(assets_root, model_name_b, "style_vectors.npy") ) # (style_num_b, 256) with open( - os.path.join(model_dir, model_name_a, "config.json"), encoding="utf-8" + os.path.join(assets_root, model_name_a, "config.json"), encoding="utf-8" ) as f: config_a = json.load(f) with open( - os.path.join(model_dir, model_name_b, "config.json"), encoding="utf-8" + os.path.join(assets_root, model_name_b, "config.json"), encoding="utf-8" ) as f: config_b = json.load(f) style2id_a = config_a["data"]["style2id"] @@ -69,7 +73,7 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li new_style2id[style_out] = len(new_style_vecs) - 1 new_style_vecs = np.array(new_style_vecs) - output_style_path = os.path.join(model_dir, output_name, "style_vectors.npy") + output_style_path = os.path.join(assets_root, output_name, "style_vectors.npy") np.save(output_style_path, new_style_vecs) new_config = config_a.copy() @@ -77,9 +81,9 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li new_config["data"]["style2id"] = new_style2id new_config["model_name"] = output_name with open( - os.path.join(model_dir, output_name, "config.json"), "w", encoding="utf-8" + os.path.join(assets_root, output_name, "config.json"), "w", encoding="utf-8" ) as f: - json.dump(new_config, f, indent=2) + json.dump(new_config, f, indent=2, ensure_ascii=False) return output_style_path, list(new_style2id.keys()) @@ -120,7 +124,7 @@ def merge_models( ) merged_model_path = os.path.join( - model_dir, output_name, f"{output_name}.safetensors" + assets_root, output_name, f"{output_name}.safetensors" ) os.makedirs(os.path.dirname(merged_model_path), exist_ok=True) save_file(merged_model_weight, merged_model_path) @@ -180,9 +184,9 @@ def merge_style_gr( def simple_tts(model_name, text, style=DEFAULT_STYLE, emotion_weight=1.0): - model_path = os.path.join(model_dir, model_name, f"{model_name}.safetensors") - config_path = os.path.join(model_dir, model_name, "config.json") - style_vec_path = os.path.join(model_dir, model_name, "style_vectors.npy") + model_path = os.path.join(assets_root, model_name, f"{model_name}.safetensors") + config_path = os.path.join(assets_root, model_name, "config.json") + style_vec_path = os.path.join(assets_root, model_name, "style_vectors.npy") model = Model(model_path, config_path, style_vec_path, device) return model.infer(text, style=style, style_weight=emotion_weight) @@ -194,12 +198,12 @@ def update_two_model_names_dropdown(): def load_styles_gr(model_name_a, model_name_b): - config_path_a = os.path.join(model_dir, model_name_a, "config.json") + config_path_a = os.path.join(assets_root, model_name_a, "config.json") with open(config_path_a, encoding="utf-8") as f: config_a = json.load(f) styles_a = list(config_a["data"]["style2id"].keys()) - config_path_b = os.path.join(model_dir, model_name_b, "config.json") + config_path_b = os.path.join(assets_root, model_name_b, "config.json") with open(config_path_b, encoding="utf-8") as f: config_b = json.load(f) styles_b = list(config_b["data"]["style2id"].keys()) @@ -245,7 +249,7 @@ Happy, Surprise, HappySurprise model_names = model_holder.model_names if len(model_names) == 0: - logger.error(f"モデルが見つかりませんでした。{model_dir}にモデルを置いてください。") + logger.error(f"モデルが見つかりませんでした。{assets_root}にモデルを置いてください。") sys.exit(1) initial_id = 0 initial_model_files = model_holder.model_files_dict[model_names[initial_id]] diff --git a/webui_style_vectors.py b/webui_style_vectors.py index 4f06020..e58dcea 100644 --- a/webui_style_vectors.py +++ b/webui_style_vectors.py @@ -114,7 +114,7 @@ def do_clustering_gradio(n_clusters=4, method="KMeans"): def save_style_vectors(model_name, style_names: str): """centerとcentroidsを保存する""" - result_dir = os.path.join(config.out_dir, model_name) + result_dir = os.path.join(config.assets_root, model_name) os.makedirs(result_dir, exist_ok=True) style_vectors = np.stack([mean] + centroids) style_vector_path = os.path.join(result_dir, "style_vectors.npy") @@ -136,7 +136,7 @@ def save_style_vectors(model_name, style_names: str): style_dict = {name: i for i, name in enumerate(style_name_list)} json_dict["data"]["style2id"] = style_dict with open(config_path, "w", encoding="utf-8") as f: - json.dump(json_dict, f, indent=2) + json.dump(json_dict, f, indent=2, ensure_ascii=False) return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。" @@ -147,7 +147,7 @@ def save_style_vectors_from_files(model_name, audio_files_text, style_names_text return "Error: スタイルベクトルを読み込んでください。" mean = np.mean(x, axis=0) - result_dir = os.path.join(config.out_dir, model_name) + result_dir = os.path.join(config.assets_root, model_name) os.makedirs(result_dir, exist_ok=True) audio_files = audio_files_text.split(",") style_names = style_names_text.split(",") @@ -182,7 +182,7 @@ def save_style_vectors_from_files(model_name, audio_files_text, style_names_text json_dict["data"]["style2id"] = style_dict with open(config_path, "w", encoding="utf-8") as f: - json.dump(json_dict, f, indent=2) + json.dump(json_dict, f, indent=2, ensure_ascii=False) return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。" diff --git a/webui_train.py b/webui_train.py index 7ffb00c..1c3a93d 100644 --- a/webui_train.py +++ b/webui_train.py @@ -1,7 +1,6 @@ import json import os import shutil -import sys from multiprocessing import cpu_count import gradio as gr @@ -10,24 +9,16 @@ import yaml from common.log import logger from common.subprocess_utils import run_script_with_log, second_elem_of -try: - import google.colab - - IS_COLAB = True -except ImportError: - IS_COLAB = False +# Get path settings +with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: + path_config: dict[str, str] = yaml.safe_load(f.read()) + dataset_root = path_config["dataset_root"] + # assets_root = path_config["assets_root"] def get_path(model_name): assert model_name != "", "モデル名は空にできません" - if IS_COLAB: - logger.info("Colab detected, so use mounted Google Drive as dataset path:") - dataset_path = os.path.join( - "/content/drive/MyDrive/Style-Bert-VITS2/Data", model_name - ) - logger.info(dataset_path) - else: - dataset_path = os.path.join("Data", model_name) + dataset_path = os.path.join(dataset_root, model_name) lbl_path = os.path.join(dataset_path, "esd.list") train_path = os.path.join(dataset_path, "train.list") val_path = os.path.join(dataset_path, "val.list") @@ -61,13 +52,12 @@ def initialize(model_name, batch_size, epochs, save_every_steps, bf16_run): ) except FileExistsError: logger.warning(f"Step 1: {model_path} already exists.") - return False, f"Step1, Error: モデルフォルダ {model_path} が既に存在します。問題なければ削除してください。" except FileNotFoundError: logger.error("Step 1: `pretrained` folder not found.") return False, "Step 1, Error: pretrainedフォルダが見つかりません。" with open(config_path, "w", encoding="utf-8") as f: - json.dump(config, f, indent=2) + json.dump(config, f, indent=2, ensure_ascii=False) if not os.path.exists("config.yml"): shutil.copy(src="default_config.yml", dst="config.yml") # yml_data = safe_load(open("config.yml", "r", encoding="utf-8"))