Refactor and add one-click training

This commit is contained in:
litagin02
2023-12-28 23:21:27 +09:00
parent 5ec33b8d85
commit b0fbcc4667
3 changed files with 230 additions and 97 deletions

View File

@@ -65,6 +65,8 @@ if __name__ == "__main__":
twople = (spk_dir, filename, args) twople = (spk_dir, filename, args)
tasks.append(twople) tasks.append(twople)
if len(tasks) == 0:
raise ValueError(f"No wav files found in {args.in_dir}")
for _ in tqdm( for _ in tqdm(
pool.imap_unordered(process, tasks), file=sys.stdout, total=len(tasks) pool.imap_unordered(process, tasks), file=sys.stdout, total=len(tasks)
): ):

33
tools/subprocess_utils.py Normal file
View File

@@ -0,0 +1,33 @@
import subprocess
import sys
from .log import logger
python = sys.executable
def run_script_with_log(cmd: list[str]) -> tuple[bool, str]:
logger.info(f"Running: {' '.join(cmd)}")
result = subprocess.run(
[python] + cmd,
stdout=sys.stdout,
stderr=subprocess.PIPE,
text=True,
)
if result.returncode != 0:
logger.error(f"Error: {' '.join(cmd)}")
print(result.stderr)
return False, result.stderr
elif result.stderr:
logger.warning(f"Warning: {' '.join(cmd)}")
print(result.stderr)
return True, result.stderr
logger.success(f"Success: {' '.join(cmd)}")
return True, ""
def second_elem_of(original_function):
def inner_function(*args, **kwargs):
return original_function(*args, **kwargs)[1]
return inner_function

View File

