🎧

論文解説:VoiceStar

に公開

この記事はLLM・LLM活用 Advent Calendar 2025の22日目の記事です。

https://qiita.com/advent-calendar/2025/large-language-model

はじめに

先日、個人開発で作成したLLMベースのTTSモデルであるT5Gemma-TTSというモデル・関連コードなどを公開しました。

https://huggingface.co/Aratako/T5Gemma-TTS-2b-2b
https://github.com/Aratako/T5Gemma-TTS

モデルカードなどでも触れていますが、このモデルのアーキテクチャなどはVoiceStarというTTSモデルの論文・実装に基づいています。

VoiceStarの論文はこちらです。
https://arxiv.org/abs/2505.19462

また、実装も以下のリポジトリで公開されています。
https://github.com/jasonppy/VoiceStar

本記事では、このVoiceStarの論文解説を行います。

Neural Codec Language Modelについて

論文解説に進む前の前提知識として、現在開発が盛んなTTSモデルであるNeural Codec Language Modelの基礎についてここで解説します。VoiceStarもこれに分類されるモデルです。

このNeural Codec Language Modelは、基本的に以下の2つのコンポーネントから成り立っています。

1. Neural Audio Codec

Neural Audio Codecは、端的に言えば音声のTokenizerのようなものです。このモデルは以下の2つの処理を行います。

  • 音声波形のデータから離散トークンへの変換
    連続値である音声の波形データを、情報量の小さな離散的な音声トークン列(例えば[127, 3958, 7501, ...]のようなもの)に圧縮・変換します。
  • 離散トークンから音声波形のデータへの変換
    圧縮された音声トークン列を連続的な音声波形に逆変換し、音声を生成します。

これはText Tokenizerにおけるencode()decode()のような処理を音声に対して行っているようなものなので、Speech TokenizerやAudio Tokenizerとも表現されます。

アーキテクチャはモデルによって様々ですが、多くのものはVQ-VAEのような構造を採用しています。RVQやFSQのようなQuantizerを使用し、離散的な音声トークンを中間で生成します。学習は基本的に元の音声とencode()decode()されて復元された音声での再構成誤差をベースに行われます。


Overview of the Amphion Toolkit (v0.2)より引用

モデル自体のアーキテクチャや音声の圧縮効率、音声のサンプリングレート、コードブックのサイズなどが異なる様々なものが存在します。代表的なものとして、EncodecDACXCodec2SNACMimiなどがあります。

2. 音声トークンを予測・生成するモデル

上記のNeural Audio Codecによって、音声データを離散トークン列に変換することが出来ました。また、テキストデータは音素解析に基づく処理やLLMにおけるTokenizerのような仕組みで同様に離散トークン列に変換することができます。

このテキストトークンと音声トークンを元に、入力をテキストトークンとし、それに対応する音声トークンを生成するタスクとして何らかのモデルを学習することで、TTSモデルを作成することができます。

例えばこんにちは、私はAIです。と発声している音声データを元にする場合、このこんにちは、私はAIです。というテキストはText Tokenizerでテキストトークンに、対応する音声データはNeural Audio Codecによって音声トークンに変換したうえで、Text Token - Audio Tokenのような入出力ペアで学習するようなイメージです。自己回帰を用いることが多いですが、Flow Matchingなどを利用することもあります。

また、このモデルの初期重みには既存の学習済みLLMをベースにすることが一般的になっています。例えばLlasaOrpheusではLlama 3.1 / 3.2系列のLLMを、CosyVoice2ではQwen2.5-0.5Bが使われています。このようにLLMをベースにした場合は特にLLM-based TTS Modelのような表現をされたりもします。

LLMベースのTTSモデルについては以下のような記事も理解の参考になるかと思いますので、併せてご確認ください。

https://huggingface.co/spaces/Steveeeeeeen/SpeechLLM-Playbook
https://huggingface.co/blog/YatharthS/llm-tts-models

また、以前私が投稿したLlasaというLLMベースのTTSモデルを学習してみたという記事も理解の一助になるかもしれません。

https://zenn.dev/aratako_lm/articles/7e0b0b6e51baa6

