🤗

(Style-)Bert-VITS2の仕組み・構造と、バージョンによる違いについて

2023/12/30に公開
1

宣伝

  • Style-Bert-VITS2のチュートリアル解説動画を作りました

https://www.youtube.com/watch?v=aTUSzgDl1iY

  • discordサーバー「AI声づくり研究会」によく出没しています

https://zenn.dev/p/aivoicelab

注意 (2024-02-05)

この記事は、Bert-VITS2の日本語特化版が出る以前のものについて解説しています。最新の日本語特化版については
https://zenn.dev/litagin/articles/034819a5256ff4
をご参照ください。

概要

テキストから感情豊かで自然な抑揚の音声を生成できるBert-VITS2には、新しいバージョンでも

  • 2.1
  • 2.2
  • 2.3(最終バージョン)

の3バージョンがあります。
https://zenn.dev/litagin/articles/b1ddc1da5ea2b3

また、バージョン2.1をベースにし、感情スタイル指定をちゃんと動くようにしようとして私がリリースしたStyle-Bert-VITS2もあります:
https://github.com/litagin02/Style-Bert-VITS2

それぞれのバージョンによってモデル構造が異なっており、どちらが良いとは一概には言えません。つまり2.3が2.1より必ずしも優れているとは言えない(逆もまた然り)です。

この記事では、それぞれのモデル構造の違いを自分が理解している範囲で解説し、また感情や声音制御をそれぞれどうやっているかを解説します。

約束

以下、2.12.22.3Styleでそれぞれの本家2.1, 2.2, 2.3とStyle-Bert-VITS2を指すことにします。また単にBert-VITS2と言ったら、これら全てを総称して共通する部分を解説することにします。

また、この記事での所感等は全て日本語音声合成のみに注目しています。中国語での自然性は2.3が一番良いという意見も目にしたことがありますが、私には判断できないので日本語限定とします。

技術的な話は分からないので分かりやすく短く

はい。以下のようにまとめられます。

TL;DR: 2.1, 2.2, 2.3はどれがよいとは一概には言えない、Style-Bert-VITS2は2.1のおそらく上位互換。

もうちょっと詳細に違いを教えて

  • 2.1では感情スタイルが10種類から選べる・リファレンス音声からスタイルが指定できる、がこれはほとんど機能しない。つまり声音はあまり変わらない
  • 2.2では2.1からCLAPというすごいやつを使うことで、「感情をテキストプロンプト(Happy等)で制御、またはリファレンス音声で制御」できることになっていたが、これも自分で学習した場合は効果があった話を聞かない。2.1と2.2との変更点は大まかにはこの点のみ。
  • 2.3ではこれらの失敗から、モデルから感情制御部分が排除された。その代わりにモデルの構造が大幅に変わった:
    • gin_channelsというパラメータ数がこれまでの256から512に増やされた
    • Discriminatorが新しいものが追加されて2つになった
    • DurationPredictor(音素間隔予測)部分も大幅に改修して訓練し直した
  • しかし、2.3のこの変更は必ずしもメリットだけではない。個人の間隔やデータセットにも依存するが、以下のような(自分や他の人からの)感想や報告がある:
    • パラメータ数が増えた影響で、学習が安定するのに時間がかかったり学習しきれていない印象があるかもしれない、イントネーションも総じて良くなったとはとくに言えず微妙になっている説も(公式提供のデモでも2.3より2.1のほうがよかったという感想も)。
    • 音素間隔予測が変わったせいで、間延びしたり発音に違和感があることが増えたり、安定しない印象があるかもしれない。
    • 声音の再現度が落ちているとの報告もあり
    • 一方で、高音部分の再現度や、ちゃんとした大きいデータで大きいエポック回すと質もちゃんとする、という報告もあり(でも劇的に良くなったという話は聞かない)。

このような状況を考えて、自分のデータで回してみた結果、Style-Bert-VITS2では(個人の感想ですが2.2と2.3より質が速く安定して高いと感じる)2.1をベースにしています。

Style-Bert-VITS2での変更点は以下:

  • 2.1の感情制御は機能していないので、そこの部分を別の手法に取り替えてちゃんと効くようにした

Style textっていうのもあるんだけどこれはどういう関係?

注意: Style-Bert-VITS2のv1.2で、この名前は紛らわしいという理由からAssist textに変更されました。

