This commit is contained in:
litagin02
2024-05-28 18:37:44 +09:00
parent e8691289bb
commit ceb431192c
7 changed files with 245 additions and 68 deletions

View File

@@ -2,7 +2,9 @@
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "F7aJhsgLAWvO"
},
"source": [
"# Style-Bert-VITS2 (ver 2.5.0) のGoogle Colabでの学習\n",
"\n",
@@ -22,34 +24,52 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "L-gAIubBAWvQ"
},
"source": [
"## 0. 環境構築\n",
"\n",
"Style-Bert-VITS2の環境をcolab上に構築します。グラボモードが有効になっていることを確認し、以下のセルを順に実行してください。\n",
"\n",
"**最近のcolabのアップデートにより、エラーダイアログ「WARNING: The following packages were previously imported in this runtime: [pydevd_plugins]」が出るが、「キャンセル」を選択して続行してください。**"
"Style-Bert-VITS2の環境をcolab上に構築します。ランタイムがT4等のGPUバックエンドになっていることを確認し、実行してください。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0GNj8JyDAlm2",
"outputId": "d8be4a1a-e52d-46f8-8675-3f1a24bc9a51"
},
"outputs": [],
"source": [
"# このセルを実行して環境構築してください。\n",
"# エラーダイアログ「WARNING: The following packages were previously imported in this runtime: [pydevd_plugins]」が出るが「キャンセル」を選択して続行してください。\n",
"import os\n",
"\n",
"\n",
"os.environ[\"PATH\"] += \":/root/.cargo/bin\"\n",
"\n",
"!curl -LsSf https://astral.sh/uv/install.sh | sh\n",
"!git clone https://github.com/litagin02/Style-Bert-VITS2.git\n",
"%cd Style-Bert-VITS2/\n",
"!pip install -r requirements.txt\n",
"!python initialize.py --skip_jvnv"
"# 後で消す!!!\n",
"!git checkout dev\n",
"# 後で消す!!!\n",
"!uv pip install --system -r requirements-colab.txt\n",
"!python initialize.py --skip_default_models"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "o5z1nzkvAWvR",
"outputId": "cd87f053-18e0-4dbb-f904-d5230d1fa7ef"
},
"outputs": [],
"source": [
"# Google driveを使う方はこちらを実行してください。\n",
@@ -61,7 +81,9 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "WU9apXzcAWvR"
},
"source": [
"## 1. 初期設定\n",
"\n",
@@ -71,8 +93,10 @@
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"execution_count": null,
"metadata": {
"id": "gO3OwZV1AWvR"
},
"outputs": [],
"source": [
"# 学習に必要なファイルや途中経過が保存されるディレクトリ\n",
@@ -90,7 +114,9 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "dA_yLeezAWvS"
},
"source": [
"## 2. 学習に使うデータ準備\n",
"\n",
@@ -99,17 +125,27 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "8s9gOnTCAWvS"
},
"source": [
"### 2.1 音声ファイルからのデータセットの作成(ある人はスキップ可)\n",
"\n",
"音声ファイル1ファイル2-12秒程度とその書き起こしのデータセットを持っていない方は、日本語の音声ファイルのみから以下の手順でデータセットを作成することができます。Google drive上の`Style-Bert-VITS2/inputs/`フォルダに音声ファイルwavファイル形式、1ファイルでも複数ファイルでも可を置いて、下を実行すると、データセットが作られ、自動的に正しい場所へ配置されます。"
"音声ファイル1ファイル2-12秒程度とその書き起こしのデータセットを持っていない方は、日本語の音声ファイルのみから以下の手順でデータセットを作成することができます。Google drive上の`Style-Bert-VITS2/inputs/`フォルダに音声ファイルwavやmp3等の通常の音声ファイル形式、1ファイルでも複数ファイルでも可を置いて、下を実行すると、データセットが作られ、自動的に正しい場所へ配置されます。\n",
"\n",
"**2024-05-27のVer 2.5以降**、`inputs/`フォルダにサブフォルダを2個以上作ってそこへ音声ファイルをスタイルに応じて振り分けて置くと、学習の際にサブディレクトリに応じたスタイルが自動的に作成されます。デフォルトスタイルのみでよい場合や手動でスタイルを後で作成する場合は`inputs/`直下へ入れれば大丈夫です。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "_fXCTPuiAWvS",
"outputId": "47abd55b-efe5-48e2-f6fa-8e2016efe0ec"
},
"outputs": [],
"source": [
"# 元となる音声ファイルwav形式を入れるディレクトリ\n",
@@ -126,14 +162,18 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "j7vEWewoAWvS"
},
"source": [
"成功したらそのまま3へ進んでください"
]
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "Z3AC-3zpAWvS"
},
"source": [
"### 2.2 音声ファイルと書き起こしデータがすでにある場合\n",
"\n",
@@ -144,7 +184,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"metadata": {
"id": "esCNJl704h52"
},
@@ -157,39 +197,58 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "aaDgJCjCAWvT"
},
"source": [
"次に、学習に必要なデータを、Google driveに作成された`Style-Bert-VITS2/Data`フォルダに配置します。\n",
"まず音声データと、書き起こしテキストを用意してください。\n",
"\n",
"それを次のように配置します。\n",
"```\n",
"├── Data/\n",
"│ ├── {モデルの名前}\n",
"│ │ ├── esd.list\n",
"│ │ ├── raw/\n",
"│ │ │ ├── foo.wav\n",
"│ │ │ ├── bar.mp3\n",
"│ │ │ ├── style1/\n",
"│ │ │ │ ├── baz.wav\n",
"│ │ │ │ ├── qux.wav\n",
"│ │ │ ├── style2/\n",
"│ │ │ │ ├── corge.wav\n",
"│ │ │ │ ├── grault.wav\n",
"...\n",
"```\n",
"\n",
"### 配置の仕方\n",
"- 上のように配置すると、`style1/`と`style2/`フォルダの内部(直下以外も含む)に入っている音声ファイルたちから、自動的にデフォルトスタイルに加えて`style1`と`style2`というスタイルが作成されます\n",
"- 特にスタイルを作る必要がない場合や、スタイル分類機能等でスタイルを作る場合は、`raw/`フォルダ直下に全てを配置してください。このように`raw/`のサブディレクトリの個数が0または1の場合は、スタイルはデフォルトスタイルのみが作成されます。\n",
"- 音声ファイルのフォーマットはwav形式以外にもmp3等の多くの音声ファイルに対応しています\n",
"\n",
"### 書き起こしファイル`esd.list`\n",
"\n",
"`Data/{モデルの名前}/esd.list` ファイルには、以下のフォーマットで各音声ファイルの情報を記述してください。\n",
"\n",
"まず音声データwavファイルで1ファイルが2-12秒程度の、長すぎず短すぎない発話のものをいくつかと、書き起こしテキストを用意してください。wavファイル名やモデルの名前は空白を含まない半角で、wavファイルの拡張子は小文字`.wav`である必要があります。\n",
"\n",
"書き起こしテキストは、次の形式で記述してください。\n",
"```\n",
"****.wav|{話者名}|{言語ID、ZHかJPかEN}|{書き起こしテキスト}\n",
"path/to/audio.wav(wavファイル以外でもこう書く)|{話者名}|{言語ID、ZHかJPかEN}|{書き起こしテキスト}\n",
"```\n",
"\n",
"- ここで、最初の`path/to/audio.wav`は、`raw/`からの相対パスです。つまり、`raw/foo.wav`の場合は`foo.wav`、`raw/style1/bar.wav`の場合は`style1/bar.wav`となります。\n",
"- 拡張子がwavでない場合でも、`esd.list`には`wav`と書いてください、つまり、`raw/bar.mp3`の場合でも`bar.wav`と書いてください。\n",
"\n",
"\n",
"例:\n",
"```\n",
"wav_number1.wav|hanako|JP|こんにちは、聞こえて、いますか?\n",
"wav_next.wav|taro|JP|はい、聞こえています……。\n",
"foo.wav|hanako|JP|こんにちは、元気ですか?\n",
"bar.wav|taro|JP|はい、聞こえています……。何か用ですか?\n",
"style1/baz.wav|hanako|JP|今日はいい天気ですね。\n",
"style1/qux.wav|taro|JP|はい、そうですね。\n",
"...\n",
"english_teacher.wav|Mary|EN|How are you? I'm fine, thank you, and you?\n",
"...\n",
"```\n",
"日本語話者の単一話者データセットで構いません。\n",
"\n",
"### データセットの配置\n",
"\n",
"次にモデルの名前を適当に決めてください(空白を含まない半角英数字がよいです)。\n",
"そして、書き起こしファイルを`esd.list`という名前で保存し、またwavファイルも`raw`というフォルダを作成し、あなたのGoogle Driveの中の上で自動的に作られるはずの`Data`フォルダのなかに、次のように配置します。\n",
"```\n",
"├── Data\n",
"│ ├── {モデルの名前}\n",
"│ │ ├── esd.list\n",
"│ │ ├── raw\n",
"│ │ │ ├── ****.wav\n",
"│ │ │ ├── ****.wav\n",
"│ │ │ ├── ...\n",
"```"
"もちろん日本語話者の単一話者データセットで構いません。"
]
},
{
@@ -205,7 +264,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"metadata": {
"id": "CXR7kjuF5GlE"
},
@@ -240,7 +299,9 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "BFZdLTtpAWvT"
},
"source": [
"上のセルが実行されたら、次のセルを実行して学習の前処理を行います。"
]
@@ -253,7 +314,7 @@
"base_uri": "https://localhost:8080/"
},
"id": "xMVaOIPLabV5",
"outputId": "15fac868-9132-45d9-9f5f-365b6aeb67b0"
"outputId": "36b1c2b2-6df0-4d00-d86a-519a0fc0af63"
},
"outputs": [],
"source": [
@@ -271,7 +332,7 @@
" freeze_JP_bert=False,\n",
" freeze_ZH_bert=False,\n",
" freeze_style=False,\n",
" freeze_decoder=False, # ここをTrueにするともしかしたら違う結果になるかもしれません。\n",
" freeze_decoder=False,\n",
" use_jp_extra=use_jp_extra,\n",
" val_per_lang=0,\n",
" log_interval=200,\n",
@@ -281,7 +342,9 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"id": "sVhwI5C-AWvT"
},
"source": [
"## 4. 学習\n",
"\n",
@@ -296,11 +359,7 @@
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "laieKrbEb6Ij",
"outputId": "72238c88-f294-4ed9-84f6-84c1c17999ca"
"id": "laieKrbEb6Ij"
},
"outputs": [],
"source": [
@@ -311,7 +370,9 @@
"import yaml\n",
"from gradio_tabs.train import get_path\n",
"\n",
"dataset_path, _, _, _, config_path = get_path(model_name)\n",
"paths = get_path(model_name)\n",
"dataset_path = str(paths.dataset_path)\n",
"config_path = str(paths.config_path)\n",
"\n",
"with open(\"default_config.yml\", \"r\", encoding=\"utf-8\") as f:\n",
" yml_data = yaml.safe_load(f)\n",
@@ -323,21 +384,30 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"colab": {
"background_save": true,
"base_uri": "https://localhost:8080/"
},
"id": "JqGeHNabAWvT",
"outputId": "c51b422c-728b-420b-fa92-b787fa058adf"
},
"outputs": [],
"source": [
"# 日本語特化版を「使う」場合\n",
"!python train_ms_jp_extra.py --config {config_path} --model {dataset_path} --assets_root {assets_root}"
"!python train_ms_jp_extra.py --config {config_path} --model {dataset_path} --assets_root {assets_root} --use_custom_batch_sampler"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"id": "rVbjh-WPAWvU"
},
"outputs": [],
"source": [
"# 日本語特化版を「使わない」場合\n",
"!python train_ms.py --config {config_path} --model {dataset_path} --assets_root {assets_root}"
"!python train_ms.py --config {config_path} --model {dataset_path} --assets_root {assets_root} --use_custom_batch_sampler"
]
},
{
@@ -348,12 +418,12 @@
"base_uri": "https://localhost:8080/"
},
"id": "c7g0hrdeP1Tl",
"outputId": "94f9a6f6-027f-4554-ce0c-60ac56251c22"
"outputId": "4bb9d21e-50df-4ba5-a547-daa78a4b63dc"
},
"outputs": [],
"source": [
"# 学習結果を試す・マージ・スタイル分けはこちらから\n",
"!python app.py --share"
"!python app.py --share --skip_default_models"
]
}
],

