音声合成 Style-Bert-VITS2 をWSL2でためす

範囲
使えるようにしておく必要に迫られたので、
とくに変わったことをせず
- WSL2/Ubuntu22.04 (RAM 128GB, 3060-12G) に
- ローカルで動作するよう
- 学習までは追体験。スタイル調整とマージは後回し。
- proxy/certに悩まされないよう、個人環境で
サーバにデプロイしたり、サーバレス運用もできたりするのだな。工夫すれば。

とにかく動かす
pythonバージョンは適当。すでにあったので。
apt で ffmpeg や av関係のdebian系パッケージをインストールする必要が生じるかもしれない。
# 仮想環境を用意。今回は pyenv + pyenv-virtualenv
pyenv virtualenv 3.11.8 stylebertbits2
git clone https://github.com/litagin02/Style-Bert-VITS2
cd Style-Bert-VITS2
pyenv local stylebertbits2
# 必要なパッケージをインストール
pip install "torch<2.4" "torchaudio<2.4"
pip install -r requirements.txt
# 必要なモデルとデフォルトTTSモデルをダウンロード
python initialize.py
# 音声合成エディターを起動
python server_editor.py # http://0.0.0.0:8000
動く。VRAM 3GBほどを占有している?
エディタ上で、適当な上記記事から抜き出してしゃべらせてみる
INFO: 127.0.0.1:53264 - "GET /api/version HTTP/1.1" 200 OK
INFO: 127.0.0.1:53270 - "POST /api/g2p HTTP/1.1" 200 OK
02-08 15:18:49 | INFO | tts_model.py:259 | Start generating audio data from text:
Style-Bert-VITS2と言えば、高品質で生成が早い合成音声技術(TTS: Text to Speech)として有名ですね。
02-08 15:18:50 | INFO | tts_model.py:324 | Audio data generated successfully
INFO: 127.0.0.1:53270 - "POST /api/synthesis HTTP/1.1" 200 OK
INFO: 127.0.0.1:53270 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: 127.0.0.1:53286 - "POST /api/g2p HTTP/1.1" 200 OK
INFO: 127.0.0.1:53286 - "POST /api/g2p HTTP/1.1" 200 OK
INFO: 127.0.0.1:53286 - "POST /api/g2p HTTP/1.1" 200 OK
02-08 15:19:01 | INFO | tts_model.py:259 | Start generating audio data from text:
こんにちは、ニケです。
02-08 15:19:01 | INFO | tts_model.py:324 | Audio data generated successfully
02-08 15:19:01 | INFO | tts_model.py:259 | Start generating audio data from text:
皆さん、Style-Bert-VITS2は使っていますでしょうか?
02-08 15:19:02 | INFO | tts_model.py:324 | Audio data generated successfully
02-08 15:19:02 | INFO | tts_model.py:259 | Start generating audio data from text:
Style-Bert-VITS2と言えば、高品質で生成が早い合成音声技術(TTS: Text to Speech)として有名ですね。
02-08 15:19:02 | INFO | tts_model.py:324 | Audio data generated successfully
INFO: 127.0.0.1:53286 - "POST /api/multi_synthesis HTTP/1.1" 200 OK
INFO: 127.0.0.1:57284 - "GET /_next/static/media/2dba46bfc14ba3f6-s.woff2 HTTP/1.1" 200 OK
INFO: 127.0.0.1:57284 - "GET /_next/static/media/5640c99d8abfdde6-s.woff2 HTTP/1.1" 200 OK
INFO: 127.0.0.1:57284 - "GET /_next/static/media/8793d1fe514485a8-s.woff2 HTTP/1.1" 200 OK
INFO: 127.0.0.1:57294 - "POST /api/g2p HTTP/1.1" 200 OK
02-08 15:19:51 | INFO | tts_model.py:259 | Start generating audio data from text:
Style-Bert-VITS2と言えば、高品質で生成が早い合成音声技術(TTS: テキスト トゥー スピーチ)として有名ですね。
02-08 15:19:51 | INFO | tts_model.py:324 | Audio data generated successfully
INFO: 127.0.0.1:57294 - "POST /api/synthesis HTTP/1.1" 200 OK
INFO: 127.0.0.1:37862 - "GET /docs HTTP/1.1" 200 OK
INFO: 127.0.0.1:37862 - "GET /openapi.json HTTP/1.1" 200 OK
INFO: 127.0.0.1:56868 - "POST /api/g2p HTTP/1.1" 200 OK
INFO: 127.0.0.1:56876 - "GET /api/models_info HTTP/1.1" 200 OK
02-08 15:40:23 | ERROR | server_editor.py:287 | Model `string` is not found
INFO: 127.0.0.1:37962 - "POST /api/synthesis HTTP/1.1" 500 Internal Server Error
^CINFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [13216]
02-08 15:40:48 | DEBUG | __init__.py:147 | pyopenjtalk worker server terminated
(bertvits) morioka@legion:~/temp/Style-Bert-VITS2$
FastAPIなので、 http://localhost:8000/docs
でAPIを確認できる。例は、適当なものを用意してやる必要があるだろう。

