This commit is contained in:
tuna2134
2026-07-24 16:06:49 +09:00
22 changed files with 158 additions and 84 deletions

View File

@@ -2,7 +2,7 @@
# See https://huggingface.co/docs/hub/spaces-sdks-docker-first-demo # See https://huggingface.co/docs/hub/spaces-sdks-docker-first-demo
FROM python:3.10 FROM python:3.12
RUN useradd -m -u 1000 user RUN useradd -m -u 1000 user

View File

@@ -5,8 +5,8 @@
# 主なバージョン等 # 主なバージョン等
# Ubuntu 22.04 # Ubuntu 22.04
# Python 3.10 # Python 3.12
# PyTorch 2.1.2 (CUDA 11.8) # PyTorch 2.7.x (CUDA 11.8)
# CUDA Toolkit 12.0, CUDNN 8.9.7 # CUDA Toolkit 12.0, CUDNN 8.9.7
@@ -24,7 +24,7 @@ ENV SHELL=/bin/bash
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV APT_INSTALL="apt-get install -y --no-install-recommends" ENV APT_INSTALL="apt-get install -y --no-install-recommends"
ENV PIP_INSTALL="python3 -m pip --no-cache-dir install --upgrade" ENV PIP_INSTALL="python3.12 -m pip --no-cache-dir install --upgrade"
ENV GIT_CLONE="git clone --depth 10" ENV GIT_CLONE="git clone --depth 10"
# ================================================================== # ==================================================================
@@ -44,10 +44,14 @@ RUN apt-get update && \
nano \ nano \
ffmpeg \ ffmpeg \
software-properties-common \ software-properties-common \
gnupg \ gnupg
python3 \
python3-pip \ RUN add-apt-repository ppa:deadsnakes/ppa && \
python3-dev apt-get update && \
$APT_INSTALL python3.12 python3.12-dev && \
curl -sS https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py && \
python3.12 /tmp/get-pip.py && \
rm /tmp/get-pip.py
# ================================================================== # ==================================================================
# Git-lfs # Git-lfs
@@ -57,8 +61,9 @@ RUN curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.d
$APT_INSTALL git-lfs $APT_INSTALL git-lfs
# Add symlink so python and python3 commands use same python3.9 executable # Add symlinks so python and python3 commands use Python 3.12.
RUN ln -s /usr/bin/python3 /usr/local/bin/python RUN ln -s /usr/bin/python3.12 /usr/local/bin/python && \
ln -s /usr/bin/python3.12 /usr/local/bin/python3
# ================================================================== # ==================================================================
# Installing CUDA packages (CUDA Toolkit 12.0 and CUDNN 8.9.7) # Installing CUDA packages (CUDA Toolkit 12.0 and CUDNN 8.9.7)
@@ -106,4 +111,4 @@ RUN rm /tmp/requirements.txt
EXPOSE 8888 6006 EXPOSE 8888 6006
CMD jupyter lab --allow-root --ip=0.0.0.0 --no-browser --ServerApp.trust_xheaders=True --ServerApp.disable_check_xsrf=False --ServerApp.allow_remote_access=True --ServerApp.allow_origin='*' --ServerApp.allow_credentials=True CMD jupyter lab --allow-root --ip=0.0.0.0 --no-browser --ServerApp.trust_xheaders=True --ServerApp.disable_check_xsrf=False --ServerApp.allow_remote_access=True --ServerApp.allow_origin='*' --ServerApp.allow_credentials=True

View File