View File

@@ -15,7 +15,7 @@ def set_style_config(json_path: Path, output_path: Path):
json_dict["data"]["style2id"] = {DEFAULT_STYLE: 0}
with open(output_path, "w", encoding="utf-8") as f:
json.dump(json_dict, f, indent=2, ensure_ascii=False)
logger.info(f"Save style config (only {DEFAULT_STYLE}) to {output_path}")
logger.info(f"Saving style config (only {DEFAULT_STYLE}) to {output_path}...")
def save_neutral_vector(wav_dir: Union[Path, str], output_path: Union[Path, str]):

View File

@@ -329,7 +329,7 @@ def train(
skip_style: bool = False,
use_jp_extra: bool = True,
speedup: bool = False,
use_custom_batch_sampler: bool = False,
not_use_custom_batch_sampler: bool = False,
):
paths = get_path(model_name)
# 学習再開の場合を考えて念のためconfig.ymlの名前等を更新
@@ -352,7 +352,7 @@ def train(
cmd.append("--skip_default_style")
if speedup:
cmd.append("--speedup")
if use_custom_batch_sampler:
if not not_use_custom_batch_sampler:
cmd.append("--use_custom_batch_sampler")
success, message = run_script_with_log(cmd, ignore_warning=True)
if not success:
@@ -723,9 +723,9 @@ def create_train_app():
label="JP-Extra版を使う",
value=True,
)
use_custom_batch_sampler = gr.Checkbox(
label="カスタムバッチサンプラーを使",
info="Ver 2.5以降にうまく学習できなかったりVRAMが足りない場合に試してみてください",
not_use_custom_batch_sampler = gr.Checkbox(
label="カスタムバッチサンプラーを使わない",
info="VRAMに余裕がある場合にチェックすると、長い音声ファイルも学習に使われるようになります",
value=False,
)
speedup = gr.Checkbox(
@@ -820,7 +820,7 @@ def create_train_app():
skip_style,
use_jp_extra_train,
speedup,
use_custom_batch_sampler,
not_use_custom_batch_sampler,
],
outputs=[info_train],
)