ダウンロードするモデル
モデルとしてダウンロードしているもの
deberta-v2-large-japanese-char-wwm pytorch_model.bin| 1.32G/1.32G [00:16<00:00, chinese-roberta-wwm-ext-large pytorch_model.bin| 1.31G/1.31G [00:17<00:00, 74.7MB/s]
deberta-v3-large spm.model| 2.46M/2.46M [00:00<00:00, 91.8MB/s]
deberta-v3-large pytorch_model.bin| 874M/874M [00:10<00:00, 86.3MB/s]
jvnv-F1-jp/config.json| 2.14k/2.14k [00:00<00:00, 5.06MB/s]
jvnv-F1-jp/jvnv-F1-jp_e160_s14000.safetensors| 251M/251M [00:05<00:00, 42.4MB/s]
jvnv-F1-jp/style_vectors.npy| 7.30k/7.30k [00:00<00:00, 67.7MB/s]
jvnv-F2-jp/config.json| 2.22k/2.22k [00:00<00:00, 22.1MB/s]
jvnv-F2-jp/jvnv-F2_e166_s20000.safetensors| 251M/251M [00:06<00:00, 39.0MB/s]
jvnv-F2-jp/style_vectors.npy| 7.30k/7.30k [00:00<00:00, 73.7MB/s]
jvnv-M1-jp/config.json| 2.14k/2.14k [00:00<00:00, 23.7MB/s]
jvnv-M1-jp/jvnv-M1-jp_e158_s14000.safetensors| 251M/251M [00:05<00:00, 42.3MB/s]
jvnv-M1-jp/style_vectors.npy| 7.30k/7.30k [00:00<00:00, 29.2MB/s]
jvnv-M2-jp/config.json| 2.14k/2.14k [00:00<00:00, 15.8MB/s]
jvnv-M2-jp/jvnv-M2-jp_e159_s17000.safetensors| 251M/251M [00:05<00:00, 42.4MB/s]
jvnv-M2-jp/style_vectors.npy| 7.30k/7.30k [00:00<00:00, 25.3MB/s]
koharune-ami/config.json| 2.33k/2.33k [00:00<00:00, 8.18MB/s]
koharune-ami/style_vectors.npy| 6.27k/6.27k [00:00<00:00, 29.4MB/s]
koharune-ami/koharune-ami.safetensors| 251M/251M [00:05<00:00, 42.7MB/s]
amitaro/config.json| 2.21k/2.21k [00:00<00:00, 10.9MB/s]
amitaro/style_vectors.npy| 5.25k/5.25k [00:00<00:00, 21.5MB/s]
amitaro/amitaro.safetensors| 251M/251M [00:06<00:00, 38.1MB/s]
wavlm-base-plus pytorch_model.bin| 378M/378M [00:05<00:00, 70.3MB/s]
pretrained G_0.safetensors| 234M/234M [00:05<00:00, 42.4MB/s]
pretrained D_0.safetensors| 187M/187M [00:04<00:00, 42.6MB/s]
pretrained DUR_0.safetensors| 2.42M/2.42M [00:00<00:00, 46.6MB/s]
JP-Extra pretrained G_0.safetensors| 293M/293M [00:06<00:00, 42.2MB/s]
JP-Extra pretrained D_0.safetensors| 187M/187M [00:04<00:00, 41.4MB/s]
JP-Extra pretrained WD_0.safetensors| 4.70M/4.70M [00:00<00:00, 43.8MB/s]

このあと
- API経由での音声合成
- 学習データの作成と、学習

おう...
勉強になる。昔々3か月だけ音声合成を勉強したが。
テキストから、BERT経由で音素列(とアクセント?)を、別の?BERTで感情から抑揚を求めているのかな?(よく見ないで想像)
↑この辺が、学習からAPI利用までひととおり流している感じ。

学習
python app.py # http://localhost:7860
学習データの準備は、音声データを所定の時間間隔?で区切って、音声認識で仮の読みを与えて、それを修正させていく感じらしい。
よくはわからんが、webui に従っていくとよい感じ。
CLIでも同じか。
↓これをなぞる。画面に違いはあるが気にならない。学習用WebUIでは音声合成/データセット作成/学習/スタイル作成/マージの5つのタブがあり、それを切り替えて手順を進めていく。動画との大きな違いはWindows上でなく、WSL2上であることだけ。
ずんだもんデータセットのライセンス同意と取得にtwitterでログインする必要があるので、企業だと、その点だけハードルが高いかもしれない。
tensorboardは http://localhost:6006
とりあえず、数時間放っておく。5hほどで完了。VRAM 8から9GB程度。たしかに、ずんだもんっぽい。
↓(追記) WebUIベースの学習手順は、これにならえばよかった

API利用
API利用...といっても、編集(8000), 学習(7860), 音声合成のみ(5000) と、コードが別々でAPIも異なる。
音声合成APIについていえば、
を丸ごとまねる。(モデル名を修正。非推奨のGETメソッドからPOSTメソッドに変更)
# 必要なライブラリをインポートします
import requests
import io
import soundfile
import argparse
import sounddevice as sd
# Sbv2Adapterというクラスを定義します
class Sbv2Adapter:
URL = "http://127.0.0.1:5000/voice"
def __init__(self) -> None:
pass
# 音声を取得するためのクエリを作成するメソッドです
def _create_audio_query(self, text: str) -> dict:
# パラメータを定義します
params = {
"text": text,
"speaker_id": 0,
#"model_name": "shirohana",
"model_name": "koharune-ami",
"length": 1, #音声の話速(1.0=標準、2.0=2倍遅く、0.5=2倍速く)
"sdp_ratio": 0.2,
"noise": 0.6,
"noisew": 0.8,
"auto_split": True,
"split_interval": 1,
"language": "JP",
"style": "Neutral",
"style_weight": 5,
}
return params
# 音声をリクエストするメソッドです
def _create_request_audio(self, query_data: dict) -> bytes:
headers = {"accept": "audio/wav"}
#response = requests.get(self.URL, params=query_data, headers=headers)
response = requests.post(self.URL, params=query_data, headers=headers)
# エラーハンドリング
if response.status_code != 200:
raise Exception(f"Error: {response.status_code}")
return response.content
# 音声を取得するメソッドです
def get_voice(self, text: str):
query_data = self._create_audio_query(text)
audio_bytes = self._create_request_audio(query_data)
audio_stream = io.BytesIO(audio_bytes)
data, sample_rate = soundfile.read(audio_stream)
return data, sample_rate
def main(text: str):
adapter = Sbv2Adapter()
data, sample_rate = adapter.get_voice(text)
sd.play(data, sample_rate)
sd.wait()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Text to Speech")
parser.add_argument("text", type=str, help="The text to be converted to speech")
args = parser.parse_args()
main(args.text)
pip install sounddevice
python sbv2.py "こんにちは、世界!"
$ python server_fastapi.py
02-08 16:57:26 | DEBUG | __init__.py:92 | try starting pyopenjtalk worker server
02-08 16:57:27 | DEBUG | __init__.py:130 | pyopenjtalk worker server started
02-08 16:57:47 | INFO | bert_models.py:92 | Loaded the Languages.JP BERT model from /home/morioka/temp/Style-Bert-VITS2/bert/deberta-v2-large-japanese-char-wwm
02-08 16:57:47 | INFO | bert_models.py:154 | Loaded the Languages.JP BERT tokenizer from /home/morioka/temp/Style-Bert-VITS2/bert/deberta-v2-large-japanese-char-wwm
02-08 16:58:00 | INFO | bert_models.py:92 | Loaded the Languages.EN BERT model from /home/morioka/temp/Style-Bert-VITS2/bert/deberta-v3-large
02-08 16:58:00 | INFO | bert_models.py:154 | Loaded the Languages.EN BERT tokenizer from /home/morioka/temp/Style-Bert-VITS2/bert/deberta-v3-large
Some weights of the model checkpoint at /home/morioka/temp/Style-Bert-VITS2/bert/chinese-roberta-wwm-ext-large were not used when initializing BertForMaskedLM: ['bert.pooler.dense.bias', 'bert.pooler.dense.weight', 'cls.seq_relationship.bias', 'cls.seq_relationship.weight']
- This IS expected if you are initializing BertForMaskedLM from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertForMaskedLM from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
02-08 16:58:13 | INFO | bert_models.py:92 | Loaded the Languages.ZH BERT model from /home/morioka/temp/Style-Bert-VITS2/bert/chinese-roberta-wwm-ext-large
02-08 16:58:13 | INFO | bert_models.py:154 | Loaded the Languages.ZH BERT tokenizer from /home/morioka/temp/Style-Bert-VITS2/bert/chinese-roberta-wwm-ext-large
02-08 16:58:13 |WARNING | tts_model.py:397 | No model files found in model_assets/.cache, so skip it
02-08 16:58:13 | INFO | server_fastapi.py:113 | Loading models...
02-08 16:58:13 | INFO | server_fastapi.py:120 | The maximum length of the text is 100. If you want to change it, modify config.yml. Set limit to -1 to remove the limit.
02-08 16:58:13 |WARNING | server_fastapi.py:126 | CORS allow_origins=['*']. If you don't want, modify config.yml
02-08 16:58:13 | INFO | server_fastapi.py:335 | server listen: http://127.0.0.1:5000
02-08 16:58:13 | INFO | server_fastapi.py:336 | API docs: http://127.0.0.1:5000/docs
02-08 16:58:13 | INFO | server_fastapi.py:337 | Input text length limit: 100. You can change it in server.limit in config.yml
02-08 16:58:39 | INFO | server_fastapi.py:193 | 127.0.0.1:56044/voice text=こんにちは、世界!&speaker_id=0&model_name=koharune-ami&length=1&sdp_ratio=0.2&noise=0.6&noisew=0.8&auto_split=True&split_interval=1&language=JP&style=Neutral&style_weight=5
02-08 16:58:39 |WARNING | server_fastapi.py:197 | The GET method is not recommended for this endpoint due to various restrictions. Please use the POST method.
02-08 16:58:39 | INFO | tts_model.py:259 | Start generating audio data from text:
こんにちは、世界!
02-08 16:58:39 | INFO | infer.py:24 | Using JP-Extra model
/home/morioka/.pyenv/versions/bertvits/lib/python3.11/site-packages/torch/nn/utils/weight_norm.py:28: UserWarning: torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.
warnings.warn("torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.")
02-08 16:58:43 | INFO | safetensors.py:50 | Loaded 'model_assets/koharune-ami/koharune-ami.safetensors' (iteration 60)
02-08 16:58:45 | INFO | tts_model.py:324 | Audio data generated successfully
02-08 16:58:45 |SUCCESS | server_fastapi.py:253 | Audio data generated and sent successfully
02-08 17:07:38 | INFO | server_fastapi.py:193 | 127.0.0.1:34462/voice text=こんにちは、世界!&speaker_id=0&model_name=koharune-ami&length=1&sdp_ratio=0.2&noise=0.6&noisew=0.8&auto_split=True&split_interval=1&language=JP&style=Neutral&style_weight=5
02-08 17:07:38 | INFO | tts_model.py:259 | Start generating audio data from text:
こんにちは、世界!
02-08 17:07:39 | INFO | tts_model.py:324 | Audio data generated successfully
02-08 17:07:39 |SUCCESS | server_fastapi.py:253 | Audio data generated and sent successfully

調整は Style-Bert-VITS2 の環境のままでよいとして、実利用はどうするか。
AvisSpeech向けに変換すると、model Hubに間違って登録してしまいそう+ライセンスがオープンになる?懸念があるので、onnxに直すくらいで、ローカルでホストかな。APIは何がよいだろうか。
style-bert-vits2