Neural Codec Language Modelの問題点

Neural Codec Language Model、中でも特にLLMをベースとしたモデルは非常に表現力が高く、生成される音声の品質は非常に高いです。また、Zero-shot Voice Cloningが得意であるなどの利点も備えています。一方、特有の問題点もいくつか抱えています。

  • モデルサイズがTTSモデルとしては比較的大きく、計算コストが高い
  • 安定性が低く、テキストの読み飛ばしや存在しない文字の読み上げが発生したり、長時間の無音が発生したりする
  • 長さ制御やスタイル制御などの高い制御性を取り入れるためにはモデル・データ側で何らかの工夫が必要となり、制御性が低い状態では音声の一貫性を保つのが難しい

VoiceStarでは、この中でも特に長さ制御に重点を置いた手法を提案しています。

本題:VoiceStarについて

本題に移りまして、VoiceStarにおけるアプローチを解説します。

概要

VoiceStarはEncoder-DecoderのTransformerでの自己回帰による音声合成モデルです。基本的には前のセクションで解説したNeural Codec Language Modelと同じ仕組みを取っていて、Neural Audio Codecで離散化した音声トークンを生成することで音声を合成します。

まずはVoiceStarの基本的な情報を箇条書きします。

  • Encoder-DecoderのTransformer、タスクは自己回帰
    • 最近のLLMベースTTSモデルは基本的にDecoder-onlyなので、かなり珍しい
    • LLMの重みは初期化に利用しておらず、完全なスクラッチ(なので、LLM-based TTSモデルではない)
    • 初期化にLLMを利用しなかった理由は不明だが、シンプルに論文の発表された時期には直近で公開された新しいEncoder-Decoder LLMが存在しなかったから?
  • コーデックにはVoiceCraftが公開した4-codebook / 50HzのEncodecを採用
  • 入力は音素ベースのアプローチを採用し、テキストを音素に変換してそれをモデルの入力として利用
  • 学習データはEmiliaLibriheavyLibriheavy-longの英語サブセットの一部、合計で6万5千時間を利用
  • Zipformerで提案されたScaledAdam Optimizer + Eden Schedulerを学習に利用

基本的な部分以外で、VoiceStarの取っている特筆すべきアプローチは主に以下の2つです。

  1. 長さ制御のためのPM-RoPEという特殊な位置エンコーディングの提案
  2. Zero-shot Voice Cloningの安定性向上のためのCPM trainingという学習手法の提案

以下にそれぞれ詳しく解説します。

PM-RoPEについて

VoiceStarでは生成音声の長さ制御のために Progress-Monitoring Rotary Position Embeddings(PM-RoPE) という特殊なRoPEを提案し、Self-AttentionとCross-Attentionに適用しています。

PM-RoPEでは最近のLLMなどでよく採用される位置エンコーディングであるRoPEをベースに、各トークンの位置を全体の生成長からの進捗率のような値に対応させて学習・推論に利用することで、モデルに対して生成長の情報を与えます。

そもそもRoPEとは何ぞやという方は、以下の記事などが分かりやすいかと思いますのでご参照ください。

https://tech-blog.abeja.asia/entry/advent-2025-day10

Cross-Attentionにおける通常のRoPEの適用は以下のような式で表せます。ここで、sはEncoder入力(つまり、入力されるテキストトークン)の各トークンのインデックスで、tはDecoder出力(つまり、生成される音声トークン)の各トークンのインデックスです。\thetaはRoPEにおける回転の角周波数のパラメータで、RはRoPEにおける回転の操作です。


通常のRoPEの数式(VoiceStarより引用)

これに対し、PM-RoPEの式は以下のようになります。ここで、SはEncoder入力全体のトークン長、TはDecoder出力全体のトークン長です。つまり、通常はインデックスを用いて各トークンの位置を表現するのに対し、PM-RoPEでは全体に対する各トークンの進捗率\frac{s}{S}\frac{t}{T}のような割合で位置を表現しています。また、Nはこの進捗率をスケールするためのパラメータで、VoiceStarの論文では2000が使われています。


