# テキスト読み上げ（TTS）ファインチューニングガイド

TTSモデルのファインチューニングにより、特定のデータセット、ユースケース、または望ましいスタイルやトーンに適応させることができます。目的は、音声のクローン作成、話し方やトーンの適応、新しい言語のサポート、特定タスクの処理などのカスタマイズです。私たちはまた **音声→テキスト（STT）** のようなモデル（例：OpenAIのWhisper）もサポートしています。

を使えば、 [Unsloth](https://github.com/unslothai/unsloth)任意の **TTSモデル（** 対応）を、`transformers` Flash Attention 2を使用した他の実装より1.5倍高速かつメモリ50%削減でファインチューニングできます。

⭐ **Unslothは任意の `transformers` 対応TTSモデルをサポートします。** ノートブックやアップロードがまだ無くてもサポート対象です。例えば、Dia-TTSやMoshiのファインチューニングを試してみてください。

{% hint style="info" %}
ゼロショットクローンはトーンを捉えますが、ペーシングや表現が欠け、しばしばロボットのようで不自然に聞こえます。ファインチューニングははるかに正確で現実的な音声再現をもたらします。 [詳細はこちらを参照してください](#fine-tuning-voice-models-vs.-zero-shot-voice-cloning).
{% endhint %}

### ファインチューニング用ノートブック：

私たちはまた、TTSモデル（オリジナルおよび量子化版）を私たちの [Hugging Faceページ](https://huggingface.co/collections/unsloth/text-to-speech-tts-models-68007ab12522e96be1e02155).

| [Sesame-CSM（1B）](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Sesame_CSM_\(1B\)-TTS.ipynb) | [Orpheus-TTS（3B）](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Orpheus_\(3B\)-TTS.ipynb) | [Whisper Large V3](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Whisper.ipynb) （STT） |
| ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| [Spark-TTS（0.5B）](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Spark_TTS_\(0_5B\).ipynb)   | [Llasa-TTS（1B）](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llasa_TTS_\(1B\).ipynb)     | [Oute-TTS（1B）](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Oute_TTS_\(1B\).ipynb)   |

{% hint style="success" %}
出力の長さが最大で10秒に達する場合は、`max_new_tokens = 125` のデフォルト値125を増やしてください。125トークンは約10秒の音声に相当するため、より長い出力にはより高い値を設定する必要があります。
{% endhint %}

### TTSモデルの選択と読み込み

TTSでは、エンドユーザーに対するレイテンシー低下や推論の高速化のために小さなモデルが好まれることが多いです。3Bパラメータ未満のモデルをファインチューニングするのが理想的であることが多く、当社の主要な例ではSesame-CSM（1B）とLlamaベースの音声モデルであるOrpheus-TTS（3B）を使用しています。

#### Sesame-CSM（1B）の詳細

**CSM-1B** はベースモデルで、一方で **Orpheus-ft** は8人のプロ声優でファインチューニングされており、音声の一貫性が主な違いです。CSMは各話者のオーディオコンテキストを必要とするのに対し、Orpheus-ftはこの一貫性が最初から組み込まれています。

CSMのようなベースモデルからファインチューニングするには一般により多くの計算資源が必要ですが、Orpheus-ftのような既にファインチューニングされたモデルから始めると最初から良好な結果が得られます。

CSMに役立てるために、新しいサンプリングオプションと音声の一貫性を向上させるためにオーディオコンテキストを使用する例を追加しました。

#### Orpheus-TTS（3B）の詳細

Orpheusは大規模な音声コーパスで事前学習されており、笑いやため息などの感情的な手がかりを含む現実的な音声生成に優れています。そのアーキテクチャは利用・学習が容易で、llama.cpp経由でエクスポートできるため、あらゆる推論エンジンとの互換性が高いです。サポートされていないモデルでは、LoRAアダプタのsafetensorsのみ保存可能です。

#### モデルの読み込み

音声モデルは通常サイズが小さいため、LoRA 16ビットやフルファインチューニング（FFT）で学習させることができ、より高品質な結果が得られる場合があります。LoRA 16ビットで読み込むには：

```python
from unsloth import FastModel

model_name = "unsloth/orpheus-3b-0.1-pretrained"
model, tokenizer = FastModel.from_pretrained(
    model_name,
    load_in_4bit=False  # 4ビット精度（QLoRA）を使用
)
```

これが実行されると、Unslothはモデル重みをダウンロードします。8ビットを好む場合は、 `load_in_8bit = True`を使用できます、またはフルファインチューニングする場合は `full_finetuning = True` （VRAMが十分にあることを確認してください）。モデル名は他のTTSモデルに置き換えることもできます。

{% hint style="info" %}
**注意：** Orpheusのトークナイザーには既に音声出力用の特殊トークンが含まれています（詳細は後述）。 *別途* ボコーダーは必要ありません – Orpheusは直接音声トークンを出力し、それを波形にデコードできます。
{% endhint %}

### データセットの準備

最低限、TTSファインチューニング用データセットは **音声クリップとそれに対応する書き起こし** （テキスト）で構成されます。ここでは [*Elise* データセットを使います](https://huggingface.co/datasets/MrDragonFox/Elise) これは約3時間の単一話者英語音声コーパスです。2つのバリアントがあります：

* [`MrDragonFox/Elise`](https://huggingface.co/datasets/MrDragonFox/Elise) – トランスクリプトに **感情タグ** （例：\<sigh>, \<laughs>）が埋め込まれた拡張版。角括弧内のこれらのタグは表現（笑い、ため息など）を示し、Orpheusのトークナイザーによって特殊トークンとして扱われます。
* [`Jinsaryko/Elise`](https://huggingface.co/datasets/Jinsaryko/Elise) – 特殊タグのないトランスクリプトを含むベース版。

データセットは各エントリに1つの音声とトランスクリプトがあるように整理されています。Hugging Faceでは、これらのデータセットに `audio` （波形） `text` （書き起こし）、およびいくつかのメタデータ（話者名、ピッチ統計など）のようなフィールドがあります。Unslothには音声とテキストのペアのデータセットを与える必要があります。

{% hint style="success" %}
トーン、ケイデンス、ピッチだけに注目するのではなく、データセットが完全に注釈され、適切に正規化されていることを優先してください。
{% endhint %}

{% hint style="info" %}
Sesame-CSM-1Bのような一部のモデルでは、 **Sesame-CSM-1B**のように、スピーカーID 0を使った生成で音声のばらつきが見られることがあります。これは **ベースモデル**であり、固定された音声アイデンティティを持っていないためです。スピーカーIDトークンは主に **会話内での一貫性を維持するのに役立ちます**が、別個の生成間での一貫性を保証するものではありません。

一貫した音声を得るには、 **コンテキスト例**（いくつかの参照音声クリップや前の発話など）を提供してください。これによりモデルは望ましい音声をより確実に模倣できます。これがないと、同じスピーカーIDを使っても変動が生じることが予想されます。
{% endhint %}

**オプション1：Hugging Face Datasetsライブラリを使用する** – Hugging Faceの `datasets` ライブラリを使ってEliseデータセットを読み込めます：

```python
from datasets import load_dataset, Audio

# Eliseデータセットを読み込む（例：感情タグ付きバージョン）
dataset = load_dataset("MrDragonFox/Elise", split="train")
print(len(dataset), "samples")  # Eliseは約1200サンプル

# すべての音声が24 kHzのサンプリングレート（Orpheusの期待レート）であることを確認
dataset = dataset.cast_column("audio", Audio(sampling_rate=24000))
```

これによりデータセットがダウンロードされます（約1.2kサンプルで約328MB）。各アイテムは `データセットを使います` が少なくとも以下を含む辞書です：

* `"audio"`：音声クリップ（波形配列とサンプリングレートなどのメタデータ）、および
* `"text"`：書き起こし文字列

Orpheusは次のようなタグをサポートします： `<laugh>`, `<chuckle>`, `<sigh>`, `<cough>`, `<sniffle>`, `<groan>`, `<yawn>`, `<gasp>`など。例えば： `"I missed you <laugh> so much!"`。これらのタグは山括弧で囲まれておりモデルによって特殊トークンとして扱われます（それらは [Orpheusの期待するタグ](https://github.com/canopyai/Orpheus-TTS) のように一致します）。トレーニング中、モデルはこれらのタグを対応する音声パターンと結びつけて学習します。タグ付きのEliseデータセットにはこれらの多くが既に含まれています（例："laughs"が336件、"sighs"が156件など、カードに記載）。データセットにそのようなタグがないが組み込みたい場合は、音声にその表現が含まれる箇所に手動でトランスクリプトを注釈できます。 `<laugh>` および `<sigh>`オプション2：カスタムデータセットの準備

**– 自分の音声ファイルとトランスクリプトがある場合：** WAV/FLACファイルとして音声クリップをフォルダに整理します。

* ファイルパスとトランスクリプトの列を持つCSVまたはTSVファイルを作成します。例：
* filename,text

  ```
  0001.wav,Hello there!
  0002.wav,<sigh> I am very tired.
  load_dataset("csv", data_files="mydata.csv", split="train")
  ```
* 使用する `で読み込みます。データセットローダーに音声パスの扱いを伝える必要があるかもしれません。代替として` datasets.Audio `機能を使って音声データをオンザフライで読み込む方法があります：` from datasets import Audio

  ```python
  dataset = load_dataset("csv", data_files="mydata.csv", split="train")
  dataset = dataset.cast_column("filename", Audio(sampling_rate=24000))
  その後、
  ```

  dataset\[i]\["audio"] `は音声配列を含みます。` トランスクリプトが正規化されていることを確認してください
* **（トークナイザーが認識しないような特殊文字は避け、使用する場合は感情タグなどを除いて）。また、すべての音声が一貫したサンプリングレートであることを確認してください（必要に応じてモデルが期待するターゲットレート、例えばOrpheusなら24kHzにリサンプリングしてください）。** 要約すると、

データセット準備では **次のものが必要です：**:

* （audio, text）の **リスト** ペア。
* 読み込みやオプションの前処理（リサンプリングなど）にはHFの `datasets` ライブラリを使用してください。
* モデルに学習させたい **特殊タグ** がテキストにある場合は含めてください（それらがモデルに学習されるように、 `<angle_brackets>` 形式で記述し、モデルがそれらを別個のトークンとして扱うようにしてください）。
* （オプション）マルチスピーカーの場合は、テキストにスピーカーIDトークンを含めるか、別のスピーカー埋め込みアプローチを使うこともできますが、これはこの基本ガイドの範囲を超えます（Eliseは単一話者です）。

### UnslothでのTTSファインチューニング

それではファインチューニングを開始しましょう！ここではPythonコード（JupyterノートブックやColabで実行可能）で説明します。

**ステップ1：モデルとデータセットの読み込み**

すべてのTTSノートブックでは、LoRA（16ビット）トレーニングを有効にし、QLoRA（4ビット）トレーニングを無効にしています： `load_in_4bit = False`。これは通常、モデルがデータセットをよりよく学習し、高い精度を得られるようにするためです。

```python
from unsloth import FastLanguageModel
import torch
dtype = None # 自動検出のためNone。Tesla T4やV100ならFloat16、Ampere以上ならBfloat16
load_in_4bit = False # メモリ使用量を減らすための4ビット量子化を使用。Falseにすることも可能。

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/orpheus-3b-0.1-ft",
    max_seq_length= 2048, # 長いコンテキストのために任意で選択！
    dtype = dtype,
    load_in_4bit = load_in_4bit,
    #token = "hf_...", # meta-llama/Llama-2-7b-hfのようなゲート付きモデルを使う場合は使用
)

from datasets import load_dataset
dataset = load_dataset("MrDragonFox/Elise", split = "train")
```

{% hint style="info" %}
メモリが非常に限られているかデータセットが大きい場合は、ストリーミングやチャンク読み込みが可能です。ここでは3時間の音声はRAMに簡単に収まります。自分のCSVデータセットを使う場合も同様に読み込んでください。
{% endhint %}

**ステップ2：高度な処理 - トレーニング用データの前処理（オプション）**

Trainer用の入力を準備する必要があります。TTSの場合の一つのアプローチは、テキストと音声トークンIDを因果的に連結してターゲットシーケンスとすることです。しかし、Orpheusは音声を出力するデコーダのみのLLMなので、テキストを入力（コンテキスト）として与え、音声トークンIDをラベルにすることができます。実際には、モデルの設定がそれを音声合成として識別すればUnslothの統合が自動的に行うかもしれません。そうでない場合は、次のような処理を行えます：

```python
# テキストのトークナイズ
def preprocess_function(example):
    # テキストをトークナイズ（<laugh>等の特殊トークンを保持）
    tokens = tokenizer(example["text"], return_tensors="pt")
    # トークンIDのリストにフラット化
    input_ids = tokens["input_ids"].squeeze(0)
    # モデルはこれらのテキストトークンの後に音声トークンを生成します。
    # トレーニングでは、labelsをinput_idsと等しく設定して次のトークンを予測するように学習させることができます。
    # ただしそれはテキストトークンが次のテキストトークン（もしくは音声トークンや終了）を予測するのみを扱います。
    # より洗練された方法：音声開始を示す特殊トークンを付け加えてモデルに残りを生成させる。
    # 単純化のため、同じinputをlabelsとして使います（モデルは自己与件からシーケンスを出力することを学習します）。
    return {"input_ids": input_ids, "labels": input_ids}

train_data = dataset.map(preprocess_function, remove_columns=dataset.column_names)
```

{% hint style="info" %}
上記は簡略化です。実際にはOrpheusを正しくファインチューニングするには、 *音声トークンをトレーニングラベルの一部として用意する必要があります*。Orpheusの事前学習はおそらく音声を離散トークン（オーディオコーデック経由）に変換し、前のテキストを与えてそれを予測するように学習しています。新しい音声データでファインチューニングする場合も同様に各クリップの音声トークンを取得する必要があります（Orpheusのオーディオコーデックを使用）。OrpheusのGitHubにはデータ処理用スクリプトがあり、音声を `<custom_token_x>` のようなトークン列にエンコードする処理が含まれています。
{% endhint %}

ただし、 **Unslothはこれを抽象化している可能性があります**：モデルがFastModelで、音声を扱う方法を知る関連プロセッサがある場合、データセット内の音声を自動的にトークン化してくれるかもしれません。もしそうでなければ、各音声クリップを手動でトークンIDにエンコードする必要があります（Orpheusのコードブックを使用）。これは本ガイドの範囲を超える高度な手順ですが、単にテキストトークンだけを使っても実際の音声を教えることにはならない点を覚えておいてください—音声パターンと一致させる必要があります。

Unslothが音声を直接渡す方法を提供していると仮定しましょう（例： `processor` を設定し音声配列を渡す）。もしUnslothが自動音声トークン化をまだサポートしていない場合は、Orpheusリポジトリの `encode_audio` 関数を使って音声のトークン列を取得し、それをラベルとして使う必要があるかもしれません（データセットの項目には `phonemes` やいくつかの音響特徴が含まれており、パイプラインが示唆されています）。

**ステップ3：トレーニング引数とTrainerの設定**

```python
from transformers import TrainingArguments,Trainer,DataCollatorForSeq2Seq
from unsloth import is_bfloat16_supported

trainer = Trainer(
    model = model,
    train_dataset = dataset,
    args = TrainingArguments(
        per_device_train_batch_size = 1,
        gradient_accumulation_steps = 4,
        warmup_steps = 5,
        # num_train_epochs = 1, # 1エポックの完全なトレーニングを設定する場合
        max_steps = 60,
        learning_rate = 2e-4,
        fp16 = not is_bfloat16_supported(),
        bf16 = is_bfloat16_supported(),
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 3407,
        output_dir = "outputs",
        report_to = "none", # WandB等を使う場合は適宜変更
    ),
)
```

高速化のために60ステップにしていますが、完全な実行には `num_train_epochs=1` を設定し、 `max_steps=None`にしてください。per\_device\_train\_batch\_sizeを1より大きくするとマルチGPU設定でエラーが出る可能性があります。問題を避けるためにCUDA\_VISIBLE\_DEVICESを単一GPUに設定してください（例：CUDA\_VISIBLE\_DEVICES=0）。必要に応じて調整してください。

**ステップ4：ファインチューニング開始**

これでトレーニングループが開始されます。ログとして損失が50ステップごと（logging\_stepsで設定した通り）に表示されるはずです。トレーニングにはGPU次第で時間がかかります—例えばColabのT4 GPUでは3時間分のデータで数エポックで1〜2時間かかることがあります。Unslothの最適化により標準のHFトレーニングより高速になります。 `logging_steps`）に設定されています。

**ステップ5：ファインチューニング済みモデルの保存**

トレーニングが完了したら（または途中で十分だと感じたら停止した場合）、モデルを保存します。これはLoRAアダプタのみを保存し、フルモデルを保存するわけではありません。16bitやGGUFで保存する方法は下へスクロールしてください！

```python
model.save_pretrained("lora_model")  # ローカルに保存
tokenizer.save_pretrained("lora_model")
# model.push_to_hub("your_name/lora_model", token = "...") # オンライン保存
# tokenizer.push_to_hub("your_name/lora_model", token = "...") # オンライン保存
```

これによりモデル重みが保存されます（LoRAの場合、ベースが完全にファインチューニングされていないならアダプタ重みのみが保存されることがあります）。CLIで `--push_model` を使ったり、 `trainer.push_to_hub()`を使えば、Hugging Face Hubに直接アップロードできます。

これでディレクトリにファインチューニング済みTTSモデルがあるはずです。次のステップはそれをテストすることで、サポートされていればllama.cppを使ってGGUFファイルに変換できます。

### ボイスモデルのファインチューニング vs ゼロショット音声クローン

XTTSのようなモデルを使えば30秒の音声だけでクローンできる、トレーニング不要だと言われます。それは技術的には正しいですが、本質を見落としています。

OrpheusやCSMのようなモデルで利用できるゼロショット音声クローンは近似です。話者の **トーンや音色** を捉えますが、表現の全範囲を再現するわけではありません。話速、フレージング、声の癖、プロソディの微妙さなどの詳細が失われます—これらが音声に **個性や独自性**.

を与える要素です。 **単に異なる声が欲しくて配信パターンが同じでも構わないなら、ゼロショットで十分なことが多いです。しかし音声は依然として**モデルのスタイル

に従うものであって、話者自身のものではありません。より個別化され表現力のあるものが欲しいなら、LoRAのような手法でのトレーニングが必要です。