(私がプルリクで本家に提案した)Style text(本家WebUIで融合文本语义)というものがあります。これは、読み上げテキストとは別にスタイルテキストというものを指定することで、「そのスタイルテキストを読み上げた場合と似たような声音・感情で元のテキストを読み上げてくれる」というものです。

このスタイルテキスト機能は、本家バージョン2.3に上がる際に実装されましたが、現在本家最新版では2.1, 2.2, 2.3どのモデルでも使えます。何故ならばモデル構造には何も触れていないからです(詳しくは後で詳述)。
またStyle-Bert-VITS2でも、スタイル指定とは別に使えます。

但しStyle textはあくまで強引な方法で声音を制御しようとしているため、万能ではなく、スタイルテキストを指定することで抑揚やイントネーションや発音やテンポに明らかな劣化が出てくるので、これでスタイルが完全に指定できるわけではありません(なのでStyle-Bert-VITS2を作りました)。

技術的な詳細

以下はこれら2.1, 2.2, 2.3, Styleについてのより正確な技術的な解説・比較となります。

共通する部分

基本的な流れやコンポーネントは全てのバージョンで共通しています。まずこれについて述べます。
(機械学習やTTSをちゃんと勉強したことがないエアプなので詳しいことは自分は全くわからないので、間違っていたらご指摘ください)

全体の流れ

(Style-)Bert-VITS2のどのバージョンでも、推論に必要なコンポーネントは以下からなります。

  • TextEncoder(テキストを受け取り、いろんな情報を返す)
  • DurationPredictor(音素間隔(ある文字(正確には音素)の発音の長さ)を返す)
  • StochasticDurationPredictor(DurationPredictorにランダム性をつけたやつ?)
  • Flow(声音、とくに声の高さの情報を持つ)
  • Decoder(いろんな情報から最終的に合成して声を出力、ここも声音情報を持つ)

音声合成時の流れ:

  1. テキストが入力される
  2. それがTextEncoderを通る
  3. その結果をもとに(Stochastic)DurationPredictorで各音素の長さが決まる
  4. それらの結果がflowを通る
  5. それがdecoderに流される

たぶんこの構造は別のTTSモデルでも共通しているんだと思います。(学習時のGANやらそこらへんは面倒なので説明を省きます、自分もよく知りません)。

Bert-VITS2の工夫

上の流れだけでは既存のVITSとたぶん変わりませんが、Bert-VITS2の革新的な点は以下です。

  1. Flow部分がTransformerに(これはVITS2の工夫です)
  2. TextEncoder部分にBERT特徴量を注入

1.のFlow部分は自分はあまり効果を知らないので省略します。

BERTとは大量のテキストデータをもとに学習されたモデルで、文章に対する意味や文脈や感情理解のようなものを持っていると把握しています。(正確にはBert-VITS2では日本語に対してはJapanese character-level DeBERTa V2 largeが使われています、これはトークンが日本語の通常文字レベルで学習されているので他の部分と都合がよいみたいです)。

Bert-VITS2では、TextEncoderの入力に、(日本語英語中国語の三カ国分のそれぞれの)Bert特徴量を受け取る部分が追加されています(Conv1d)。つまり、学習時・推論時の流れとして、

  1. テキストを受け取る
  2. BertモデルでそのBert特徴量を得る(1文字1文字に対して1つのベクトルがある)
  3. その特徴量をConv1dでTextEncoderに流す

そこからの流れは同じで変わりません。これによって、TextEncoderが入力テキストの内容の意味を「理解」し、学習の結果「楽しい発話の文章は楽しそうに読む」というBert-VITS2の最大の特徴が実現されています。

Style textとは?

注意: Style-Bert-VITS2のv1.2で、この名前は紛らわしいという理由からAssist textに変更されました。

これは単純で、音声合成時に使われるBertの出力をいじったものです。なのでモデル構造には何も手を入れておらず、使われているBertモデルが同じならどのモデルでも動きます。

具体的には、

  • 「入力テキスト」とともに「スタイルテキスト」というものを指定
  • テキストからBert特徴量bert_originalを取る(文字数分のベクトルが出てくる)
  • スタイルテキストからBert特徴量bert_styleを取る(文字数分のベクトルが出てくる、つまり文字数が合わないと混ぜられない)
  • bert_styleを文字ごとに平均しbert_style_meanを作る(混ぜられるようにするため)、こうして1つのベクトルができる
  • bert_originalの各文字ごとのベクトルとbert_style_meanとを指定した一定の比率で混ぜる