PM-RoPEの数式(VoiceStarより引用)

Self-Attentionの場合も式としてはほぼ変わらず、同じシーケンス内2つのトークンs_1s_2St_1t_2Tを使ってAttentionを計算するだけです。

実装としては非常にシンプルで、Self-Attentiton / Cross-Attentionに渡す前のposition idに対してこの進捗率をベースにした値を割り当て、その後通常のRoPEを適用するだけになります。
position idは以下のような処理で簡単に割り当てられます。コード中のlengthsが全体のトークン長で式中のSTに、posが各トークンのインデックスで式中のstに対応しています。また、self.progress_scaleがスケール定数で、式中のNです。posは1からではなく0から始まるので、self.progress_scaleまでの全体の値を使えるように実際にはSTではなくS-1T-1で割って進捗率を計算しています。

def _build_position_ids(
    self, lengths: torch.Tensor, max_len: int, device
) -> torch.Tensor:
    lengths = lengths.to(device=device)
    pos = torch.arange(max_len, device=device, dtype=torch.float32)[None, :]  # [1, T]

    denom = (lengths.clamp(min=2).to(torch.float32) - 1.0)[:, None]  # [B, 1]
    position_ids = pos / denom * self.progress_scale  # [B, T]

    mask = pos < lengths[:, None]  # [B, T] (bool)
    return position_ids.masked_fill(~mask, 0.0)

論文と同じN=2000の設定の中で実際に各トークンのposition idがどのように割り振られるかをいくつかの例でみてみましょう。

例1:トークン長5の場合
[\frac{0}{5-1} \times 2000, \frac{1}{5-1} \times 2000, \frac{2}{5-1} \times 2000, \frac{3}{5-1} \times 2000, \frac{4}{5-1} \times 2000] = [0, 500, 1000, 1500, 2000]

例2:トークン長150の場合
[\frac{0}{150-1} \times 2000, \frac{1}{150-1} \times 2000, \frac{2}{150-1} \times 2000, ..., \frac{148}{150-1} \times 2000, \frac{149}{150-1} \times 2000] \simeq [0, 13.423, 26.846, ..., 1986.577, 2000]

例3:トークン長1250の場合
[\frac{0}{1250-1} \times 2000, \frac{1}{1250-1} \times 2000, \frac{2}{1250-1} \times 2000, ..., \frac{1248}{1250-1} \times 2000, \frac{1249}{1250-1} \times 2000] \simeq [0, 1.601, 3.203, ..., 1998.399, 2000]

このように、最初のトークンが0に、最後のトークンがN=2000に対応するように正規化してposition idを割り当てます。

Encoder側の入力トークン長Sについては、学習・推論ともにテキスト全体が入力されるため、どちらもシンプルに入力から計算して算出することができます。一方、Decoder側の出力音声トークン長Tは、学習時は学習データの音声長から分かりますが、推論時にはこれを別途決定して進捗率を計算する必要があります。VoiceStarでは推論時の音声長はユーザによる指定、または未指定の場合には参照音声の話速から文字長ベースで推定しています。

この処理により0Nまでの正規化された範囲で均等にposition idが割り当てられ、これらがSelf-AttentionやCross-AttentionでのRoPEに使われます。これがPM-RoPEの概要です。

