Irodori-TTSを使って音声ファイルよりも軽い埋め込みだけで声を学習する
概要
Irodori-TTS をベースに、LoRA の代わりとして 話者埋め込みを軽量に学習 する手法 (以下、Speaker Inversion) を実装しました。特徴:
- 学習パラメータ数 1/1000: LoRA (rank 4, diffusion_attn 対象) が 17M パラメータなのに対して、Speaker Inversion 埋め込み (16 tokens) では 12K パラメータ のみを学習
-
ファイルサイズ も軽量: 16 トークンの Speaker Inversion 埋め込み (FP32) は 49 KB。5 秒程度の無圧縮
.wavファイルであれば約 500 KB 前後になると思うので、ざっくり 1/10 のファイルサイズ
コード(マージされました)はここです:
Irodori-TTS とは
Irodori-TTS は、Rectified Flow に基づいた日本語 TTS の事前学習モデルです。
今回は Aratako/Irodori-TTS-500M-v3 を用いました。
背景
Irodori-TTS は Flow Matching で TTS を行う基盤モデルです。音声ファイルから声を参照した TTS もできるほか、LoRA の学習も公式で対応しています。
ところで、拡散モデルの基盤モデルといえば Stable Diffusion が連想されますが、Stable Diffusion では Textual Inversion という学習手法があります。(Negative Embedding でよく使われますね。)
Stable Diffusion では CLIP のテキストエンコーダーを使って条件付けを行うのですが、Textual Inversion はテキストエンコーダーに入力する埋め込みを学習することで、ファインチューンする手法です。利点としては、学習が軽い ことと、学習後にできる重みファイルも軽い ことです。
そこで今回は同様にして、話者埋め込みを学習 することで、LoRA よりも軽い 学習方法を実装してみました。ここではこれを Speaker Inversion と呼ぶことにします。
方法
Irodori-TTS v3 には参照音声を入力するためのモジュールが存在し、ゼロショットで Voice Cloning ができます。このとき、音声は DACVAE でエンコードされたのち、Speaker Encoder (Transformer 何層か) を通り、speaker_state を作成します。speaker_state は JointAttention ブロックで Key, Value に変換され、in-context の Self Attention で条件付けします。
今回は、この speaker_state に当たる部分 を学習することにしました。Speaker Encoder への入力部分を学習してみても良かったのですが今回は試していません。
実験・結果
学習設定
ベースモデルはすべて Aratako/Irodori-TTS-500M-v3 を使用しました。
以下の条件を比較しました
- 音声参照: 学習データセットのうち1つの音声を参照して生成
- LoRA: 学習データセット全てを用いて LoRA を学習して生成
- Speaker Inversion: 学習データセット全てを用いて話者埋め込みを学習して生成
- Ground Truth: 公開されている、学習データセットと同様の音声 (読み上げ内容は異なる)
以下は今回学習に用いた学習設定です:
LoRA 学習設定
要約:
- gradient checkpointing 有効
- LoRA rank:
4 - LoRA alpha:
1 - オプティマイザ: AdamW, 学習率:
1e-3
model:
latent_dim: 32
latent_patch_size: 1
text_vocab_size: 99574
text_tokenizer_repo: llm-jp/llm-jp-3-150m
model_dim: 1280
num_layers: 12
num_heads: 20
mlp_ratio: 2.875
text_mlp_ratio: 2.6
speaker_mlp_ratio: 2.6
text_dim: 512
text_layers: 10
text_heads: 8
speaker_dim: 768
speaker_layers: 8
speaker_heads: 12
speaker_patch_size: 1
timestep_embed_dim: 512
adaln_rank: 192
use_duration_predictor: true
duration_aux_dim: 14
duration_hidden_dim: 1024
duration_layers: 3
duration_dropout: 0.1
duration_attention_heads: 8
duration_architecture: token_sum_adarn_zero_no_aux
duration_token_init_frames: 9.0
duration_speaker_fusion: adarn_zero
train:
train_mode: rf
batch_size: 16
gradient_accumulation_steps: 1
gradient_checkpointing: true
num_workers: 8
dataloader_persistent_workers: true
dataloader_prefetch_factor: 4
allow_tf32: true
compile_model: false
precision: bf16
optimizer: adamw
learning_rate: 0.001
weight_decay: 0.0
adam_beta1: 0.9
adam_beta2: 0.999
lr_scheduler: none
warmup_steps: 100
max_steps: 3000
max_text_len: 256
text_condition_dropout: 0.0
speaker_condition_dropout: 0.0
caption_condition_dropout: 0.0
timestep_stratified: true
max_latent_steps: 750
fixed_target_latent_steps:
fixed_target_full_mask: false
rf_loss_mode: utterance_mean
duration_loss_weight: 0.1
duration_speaker_dropout: 0.1
duration_huber_delta: 0.1
log_every: 20
save_every: 250
checkpoint_best_n: 5
wandb_enabled: true
wandb_project: irodori-tts-speaker-inversion
wandb_entity:
wandb_run_name:
wandb_mode: online
ddp_find_unused_parameters: false
valid_ratio: 0.0005
valid_every: 1000
lora_enabled: true
lora_r: 4
lora_alpha: 1
lora_dropout: 0.0
lora_bias: none
lora_target_modules: diffusion_attn
lora_modules_to_save: auto
seed: 0
Speaker Inversion 学習設定
要約:
- gradient checkpointing 有効
- Speaker Inversion トークン数:
16(自然数の範囲で自由に変更可能で、増やせば表現力が向上するはず。) - オプティマイザ: AdamW, 学習率:
1e-2[1]
model:
latent_dim: 32
latent_patch_size: 1
text_vocab_size: 99574
text_tokenizer_repo: llm-jp/llm-jp-3-150m
model_dim: 1280
num_layers: 12
num_heads: 20
mlp_ratio: 2.875
text_mlp_ratio: 2.6
speaker_mlp_ratio: 2.6
text_dim: 512
text_layers: 10
text_heads: 8
speaker_dim: 768
speaker_layers: 8
speaker_heads: 12
speaker_patch_size: 1
timestep_embed_dim: 512
adaln_rank: 192
use_duration_predictor: true
duration_aux_dim: 14
duration_hidden_dim: 1024
duration_layers: 3
duration_dropout: 0.1
duration_attention_heads: 8
duration_architecture: token_sum_adarn_zero_no_aux
duration_token_init_frames: 9.0
duration_speaker_fusion: adarn_zero
train:
train_mode: rf
# speaker inversion settings
speaker_inversion_enabled: true
speaker_inversion_tokens: 16
speaker_inversion_init_std: 0.02
speaker_inversion_init_embedding:
#
batch_size: 16
gradient_accumulation_steps: 1
num_workers: 8
dataloader_persistent_workers: true
dataloader_prefetch_factor: 4
allow_tf32: true
compile_model: false
gradient_checkpointing: true
precision: bf16
optimizer: adamw
learning_rate: 0.01
weight_decay: 0.0
adam_beta1: 0.9
adam_beta2: 0.999
adam_eps: 0.00000001
lr_scheduler: none
max_steps: 3000
max_text_len: 256
text_condition_dropout: 0.0
speaker_condition_dropout: 0.0
caption_condition_dropout: 0.0
timestep_stratified: true
max_latent_steps: 750
fixed_target_latent_steps:
fixed_target_full_mask: false
rf_loss_mode: utterance_mean
duration_loss_weight: 0.1
duration_speaker_dropout: 0.0
duration_huber_delta: 0.1
log_every: 20
save_every: 250
checkpoint_best_n: 0
valid_ratio: 0.0
valid_every: 0
wandb_enabled: true
wandb_project: irodori-tts-speaker-inversion
wandb_entity:
wandb_run_name:
wandb_mode: online
ddp_find_unused_parameters: false
seed: 0
Speaker Inversion のトークン数が埋め込みの表現力を決定するハイパーパラメータになります。実験では適当に 16 トークンにしていて、他のサイズは試していません。
GPU は NVIDIA RTX 4070 Ti Super (VRAM 16GB) x1 を使いました。
サンプル1: あみたろコーパス読み上げ音声
データセットは「あみたろの声素材工房」からあみたろコーパス読み上げ音声を使用しました。
データセット:
読み上げテキスト:
自然言語処理や画像処理など、ディープラーニングは私たちの身の回りの様々な場所で活用されています。
生成サンプル (ログイン不要です):
-
音声参照 (データセット内の
AMITARO_049.wavを参照) -
LoRA
-
Speaker Inversion
-
Ground Truth サンプル (あみたろの声素材工房 ITAコーパスサンプル音声)
音声参照によるゼロショット生成の時点でかなり雰囲気を掴めてるので、ちょっと比較難しいかもしれません。
逆に考えれば、参照音声だけで再現できるくらい Irodori-TTS が多様な表現を既に獲得しているということなので、よっぽど特殊なケースでなければ少量のファインチューンで済む可能性を示唆しています。
| 手法 | 学習パラメータ数 | 学習時 VRAM | 音声/モデルファイルサイズ | 学習時間 |
|---|---|---|---|---|
| 音声参照 | なし | なし | 404 KB (5秒) | なし |
| LoRA 学習 | 17M | 3.65 GB | 69 MB (FP32) | 約 7 分 |
| Speaker Inversion | 12K | 3.2 GB | 49 KB (FP32) | 約 4.7 分 |
サンプル2: ずんだもん ささやき
ずんだもん ITA コーパスのささやき 感情100 サブセットから、リップノイズ除去後の100ペアを用いて学習しました。
データセット:
読み上げテキスト:
自然言語処理や画像処理など、ディープラーニングは私たちの身の回りの様々な場所で活用されています。
生成サンプル (ログイン不要です):
-
音声参照 (データセット内の
sasa1_006.wavを参照) -
LoRA
-
Speaker Inversion
- Ground Truth サンプル (VOICEVOX サンプル)
こちらは、音声参照では再現度が高くなかったものの、LoRA と Speaker Inversion ではかなり再現できていると思います。
音声参照の再現度が低い要因として、おそらくささやき音声は絵文字のアノテーションが使用されており、読み上げテキスト側で条件付けされるように学習されていたために、音声エンコーダー側が担当していなかった可能性が考えられます。
| 手法 | 学習パラメータ数 | 学習時 VRAM | 音声/モデルファイルサイズ | 学習時間 |
|---|---|---|---|---|
| 音声参照 | なし | なし | 3.1 MB (11秒) | なし |
| LoRA 学習 | 17M | 4.98 GB | 69 MB (FP32) | 約 22 分 |
| Speaker Inversion | 12K | 4.39 GB | 49 KB (FP32) | 約 14 分 |
まとめ・おわりに
学習パラメータ数は 1/1000 くらいですが、LoRA の時点でフルパラメータと比較して少量のパラメータしか学習しないため、学習時の VRAM 削減は 10% くらい にとどまりました。また、どちらのデータセットでも 学習時間は 30% 程度の削減 でした。
一方で、学習後の埋め込みのファイルサイズは FP32 の 16 トークンで 49 KB と、単一の参照音声ファイルよりも小さく なっています。
以下のようなケースに向いているかもしれません:
- 複数の声を色々切り替えたい: LoRA の代わりにこの方法で学習すると ストレージへの負担が減る と思います。また、他の人と埋め込みを共有する際も通信量を削減できます。
- データセットを持っていて、音声参照でも雰囲気出てるけど、軽く学習したい: Speaker Inversion はいじれるパラメータがトークン数くらいしかないので、実験しやすいと思います。
- マージしやすいかも: これは試していないのですが、LoRA は複数の層を学習するのに対して、Speaker Inversion は数トークン分の埋め込みだけを学習するため、複数学習した声をマージするのがやりやすいかもしれません。
一長一短については、
- ベースモデルの能力を壊さない: DiT ブロック内の Attention や MLP に対しては特に触らないので、元々 Irodori-TTS が持っていた 表現を壊さない と思います。一方で、逆に 元々持ってない表現は獲得しづらい と思うので、LoRA に比べたら 表現力は劣る可能性が高い です。ただ、明確に何が得意/向いていないかは実験が足りていないので不明です。
-
学習率が高すぎるのではと思うかもしれませんが、Textual Inversion では最終的に学習率
0.04を使ったそうです。なので、この形式の学習はかなり学習率を高くした方が良さそうです。ずんだもんささやきデータセットでは、学習率0.01では何も学習されませんでした。 ↩︎
Discussion
@platina
サンプル1: あみたろコーパス読み上げ音声 の SoundCloud のリンク先が LoRA と Speaker Inversion で逆になっているようです。
お手すきの際に修正をお願いします。
指摘ありがとうございます!修正しました!