Refactor: Use PathConfig and pathlib instead of paths.yml loading

This commit is contained in:
litagin02
2024-05-25 10:25:22 +09:00
parent 2771fbd209
commit 8976a3ed2f
22 changed files with 266 additions and 253 deletions

10
app.py
View File

@@ -3,7 +3,6 @@ from pathlib import Path
import gradio as gr import gradio as gr
import torch import torch
import yaml
from gradio_tabs.dataset import create_dataset_app from gradio_tabs.dataset import create_dataset_app
from gradio_tabs.inference import create_inference_app from gradio_tabs.inference import create_inference_app
@@ -14,6 +13,7 @@ from style_bert_vits2.constants import GRADIO_THEME, VERSION
from style_bert_vits2.nlp.japanese import pyopenjtalk_worker from style_bert_vits2.nlp.japanese import pyopenjtalk_worker
from style_bert_vits2.nlp.japanese.user_dict import update_dict from style_bert_vits2.nlp.japanese.user_dict import update_dict
from style_bert_vits2.tts_model import TTSModelHolder from style_bert_vits2.tts_model import TTSModelHolder
from config import get_path_config
# このプロセスからはワーカーを起動して辞書を使いたいので、ここで初期化 # このプロセスからはワーカーを起動して辞書を使いたいので、ここで初期化
@@ -22,11 +22,6 @@ pyopenjtalk_worker.initialize_worker()
# dict_data/ 以下の辞書データを pyopenjtalk に適用 # dict_data/ 以下の辞書データを pyopenjtalk に適用
update_dict() update_dict()
# Get path settings
with Path("configs/paths.yml").open("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"]
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--device", type=str, default="cuda") parser.add_argument("--device", type=str, default="cuda")
@@ -40,7 +35,8 @@ device = args.device
if device == "cuda" and not torch.cuda.is_available(): if device == "cuda" and not torch.cuda.is_available():
device = "cpu" device = "cpu"
model_holder = TTSModelHolder(Path(assets_root), device) path_config = get_path_config()
model_holder = TTSModelHolder(Path(path_config.assets_root), device)
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks(theme=GRADIO_THEME) as app:
gr.Markdown(f"# Style-Bert-VITS2 WebUI (version {VERSION})") gr.Markdown(f"# Style-Bert-VITS2 WebUI (version {VERSION})")

View File

@@ -5,13 +5,12 @@ import torch
import torch.multiprocessing as mp import torch.multiprocessing as mp
from tqdm import tqdm from tqdm import tqdm
from config import config from config import get_config
from style_bert_vits2.constants import Languages from style_bert_vits2.constants import Languages
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.models import commons from style_bert_vits2.models import commons
from style_bert_vits2.models.hyper_parameters import HyperParameters from style_bert_vits2.models.hyper_parameters import HyperParameters
from style_bert_vits2.nlp import ( from style_bert_vits2.nlp import (
bert_models,
cleaned_text_to_sequence, cleaned_text_to_sequence,
extract_bert_feature, extract_bert_feature,
) )
@@ -20,6 +19,7 @@ from style_bert_vits2.nlp.japanese.user_dict import update_dict
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
config = get_config()
# このプロセスからはワーカーを起動して辞書を使いたいので、ここで初期化 # このプロセスからはワーカーを起動して辞書を使いたいので、ここで初期化
pyopenjtalk_worker.initialize_worker() pyopenjtalk_worker.initialize_worker()
@@ -61,7 +61,7 @@ def process_line(x: tuple[str, bool]):
bert = torch.load(bert_path) bert = torch.load(bert_path)
assert bert.shape[-1] == len(phone) assert bert.shape[-1] == len(phone)
except Exception: except Exception:
bert = extract_bert_feature(text, word2ph, language_str, device) bert = extract_bert_feature(text, word2ph, Languages(language_str), device)
assert bert.shape[-1] == len(phone) assert bert.shape[-1] == len(phone)
torch.save(bert, bert_path) torch.save(bert, bert_path)

View File

@@ -4,7 +4,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# Style-Bert-VITS2 (ver 2.4.1) のGoogle Colabでの学習\n", "# Style-Bert-VITS2 (ver 2.5.0) のGoogle Colabでの学習\n",
"\n", "\n",
"Google Colab上でStyle-Bert-VITS2の学習を行うことができます。\n", "Google Colab上でStyle-Bert-VITS2の学習を行うことができます。\n",
"\n", "\n",

163
config.py
View File

