Merge branch 'colab' into dev

This commit is contained in:
litagin02
2023-12-30 18:45:53 +09:00
13 changed files with 154 additions and 22 deletions

5
app.py
View File

@@ -360,6 +360,9 @@ if __name__ == "__main__":
parser.add_argument( parser.add_argument(
"--dir", "-d", type=str, help="Model directory", default=config.out_dir "--dir", "-d", type=str, help="Model directory", default=config.out_dir
) )
parser.add_argument(
"--share", action="store_true", help="Share this app publicly", default=False
)
args = parser.parse_args() args = parser.parse_args()
model_dir = args.dir model_dir = args.dir
@@ -518,4 +521,4 @@ if __name__ == "__main__":
outputs=[style, ref_audio_path], outputs=[style, ref_audio_path],
) )
app.launch(inbrowser=True) app.launch(inbrowser=True, share=args.share)

View File

@@ -1,5 +1,4 @@
import argparse import argparse
import sys
from multiprocessing import Pool from multiprocessing import Pool
import torch import torch
@@ -10,6 +9,7 @@ import commons
import utils import utils
from config import config from config import config
from text import cleaned_text_to_sequence, get_bert from text import cleaned_text_to_sequence, get_bert
from tools.stdout_wrapper import SAFE_STDOUT
def process_line(x): def process_line(x):
@@ -76,7 +76,7 @@ if __name__ == "__main__":
for _ in tqdm( for _ in tqdm(
pool.imap_unordered(process_line, zip(lines, add_blank)), pool.imap_unordered(process_line, zip(lines, add_blank)),
total=len(lines), total=len(lines),
file=sys.stdout, file=SAFE_STDOUT,
): ):
# 这里是缩进的代码块,表示循环体 # 这里是缩进的代码块,表示循环体
pass # 使用pass语句作为占位符 pass # 使用pass语句作为占位符

29
default_style.py Normal file
View File

@@ -0,0 +1,29 @@
import os
from tools.log import logger
import numpy as np
import json
def set_style_config(json_path, output_path):
with open(json_path, "r") as f:
json_dict = json.load(f)
json_dict["data"]["num_styles"] = 1
json_dict["data"]["style2id"] = {"Neutral": 0}
with open(output_path, "w") as f:
json.dump(json_dict, f, indent=2)
logger.info(f"Update style config (only Neutral style) to {output_path}")
def save_mean_vector(wav_dir, output_path):
embs = []
for file in os.listdir(wav_dir):
if file.endswith(".npy"):
xvec = np.load(os.path.join(wav_dir, file))
embs.append(np.expand_dims(xvec, axis=0))
x = np.concatenate(embs, axis=0) # (N, 256)
mean = np.mean(x, axis=0) # (256,)
only_mean = np.stack([mean]) # (1, 256)
np.save(output_path, only_mean)
logger.info(f"Saved mean style vector to {output_path}")

View File