という操作をしています(すごく強引でこれでうまくいくのが不思議という感じですね)。

しかし以下のようなデメリットがあります。

  • Bertが文字に応じてあるおかげで、全体の抑揚・イントネーションやアクセントやテンポが学習されているはず
  • それを変に別のと混ぜちゃうことで、それらがおかしくなる

となります。これはスタイルテキストのbertの平均という、一定のベクトルを各文字に足しているからそうなりますね。例えばスタイルテキストの重みを1にしてもとを0にしたら、たしかにそのスタイルテキストの読み上げの声音になるのですが、音素間隔はバグりテンポも不自然になり棒読みになります。

なので万能ではなく、その場しのぎの一時的な感情制御という感じがちょっとあります(重みを0.7くらいにするといいかもという感じですが)。

バージョンによる違い

大まかには、このTextEncoderにBertにくわえて更にどのような情報を注入するか・しないかが違います。

2.1

Bertに、wav2vecの感情認識のモデルで抽出したものを入れています。これは、1つの音声ファイルから1つの感情ベクトル(1024次元)を取り出したものです。

ただしその入れ方がちょっと複雑で微妙になっており、以下の感じです:

https://github.com/fishaudio/Bert-VITS2/blob/4c8877f151d16ab6001485ba1eb49af447c73411/oldVersion/V210/models.py#L347-L356

  • 感情量ベクトル1024次元から1024次元へ全結合層で流れる(←これ必要?)
  • ベクトル量子化とやらで1024次元ベクトルたちから10個のベクトルへ量子化する(ベクトル量子化について自分は全く知らないので詳しくは分かりませんが、代表的な10個のベクトルを学習の過程で更新しながら計算しているみたいです)
  • 最後にその量子化されたベクトルを後に流す

ということをしています。実験したところベクトル量子化で10個を取り出すところがあまり学習されておらず、その結果本家の10種類の感情指定があまり効果が出ていません

Style

2.1ベースなので先に書きます。

上の感情量ベクトルの部分を、単に量子化を取っ払って単なる全結合層一つにしたらうまく感情が制御できるようになったので、それを使っています:
https://github.com/litagin02/Style-Bert-VITS2/blob/a3d03805236cef298fd775edcd9c525f05a23ced/models.py#L346

より詳しくは、感情ベクトルを取り出すモデルをこの256次元のモデルに取り替えています。

このモデルはもともと話者識別のモデル(複数話者を分類する)ですが、声音や喋り方やトーンについての情報も持っているので、きちんと感情特徴量として使えます。
その代わり、話者が違うとごとにベクトルはかなり違う位置にいるので、全ての話者で共通する感情ベクトルのようなものはありません
なので、現在のStyle-Bert-VITS2では、style_vectors.npyという、その話者固有の(スタイルごとの)スタイルベクトルを、学習プロセスとは独立して作る必要があります。この作業はWebUIで簡単に出来るように工夫しており、面倒な場合は感情に特に起伏がない場合は平均スタイルを1クリックで作成できます。
(学習時に自動的に平均スタイルを保存するような挙動を今後実装予定です)。

Future workとして、この感情ベクトルモデルを、話者非依存の感情認識モデルに取り替えることで、普遍的な感情ベクトルのようなものを作れば、どの話者でもいちいちスタイルベクトルを作ることなく制御できるのかも、というものがあると思います(が今のままでも十分使えるのであまり実験していません)。

その他Style-Bert-VITS2ではsafetensors対応等いろいろ細々改修はしていますが、モデル構造の観点からは以上のみが2.1からの変更点です。

2.2

おそらく2.1での感情制御がうまく行かなかったことと、新しいことを実験して試すという動機から、2.2では2.1の感情制御部分を取り払い、以下のように変更しています:

https://github.com/fishaudio/Bert-VITS2/blob/4c8877f151d16ab6001485ba1eb49af447c73411/oldVersion/V220/models.py#L380-L403