@@ -2,9 +2,9 @@
@Desc: 全局配置文件读取 @Desc: 全局配置文件读取
""" """
import os
import shutil import shutil
from typing import Dict, List from pathlib import Path
from typing import Any
import torch import torch
import yaml import yaml
@@ -12,6 +12,12 @@ import yaml
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
class PathConfig:
def __init__(self, dataset_root: str, assets_root: str):
self.dataset_root = Path(dataset_root)
self.assets_root = Path(assets_root)
# If not cuda available, set possible devices to cpu # If not cuda available, set possible devices to cpu
cuda_available = torch.cuda.is_available() cuda_available = torch.cuda.is_available()
@@ -20,17 +26,17 @@ class Resample_config:
"""重采样配置""" """重采样配置"""
def __init__(self, in_dir: str, out_dir: str, sampling_rate: int = 44100): def __init__(self, in_dir: str, out_dir: str, sampling_rate: int = 44100):
self.sampling_rate: int = sampling_rate # 目标采样率 self.sampling_rate = sampling_rate # 目标采样率
self.in_dir: str = in_dir # 待处理音频目录路径 self.in_dir = Path(in_dir) # 待处理音频目录路径
self.out_dir: str = out_dir # 重采样输出路径 self.out_dir = Path(out_dir) # 重采样输出路径
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
"""从字典中生成实例""" """从字典中生成实例"""
# 不检查路径是否有效此逻辑在resample.py中处理 # 不检查路径是否有效此逻辑在resample.py中处理
data["in_dir"] = os.path.join(dataset_path, data["in_dir"]) data["in_dir"] = dataset_path / data["in_dir"]
data["out_dir"] = os.path.join(dataset_path, data["out_dir"]) data["out_dir"] = dataset_path / data["out_dir"]
return cls(**data) return cls(**data)
@@ -49,39 +55,27 @@ class Preprocess_text_config:
max_val_total: int = 10000, max_val_total: int = 10000,
clean: bool = True, clean: bool = True,
): ):
self.transcription_path: str = ( self.transcription_path = Path(transcription_path)
transcription_path # 原始文本文件路径,文本格式应为{wav_path}|{speaker_name}|{language}|{text}。 self.cleaned_path = Path(cleaned_path)
) self.train_path = Path(train_path)
self.cleaned_path: str = ( self.val_path = Path(val_path)
cleaned_path # 数据清洗后文本路径,可以不填。不填则将在原始文本目录生成 self.config_path = Path(config_path)
) self.val_per_lang = val_per_lang
self.train_path: str = ( self.max_val_total = max_val_total
train_path # 训练集路径,可以不填。不填则将在原始文本目录生成 self.clean = clean
)
self.val_path: str = (
val_path # 验证集路径,可以不填。不填则将在原始文本目录生成
)
self.config_path: str = config_path # 配置文件路径
self.val_per_lang: int = val_per_lang # 每个speaker的验证集条数
self.max_val_total: int = (
max_val_total # 验证集最大条数,多于的会被截断并放到训练集中
)
self.clean: bool = clean # 是否进行数据清洗
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
"""从字典中生成实例""" """从字典中生成实例"""
data["transcription_path"] = os.path.join( data["transcription_path"] = dataset_path / data["transcription_path"]
dataset_path, data["transcription_path"]
)
if data["cleaned_path"] == "" or data["cleaned_path"] is None: if data["cleaned_path"] == "" or data["cleaned_path"] is None:
data["cleaned_path"] = None data["cleaned_path"] = None
else: else:
data["cleaned_path"] = os.path.join(dataset_path, data["cleaned_path"]) data["cleaned_path"] = dataset_path / data["cleaned_path"]
data["train_path"] = os.path.join(dataset_path, data["train_path"]) data["train_path"] = dataset_path / data["train_path"]
data["val_path"] = os.path.join(dataset_path, data["val_path"]) data["val_path"] = dataset_path / data["val_path"]
data["config_path"] = os.path.join(dataset_path, data["config_path"]) data["config_path"] = dataset_path / data["config_path"]
return cls(**data) return cls(**data)
@@ -96,7 +90,7 @@ class Bert_gen_config:
device: str = "cuda", device: str = "cuda",
use_multi_device: bool = False, use_multi_device: bool = False,
): ):
self.config_path = config_path self.config_path = Path(config_path)
self.num_processes = num_processes self.num_processes = num_processes
if not cuda_available: if not cuda_available:
device = "cpu" device = "cpu"
@@ -104,8 +98,8 @@ class Bert_gen_config:
self.use_multi_device = use_multi_device self.use_multi_device = use_multi_device
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
data["config_path"] = os.path.join(dataset_path, data["config_path"]) data["config_path"] = dataset_path / data["config_path"]
return cls(**data) return cls(**data)
@@ -119,15 +113,15 @@ class Style_gen_config:
num_processes: int = 4, num_processes: int = 4,
device: str = "cuda", device: str = "cuda",
): ):
self.config_path = config_path self.config_path = Path(config_path)
self.num_processes = num_processes self.num_processes = num_processes
if not cuda_available: if not cuda_available:
device = "cpu" device = "cpu"
self.device = device self.device = device
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
data["config_path"] = os.path.join(dataset_path, data["config_path"]) data["config_path"] = dataset_path / data["config_path"]
return cls(**data) return cls(**data)
@@ -138,7 +132,7 @@ class Train_ms_config:
def __init__( def __init__(
self, self,
config_path: str, config_path: str,
env: Dict[str, any], env: dict[str, Any],
# base: Dict[str, any], # base: Dict[str, any],
model_dir: str, model_dir: str,
num_workers: int, num_workers: int,
@@ -147,16 +141,18 @@ class Train_ms_config:
): ):
self.env = env # 需要加载的环境变量 self.env = env # 需要加载的环境变量
# self.base = base # 底模配置 # self.base = base # 底模配置
self.model_dir = model_dir # 训练模型存储目录该路径为相对于dataset_path的路径而非项目根目录 self.model_dir = Path(
self.config_path = config_path # 配置文件路径 model_dir
) # 训练模型存储目录该路径为相对于dataset_path的路径而非项目根目录
self.config_path = Path(config_path) # 配置文件路径
self.num_workers = num_workers # worker数量 self.num_workers = num_workers # worker数量
self.spec_cache = spec_cache # 是否启用spec缓存 self.spec_cache = spec_cache # 是否启用spec缓存
self.keep_ckpts = keep_ckpts # ckpt数量 self.keep_ckpts = keep_ckpts # ckpt数量
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
# data["model"] = os.path.join(dataset_path, data["model"]) # data["model"] = os.path.join(dataset_path, data["model"])
data["config_path"] = os.path.join(dataset_path, data["config_path"]) data["config_path"] = dataset_path / data["config_path"]
return cls(**data) return cls(**data)
@@ -176,20 +172,18 @@ class Webui_config:
): ):
if not cuda_available: if not cuda_available:
device = "cpu" device = "cpu"
self.device: str = device self.device = device
self.model: str = model # 端口号 self.model = Path(model)
self.config_path: str = config_path # 是否公开部署,对外网开放 self.config_path = Path(config_path)
self.port: int = port # 是否开启debug模式 self.port: int = port
self.share: bool = share # 模型路径 self.share: bool = share
self.debug: bool = debug # 配置文件路径 self.debug: bool = debug
self.language_identification_library: str = ( self.language_identification_library: str = language_identification_library
language_identification_library # 语种识别库
)
@classmethod @classmethod
def from_dict(cls, dataset_path: str, data: Dict[str, any]): def from_dict(cls, dataset_path: Path, data: dict[str, Any]):
data["config_path"] = os.path.join(dataset_path, data["config_path"]) data["config_path"] = dataset_path / data["config_path"]
data["model"] = os.path.join(dataset_path, data["model"]) data["model"] = dataset_path / data["model"]
return cls(**data) return cls(**data)
@@ -200,7 +194,7 @@ class Server_config:
device: str = "cuda", device: str = "cuda",
limit: int = 100, limit: int = 100,
language: str = "JP", language: str = "JP",
origins: List[str] = None, origins: list[str] = ["*"],
): ):
self.port: int = port self.port: int = port
if not cuda_available: if not cuda_available:
@@ -208,10 +202,10 @@ class Server_config:
self.device: str = device self.device: str = device
self.language: str = language self.language: str = language
self.limit: int = limit self.limit: int = limit
self.origins: List[str] = origins self.origins: list[str] = origins
@classmethod @classmethod
def from_dict(cls, data: Dict[str, any]): def from_dict(cls, data: dict[str, Any]):
return cls(**data) return cls(**data)
@@ -223,32 +217,32 @@ class Translate_config:
self.secret_key = secret_key self.secret_key = secret_key
@classmethod @classmethod
def from_dict(cls, data: Dict[str, any]): def from_dict(cls, data: dict[str, Any]):
return cls(**data) return cls(**data)
class Config: class Config:
def __init__(self, config_path: str, path_config: dict[str, str]): def __init__(self, config_path: str, path_config: PathConfig):
if not os.path.isfile(config_path) and os.path.isfile("default_config.yml"): if not Path(config_path).exists():
shutil.copy(src="default_config.yml", dst=config_path) shutil.copy(src="default_config.yml", dst=config_path)
logger.info( logger.info(
f"A configuration file {config_path} has been generated based on the default configuration file default_config.yml." f"A configuration file {config_path} has been generated based on the default configuration file default_config.yml."
) )
logger.info( logger.info(
"If you have no special needs, please do not modify default_config.yml." "Please do not modify default_config.yml. Instead, modify config.yml."
) )
# sys.exit(0) # sys.exit(0)
with open(config_path, "r", encoding="utf-8") as file: with open(config_path, "r", encoding="utf-8") as file:
yaml_config: Dict[str, any] = yaml.safe_load(file.read()) yaml_config: dict[str, Any] = yaml.safe_load(file.read())
model_name: str = yaml_config["model_name"] model_name: str = yaml_config["model_name"]
self.model_name: str = model_name self.model_name: str = model_name
if "dataset_path" in yaml_config: if "dataset_path" in yaml_config:
dataset_path = yaml_config["dataset_path"] dataset_path = Path(yaml_config["dataset_path"])
else: else:
dataset_path = os.path.join(path_config["dataset_root"], model_name) dataset_path = path_config.dataset_root / model_name
self.dataset_path: str = dataset_path self.dataset_path = dataset_path
self.assets_root: str = path_config["assets_root"] self.assets_root = path_config.assets_root
self.out_dir = os.path.join(self.assets_root, model_name) self.out_dir = self.assets_root / model_name
self.resample_config: Resample_config = Resample_config.from_dict( self.resample_config: Resample_config = Resample_config.from_dict(
dataset_path, yaml_config["resample"] dataset_path, yaml_config["resample"]
) )
@@ -277,16 +271,31 @@ class Config:
# ) # )
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: # Load and initialize the configuration
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"
try: def get_path_config() -> PathConfig:
path_config_path = Path("configs/paths.yml")
if not path_config_path.exists():
shutil.copy(src="configs/default_paths.yml", dst=path_config_path)
logger.info(
f"A configuration file {path_config_path} has been generated based on the default configuration file default_paths.yml."
)
logger.info(
"Please do not modify configs/default_paths.yml. Instead, modify configs/paths.yml."
)
with open(path_config_path, "r", encoding="utf-8") as file:
path_config_dict: dict[str, str] = yaml.safe_load(file.read())
return PathConfig(**path_config_dict)
def get_config() -> Config:
path_config = get_path_config()
try:
config = Config("config.yml", path_config) config = Config("config.yml", path_config)
except (TypeError, KeyError): except (TypeError, KeyError):
logger.warning("Old config.yml found. Replace it with default_config.yml.") logger.warning("Old config.yml found. Replace it with default_config.yml.")
shutil.copy(src="default_config.yml", dst="config.yml") shutil.copy(src="default_config.yml", dst="config.yml")
config = Config("config.yml", path_config) config = Config("config.yml", path_config)
return config

