🔊

Irodori-TTSを使って音声ファイルよりも軽い埋め込みだけで声を学習する

に公開
2

概要

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 のファイルサイズ

コード(マージされました)はここです:

https://github.com/Aratako/Irodori-TTS

Irodori-TTS とは

Irodori-TTS は、Rectified Flow に基づいた日本語 TTS の事前学習モデルです。

https://huggingface.co/collections/Aratako/irodori-tts

今回は Aratako/Irodori-TTS-500M-v3 を用いました。

https://huggingface.co/Aratako/Irodori-TTS-500M-v3

背景

Irodori-TTS は Flow Matching で TTS を行う基盤モデルです。音声ファイルから声を参照した TTS もできるほか、LoRA の学習も公式で対応しています。
ところで、拡散モデルの基盤モデルといえば Stable Diffusion が連想されますが、Stable Diffusion では Textual Inversion という学習手法があります。(Negative Embedding でよく使われますね。)

https://arxiv.org/abs/2208.01618

Stable Diffusion では CLIP のテキストエンコーダーを使って条件付けを行うのですが、Textual Inversion はテキストエンコーダーに入力する埋め込みを学習することで、ファインチューンする手法です。利点としては、学習が軽い ことと、学習後にできる重みファイルも軽い ことです。
そこで今回は同様にして、話者埋め込みを学習 することで、LoRA よりも軽い 学習方法を実装してみました。ここではこれを Speaker Inversion と呼ぶことにします。

方法

Irodori-TTS v3 には参照音声を入力するためのモジュールが存在し、ゼロショットで Voice Cloning ができます。このとき、音声は DACVAE でエンコードされたのち、Speaker Encoder (Transformer 何層か) を通り、speaker_state を作成します。speaker_stateJointAttention ブロックで 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
train_v3_lora.yaml
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]
train_v3_si.yaml
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: あみたろコーパス読み上げ音声

データセットは「あみたろの声素材工房」からあみたろコーパス読み上げ音声を使用しました。
データセット:

https://amitaro.net/voice/corpus-list/amitaro/

読み上げテキスト:

自然言語処理や画像処理など、ディープラーニングは私たちの身の回りの様々な場所で活用されています。

生成サンプル (ログイン不要です):

  • 音声参照 (データセット内の 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ペアを用いて学習しました。
データセット:

https://zunko.jp/multimodal_dev/login.php

読み上げテキスト:

自然言語処理や画像処理など、ディープラーニングは私たちの身の回りの様々な場所で活用されています。

生成サンプル (ログイン不要です):

こちらは、音声参照では再現度が高くなかったものの、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 に比べたら 表現力は劣る可能性が高い です。ただ、明確に何が得意/向いていないかは実験が足りていないので不明です。
脚注
  1. 学習率が高すぎるのではと思うかもしれませんが、Textual Inversion では最終的に学習率 0.04 を使ったそうです。なので、この形式の学習はかなり学習率を高くした方が良さそうです。ずんだもんささやきデータセットでは、学習率 0.01 では何も学習されませんでした。 ↩︎

GitHubで編集を提案

Discussion