これは、2.1の感情モデルの代わりに、CLAPという、テキストと音声のクロスモーダルモデル(画像生成AIでお馴染みのCLIPの音版)を使っています。
そのCLAP特徴量を学習時は音声ファイルから読み取り、上に載せた層を通って次に渡されます。推論時にはテキストまたはオーディオからCLAP特徴量を取り出し、それを入れます。

個人的な感想ですがここでもベクトル量子化が悪さをしているのではという思いもあります。これを外してどうなるかは実験していません。

この効果は公式デモでは確かにちょっとあり、Angryと入れると怒っているように聞こえます。ですが、個人で学習した場合はその効果は感じられず、ほとんど変わらないようです(うまく行った報告は聞いたことありません)。

実際にCLAPによるテキストと音声とのペアリングがどういう感じかを実験してみたところ、音声からの特徴量とテキストからの特徴量がかなーり離れたところに位置していたので、それもうまくいっていない原因ではと疑っています。

2.3

以上の2.1と2.2の経緯から、感情についておそらく開発陣は諦め、感情埋め込み部分を全く取り払って、代わりにモデル構造を大きく変えて質を良くしようとしたのが2.3です。

具体的には、

  • いろいろなところで使われるgin_channelsという次元パラメータを256から512に引き上げた
  • GANに新しいDiscriminatorを追加。具体的には音のSSLモデルらしいWavLMを利用したモデルを追加
  • (Stochastic) Duration Predictorあたりの構造を変えたみたい

一番最後については、おそらくこのプルリク
https://github.com/fishaudio/Bert-VITS2/pull/226
において作業されていたもので、それまでのバージョンの訓練にはバグがあったと述べられており、フルスクラッチからのトレーニングが必要だと述べられています。

2.3の変更についての個人の感想

個人の感想です、データセットに依存します

構造の変化点は上述の通りです。が最初の方に述べている通り、この音素間隔の変更は、そこまで効果がない(個人的には逆効果)という気がしています。2.1-2.2での音素間隔でも十分おかしいところはなく感じるのですが、2.3だと逆におかしくなっている感じがあります。

他にも、画像生成AIでちょっと聞いた話で、学習のときのパラメータ(LoRAの次元等)は、増やせば増やすだけいいというものではなく、そこまで学習し切るのに時間がかかったり、そもそもそんなに次元がいらなくて変なものを学習してガビったり、ということが多いので、一番目の引き上げも一概にいい効果とは言えないと個人的には思っています。

また公式が提供しているデモでも2.1のほうが2.3より良かったという報告もあります。

DurationPredictorを以前のに戻したり、gin_channelsを256にしたりもして実験したのですが、あまりうまくいきませんでした(みなさんも興味があれば実験して)。

まとめ

おわり。機械学習や既存TTSについて全く勉強したことがないのでおかしいところがあったらご指摘ください・ご容赦ください。みなさんも興味があれば実験してみてください。

また個人の感想ばかりになってすみません。正直v2.1の衝撃が強すぎて(それで30個くらいモデルつくってそれに慣れてしまったので)他のverが微妙に感じる、というプラセボもあるとは思います。

それは置いておいて、みんなStyle-Bert-VITS2使ってね!感情スタイル指定を重み付きで操作できるすごいやつだよ!
https://github.com/litagin02/Style-Bert-VITS2
インストールや使い方はわざわざ記事を書くまでもなく簡単だよ!

Discussion

八仙飯店萌え豚饅頭八仙飯店萌え豚饅頭

1.30を毎日たのしく使わせて頂いています。技術者にはなれず、ITの分かる事務員を目指している。そんなレベルの人間で、上手くお伝えできないのですが、音声合成時のアサーションエラーと話者を作る時の色々なエラーで悩んでいます。後者は、問題になっているサンプルを必死に探し出して排除してなんとか抜けています。前者は、yyyy年などで、電話番号の読み上げの仕組みに飛ばされて、そちらでエラーが出ているようでした。あの、更にしつこく、技術に寄らない質問で申し訳ないのですが、「君が判定して→きみがはねいして」などと必ず合成される際ですが、「はねい」だけ音素で表記して直す方法って、あったりしますでしょうか。本当に、技術に寄らない、くだらない質問で、申し訳ありません。資格試験の耳学習を、音声合成でさせて頂いていて、自殺さえ考えていた人生に光が差しました。本当に感謝しております。ありがとうございます。ご無理はなさらずに、また、続けて頂ければ...。本当に感謝しております。