@@ -1,22 +1,14 @@
import json import json
import os import os
import shutil import shutil
import subprocess
import sys import sys
from multiprocessing import Pool, cpu_count
import gradio as gr import gradio as gr
import yaml import yaml
python = sys.executable from tools.log import logger
from tools.subprocess_utils import run_script_with_log, second_elem_of
def subprocess_wrapper(cmd):
return subprocess.run(
cmd,
stdout=sys.stdout,
stderr=subprocess.PIPE,
text=True,
)
def get_path(model_name): def get_path(model_name):
@@ -30,6 +22,7 @@ def get_path(model_name):
def initialize(model_name, batch_size, epochs, bf16_run): def initialize(model_name, batch_size, epochs, bf16_run):
logger.info("Step 1: start initialization...")
dataset_path, _, train_path, val_path, config_path = get_path(model_name) dataset_path, _, train_path, val_path, config_path = get_path(model_name)
if os.path.isfile(config_path): if os.path.isfile(config_path):
config = json.load(open(config_path, "r", encoding="utf-8")) config = json.load(open(config_path, "r", encoding="utf-8"))
@@ -47,9 +40,11 @@ def initialize(model_name, batch_size, epochs, bf16_run):
try: try:
shutil.copytree(src="pretrained", dst=model_path) shutil.copytree(src="pretrained", dst=model_path)
except FileExistsError: except FileExistsError:
return f"Error: モデルフォルダ {model_path} が既に存在します。問題なければ削除してください。" logger.error(f"Step 1: {model_path} already exists.")
return False, f"Step1, Error: モデルフォルダ {model_path} が既に存在します。問題なければ削除してください。"
except FileNotFoundError: except FileNotFoundError:
return "Error: pretrainedフォルダが見つかりません。" logger.error("Step 1: `pretrained` folder not found.")
return False, "Step 1, Error: pretrainedフォルダが見つかりません。"
with open(config_path, "w", encoding="utf-8") as f: with open(config_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2) json.dump(config, f, indent=2)
@@ -62,16 +57,17 @@ def initialize(model_name, batch_size, epochs, bf16_run):
yml_data["dataset_path"] = dataset_path yml_data["dataset_path"] = 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)
return "Step 1: 初期設定が完了しました" logger.success("Step 1: initialization finished.")
return True, "Step 1, Success: 初期設定が完了しました"
def resample(model_name): def resample(model_name):
logger.info("Step 2: start resampling...")
dataset_path, _, _, _, _ = get_path(model_name) dataset_path, _, _, _, _ = get_path(model_name)
in_dir = os.path.join(dataset_path, "raw") in_dir = os.path.join(dataset_path, "raw")
out_dir = os.path.join(dataset_path, "wavs") out_dir = os.path.join(dataset_path, "wavs")
result = subprocess_wrapper( success, message = run_script_with_log(
[ [
python,
"resample.py", "resample.py",
"--in_dir", "--in_dir",
in_dir, in_dir,
@@ -81,14 +77,24 @@ def resample(model_name):
"44100", "44100",
] ]
) )
if result.stderr: if not success:
return f"{result.stderr}" logger.error(f"Step 2: resampling failed.")
return "Step 2: 音声ファイルの前処理が完了しました" return False, f"Step 2, Error: 音声ファイルの前処理に失敗しました:\n{message}"
elif message:
logger.warning(f"Step 2: resampling finished with stderr.")
return True, f"Step 2, Success: 音声ファイルの前処理が完了しました:\n{message}"
logger.success("Step 2: resampling finished.")
return True, "Step 2, Success: 音声ファイルの前処理が完了しました"
def preprocess_text(model_name): def preprocess_text(model_name):
logger.info("Step 3: start preprocessing text...")
dataset_path, lbl_path, train_path, val_path, config_path = get_path(model_name) dataset_path, lbl_path, train_path, val_path, config_path = get_path(model_name)
lines = open(lbl_path, "r", encoding="utf-8").readlines() try:
lines = open(lbl_path, "r", encoding="utf-8").readlines()
except FileNotFoundError:
logger.error(f"Step 3: {lbl_path} not found.")
return False, f"Step 3, Error: 書き起こしファイル {lbl_path} が見つかりません。"
with open(lbl_path, "w", encoding="utf-8") as f: with open(lbl_path, "w", encoding="utf-8") as f:
for line in lines: for line in lines:
path, spk, language, text = line.strip().split("|") path, spk, language, text = line.strip().split("|")
@@ -96,9 +102,8 @@ def preprocess_text(model_name):
"\\", "/" "\\", "/"
) )
f.writelines(f"{path}|{spk}|{language}|{text}\n") f.writelines(f"{path}|{spk}|{language}|{text}\n")
result = subprocess_wrapper( success, message = run_script_with_log(
[ [
python,
"preprocess_text.py", "preprocess_text.py",
"--config-path", "--config-path",
config_path, config_path,
@@ -110,25 +115,33 @@ def preprocess_text(model_name):
val_path, val_path,
] ]
) )
if not success:
if result.stderr: logger.error(f"Step 3: preprocessing text failed.")
return f"{result.stderr}" return False, f"Step 3, Error: 書き起こしファイルの前処理に失敗しました:\n{message}"
return "Step 3: 書き起こしファイルの前処理が完了しました" elif message:
logger.warning(f"Step 3: preprocessing text finished with stderr.")
return True, f"Step 3, Success: 書き起こしファイルの前処理が完了しました:\n{message}"
logger.success("Step 3: preprocessing text finished.")
return True, "Step 3, Success: 書き起こしファイルの前処理が完了しました"
def bert_gen(model_name): def bert_gen(model_name):
_, _, _, _, config_path = get_path(model_name) _, _, _, _, config_path = get_path(model_name)
result = subprocess_wrapper([python, "bert_gen.py", "--config", config_path]) success, message = run_script_with_log(["bert_gen.py", "--config", config_path])
if result.stderr: if not success:
return f"{result.stderr}" logger.error(f"Step 4: bert_gen failed.")
return "Step 4: BERT特徴ファイルの生成が完了しました" return False, f"Step 4, Error: BERT特徴ファイルの生成に失敗しました:\n{message}"
elif message:
logger.warning(f"Step 4: bert_gen finished with stderr.")
return True, f"Step 4, Success: BERT特徴ファイルの生成が完了しました:\n{message}"
logger.success("Step 4: bert_gen finished.")
return True, "Step 4, Success: BERT特徴ファイルの生成が完了しました"
def style_gen(model_name, num_processes): def style_gen(model_name, num_processes):
_, _, _, _, config_path = get_path(model_name) _, _, _, _, config_path = get_path(model_name)
result = subprocess_wrapper( success, message = run_script_with_log(
[ [
python,
"style_gen.py", "style_gen.py",
"--config", "--config",
config_path, config_path,
@@ -136,19 +149,49 @@ def style_gen(model_name, num_processes):
str(num_processes), str(num_processes),
] ]
) )
if result.stderr: if not success:
return f"{result.stderr}" logger.error(f"Step 5: style_gen failed.")
return "Step 5: スタイル特徴ファイルの生成が完了しました" return False, f"Step 5, Error: スタイル特徴ファイルの生成に失敗しました:\n{message}"
elif message:
logger.warning(f"Step 5: style_gen finished with stderr.")
return True, f"Step 5, Success: スタイル特徴ファイルの生成が完了しました:\n{message}"
logger.success("Step 5: style_gen finished.")
return True, "Step 5, Success: スタイル特徴ファイルの生成が完了しました"
def preprocess_all(model_name, batch_size, epochs, bf16_run, num_processes):
success, message = initialize(model_name, batch_size, epochs, bf16_run)
if not success:
return success, message
success, message = resample(model_name)
if not success:
return success, message
success, message = preprocess_text(model_name)
if not success:
return success, message
success, message = bert_gen(model_name)
if not success:
return success, message
success, message = style_gen(model_name, num_processes)
if not success:
return success, message
logger.success("Success: all preprocess finished.")
return True, "Success: 全ての前処理が完了しました。ターミナルを確認しておかしいところがないか確認するのをおすすめします。"
def train(model_name): def train(model_name):
dataset_path, _, _, _, config_path = get_path(model_name) dataset_path, _, _, _, config_path = get_path(model_name)
result = subprocess_wrapper( success, message = run_script_with_log(
[python, "train_ms.py", "--config", config_path, "--model", dataset_path] ["train_ms.py", "--config", config_path, "--model", dataset_path]
) )
if result.stderr: if not success:
return f"{result.stderr}" logger.error(f"Final Step: train failed.")
return "Final Step: 学習が完了しました!" return False, f"Final Step, Error: 学習に失敗しました:\n{message}"
elif message:
logger.warning(f"Final Step: train finished with stderr.")
return True, f"Final Step, Success: 学習が完了しました:\n{message}"
logger.success("Final Step: train finished.")
return True, "Final Step, Success: 学習が完了しました"
initial_md = """ initial_md = """
@@ -195,11 +238,9 @@ english_teacher.wav|Mary|EN|How are you? I'm fine, thank you, and you?
""" """
css = """ css = """
/* マークダウン要素のスタイル */
div.panel { div.panel {
justify-content: space-between; justify-content: space-between;
} }
""" """
if __name__ == "__main__": if __name__ == "__main__":
@@ -211,66 +252,123 @@ if __name__ == "__main__":
label="モデル名", label="モデル名",
) )
info = gr.Textbox(label="状況") info = gr.Textbox(label="状況")
gr.Markdown("### 自動前処理")
with gr.Row(variant="panel"): with gr.Row(variant="panel"):
with gr.Column(variant="panel", min_width=160): batch_size = gr.Slider(
gr.Markdown(value="Step 1: 設定ファイルの生成") label="バッチサイズ",
batch_size = gr.Slider( info="VRAM 12GBで4くらい",
label="バッチサイズ", value=4,
info="VRAM 12GBで4くらい", minimum=1,
value=4, maximum=64,
minimum=1, step=1,
maximum=64, )
step=1, epochs = gr.Slider(
) label="エポック数",
epochs = gr.Slider( info="100もあれば十分そう",
label="エポック数", value=100,
info="100もあれば十分そう", minimum=1,
value=100, maximum=1000,
minimum=1, step=1,
maximum=1000, )
step=1, bf16_run = gr.Checkbox(
) label="bfloat16を使う",
bf16_run = gr.Checkbox( info="bfloat16を使うかどうか。新しめのグラボだと学習が早くなるかも、古いグラボだと動かないかも。",
label="bfloat16を使う", value=True,
info="新しめのグラボだと学習が早くなるかも、古いグラボだと動かないかも", )
value=True, num_processes = gr.Slider(
) label="プロセス数",
generate_config_btn = gr.Button(value="実行", variant="primary") info="前処理時の並列処理プロセス数、大きすぎるとフリーズするかも",
with gr.Column(variant="panel", min_width=160): value=cpu_count() // 2,
gr.Markdown(value="Step 2: 音声ファイルの前処理") minimum=1,
resample_btn = gr.Button(value="実行", variant="primary") maximum=cpu_count(),
with gr.Column(variant="panel", min_width=160): step=1,
gr.Markdown(value="Step 3: 書き起こしファイルの前処理") )
preprocess_text_btn = gr.Button(value="実行", variant="primary") preprocess_button = gr.Button(value="実行", variant="primary")
with gr.Column(variant="panel", min_width=160): with gr.Accordion(open=False, label="手動前処理"):
gr.Markdown(value="Step 4: BERT特徴ファイルの生成") with gr.Row(variant="panel"):
bert_gen_btn = gr.Button(value="実行", variant="primary") with gr.Column(variant="panel", min_width=160):
with gr.Column(variant="panel", min_width=160): gr.Markdown(value="#### Step 1: 設定ファイルの生成")
gr.Markdown(value="Step 5: スタイル特徴ファイルの生成") batch_size_manual = gr.Slider(
num_processes = gr.Slider( label="バッチサイズ",
label="プロセス数", value=4,
value=4, minimum=1,
minimum=1, maximum=64,
maximum=16, step=1,
step=1, )
) epochs_manual = gr.Slider(
style_gen_btn = gr.Button(value="実行", variant="primary") label="エポック数",
value=100,
minimum=1,
maximum=1000,
step=1,
)
bf16_run_manual = gr.Checkbox(
label="bfloat16を使う",
value=True,
)
generate_config_btn = gr.Button(value="実行", variant="primary")
with gr.Column(variant="panel", min_width=160):
gr.Markdown(value="#### Step 2: 音声ファイルの前処理")
num_processes_resample = gr.Slider(
label="プロセス数",
value=cpu_count() // 2,
minimum=1,
maximum=cpu_count(),
step=1,
)
resample_btn = gr.Button(value="実行", variant="primary")
with gr.Column(variant="panel", min_width=160):
gr.Markdown(value="#### Step 3: 書き起こしファイルの前処理")
preprocess_text_btn = gr.Button(value="実行", variant="primary")
with gr.Column(variant="panel", min_width=160):
gr.Markdown(value="#### Step 4: BERT特徴ファイルの生成")
num_processes_bert = gr.Slider(
label="プロセス数",
value=cpu_count() // 2,
minimum=1,
maximum=cpu_count(),
step=1,
)
bert_gen_btn = gr.Button(value="実行", variant="primary")
with gr.Column(variant="panel", min_width=160):
gr.Markdown(value="#### Step 5: スタイル特徴ファイルの生成")
num_processes_style = gr.Slider(
label="プロセス数",
value=cpu_count() // 2,
minimum=1,
maximum=cpu_count(),
step=1,
)
style_gen_btn = gr.Button(value="実行", variant="primary")
with gr.Row(variant="panel"): with gr.Row(variant="panel"):
with gr.Column(): with gr.Column():
gr.Markdown(value="Final Step: 学習") gr.Markdown("## 学習")
train_btn = gr.Button(value="学習", variant="primary") train_btn = gr.Button(value="学習を開始する", variant="primary")
generate_config_btn.click( preprocess_button.click(
initialize, second_elem_of(preprocess_all),
inputs=[model_name, batch_size, epochs, bf16_run], inputs=[model_name, batch_size, epochs, bf16_run, num_processes],
outputs=[info], outputs=[info],
) )
resample_btn.click(resample, inputs=[model_name], outputs=[info]) generate_config_btn.click(
preprocess_text_btn.click(preprocess_text, inputs=[model_name], outputs=[info]) second_elem_of(initialize),
bert_gen_btn.click(bert_gen, inputs=[model_name], outputs=[info]) inputs=[model_name, batch_size_manual, epochs_manual, bf16_run_manual],
style_gen_btn.click( outputs=[info],
style_gen, inputs=[model_name, num_processes], outputs=[info]
) )
train_btn.click(train, inputs=[model_name], outputs=[info]) resample_btn.click(
second_elem_of(resample), inputs=[model_name], outputs=[info]
)
preprocess_text_btn.click(
second_elem_of(preprocess_text), inputs=[model_name], outputs=[info]
)
bert_gen_btn.click(
second_elem_of(bert_gen), inputs=[model_name], outputs=[info]
)
style_gen_btn.click(
second_elem_of(style_gen),
inputs=[model_name, num_processes_style],
outputs=[info],
)
train_btn.click(second_elem_of(train), inputs=[model_name], outputs=[info])
app.launch(share=False, inbrowser=True) app.launch(inbrowser=True)