このPM-RoPEの適用により、以下のようなメリットを受けられます。

  1. 音声合成時、モデルに暗黙的に進捗情報を与えることで、自然な長さ制御が可能となる
    一定範囲で正規化された進捗情報を与えたうえで学習されることで、音声の生成時に「position idが1000くらいだから真ん中辺り」「2000近くだからそろそろ音声の終端」といった進捗情報を暗黙的にモデルが理解しながら生成することが出来るようになります。これにより、指定された生成長全体を上手く使った自然な長さ制御が可能になります。
  2. Cross-Attentionにおけるテキストと音声の進捗の対応情報を上手くアライメントできる
    TTSというタスクでは、入力側のテキストの進捗と出力側の音声の進捗がある程度対応します。例えば、テキスト側の中間辺りに対応する音声は音声全体の中間辺りに相当するはずです。
    RoPEには「相対的な位置(角度)が近いトークン同士の内積が大きくなり、Attentionスコアが高くなる」という性質があります。PM-RoPEにおいて、これは「進捗率(\frac{s}{S}\frac{t}{T})が近いトークン同士が結びつきやすくなる」ことを意味します。
    これにより、学習初期段階から「テキストの進捗10%地点」と「音声の進捗10%地点」が自然と対応付けられるようになり、両者の進捗に対する強力なアライメント効果(flat-start initial alignment)が発揮されます。
  3. 学習データに存在しない未知の長時間音声もある程度上手く合成できるようになる
    学習の際、position idは0Nの間に正規化されています。学習データに存在しないような長時間音声を合成しようとした時も、position idの値自体はこの0Nの範囲に収まります(単にid間の刻み幅が学習時より細かくなるだけです)。通常のRoPEでは学習時より大きなposition idが出現する外挿の問題になりますが、PM-RoPEでは既知の範囲内でサンプリング点を増やすということになるためOODが起きにくく、学習データ外の長時間音声の合成が他のモデルより上手く出来るようになることが報告されています。


学習データ外の長時間音声の合成における精度比較(VoiceStarより引用)

CPM trainingについて

Zero-shot Voice Cloningの安定性向上のため、VoiceStarでは学習の際にContinuation-Prompt Mixed(CPM) trainingという手法を提案・利用しています。


VoiceStarより引用

この手法は非常にシンプルです。データセットの前処理時に、データのspeaker情報を使って同じspeakerのデータの一覧を保持しておき、学習の際に一定の確率pで同じspeakerの音声データのテキストと音声トークンを上図のようにreference speechとして配置してその形式で学習を行います。それ以外の場合は単一データを分割して一部をreferenceに、残りをtargetに配置して学習します。論文はp=0.5が最適だったと報告されています。(なお、GitHubにある公式実装には後者の音声の一部をreferenceとする実装は含まれておらず、テキスト・音声トークンペアで通常通りNext Token Predictionで学習を行っています。公開版で省略されているのか、論文での記述ミスなのかは不明です)

また、論文ではこの時のreference speechを一定の確率p^{\prime}で0.75倍~1.25倍の速度に変換して学習に使うSA(speech augmentation)も効果があったと報告しています。このp^{\prime}については、論文ではp^{\prime}=0.3が最適だったと報告されています。


各手法を取り入れることによる精度改善(VoiceStarより引用)


CPMとSAにおける最適な確率の探索(VoiceStarより引用)

余談:T5Gemma-TTSの場合

T5Gemma-TTSではこのVoiceStarをベースとしてPM-RoPEやCPMを取り入れつつ、以下のような変更を加えて学習しています。

  1. 重みの初期化にGoogleが公開した最新のEncoder-Decoder LLMであるT5Gemmaを利用
    T5GemmaはVoiceStarの発表後に公開された最新のEncoder-Decoder LLMです。これを利用することで精度向上が測れると考え、初期重みに利用しています。
  2. テキスト入力を音素ベースからT5GemmaのText Tokenizerを使う形に変更
    VoiceStarでは入力は音素ベースでしたが、今回は元のT5Gemmaのテキスト理解力を活かすためにT5GemmaのText Tokenizerを通してテキストを入力しています。これにより、Encoder側のEmbedding層の重みをそのまま使えるようになります。ただし、音素ベースの方がテキストトークンは綺麗にtokenizeされるので、PM-RoPEはそちらの方が上手く働くようになると考えられます(ablationは出来ていません)。
  3. 音声コーデックをXCodec2ベースのモデルに変更
    VoiceStarではEncodecをコーデックに使っていましたが、実装上single codebookの方がヘッドの設計が容易だったため、XCodec2に変更しています。

まとめ

この記事では、VoiceStarというTTSモデルの提案手法などを解説しました。これに限らず、Neural Codec Language ModelやLLM-based TTS Modelに関する論文やモデルの発表は非常に盛んなので、ご興味を持った方は関連する論文などを是非読んでみてください。

Discussion