@@ -1,6 +1,5 @@
import json import json
import os import os
import sys
from collections import defaultdict from collections import defaultdict
from random import shuffle from random import shuffle
from typing import Optional from typing import Optional
@@ -10,6 +9,7 @@ from tqdm import tqdm
from config import config from config import config
from text.cleaner import clean_text from text.cleaner import clean_text
from tools.stdout_wrapper import SAFE_STDOUT
preprocess_text_config = config.preprocess_text_config preprocess_text_config = config.preprocess_text_config
@@ -52,7 +52,7 @@ def preprocess(
lines = trans_file.readlines() lines = trans_file.readlines()
# print(lines, ' ', len(lines)) # print(lines, ' ', len(lines))
if len(lines) != 0: if len(lines) != 0:
for line in tqdm(lines, file=sys.stdout): for line in tqdm(lines, file=SAFE_STDOUT):
try: try:
utt, spk, language, text = line.strip().split("|") utt, spk, language, text = line.strip().split("|")
norm_text, phones, tones, word2ph = clean_text( norm_text, phones, tones, word2ph = clean_text(

View File

@@ -1,6 +1,5 @@
import argparse import argparse
import os import os
import sys
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
import librosa import librosa
@@ -10,6 +9,7 @@ from tqdm import tqdm
from config import config from config import config
from tools.log import logger from tools.log import logger
from tools.stdout_wrapper import SAFE_STDOUT
def normalize_audio(data, sr): def normalize_audio(data, sr):
@@ -97,7 +97,7 @@ if __name__ == "__main__":
pool = Pool(processes=processes) pool = Pool(processes=processes)
for _ in tqdm( for _ in tqdm(
pool.imap_unordered(process, tasks), file=sys.stdout, total=len(tasks) pool.imap_unordered(process, tasks), file=SAFE_STDOUT, total=len(tasks)
): ):
pass pass

View File

@@ -1,12 +1,13 @@
import argparse import argparse
import os import os
import shutil import shutil
import sys
import soundfile as sf import soundfile as sf
import torch import torch
from tqdm import tqdm from tqdm import tqdm
from tools.stdout_wrapper import SAFE_STDOUT
vad_model, utils = torch.hub.load( vad_model, utils = torch.hub.load(
repo_or_dir="snakers4/silero-vad", repo_or_dir="snakers4/silero-vad",
model="silero_vad", model="silero_vad",
@@ -106,7 +107,7 @@ if __name__ == "__main__":
shutil.rmtree(output_dir) shutil.rmtree(output_dir)
total_sec = 0 total_sec = 0
for wav_file in tqdm(wav_files, file=sys.stdout): for wav_file in tqdm(wav_files, file=SAFE_STDOUT):
time_sec = split_wav( time_sec = split_wav(
wav_file, wav_file,
output_dir, output_dir,

View File

@@ -1,6 +1,5 @@
import argparse import argparse
import concurrent.futures import concurrent.futures
import sys
import warnings import warnings
import numpy as np import numpy as np
@@ -9,6 +8,7 @@ from tqdm import tqdm
import utils import utils
from config import config from config import config
from tools.stdout_wrapper import SAFE_STDOUT
warnings.filterwarnings("ignore", category=UserWarning) warnings.filterwarnings("ignore", category=UserWarning)
from pyannote.audio import Inference, Model from pyannote.audio import Inference, Model
@@ -25,8 +25,13 @@ def extract_style_vector(wav_path):
def save_style_vector(wav_path): def save_style_vector(wav_path):
style_vec = extract_style_vector(wav_path) style_vec = extract_style_vector(wav_path)
# `test.wav` -> `test.wav.npy` np.save(f"{wav_path}.npy", style_vec) # `test.wav` -> `test.wav.npy`
np.save(f"{wav_path}.npy", style_vec) return style_vec
def save_average_style_vector(style_vectors, filename="style_vectors.npy"):
average_vector = np.mean(style_vectors, axis=0)
np.save(filename, average_vector)
if __name__ == "__main__": if __name__ == "__main__":
@@ -59,7 +64,7 @@ if __name__ == "__main__":
tqdm( tqdm(
executor.map(save_style_vector, wavnames), executor.map(save_style_vector, wavnames),
total=len(wavnames), total=len(wavnames),
file=sys.stdout, file=SAFE_STDOUT,
) )
) )

View File

@@ -2,8 +2,8 @@
logger封装 logger封装
""" """
from loguru import logger from loguru import logger
import sys
from .stdout_wrapper import SAFE_STDOUT
# 移除所有默认的处理器 # 移除所有默认的处理器
logger.remove() logger.remove()
@@ -13,4 +13,4 @@ log_format = (
"<g>{time:MM-DD HH:mm:ss}</g> |<lvl>{level:^8}</lvl>| {file}:{line} | {message}" "<g>{time:MM-DD HH:mm:ss}</g> |<lvl>{level:^8}</lvl>| {file}:{line} | {message}"
) )
logger.add(sys.stdout, format=log_format, backtrace=True, diagnose=True) logger.add(SAFE_STDOUT, format=log_format, backtrace=True, diagnose=True)

34
tools/stdout_wrapper.py Normal file
View File

@@ -0,0 +1,34 @@
import sys
import tempfile
class StdoutWrapper:
def __init__(self):
self.temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False)
self.original_stdout = sys.stdout
def write(self, message: str):
self.temp_file.write(message)
self.temp_file.flush()
print(message, end="", file=self.original_stdout)
def flush(self):
self.temp_file.flush()
def read(self):
self.temp_file.seek(0)
return self.temp_file.read()
def close(self):
self.temp_file.close()
def fileno(self):
return self.temp_file.fileno()
try:
import google.colab
SAFE_STDOUT = StdoutWrapper()
except ImportError:
SAFE_STDOUT = sys.stdout

View File

@@ -2,6 +2,7 @@ import subprocess
import sys import sys
from .log import logger from .log import logger
from .stdout_wrapper import SAFE_STDOUT
python = sys.executable python = sys.executable
@@ -10,7 +11,7 @@ def run_script_with_log(cmd: list[str]) -> tuple[bool, str]:
logger.info(f"Running: {' '.join(cmd)}") logger.info(f"Running: {' '.join(cmd)}")
result = subprocess.run( result = subprocess.run(
[python] + cmd, [python] + cmd,
stdout=sys.stdout, stdout=SAFE_STDOUT, # type: ignore
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, text=True,
) )

View File

@@ -17,6 +17,7 @@ from tqdm import tqdm
# logging.getLogger("numba").setLevel(logging.WARNING) # logging.getLogger("numba").setLevel(logging.WARNING)
import commons import commons
import default_style
import utils import utils
from config import config from config import config
from data_utils import ( from data_utils import (
@@ -29,6 +30,7 @@ from mel_processing import mel_spectrogram_torch, spec_to_mel_torch
from models import DurationDiscriminator, MultiPeriodDiscriminator, SynthesizerTrn from models import DurationDiscriminator, MultiPeriodDiscriminator, SynthesizerTrn
from text.symbols import symbols from text.symbols import symbols
from tools.log import logger from tools.log import logger
from tools.stdout_wrapper import SAFE_STDOUT
torch.backends.cuda.matmul.allow_tf32 = True torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = ( torch.backends.cudnn.allow_tf32 = (
@@ -41,6 +43,14 @@ 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
torch.backends.cuda.enable_math_sdp(True) torch.backends.cuda.enable_math_sdp(True)
try:
import google.colab
IS_COLAB = True
except ImportError:
IS_COLAB = False
global_step = 0 global_step = 0
@@ -106,9 +116,39 @@ def run():
data = f.read() data = f.read()
with open(config.train_ms_config.config_path, "w", encoding="utf-8") as f: with open(config.train_ms_config.config_path, "w", encoding="utf-8") as f:
f.write(data) f.write(data)
"""
Path constants are a bit complicated...
TODO: Refactor or rename these?
(Both `config.yml` and `config.json` are used, which is confusing I think.)
args.model: For saving all info needed for training.
default: `Data/{model_name}`.
hps.model_dir = model_dir: For saving checkpoints (for resuming training).
default: `Data/{model_name}/models`.
config.out_dir: Root directory of model assets needed for inference.
default: `model_assets`.
out_dir: For saving resulting models (for inference).
default: `model_assets/{model_name}`, which is used for inference.
"""
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) out_dir = os.path.join(config.out_dir, config.model_name)
os.makedirs(out_dir, exist_ok=True) os.makedirs(out_dir, exist_ok=True)
shutil.copy(args.config, os.path.join(out_dir, "config.json"))
# 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"),
)
torch.manual_seed(hps.train.seed) torch.manual_seed(hps.train.seed)
torch.cuda.set_device(local_rank) torch.cuda.set_device(local_rank)
@@ -427,7 +467,7 @@ def train_and_evaluate(
ja_bert, ja_bert,
en_bert, en_bert,
style_vec, style_vec,
) in enumerate(tqdm(train_loader, file=sys.stdout)): ) in enumerate(tqdm(train_loader, file=SAFE_STDOUT)):
if net_g.module.use_noise_scaled_mas: if net_g.module.use_noise_scaled_mas:
current_mas_noise_scale = ( current_mas_noise_scale = (
net_g.module.mas_noise_scale_initial net_g.module.mas_noise_scale_initial

View File

@@ -5,6 +5,8 @@ import sys
from faster_whisper import WhisperModel from faster_whisper import WhisperModel
from tqdm import tqdm from tqdm import tqdm
from tools.stdout_wrapper import SAFE_STDOUT
def transcribe(wav_path, initial_prompt=None): def transcribe(wav_path, initial_prompt=None):
segments, _ = model.transcribe( segments, _ = model.transcribe(
@@ -45,7 +47,7 @@ if __name__ == "__main__":
os.rename(output_file, output_file + ".bak") os.rename(output_file, output_file + ".bak")
with open(output_file, "w", encoding="utf-8") as f: with open(output_file, "w", encoding="utf-8") as f:
for wav_file in tqdm(wav_files, file=sys.stdout): for wav_file in tqdm(wav_files, file=SAFE_STDOUT):
file_name = os.path.basename(wav_file) file_name = os.path.basename(wav_file)
text = transcribe(wav_file, initial_prompt=initial_prompt) text = transcribe(wav_file, initial_prompt=initial_prompt)
f.write(f"{file_name}|{speaker_name}|JP|{text}\n") f.write(f"{file_name}|{speaker_name}|JP|{text}\n")

View File

@@ -10,10 +10,24 @@ import yaml
from tools.log import logger from tools.log import logger
from tools.subprocess_utils import run_script_with_log, second_elem_of from tools.subprocess_utils import run_script_with_log, second_elem_of
try:
import google.colab
IS_COLAB = True
except ImportError:
IS_COLAB = False
def get_path(model_name): def get_path(model_name):
assert model_name != "", "モデル名は空にできません" assert model_name != "", "モデル名は空にできません"
dataset_path = os.path.join("Data", 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)
lbl_path = os.path.join(dataset_path, "esd.list") lbl_path = os.path.join(dataset_path, "esd.list")
train_path = os.path.join(dataset_path, "train.list") train_path = os.path.join(dataset_path, "train.list")
val_path = os.path.join(dataset_path, "val.list") val_path = os.path.join(dataset_path, "val.list")
@@ -39,9 +53,12 @@ def initialize(model_name, batch_size, epochs, save_every_steps, bf16_run):
model_path = os.path.join(dataset_path, "models") model_path = os.path.join(dataset_path, "models")
try: try:
shutil.copytree(src="pretrained", dst=model_path) shutil.copytree(
src="pretrained",
dst=model_path,
)
except FileExistsError: except FileExistsError:
logger.error(f"Step 1: {model_path} already exists.") logger.warning(f"Step 1: {model_path} already exists.")
return False, f"Step1, Error: モデルフォルダ {model_path} が既に存在します。問題なければ削除してください。" return False, f"Step1, Error: モデルフォルダ {model_path} が既に存在します。問題なければ削除してください。"
except FileNotFoundError: except FileNotFoundError:
logger.error("Step 1: `pretrained` folder not found.") logger.error("Step 1: `pretrained` folder not found.")