15
requirements-colab.txt Normal file
View File

@@ -0,0 +1,15 @@
cmudict
cn2an
g2p_en
gradio
jieba
librosa==0.9.2
loguru
num2words
pyannote.audio>=3.1.0
pyloudnorm
pyopenjtalk-dict
pypinyin
pyworld-prebuilt
transformers
umap-learn

View File

@@ -5,7 +5,6 @@ g2p_en
GPUtil
gradio
jieba
langid
librosa==0.9.2
loguru
num2words

View File

@@ -62,6 +62,7 @@ def resample(
if trim:
wav, _ = librosa.effects.trim(wav, top_db=30)
relative_path = file.relative_to(input_dir)
# ここで拡張子が.wav以外でも.wavに置き換えられる
output_path = output_dir / relative_path.with_suffix(".wav")
output_path.parent.mkdir(parents=True, exist_ok=True)
soundfile.write(output_path, wav, sr)

92
vad_filter.py Normal file
View File

@@ -0,0 +1,92 @@
import argparse
import os
import shutil
import sys
from pathlib import Path
import pandas as pd
import torch
from tqdm import tqdm
from style_bert_vits2.logging import logger
vad_model, utils = torch.hub.load(
repo_or_dir="litagin02/silero-vad",
model="silero_vad",
onnx=True,
trust_repo=True,
)
(get_speech_timestamps, _, read_audio, *_) = utils
def get_speech_ratio(audio_file):
sampling_rate = 16000
wav = read_audio(audio_file, sampling_rate=sampling_rate)
speech_timestamps = get_speech_timestamps(
wav, vad_model, sampling_rate=sampling_rate
)
speech_dur_ms = 0
for ts in speech_timestamps:
start_ms = ts["start"] / 16
end_ms = ts["end"] / 16
speech_dur_ms += end_ms - start_ms
total_dur_ms = len(wav) / sampling_rate * 1000
return speech_dur_ms / total_dur_ms
def process(file: Path):
speech_ratio = get_speech_ratio(file)
return file, speech_ratio
def main():
parser = argparse.ArgumentParser(description="Calculate speech ratio.")
parser.add_argument(
"-i", "--input", help="Directory containing audio files", required=True
)
args = parser.parse_args()
if os.path.exists(os.path.join(args.input, "low_speech_ratio")):
logger.info("Low speech ratio directory already exists, skipping...")
exit(0)
data_dir = Path(args.input)
wav_files = list(data_dir.glob("*.wav"))
wav_files.sort()
if len(wav_files) < 100:
logger.warning("Too few files, skipping...")
exit(0)
logger.info(f"Start VAD filtering for {data_dir}...")
results = []
for wav_file in tqdm(wav_files, file=sys.stdout):
speech_ratio = get_speech_ratio(wav_file)
results.append((wav_file, speech_ratio))
results_df = pd.DataFrame(results, columns=["file", "speech_ratio"])
results_df.to_csv(os.path.join(data_dir, "speech_ratio.csv"), index=False)
logger.info(f"Speech ratio stats:\n{results_df['speech_ratio'].describe()}")
threshold = 0.5
low_speech_ratio_dir = os.path.join(data_dir, "low_speech_ratio")
os.makedirs(low_speech_ratio_dir, exist_ok=True)
low_speech_files = results_df[results_df["speech_ratio"] < threshold]["file"]
logger.info(f"Moving {len(low_speech_files)} files to {low_speech_ratio_dir}...")
for low_speech_file in low_speech_files:
shutil.move(low_speech_file, low_speech_ratio_dir)
logger.success("VAD filtering completed.")
if __name__ == "__main__":
main()