@@ -79,9 +79,9 @@ Pythonの仮想環境・パッケージ管理ツールである[uv](https://gith
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
git clone https://github.com/litagin02/Style-Bert-VITS2.git git clone https://github.com/litagin02/Style-Bert-VITS2.git
cd Style-Bert-VITS2 cd Style-Bert-VITS2
uv venv venv uv venv --python 3.12 venv
venv\Scripts\activate venv\Scripts\activate
uv pip install "torch<2.4" "torchaudio<2.4" --index-url https://download.pytorch.org/whl/cu118 uv pip install "torch>=2.8,<2.9" "torchaudio>=2.8,<2.9" --index-url https://download.pytorch.org/whl/cu126
uv pip install -r requirements.txt uv pip install -r requirements.txt
python initialize.py # 必要なモデルとデフォルトTTSモデルをダウンロード python initialize.py # 必要なモデルとデフォルトTTSモデルをダウンロード
``` ```

3
app.py
View File

@@ -49,7 +49,7 @@ model_holder = TTSModelHolder(
ignore_onnx=True, ignore_onnx=True,
) )
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks() as app:
gr.Markdown(f"# Style-Bert-VITS2 WebUI (version {VERSION})") gr.Markdown(f"# Style-Bert-VITS2 WebUI (version {VERSION})")
with gr.Tabs(): with gr.Tabs():
with gr.Tab("音声合成"): with gr.Tab("音声合成"):
@@ -66,6 +66,7 @@ with gr.Blocks(theme=GRADIO_THEME) as app:
create_onnx_app(model_holder=model_holder) create_onnx_app(model_holder=model_holder)
app.launch( app.launch(
theme=GRADIO_THEME,
server_name=args.host, server_name=args.host,
server_port=args.port, server_port=args.port,
inbrowser=not args.no_autolaunch, inbrowser=not args.no_autolaunch,

View File

@@ -55,7 +55,7 @@ def process_line(x: tuple[str, bool]):
bert_path = wav_path.replace(".WAV", ".wav").replace(".wav", ".bert.pt") bert_path = wav_path.replace(".WAV", ".wav").replace(".wav", ".bert.pt")
try: try:
bert = torch.load(bert_path) bert = torch.load(bert_path, weights_only=True)
assert bert.shape[-1] == len(phone) assert bert.shape[-1] == len(phone)
except Exception: except Exception:
bert = extract_bert_feature(text, word2ph, Languages(language_str), device) bert = extract_bert_feature(text, word2ph, Languages(language_str), device)

View File

@@ -129,7 +129,7 @@ class TextAudioSpeakerLoader(torch.utils.data.Dataset):
if self.use_mel_spec_posterior: if self.use_mel_spec_posterior:
spec_filename = spec_filename.replace(".spec.pt", ".mel.pt") spec_filename = spec_filename.replace(".spec.pt", ".mel.pt")
try: try:
spec = torch.load(spec_filename) spec = torch.load(spec_filename, weights_only=True)
except: except:
if self.use_mel_spec_posterior: if self.use_mel_spec_posterior:
spec = mel_spectrogram_torch( spec = mel_spectrogram_torch(
@@ -168,7 +168,7 @@ class TextAudioSpeakerLoader(torch.utils.data.Dataset):
word2ph[0] += 1 word2ph[0] += 1
bert_path = wav_path.replace(".wav", ".bert.pt") bert_path = wav_path.replace(".wav", ".bert.pt")
try: try:
bert_ori = torch.load(bert_path) bert_ori = torch.load(bert_path, weights_only=True)
assert bert_ori.shape[-1] == len(phone) assert bert_ori.shape[-1] == len(phone)
except Exception as e: except Exception as e:
logger.warning("Bert load Failed") logger.warning("Bert load Failed")

View File

@@ -52,7 +52,7 @@ def create_onnx_app(model_holder: TTSModelHolder) -> gr.Blocks:
initial_id = 0 initial_id = 0
initial_pth_files = get_model_files(model_names[initial_id]) initial_pth_files = get_model_files(model_names[initial_id])
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks() as app:
gr.Markdown(initial_md) gr.Markdown(initial_md)
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
@@ -100,4 +100,4 @@ if __name__ == "__main__":
assets_root = path_config.assets_root assets_root = path_config.assets_root
model_holder = TTSModelHolder(assets_root, "cpu", "", ignore_onnx=True) model_holder = TTSModelHolder(assets_root, "cpu", "", ignore_onnx=True)
app = create_onnx_app(model_holder) app = create_onnx_app(model_holder)
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -115,7 +115,7 @@ Style-Bert-VITS2の学習用データセットを作成するためのツール
def create_dataset_app() -> gr.Blocks: def create_dataset_app() -> gr.Blocks:
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks() as app:
gr.Markdown( gr.Markdown(
"**既に1ファイル2-12秒程度の音声ファイル集とその書き起こしデータがある場合は、このタブは使用せずに学習できます。**" "**既に1ファイル2-12秒程度の音声ファイル集とその書き起こしデータがある場合は、このタブは使用せずに学習できます。**"
) )
@@ -273,4 +273,4 @@ def create_dataset_app() -> gr.Blocks:
if __name__ == "__main__": if __name__ == "__main__":
app = create_dataset_app() app = create_dataset_app()
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -361,7 +361,7 @@ def create_inference_app(model_holder: TTSModelHolder) -> gr.Blocks:
initial_id = 0 initial_id = 0
initial_pth_files = get_model_files(model_names[initial_id]) initial_pth_files = get_model_files(model_names[initial_id])
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks() as app:
gr.Markdown(initial_md) gr.Markdown(initial_md)
gr.Markdown(terms_of_use_md) gr.Markdown(terms_of_use_md)
null_models = gr.State({}) null_models = gr.State({})
@@ -742,4 +742,4 @@ if __name__ == "__main__":
assets_root, device, torch_device_to_onnx_providers(device) assets_root, device, torch_device_to_onnx_providers(device)
) )
app = create_inference_app(model_holder) app = create_inference_app(model_holder)
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -1042,7 +1042,7 @@ def create_merge_app(model_holder: TTSModelHolder) -> gr.Blocks:
str(f) for f in model_holder.model_files_dict[model_names[initial_id]] 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() as app:
gr.Markdown( gr.Markdown(
"複数のStyle-Bert-VITS2モデルから、声質・話し方・話す速さを取り替えたり混ぜたり引いたりして新しいモデルを作成できます。" "複数のStyle-Bert-VITS2モデルから、声質・話し方・話す速さを取り替えたり混ぜたり引いたりして新しいモデルを作成できます。"
) )
@@ -1554,4 +1554,4 @@ if __name__ == "__main__":
assets_root, device, torch_device_to_onnx_providers(device), ignore_onnx=True assets_root, device, torch_device_to_onnx_providers(device), ignore_onnx=True
) )
app = create_merge_app(model_holder) app = create_merge_app(model_holder)
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -416,7 +416,7 @@ https://ja.wikipedia.org/wiki/DBSCAN
def create_style_vectors_app(): def create_style_vectors_app():
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks() as app:
with gr.Accordion("使い方", open=False): with gr.Accordion("使い方", open=False):
gr.Markdown(how_to_md) gr.Markdown(how_to_md)
model_name = gr.Textbox(placeholder="your_model_name", label="モデル名") model_name = gr.Textbox(placeholder="your_model_name", label="モデル名")
@@ -583,4 +583,4 @@ def create_style_vectors_app():
if __name__ == "__main__": if __name__ == "__main__":
app = create_style_vectors_app() app = create_style_vectors_app()
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -492,7 +492,7 @@ english_teacher.wav|Mary|EN|How are you? I'm fine, thank you, and you?
def create_train_app(): def create_train_app():
with gr.Blocks(theme=GRADIO_THEME).queue() as app: with gr.Blocks().queue() as app:
gr.Markdown(change_log_md) gr.Markdown(change_log_md)
with gr.Accordion("使い方", open=False): with gr.Accordion("使い方", open=False):
gr.Markdown(how_to_md) gr.Markdown(how_to_md)
@@ -852,4 +852,4 @@ def create_train_app():
if __name__ == "__main__": if __name__ == "__main__":
app = create_train_app() app = create_train_app()
app.launch(inbrowser=True) app.launch(inbrowser=True, theme=GRADIO_THEME)