View File

@@ -69,5 +69,5 @@
"use_spectral_norm": false, "use_spectral_norm": false,
"gin_channels": 256 "gin_channels": 256
}, },
"version": "2.4.1" "version": "2.5.0"
} }

View File

@@ -76,5 +76,5 @@
"initial_channel": 64 "initial_channel": 64
} }
}, },
"version": "2.4.1-JP-Extra" "version": "2.5.0-JP-Extra"
} }

View File

@@ -1,5 +1,4 @@
import json import json
import os
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union

View File

@@ -1,14 +1,13 @@
import json import json
import os
from pathlib import Path from pathlib import Path
import gradio as gr import gradio as gr
import numpy as np import numpy as np
import torch import torch
import yaml
from safetensors import safe_open from safetensors import safe_open
from safetensors.torch import save_file from safetensors.torch import save_file
from config import get_path_config
from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.tts_model import TTSModel, TTSModelHolder from style_bert_vits2.tts_model import TTSModel, TTSModelHolder
@@ -20,15 +19,17 @@ speech_style_keys = ["enc_p"]
tempo_keys = ["sdp", "dp"] tempo_keys = ["sdp", "dp"]
device = "cuda" if torch.cuda.is_available() else "cpu" device = "cuda" if torch.cuda.is_available() else "cpu"
path_config = get_path_config()
# Get path settings assets_root = path_config.assets_root
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 merge_style(model_name_a, model_name_b, weight, output_name, style_triple_list): def merge_style(
model_name_a: str,
model_name_b: str,
weight: float,
output_name: str,
style_triple_list: list[tuple[str, str, str]],
) -> tuple[Path, list[str]]:
""" """
style_triple_list: list[(model_aでのスタイル名, model_bでのスタイル名, 出力するスタイル名)] style_triple_list: list[(model_aでのスタイル名, model_bでのスタイル名, 出力するスタイル名)]
""" """
@@ -41,18 +42,14 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li
raise ValueError(f"No element with {DEFAULT_STYLE} output style name found.") raise ValueError(f"No element with {DEFAULT_STYLE} output style name found.")
style_vectors_a = np.load( style_vectors_a = np.load(
os.path.join(assets_root, model_name_a, "style_vectors.npy") assets_root / model_name_a / "style_vectors.npy"
) # (style_num_a, 256) ) # (style_num_a, 256)
style_vectors_b = np.load( style_vectors_b = np.load(
os.path.join(assets_root, model_name_b, "style_vectors.npy") assets_root / model_name_b / "style_vectors.npy"
) # (style_num_b, 256) ) # (style_num_b, 256)
with open( with open(assets_root / model_name_a / "config.json", "r", encoding="utf-8") as f:
os.path.join(assets_root, model_name_a, "config.json"), "r", encoding="utf-8"
) as f:
config_a = json.load(f) config_a = json.load(f)
with open( with open(assets_root / model_name_b / "config.json", "r", encoding="utf-8") as f:
os.path.join(assets_root, model_name_b, "config.json"), "r", encoding="utf-8"
) as f:
config_b = json.load(f) config_b = json.load(f)
style2id_a = config_a["data"]["style2id"] style2id_a = config_a["data"]["style2id"]
style2id_b = config_b["data"]["style2id"] style2id_b = config_b["data"]["style2id"]
@@ -73,21 +70,19 @@ 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_style2id[style_out] = len(new_style_vecs) - 1
new_style_vecs = np.array(new_style_vecs) new_style_vecs = np.array(new_style_vecs)
output_style_path = os.path.join(assets_root, output_name, "style_vectors.npy") output_style_path = assets_root / output_name / "style_vectors.npy"
np.save(output_style_path, new_style_vecs) np.save(output_style_path, new_style_vecs)
new_config = config_a.copy() new_config = config_a.copy()
new_config["data"]["num_styles"] = len(new_style2id) new_config["data"]["num_styles"] = len(new_style2id)
new_config["data"]["style2id"] = new_style2id new_config["data"]["style2id"] = new_style2id
new_config["model_name"] = output_name new_config["model_name"] = output_name
with open( with open(assets_root / output_name / "config.json", "w", encoding="utf-8") as f:
os.path.join(assets_root, output_name, "config.json"), "w", encoding="utf-8"
) as f:
json.dump(new_config, f, indent=2, ensure_ascii=False) json.dump(new_config, f, indent=2, ensure_ascii=False)
# recipe.jsonを読み込んで、style_triple_listを追記 # recipe.jsonを読み込んで、style_triple_listを追記
info_path = os.path.join(assets_root, output_name, "recipe.json") info_path = assets_root / output_name / "recipe.json"
if os.path.exists(info_path): if info_path.exists():
with open(info_path, "r", encoding="utf-8") as f: with open(info_path, "r", encoding="utf-8") as f:
info = json.load(f) info = json.load(f)
else: else:
@@ -99,11 +94,13 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li
return output_style_path, list(new_style2id.keys()) return output_style_path, list(new_style2id.keys())
def lerp_tensors(t, v0, v1): def lerp_tensors(t: float, v0: torch.Tensor, v1: torch.Tensor):
return v0 * (1 - t) + v1 * t return v0 * (1 - t) + v1 * t
def slerp_tensors(t, v0, v1, dot_thres=0.998): def slerp_tensors(
t: float, v0: torch.Tensor, v1: torch.Tensor, dot_thres: float = 0.998
):
device = v0.device device = v0.device
v0c = v0.cpu().numpy() v0c = v0.cpu().numpy()
v1c = v1.cpu().numpy() v1c = v1.cpu().numpy()
@@ -123,23 +120,23 @@ def slerp_tensors(t, v0, v1, dot_thres=0.998):
def merge_models( def merge_models(
model_path_a, model_path_a: str,
model_path_b, model_path_b: str,
voice_weight, voice_weight: float,
voice_pitch_weight, voice_pitch_weight: float,
speech_style_weight, speech_style_weight: float,
tempo_weight, tempo_weight: float,
output_name, output_name: str,
use_slerp_instead_of_lerp, use_slerp_instead_of_lerp: bool,
): ):
"""model Aを起点に、model Bの各要素を重み付けしてマージする。 """model Aを起点に、model Bの各要素を重み付けしてマージする。
safetensors形式を前提とする。""" safetensors形式を前提とする。"""
model_a_weight = {} model_a_weight: dict[str, torch.Tensor] = {}
with safe_open(model_path_a, framework="pt", device="cpu") as f: with safe_open(model_path_a, framework="pt", device="cpu") as f:
for k in f.keys(): for k in f.keys():
model_a_weight[k] = f.get_tensor(k) model_a_weight[k] = f.get_tensor(k)
model_b_weight = {} model_b_weight: dict[str, torch.Tensor] = {}
with safe_open(model_path_b, framework="pt", device="cpu") as f: with safe_open(model_path_b, framework="pt", device="cpu") as f:
for k in f.keys(): for k in f.keys():
model_b_weight[k] = f.get_tensor(k) model_b_weight[k] = f.get_tensor(k)
@@ -161,10 +158,8 @@ def merge_models(
slerp_tensors if use_slerp_instead_of_lerp else lerp_tensors slerp_tensors if use_slerp_instead_of_lerp else lerp_tensors
)(weight, model_a_weight[key], model_b_weight[key]) )(weight, model_a_weight[key], model_b_weight[key])
merged_model_path = os.path.join( merged_model_path = assets_root / output_name / f"{output_name}.safetensors"
assets_root, output_name, f"{output_name}.safetensors" merged_model_path.parent.mkdir(parents=True, exist_ok=True)
)
os.makedirs(os.path.dirname(merged_model_path), exist_ok=True)
save_file(merged_model_weight, merged_model_path) save_file(merged_model_weight, merged_model_path)
info = { info = {
@@ -175,24 +170,22 @@ def merge_models(
"speech_style_weight": speech_style_weight, "speech_style_weight": speech_style_weight,
"tempo_weight": tempo_weight, "tempo_weight": tempo_weight,
} }
with open( with open(assets_root / output_name / "recipe.json", "w", encoding="utf-8") as f:
os.path.join(assets_root, output_name, "recipe.json"), "w", encoding="utf-8"
) as f:
json.dump(info, f, indent=2, ensure_ascii=False) json.dump(info, f, indent=2, ensure_ascii=False)
return merged_model_path return merged_model_path
def merge_models_gr( def merge_models_gr(
model_name_a, model_name_a: str,
model_path_a, model_path_a: str,
model_name_b, model_name_b: str,
model_path_b, model_path_b: str,
output_name, output_name: str,
voice_weight, voice_weight: float,
voice_pitch_weight, voice_pitch_weight: float,
speech_style_weight, speech_style_weight: float,
tempo_weight, tempo_weight: float,
use_slerp_instead_of_lerp, use_slerp_instead_of_lerp: bool,
): ):
if output_name == "": if output_name == "":
return "Error: 新しいモデル名を入力してください。" return "Error: 新しいモデル名を入力してください。"
@@ -210,10 +203,10 @@ def merge_models_gr(
def merge_style_gr( def merge_style_gr(
model_name_a, model_name_a: str,
model_name_b, model_name_b: str,
weight, weight: float,
output_name, output_name: str,
style_triple_list_str: str, style_triple_list_str: str,
): ):
if output_name == "": if output_name == "":
@@ -245,12 +238,14 @@ def merge_style_gr(
) )
def simple_tts(model_name, text, style=DEFAULT_STYLE, style_weight=1.0): def simple_tts(
model_path = os.path.join(assets_root, model_name, f"{model_name}.safetensors") model_name: str, text: str, style: str = DEFAULT_STYLE, style_weight: float = 1.0
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_path = assets_root / model_name / f"{model_name}.safetensors"
config_path = assets_root / model_name / "config.json"
style_vec_path = assets_root / model_name / "style_vectors.npy"
model = TTSModel(Path(model_path), Path(config_path), Path(style_vec_path), device) model = TTSModel(model_path, config_path, style_vec_path, device)
return model.infer(text, style=style, style_weight=style_weight) return model.infer(text, style=style, style_weight=style_weight)
@@ -259,13 +254,13 @@ def update_two_model_names_dropdown(model_holder: TTSModelHolder):
return new_names, new_files, new_names, new_files return new_names, new_files, new_names, new_files
def load_styles_gr(model_name_a, model_name_b): def load_styles_gr(model_name_a: str, model_name_b: str):
config_path_a = os.path.join(assets_root, model_name_a, "config.json") config_path_a = assets_root / model_name_a / "config.json"
with open(config_path_a, "r", encoding="utf-8") as f: with open(config_path_a, "r", encoding="utf-8") as f:
config_a = json.load(f) config_a = json.load(f)
styles_a = list(config_a["data"]["style2id"].keys()) styles_a = list(config_a["data"]["style2id"].keys())
config_path_b = os.path.join(assets_root, model_name_b, "config.json") config_path_b = assets_root / model_name_b / "config.json"
with open(config_path_b, "r", encoding="utf-8") as f: with open(config_path_b, "r", encoding="utf-8") as f:
config_b = json.load(f) config_b = json.load(f)
styles_b = list(config_b["data"]["style2id"].keys()) styles_b = list(config_b["data"]["style2id"].keys())
@@ -336,7 +331,10 @@ def create_merge_app(model_holder: TTSModelHolder) -> gr.Blocks:
) )
return app return app
initial_id = 0 initial_id = 0
initial_model_files = model_holder.model_files_dict[model_names[initial_id]] # initial_model_files = model_holder.model_files_dict[model_names[initial_id]]
initial_model_files = [
str(f) for f in model_holder.model_files_dict[model_names[initial_id]]
]
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks(theme=GRADIO_THEME) as app:
gr.Markdown( gr.Markdown(

View File

@@ -1,27 +1,23 @@
import json import json
import os
import shutil import shutil
from pathlib import Path from pathlib import Path
import gradio as gr import gradio as gr
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import yaml
from scipy.spatial.distance import pdist, squareform from scipy.spatial.distance import pdist, squareform
from sklearn.cluster import DBSCAN, AgglomerativeClustering, KMeans from sklearn.cluster import DBSCAN, AgglomerativeClustering, KMeans
from sklearn.manifold import TSNE from sklearn.manifold import TSNE
from umap import UMAP from umap import UMAP
from config import config from config import get_path_config
from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
# Get path settings path_config = get_path_config()
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: dataset_root = path_config.dataset_root
path_config: dict[str, str] = yaml.safe_load(f.read()) assets_root = path_config.assets_root
dataset_root = Path(path_config["dataset_root"])
# assets_root = path_config["assets_root"]
MAX_CLUSTER_NUM = 10 MAX_CLUSTER_NUM = 10
MAX_AUDIO_NUM = 10 MAX_AUDIO_NUM = 10
@@ -39,11 +35,7 @@ centroids = []
def load(model_name: str, reduction_method: str): def load(model_name: str, reduction_method: str):
global wav_files, x, x_reduced, mean global wav_files, x, x_reduced, mean
# wavs_dir = os.path.join(dataset_root, model_name, "wavs")
wavs_dir = dataset_root / model_name / "wavs" wavs_dir = dataset_root / model_name / "wavs"
# style_vector_files = [
# os.path.join(wavs_dir, f) for f in os.listdir(wavs_dir) if f.endswith(".npy")
# ]
style_vector_files = [f for f in wavs_dir.rglob("*.npy") if f.is_file()] style_vector_files = [f for f in wavs_dir.rglob("*.npy") if f.is_file()]
# foo.wav.npy -> foo.wav # foo.wav.npy -> foo.wav
wav_files = [f.with_suffix("") for f in style_vector_files] wav_files = [f.with_suffix("") for f in style_vector_files]
@@ -196,21 +188,21 @@ def do_clustering_gradio(n_clusters=4, method="KMeans"):
] * MAX_AUDIO_NUM ] * MAX_AUDIO_NUM
def save_style_vectors_from_clustering(model_name, style_names_str: str): def save_style_vectors_from_clustering(model_name: str, style_names_str: str):
"""centerとcentroidsを保存する""" """centerとcentroidsを保存する"""
result_dir = os.path.join(config.assets_root, model_name) result_dir = assets_root / model_name
os.makedirs(result_dir, exist_ok=True) result_dir.mkdir(parents=True, exist_ok=True)
style_vectors = np.stack([mean] + centroids) style_vectors = np.stack([mean] + centroids)
style_vector_path = os.path.join(result_dir, "style_vectors.npy") style_vector_path = result_dir / "style_vectors.npy"
if os.path.exists(style_vector_path): if style_vector_path.exists():
logger.info(f"Backup {style_vector_path} to {style_vector_path}.bak") logger.info(f"Backup {style_vector_path} to {style_vector_path}.bak")
shutil.copy(style_vector_path, f"{style_vector_path}.bak") shutil.copy(style_vector_path, f"{style_vector_path}.bak")
np.save(style_vector_path, style_vectors) np.save(style_vector_path, style_vectors)
logger.success(f"Saved style vectors to {style_vector_path}") logger.success(f"Saved style vectors to {style_vector_path}")
# config.jsonの更新 # config.jsonの更新
config_path = os.path.join(result_dir, "config.json") config_path = result_dir / "config.json"
if not os.path.exists(config_path): if not config_path.exists():
return f"{config_path}が存在しません。" return f"{config_path}が存在しません。"
style_names = [name.strip() for name in style_names_str.split(",")] style_names = [name.strip() for name in style_names_str.split(",")]
style_name_list = [DEFAULT_STYLE] + style_names style_name_list = [DEFAULT_STYLE] + style_names
@@ -233,7 +225,7 @@ def save_style_vectors_from_clustering(model_name, style_names_str: str):
def save_style_vectors_from_files( def save_style_vectors_from_files(
model_name, audio_files_str: str, style_names_str: str model_name: str, audio_files_str: str, style_names_str: str
): ):
"""音声ファイルからスタイルベクトルを作成して保存する""" """音声ファイルからスタイルベクトルを作成して保存する"""
global mean global mean
@@ -241,8 +233,8 @@ def save_style_vectors_from_files(
return "Error: スタイルベクトルを読み込んでください。" return "Error: スタイルベクトルを読み込んでください。"
mean = np.mean(x, axis=0) mean = np.mean(x, axis=0)
result_dir = os.path.join(config.assets_root, model_name) result_dir = assets_root / model_name
os.makedirs(result_dir, exist_ok=True) result_dir.mkdir(parents=True, exist_ok=True)
audio_files = [name.strip() for name in audio_files_str.split(",")] audio_files = [name.strip() for name in audio_files_str.split(",")]
style_names = [name.strip() for name in style_names_str.split(",")] style_names = [name.strip() for name in style_names_str.split(",")]
if len(audio_files) != len(style_names): if len(audio_files) != len(style_names):
@@ -252,23 +244,23 @@ def save_style_vectors_from_files(
return "スタイル名が重複しています。" return "スタイル名が重複しています。"
style_vectors = [mean] style_vectors = [mean]
wavs_dir = os.path.join(dataset_root, model_name, "wavs") wavs_dir = dataset_root / model_name / "wavs"
for audio_file in audio_files: for audio_file in audio_files:
path = os.path.join(wavs_dir, audio_file) path = wavs_dir / audio_file
if not os.path.exists(path): if not path.exists():
return f"{path}が存在しません。" return f"{path}が存在しません。"
style_vectors.append(np.load(f"{path}.npy")) style_vectors.append(np.load(f"{path}.npy"))
style_vectors = np.stack(style_vectors) style_vectors = np.stack(style_vectors)
assert len(style_name_list) == len(style_vectors) assert len(style_name_list) == len(style_vectors)
style_vector_path = os.path.join(result_dir, "style_vectors.npy") style_vector_path = result_dir / "style_vectors.npy"
if os.path.exists(style_vector_path): if style_vector_path.exists():
logger.info(f"Backup {style_vector_path} to {style_vector_path}.bak") logger.info(f"Backup {style_vector_path} to {style_vector_path}.bak")
shutil.copy(style_vector_path, f"{style_vector_path}.bak") shutil.copy(style_vector_path, f"{style_vector_path}.bak")
np.save(style_vector_path, style_vectors) np.save(style_vector_path, style_vectors)
# config.jsonの更新 # config.jsonの更新
config_path = os.path.join(result_dir, "config.json") config_path = result_dir / "config.json"
if not os.path.exists(config_path): if not config_path.exists():
return f"{config_path}が存在しません。" return f"{config_path}が存在しません。"
logger.info(f"Backup {config_path} to {config_path}.bak") logger.info(f"Backup {config_path} to {config_path}.bak")
shutil.copy(config_path, f"{config_path}.bak") shutil.copy(config_path, f"{config_path}.bak")

View File

@@ -1,5 +1,4 @@
import json import json
import os
import shutil import shutil
import socket import socket
import subprocess import subprocess
@@ -12,7 +11,8 @@ from pathlib import Path
import gradio as gr import gradio as gr
import yaml import yaml
from dataclasses import dataclass
from config import get_path_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
from style_bert_vits2.utils.subprocess import run_script_with_log, second_elem_of from style_bert_vits2.utils.subprocess import run_script_with_log, second_elem_of
@@ -21,20 +21,27 @@ from style_bert_vits2.utils.subprocess import run_script_with_log, second_elem_o
logger_handler = None logger_handler = None
tensorboard_executed = False tensorboard_executed = False
# Get path settings path_config = get_path_config()
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: dataset_root = path_config.dataset_root
path_config: dict[str, str] = yaml.safe_load(f.read())
dataset_root = Path(path_config["dataset_root"])
def get_path(model_name: str) -> tuple[Path, Path, Path, Path, Path]: @dataclass
class PathsForPreprocess:
dataset_path: Path
esd_path: Path
train_path: Path
val_path: Path
config_path: Path
def get_path(model_name: str) -> PathsForPreprocess:
assert model_name != "", "モデル名は空にできません" assert model_name != "", "モデル名は空にできません"
dataset_path = dataset_root / model_name dataset_path = dataset_root / model_name
lbl_path = dataset_path / "esd.list" esd_path = dataset_path / "esd.list"
train_path = dataset_path / "train.list" train_path = dataset_path / "train.list"
val_path = dataset_path / "val.list" val_path = dataset_path / "val.list"
config_path = dataset_path / "config.json" config_path = dataset_path / "config.json"
return dataset_path, lbl_path, train_path, val_path, config_path return PathsForPreprocess(dataset_path, esd_path, train_path, val_path, config_path)
def initialize( def initialize(
@@ -51,14 +58,14 @@ def initialize(
log_interval: int, log_interval: int,
): ):
global logger_handler global logger_handler
dataset_path, _, train_path, val_path, config_path = get_path(model_name) paths = get_path(model_name)
# 前処理のログをファイルに保存する # 前処理のログをファイルに保存する
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name = f"preprocess_{timestamp}.log" file_name = f"preprocess_{timestamp}.log"
if logger_handler is not None: if logger_handler is not None:
logger.remove(logger_handler) logger.remove(logger_handler)
logger_handler = logger.add(os.path.join(dataset_path, file_name)) logger_handler = logger.add(paths.dataset_path / file_name)
logger.info( logger.info(
f"Step 1: start initialization...\nmodel_name: {model_name}, batch_size: {batch_size}, epochs: {epochs}, save_every_steps: {save_every_steps}, freeze_ZH_bert: {freeze_ZH_bert}, freeze_JP_bert: {freeze_JP_bert}, freeze_EN_bert: {freeze_EN_bert}, freeze_style: {freeze_style}, freeze_decoder: {freeze_decoder}, use_jp_extra: {use_jp_extra}" f"Step 1: start initialization...\nmodel_name: {model_name}, batch_size: {batch_size}, epochs: {epochs}, save_every_steps: {save_every_steps}, freeze_ZH_bert: {freeze_ZH_bert}, freeze_JP_bert: {freeze_JP_bert}, freeze_EN_bert: {freeze_EN_bert}, freeze_style: {freeze_style}, freeze_decoder: {freeze_decoder}, use_jp_extra: {use_jp_extra}"
@@ -71,8 +78,8 @@ def initialize(
with open(default_config_path, "r", encoding="utf-8") as f: with open(default_config_path, "r", encoding="utf-8") as f:
config = json.load(f) config = json.load(f)
config["model_name"] = model_name config["model_name"] = model_name
config["data"]["training_files"] = str(train_path) config["data"]["training_files"] = str(paths.train_path)
config["data"]["validation_files"] = str(val_path) config["data"]["validation_files"] = str(paths.val_path)
config["train"]["batch_size"] = batch_size config["train"]["batch_size"] = batch_size
config["train"]["epochs"] = epochs config["train"]["epochs"] = epochs
config["train"]["eval_interval"] = save_every_steps config["train"]["eval_interval"] = save_every_steps
@@ -89,14 +96,14 @@ def initialize(
# 今はデフォルトであるが、以前は非JP-Extra版になくバグの原因になるので念のため # 今はデフォルトであるが、以前は非JP-Extra版になくバグの原因になるので念のため
config["data"]["use_jp_extra"] = use_jp_extra config["data"]["use_jp_extra"] = use_jp_extra
model_path = dataset_path / "models" model_path = paths.dataset_path / "models"
if model_path.exists(): if model_path.exists():
logger.warning( logger.warning(
f"Step 1: {model_path} already exists, so copy it to backup to {model_path}_backup" f"Step 1: {model_path} already exists, so copy it to backup to {model_path}_backup"
) )
shutil.copytree( shutil.copytree(
src=model_path, src=model_path,
dst=dataset_path / "models_backup", dst=paths.dataset_path / "models_backup",
dirs_exist_ok=True, dirs_exist_ok=True,
) )
shutil.rmtree(model_path) shutil.rmtree(model_path)
@@ -110,14 +117,14 @@ def initialize(
logger.error(f"Step 1: {pretrained_dir} folder not found.") logger.error(f"Step 1: {pretrained_dir} folder not found.")
return False, f"Step 1, Error: {pretrained_dir}フォルダが見つかりません。" return False, f"Step 1, Error: {pretrained_dir}フォルダが見つかりません。"
with open(config_path, "w", encoding="utf-8") as f: with open(paths.config_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False) json.dump(config, f, indent=2, ensure_ascii=False)
if not Path("config.yml").exists(): if not Path("config.yml").exists():
shutil.copy(src="default_config.yml", dst="config.yml") shutil.copy(src="default_config.yml", dst="config.yml")
with open("config.yml", "r", encoding="utf-8") as f: with open("config.yml", "r", encoding="utf-8") as f:
yml_data = yaml.safe_load(f) yml_data = yaml.safe_load(f)
yml_data["model_name"] = model_name yml_data["model_name"] = model_name
yml_data["dataset_path"] = str(dataset_path) yml_data["dataset_path"] = str(paths.dataset_path)
with open("config.yml", "w", encoding="utf-8") as f: with open("config.yml", "w", encoding="utf-8") as f:
yaml.dump(yml_data, f, allow_unicode=True) yaml.dump(yml_data, f, allow_unicode=True)
logger.success("Step 1: initialization finished.") logger.success("Step 1: initialization finished.")
@@ -126,7 +133,7 @@ def initialize(
def resample(model_name: str, normalize: bool, trim: bool, num_processes: int): def resample(model_name: str, normalize: bool, trim: bool, num_processes: int):
logger.info("Step 2: start resampling...") logger.info("Step 2: start resampling...")
dataset_path, _, _, _, _ = get_path(model_name) dataset_path = get_path(model_name).dataset_path
input_dir = dataset_path / "raw" input_dir = dataset_path / "raw"
output_dir = dataset_path / "wavs" output_dir = dataset_path / "wavs"
cmd = [ cmd = [
@@ -159,21 +166,24 @@ def preprocess_text(
model_name: str, use_jp_extra: bool, val_per_lang: int, yomi_error: str model_name: str, use_jp_extra: bool, val_per_lang: int, yomi_error: str
): ):
logger.info("Step 3: start preprocessing text...") logger.info("Step 3: start preprocessing text...")
_, lbl_path, train_path, val_path, config_path = get_path(model_name) paths = get_path(model_name)
if not lbl_path.exists(): if not paths.esd_path.exists():
logger.error(f"Step 3: {lbl_path} not found.") logger.error(f"Step 3: {paths.esd_path} not found.")
return False, f"Step 3, Error: 書き起こしファイル {lbl_path} が見つかりません。" return (
False,
f"Step 3, Error: 書き起こしファイル {paths.esd_path} が見つかりません。",
)
cmd = [ cmd = [
"preprocess_text.py", "preprocess_text.py",
"--config-path", "--config-path",
str(config_path), str(paths.config_path),
"--transcription-path", "--transcription-path",
str(lbl_path), str(paths.esd_path),
"--train-path", "--train-path",
str(train_path), str(paths.train_path),
"--val-path", "--val-path",
str(val_path), str(paths.val_path),
"--val-per-lang", "--val-per-lang",
str(val_per_lang), str(val_per_lang),
"--yomi_error", "--yomi_error",
@@ -201,7 +211,7 @@ def preprocess_text(
def bert_gen(model_name: str): def bert_gen(model_name: str):
logger.info("Step 4: start bert_gen...") logger.info("Step 4: start bert_gen...")
_, _, _, _, config_path = get_path(model_name) config_path = get_path(model_name).config_path
success, message = run_script_with_log( success, message = run_script_with_log(
["bert_gen.py", "--config", str(config_path)] ["bert_gen.py", "--config", str(config_path)]
) )
@@ -220,7 +230,7 @@ def bert_gen(model_name: str):
def style_gen(model_name: str, num_processes: int): def style_gen(model_name: str, num_processes: int):
logger.info("Step 5: start style_gen...") logger.info("Step 5: start style_gen...")
_, _, _, _, config_path = get_path(model_name) config_path = get_path(model_name).config_path
success, message = run_script_with_log( success, message = run_script_with_log(
[ [
"style_gen.py", "style_gen.py",
@@ -319,17 +329,23 @@ def train(
use_jp_extra: bool = True, use_jp_extra: bool = True,
speedup: bool = False, speedup: bool = False,
): ):
dataset_path, _, _, _, config_path = get_path(model_name) paths = get_path(model_name)
# 学習再開の場合を考えて念のためconfig.ymlの名前等を更新 # 学習再開の場合を考えて念のためconfig.ymlの名前等を更新
with open("config.yml", "r", encoding="utf-8") as f: with open("config.yml", "r", encoding="utf-8") as f:
yml_data = yaml.safe_load(f) yml_data = yaml.safe_load(f)
yml_data["model_name"] = model_name yml_data["model_name"] = model_name
yml_data["dataset_path"] = str(dataset_path) yml_data["dataset_path"] = str(paths.dataset_path)
with open("config.yml", "w", encoding="utf-8") as f: with open("config.yml", "w", encoding="utf-8") as f:
yaml.dump(yml_data, f, allow_unicode=True) yaml.dump(yml_data, f, allow_unicode=True)
train_py = "train_ms.py" if not use_jp_extra else "train_ms_jp_extra.py" train_py = "train_ms.py" if not use_jp_extra else "train_ms_jp_extra.py"
cmd = [train_py, "--config", str(config_path), "--model", str(dataset_path)] cmd = [
train_py,
"--config",
str(paths.config_path),
"--model",
str(paths.dataset_path),
]
if skip_style: if skip_style:
cmd.append("--skip_default_style") cmd.append("--skip_default_style")
if speedup: if speedup:

View File

@@ -7,7 +7,7 @@ from typing import Optional
from tqdm import tqdm from tqdm import tqdm
from config import Preprocess_text_config, config from config import get_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.nlp import clean_text from style_bert_vits2.nlp import clean_text
from style_bert_vits2.nlp.japanese import pyopenjtalk_worker from style_bert_vits2.nlp.japanese import pyopenjtalk_worker
@@ -22,7 +22,7 @@ pyopenjtalk_worker.initialize_worker()
update_dict() update_dict()
preprocess_text_config: Preprocess_text_config = config.preprocess_text_config preprocess_text_config = get_config().preprocess_text_config
# Count lines for tqdm # Count lines for tqdm

View File

@@ -12,6 +12,7 @@ matplotlib
num2words num2words
numba numba
numpy numpy
protobuf==4.25
psutil psutil
pyannote.audio>=3.1.0 pyannote.audio>=3.1.0
pydantic>=2.0 pydantic>=2.0

View File

@@ -10,7 +10,7 @@ import soundfile
from numpy.typing import NDArray from numpy.typing import NDArray
from tqdm import tqdm from tqdm import tqdm
from config import config from config import get_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
@@ -70,6 +70,7 @@ def resample(
if __name__ == "__main__": if __name__ == "__main__":
config = get_config()
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
"--sr", "--sr",

View File

@@ -22,7 +22,6 @@ import numpy as np
import requests import requests
import torch import torch
import uvicorn import uvicorn
import yaml
from fastapi import APIRouter, FastAPI, HTTPException, status from fastapi import APIRouter, FastAPI, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, Response from fastapi.responses import JSONResponse, Response
@@ -30,6 +29,7 @@ from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel from pydantic import BaseModel
from scipy.io import wavfile from scipy.io import wavfile
from config import get_path_config
from style_bert_vits2.constants import ( from style_bert_vits2.constants import (
DEFAULT_ASSIST_TEXT_WEIGHT, DEFAULT_ASSIST_TEXT_WEIGHT,
DEFAULT_NOISE, DEFAULT_NOISE,
@@ -174,22 +174,14 @@ origins = [
"http://127.0.0.1:8000", "http://127.0.0.1:8000",
] ]
# Get path settings path_config = get_path_config()
with open(Path("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"]
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--model_dir", type=str, default="model_assets/") parser.add_argument("--model_dir", type=str, default=path_config.assets_root)
parser.add_argument("--device", type=str, default="cuda") parser.add_argument("--device", type=str, default="cuda")
parser.add_argument("--port", type=int, default=8000) parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--inbrowser", action="store_true") parser.add_argument("--inbrowser", action="store_true")
parser.add_argument("--line_length", type=int, default=None) parser.add_argument("--line_length", type=int, default=None)
parser.add_argument("--line_count", type=int, default=None) parser.add_argument("--line_count", type=int, default=None)
parser.add_argument(
"--dir", "-d", type=str, help="Model directory", default=assets_root
)
args = parser.parse_args() args = parser.parse_args()
device = args.device device = args.device

View File

@@ -20,7 +20,7 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, Response from fastapi.responses import FileResponse, Response
from scipy.io import wavfile from scipy.io import wavfile
from config import config from config import get_config
from style_bert_vits2.constants import ( from style_bert_vits2.constants import (
DEFAULT_ASSIST_TEXT_WEIGHT, DEFAULT_ASSIST_TEXT_WEIGHT,
DEFAULT_LENGTH, DEFAULT_LENGTH,
@@ -40,6 +40,7 @@ from style_bert_vits2.nlp.japanese.user_dict import update_dict
from style_bert_vits2.tts_model import TTSModel, TTSModelHolder from style_bert_vits2.tts_model import TTSModel, TTSModelHolder
config = get_config()
ln = config.server_config.language ln = config.server_config.language
@@ -113,6 +114,9 @@ if __name__ == "__main__":
load_models(model_holder) load_models(model_holder)
limit = config.server_config.limit limit = config.server_config.limit
logger.info(
f"The maximum length of the text is {limit}. If you want to change it, modify config.yml"
)
app = FastAPI() app = FastAPI()
allow_origins = config.server_config.origins allow_origins = config.server_config.origins
if allow_origins: if allow_origins:

View File

@@ -10,6 +10,7 @@ import torch
import yaml import yaml
from tqdm import tqdm from tqdm import tqdm
from config import get_path_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
@@ -150,13 +151,12 @@ if __name__ == "__main__":
) )
args = parser.parse_args() args = parser.parse_args()
with open(Path("configs/paths.yml"), "r", encoding="utf-8") as f: path_config = get_path_config()
path_config: dict[str, str] = yaml.safe_load(f.read()) dataset_root = path_config.dataset_root
dataset_root = path_config["dataset_root"]
model_name = str(args.model_name) model_name = str(args.model_name)
input_dir = Path(args.input_dir) input_dir = Path(args.input_dir)
output_dir = Path(dataset_root) / model_name / "raw" output_dir = dataset_root / model_name / "raw"
min_sec: float = args.min_sec min_sec: float = args.min_sec
max_sec: float = args.max_sec max_sec: float = args.max_sec
min_silence_dur_ms: int = args.min_silence_dur_ms min_silence_dur_ms: int = args.min_silence_dur_ms

View File

@@ -10,7 +10,7 @@ import pandas as pd
import torch import torch
from tqdm import tqdm from tqdm import tqdm
from config import config from config import get_path_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.tts_model import TTSModel from style_bert_vits2.tts_model import TTSModel
@@ -35,6 +35,8 @@ test_texts = [
"この分野の最新の研究成果を使うと、より自然で表現豊かな音声の生成が可能である。深層学習の応用により、感情やアクセントを含む声質の微妙な変化も再現することが出来る。", "この分野の最新の研究成果を使うと、より自然で表現豊かな音声の生成が可能である。深層学習の応用により、感情やアクセントを含む声質の微妙な変化も再現することが出来る。",
] ]
path_config = get_path_config()
predictor = torch.hub.load( predictor = torch.hub.load(
"tarepan/SpeechMOS:v1.2.0", "utmos22_strong", trust_repo=True "tarepan/SpeechMOS:v1.2.0", "utmos22_strong", trust_repo=True
) )
@@ -48,17 +50,16 @@ args = parser.parse_args()
model_name: str = args.model_name model_name: str = args.model_name
device: str = args.device device: str = args.device
model_path = Path(config.assets_root) / model_name model_path = path_config.assets_root / model_name
# .safetensorsファイルを検索 # .safetensorsファイルを検索
safetensors_files = model_path.glob("*.safetensors") safetensors_files = model_path.glob("*.safetensors")
def get_model(model_file: Path): def get_model(model_file: Path):
return TTSModel( return TTSModel(
model_path=str(model_file), model_path=model_file,
config_path=str(model_file.parent / "config.json"), config_path=model_file.parent / "config.json",
style_vec_path=str(model_file.parent / "style_vectors.npy"), style_vec_path=model_file.parent / "style_vectors.npy",
device=device, device=device,
) )

View File

@@ -4,7 +4,7 @@ from style_bert_vits2.utils.strenum import StrEnum
# Style-Bert-VITS2 のバージョン # Style-Bert-VITS2 のバージョン
VERSION = "2.4.1" VERSION = "2.5.0"
# Style-Bert-VITS2 のベースディレクトリ # Style-Bert-VITS2 のベースディレクトリ
BASE_DIR = Path(__file__).parent.parent BASE_DIR = Path(__file__).parent.parent

View File

@@ -8,12 +8,14 @@ from numpy.typing import NDArray
from pyannote.audio import Inference, Model from pyannote.audio import Inference, Model
from tqdm import tqdm from tqdm import tqdm
from config import config from config import get_config
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.models.hyper_parameters import HyperParameters from style_bert_vits2.models.hyper_parameters import HyperParameters
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
config = get_config()
model = Model.from_pretrained("pyannote/wespeaker-voxceleb-resnet34-LM") model = Model.from_pretrained("pyannote/wespeaker-voxceleb-resnet34-LM")
inference = Inference(model, window="whole") inference = Inference(model, window="whole")
device = torch.device(config.style_gen_config.device) device = torch.device(config.style_gen_config.device)

View File

@@ -16,7 +16,7 @@ from tqdm import tqdm
# logging.getLogger("numba").setLevel(logging.WARNING) # logging.getLogger("numba").setLevel(logging.WARNING)
import default_style import default_style
from config import config from config import get_config
from data_utils import ( from data_utils import (
DistributedBucketSampler, DistributedBucketSampler,
TextAudioSpeakerCollate, TextAudioSpeakerCollate,
@@ -48,7 +48,7 @@ torch.backends.cuda.enable_mem_efficient_sdp(
) # Not available if torch version is lower than 2.0 ) # Not available if torch version is lower than 2.0
torch.backends.cuda.enable_math_sdp(True) torch.backends.cuda.enable_math_sdp(True)
config = get_config()
global_step = 0 global_step = 0
api = HfApi() api = HfApi()

View File

@@ -16,7 +16,7 @@ from tqdm import tqdm
# logging.getLogger("numba").setLevel(logging.WARNING) # logging.getLogger("numba").setLevel(logging.WARNING)
import default_style import default_style
from config import config from config import get_config
from data_utils import ( from data_utils import (
DistributedBucketSampler, DistributedBucketSampler,
TextAudioSpeakerCollate, TextAudioSpeakerCollate,
@@ -48,6 +48,8 @@ torch.backends.cuda.enable_flash_sdp(True)
torch.backends.cuda.enable_mem_efficient_sdp( torch.backends.cuda.enable_mem_efficient_sdp(
True True
) # Not available if torch version is lower than 2.0 ) # Not available if torch version is lower than 2.0
config = get_config()
global_step = 0 global_step = 0
api = HfApi() api = HfApi()

View File

@@ -8,6 +8,7 @@ import yaml
from torch.utils.data import Dataset from torch.utils.data import Dataset
from tqdm import tqdm from tqdm import tqdm
from config import get_path_config
from style_bert_vits2.constants import Languages from style_bert_vits2.constants import Languages
from style_bert_vits2.logging import logger from style_bert_vits2.logging import logger
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
@@ -124,9 +125,8 @@ if __name__ == "__main__":
parser.add_argument("--no_repeat_ngram_size", type=int, default=10) parser.add_argument("--no_repeat_ngram_size", type=int, default=10)
args = parser.parse_args() args = parser.parse_args()
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f: path_config = get_path_config()
path_config: dict[str, str] = yaml.safe_load(f.read()) dataset_root = path_config.dataset_root
dataset_root = Path(path_config["dataset_root"])
model_name = str(args.model_name) model_name = str(args.model_name)