View File

@@ -7,7 +7,7 @@ name = "style-bert-vits2"
dynamic = ["version"] dynamic = ["version"]
description = "Style-Bert-VITS2: Bert-VITS2 with more controllable voice styles." description = "Style-Bert-VITS2: Bert-VITS2 with more controllable voice styles."
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.12"
license = "AGPL-3.0" license = "AGPL-3.0"
keywords = [] keywords = []
authors = [ authors = [
@@ -16,9 +16,7 @@ authors = [
classifiers = [ classifiers = [
"Development Status :: 4 - Beta", "Development Status :: 4 - Beta",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
] ]
dependencies = [ dependencies = [
@@ -27,13 +25,13 @@ dependencies = [
"g2p_en", "g2p_en",
"jieba", "jieba",
"loguru", "loguru",
"nltk<=3.8.1", "nltk>=3.9,<4",
"num2words", "num2words",
"numba", "numba",
"numpy<2", "numpy>=2,<2.5",
"onnxruntime", "onnxruntime",
"pydantic>=2.0", "pydantic>=2.0",
"pyopenjtalk-dict", "pyopenjtalk-plus",
"pypinyin", "pypinyin",
"pyworld-prebuilt", "pyworld-prebuilt",
"safetensors", "safetensors",
@@ -43,7 +41,7 @@ dependencies = [
[project.optional-dependencies] [project.optional-dependencies]
torch = [ torch = [
"accelerate", "accelerate",
"torch>=2.1", "torch>=2.8,<2.9",
] ]
[project.urls] [project.urls]
@@ -93,7 +91,7 @@ cov-report = ["- coverage combine", "coverage report"]
# Usage: `hatch run test:cov` # Usage: `hatch run test:cov`
cov = ["test-cov", "cov-report"] cov = ["test-cov", "cov-report"]
[[tool.hatch.envs.test.matrix]] [[tool.hatch.envs.test.matrix]]
python = ["3.9", "3.10", "3.11"] python = ["3.12"]
# for ONNX inference (without PyTorch dependency) # for ONNX inference (without PyTorch dependency)
[tool.hatch.envs.test-onnx] [tool.hatch.envs.test-onnx]
@@ -120,7 +118,7 @@ cov-report = ["- coverage combine", "coverage report"]
# Usage: `hatch run test-onnx:cov` # Usage: `hatch run test-onnx:cov`
cov = ["test-cov", "cov-report"] cov = ["test-cov", "cov-report"]
[[tool.hatch.envs.test-onnx.matrix]] [[tool.hatch.envs.test-onnx.matrix]]
python = ["3.9", "3.10", "3.11"] python = ["3.12"]
[tool.hatch.envs.style] [tool.hatch.envs.style]
detached = true detached = true

View File

@@ -4,19 +4,19 @@ cn2an
g2p_en g2p_en
gradio>=4.32 gradio>=4.32
jieba jieba
librosa==0.9.2 librosa>=0.11,<1
loguru loguru
nltk<=3.8.1 nltk>=3.9,<4
num2words num2words
numpy<2 numpy>=2,<2.5
onnx onnx
onnxconverter-common onnxconverter-common
onnxruntime onnxruntime
onnxruntime-gpu onnxruntime-gpu
onnxsim-prebuilt onnxsim-prebuilt
pyannote.audio>=3.1.0 pyannote.audio>=4,<5
pyloudnorm pyloudnorm
pyopenjtalk-dict pyopenjtalk-plus
pypinyin pypinyin
pyworld-prebuilt pyworld-prebuilt
torch torch

View File

@@ -1,33 +1,33 @@
accelerate accelerate
cmudict cmudict
cn2an cn2an
# faster-whisper==0.10.1 # faster-whisper>=1.2,<2
g2p_en g2p_en
GPUtil GPUtil
gradio>=4.32 gradio>=4.32
jieba jieba
# librosa==0.9.2 # librosa>=0.11,<1
loguru loguru
nltk<=3.8.1 nltk>=3.9,<4
num2words num2words
numpy<2 numpy>=2,<2.5
onnx onnx
onnxconverter-common onnxconverter-common
onnxruntime onnxruntime
onnxruntime-directml; sys_platform == 'win32' onnxruntime-directml; sys_platform == 'win32'
onnxruntime-gpu; sys_platform != 'darwin' onnxruntime-gpu; sys_platform != 'darwin'
onnxsim-prebuilt onnxsim-prebuilt
# protobuf==4.25 # protobuf>=5,<7
psutil psutil
# punctuators # punctuators
pyannote.audio>=3.1.0 pyannote.audio>=4,<5
# pyloudnorm # pyloudnorm
pyopenjtalk-dict pyopenjtalk-plus
pypinyin pypinyin
pyworld-prebuilt pyworld-prebuilt
# stable_ts # stable_ts
# tensorboard # tensorboard
torch<2.4 torch>=2.8,<2.9
torchaudio<2.4 torchaudio>=2.8,<2.9
transformers transformers
umap-learn umap-learn

View File

@@ -1,33 +1,33 @@
accelerate accelerate
cmudict cmudict
cn2an cn2an
faster-whisper==0.10.1 faster-whisper>=1.2,<2
g2p_en g2p_en
GPUtil GPUtil
gradio>=4.32 gradio>=4.32
jieba jieba
librosa==0.9.2 librosa>=0.11,<1
loguru loguru
nltk<=3.8.1 nltk>=3.9,<4
num2words num2words
numpy<2 numpy>=2,<2.5
onnx onnx
onnxconverter-common onnxconverter-common
onnxruntime onnxruntime
onnxruntime-directml; sys_platform == 'win32' onnxruntime-directml; sys_platform == 'win32'
onnxruntime-gpu; sys_platform != 'darwin' onnxruntime-gpu; sys_platform != 'darwin'
onnxsim-prebuilt onnxsim-prebuilt
protobuf==4.25 protobuf>=5,<7
psutil psutil
punctuators punctuators
pyannote.audio>=3.1.0 pyannote.audio>=4,<5
pyloudnorm pyloudnorm
pyopenjtalk-dict pyopenjtalk-plus
pypinyin pypinyin
pyworld-prebuilt pyworld-prebuilt
stable_ts stable_ts
tensorboard tensorboard
torch<2.4 torch>=2.8,<2.9
torchaudio<2.4 torchaudio>=2.8,<2.9
transformers transformers
umap-learn umap-learn

View File

@@ -110,8 +110,8 @@ if !errorlevel! neq 0 ( pause & popd & exit /b !errorlevel! )
echo -------------------------------------------------- echo --------------------------------------------------
echo Installing PyTorch... echo Installing PyTorch...
echo -------------------------------------------------- echo --------------------------------------------------
echo Executing: uv pip install "torch<2.4" "torchaudio<2.4" --index-url https://download.pytorch.org/whl/cu118 echo Executing: uv pip install "torch>=2.8,<2.9" "torchaudio>=2.8,<2.9" --index-url https://download.pytorch.org/whl/cu126
uv pip install "torch<2.4" "torchaudio<2.4" --index-url https://download.pytorch.org/whl/cu118 uv pip install "torch>=2.8,<2.9" "torchaudio>=2.8,<2.9" --index-url https://download.pytorch.org/whl/cu126
if !errorlevel! neq 0 ( pause & popd & exit /b !errorlevel! ) if !errorlevel! neq 0 ( pause & popd & exit /b !errorlevel! )
echo -------------------------------------------------- echo --------------------------------------------------

View File

@@ -40,8 +40,8 @@ if not exist "%PYTHON_DIR%"\ (
echo -------------------------------------------------- echo --------------------------------------------------
echo Downloading Python... echo Downloading Python...
echo -------------------------------------------------- echo --------------------------------------------------
echo Executing: %CURL_CMD% -o python.zip https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip echo Executing: %CURL_CMD% -o python.zip https://www.python.org/ftp/python/3.12.10/python-3.12.10-embed-amd64.zip
%CURL_CMD% -o python.zip https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip %CURL_CMD% -o python.zip https://www.python.org/ftp/python/3.12.10/python-3.12.10-embed-amd64.zip
if !errorlevel! neq 0 ( pause & exit /b !errorlevel! ) if !errorlevel! neq 0 ( pause & exit /b !errorlevel! )
echo -------------------------------------------------- echo --------------------------------------------------
@@ -61,8 +61,8 @@ if not exist "%PYTHON_DIR%"\ (
echo -------------------------------------------------- echo --------------------------------------------------
echo Enabling 'site' module in the embedded Python environment... echo Enabling 'site' module in the embedded Python environment...
echo -------------------------------------------------- echo --------------------------------------------------
echo Executing: %PS_CMD% "&{(Get-Content '%PYTHON_DIR%/python310._pth') -creplace '#import site', 'import site' | Set-Content '%PYTHON_DIR%/python310._pth' }" echo Executing: %PS_CMD% "&{(Get-Content '%PYTHON_DIR%/python312._pth') -creplace '#import site', 'import site' | Set-Content '%PYTHON_DIR%/python312._pth' }"
%PS_CMD% "&{(Get-Content '%PYTHON_DIR%/python310._pth') -creplace '#import site', 'import site' | Set-Content '%PYTHON_DIR%/python310._pth' }" %PS_CMD% "&{(Get-Content '%PYTHON_DIR%/python312._pth') -creplace '#import site', 'import site' | Set-Content '%PYTHON_DIR%/python312._pth' }"
if !errorlevel! neq 0 ( pause & exit /b !errorlevel! ) if !errorlevel! neq 0 ( pause & exit /b !errorlevel! )
echo -------------------------------------------------- echo --------------------------------------------------

View File

@@ -171,7 +171,7 @@ if __name__ == "__main__":
# モデルをダウンロードしておく # モデルをダウンロードしておく
_ = torch.hub.load( _ = torch.hub.load(
repo_or_dir="litagin02/silero-vad", repo_or_dir="snakers4/silero-vad",
model="silero_vad", model="silero_vad",
onnx=True, onnx=True,
trust_repo=True, trust_repo=True,
@@ -186,7 +186,7 @@ if __name__ == "__main__":
): ):
# logger.debug("Worker started.") # logger.debug("Worker started.")
vad_model, utils = torch.hub.load( vad_model, utils = torch.hub.load(
repo_or_dir="litagin02/silero-vad", repo_or_dir="snakers4/silero-vad",
model="silero_vad", model="silero_vad",
onnx=True, onnx=True,
trust_repo=True, trust_repo=True,

View File

@@ -32,7 +32,9 @@ def load_checkpoint(
""" """
assert os.path.isfile(checkpoint_path) assert os.path.isfile(checkpoint_path)
checkpoint_dict = torch.load(checkpoint_path, map_location=device) checkpoint_dict = torch.load(
checkpoint_path, map_location=device, weights_only=True
)
iteration = checkpoint_dict["iteration"] iteration = checkpoint_dict["iteration"]
learning_rate = checkpoint_dict["learning_rate"] learning_rate = checkpoint_dict["learning_rate"]
logger.info( logger.info(

View File

@@ -9,7 +9,13 @@ from enum import Enum
from re import findall, fullmatch from re import findall, fullmatch
from typing import List, Optional from typing import List, Optional
from pydantic import BaseModel, Field, validator from pydantic import (
BaseModel,
ConfigDict,
Field,
ValidationInfo,
field_validator,
)
USER_DICT_MIN_PRIORITY = 0 USER_DICT_MIN_PRIORITY = 0
@@ -39,11 +45,11 @@ class UserDictWord(BaseModel):
mora_count: Optional[int] = Field(title="モーラ数", default=None) mora_count: Optional[int] = Field(title="モーラ数", default=None)
accent_associative_rule: str = Field(title="アクセント結合規則") accent_associative_rule: str = Field(title="アクセント結合規則")
class Config: model_config = ConfigDict(validate_assignment=True, validate_default=True)
validate_assignment = True
@validator("surface") @field_validator("surface")
def convert_to_zenkaku(cls, surface): @classmethod
def convert_to_zenkaku(cls, surface: str) -> str:
return surface.translate( return surface.translate(
str.maketrans( str.maketrans(
"".join(chr(0x21 + i) for i in range(94)), "".join(chr(0x21 + i) for i in range(94)),
@@ -51,8 +57,9 @@ class UserDictWord(BaseModel):
) )
) )
@validator("pronunciation", pre=True) @field_validator("pronunciation", mode="before")
def check_is_katakana(cls, pronunciation): @classmethod
def check_is_katakana(cls, pronunciation: str) -> str:
if not fullmatch(r"[ァ-ヴー]+", pronunciation): if not fullmatch(r"[ァ-ヴー]+", pronunciation):
raise ValueError("発音は有効なカタカナでなくてはいけません。") raise ValueError("発音は有効なカタカナでなくてはいけません。")
sutegana = ["", "", "", "", "", "", "", "", "", ""] sutegana = ["", "", "", "", "", "", "", "", "", ""]
@@ -75,9 +82,12 @@ class UserDictWord(BaseModel):
) )
return pronunciation return pronunciation
@validator("mora_count", pre=True, always=True) @field_validator("mora_count", mode="before")
def check_mora_count_and_accent_type(cls, mora_count, values): @classmethod
if "pronunciation" not in values or "accent_type" not in values: def check_mora_count_and_accent_type(
cls, mora_count: Optional[int], info: ValidationInfo
) -> Optional[int]:
if "pronunciation" not in info.data or "accent_type" not in info.data:
# 適切な場所でエラーを出すようにする # 適切な場所でエラーを出すようにする
return mora_count return mora_count
@@ -91,14 +101,14 @@ class UserDictWord(BaseModel):
mora_count = len( mora_count = len(
findall( findall(
f"(?:{rule_others}|{rule_line_i}|{rule_line_u}|{rule_one_mora})", f"(?:{rule_others}|{rule_line_i}|{rule_line_u}|{rule_one_mora})",
values["pronunciation"], info.data["pronunciation"],
) )
) )
if not 0 <= values["accent_type"] <= mora_count: if not 0 <= info.data["accent_type"] <= mora_count:
raise ValueError( raise ValueError(
"誤ったアクセント型です({})。 expect: 0 <= accent_type <= {}".format( "誤ったアクセント型です({})。 expect: 0 <= accent_type <= {}".format(
values["accent_type"], mora_count info.data["accent_type"], mora_count
) )
) )
return mora_count return mora_count

View File

@@ -0,0 +1,58 @@
from pathlib import Path
import pytest
import torch
from pydantic import ValidationError
from style_bert_vits2.models.utils.checkpoints import load_checkpoint, save_checkpoint
from style_bert_vits2.nlp.japanese.user_dict.word_model import UserDictWord
def _user_dict_word(**overrides: object) -> UserDictWord:
values = {
"surface": "test",
"priority": 5,
"part_of_speech": "名詞",
"part_of_speech_detail_1": "一般",
"part_of_speech_detail_2": "*",
"part_of_speech_detail_3": "*",
"inflectional_type": "*",
"inflectional_form": "*",
"stem": "*",
"yomi": "テスト",
"pronunciation": "テスト",
"accent_type": 1,
"accent_associative_rule": "*",
}
values.update(overrides)
return UserDictWord.model_validate(values)
def test_user_dict_word_uses_pydantic_v2_validation() -> None:
word = _user_dict_word()
assert word.surface == ""
assert word.mora_count == 3
with pytest.raises(ValidationError):
_user_dict_word(pronunciation="てすと")
def test_checkpoint_round_trip_with_safe_torch_load(tmp_path: Path) -> None:
checkpoint_path = tmp_path / "checkpoint.pth"
source = torch.nn.Linear(2, 1)
optimizer = torch.optim.AdamW(source.parameters())
save_checkpoint(source, optimizer, 1e-4, 12, checkpoint_path)
destination = torch.nn.Linear(2, 1)
destination_optimizer = torch.optim.AdamW(destination.parameters())
_, _, learning_rate, iteration = load_checkpoint(
checkpoint_path, destination, destination_optimizer
)
assert learning_rate == 1e-4
assert iteration == 12
for source_parameter, destination_parameter in zip(
source.parameters(), destination.parameters()
):
assert torch.equal(source_parameter, destination_parameter)