> For the complete documentation index, see [llms.txt](https://unsloth.ai/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/text-to-speech-tts-fine-tuning.md).

# 文本转语音（TTS）微调指南

了解如何使用 Unsloth 对 TTS 和 STT 语音模型进行微调。

微调 TTS 模型可以让它们适应你的特定数据集、使用场景或期望的风格与语气。目标是定制这些模型，以克隆声音、调整说话风格和语气、支持新语言、处理特定任务等等。我们也支持 **语音转文本（STT）** 模型，例如 OpenAI 的 Whisper。

通过 [Unsloth](https://github.com/unslothai/unsloth)，你可以微调 **any** TTS 模型（`transformers` 兼容），速度比其他使用 Flash Attention 2 的实现快 1.5 倍，内存占用减少 50%。

⭐ **Unsloth 支持任何 `transformers` 兼容的 TTS 模型。** 即使我们还没有对应的 notebook 或上传文件，它仍然受支持，例如，你可以尝试微调 Dia-TTS 或 Moshi。

{% hint style="info" %}
零样本克隆能捕捉语气，但会遗漏节奏和表达，往往听起来机械而不自然。微调能带来更准确、更逼真的语音复现。 [在这里阅读更多](#fine-tuning-voice-models-vs.-zero-shot-voice-cloning).
{% endhint %}

### 微调 Notebooks：

我们还把 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 个 token 对应 10 秒音频，若要更长输出，就需要设置更高的值。
{% endhint %}

### 选择并加载 TTS 模型

对于 TTS，通常更偏好更小的模型，因为它们延迟更低、对终端用户的推理更快。微调参数少于 3B 的模型通常是理想选择，而我们的主要示例使用的是 Sesame-CSM（1B）和 Orpheus-TTS（3B），后者是基于 Llama 的语音模型。

#### Sesame-CSM（1B）详情

**CSM-1B** 是一个基础模型，而 **Orpheus-ft** 是在 8 位专业配音演员的数据上微调而成，因此声音一致性是两者的关键区别。CSM 需要每个说话者的音频上下文才能表现良好，而 Orpheus-ft 已内置这种一致性。

像 CSM 这样的基础模型做微调通常需要更多算力，而从像 Orpheus-ft 这样的已微调模型开始则能开箱即用地获得更好的结果。

为了帮助使用 CSM，我们新增了采样选项，并提供了一个示例，展示如何使用音频上下文来提升声音一致性。

#### Orpheus-TTS（3B）详情

Orpheus 经过大规模语音语料预训练，在生成逼真语音方面表现出色，并内置支持笑声、叹息等情绪线索。它的架构使其成为最容易使用和训练的 TTS 模型之一，因为它可以通过 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）
)
```

运行这段代码时，如果你更倾向于 8 位，Unsloth 会下载模型权重，你可以使用 `load_in_8bit = True`，或者对于完整微调，设置 `full_finetuning = True` （确保你有足够的 VRAM）。你也可以把模型名称替换为其他 TTS 模型。

{% hint style="info" %}
**注意：** Orpheus 的 tokenizer 已经包含用于音频输出的特殊 token（后文会详细说明）。你 *不要* 需要单独的 vocoder——Orpheus 会直接输出音频 token，这些 token 可以解码为波形。
{% endhint %}

### 准备你的数据集

至少，TTS 微调数据集由以下内容组成： **音频片段及其对应的转录文本** （文本）。让我们使用 [*Elise* dataset](https://huggingface.co/datasets/MrDragonFox/Elise) ，这是一个约 3 小时的单说话人英语语音语料库。有两个版本：

* [`MrDragonFox/Elise`](https://huggingface.co/datasets/MrDragonFox/Elise) ——带有 **情绪标签** （例如 \<sigh>、\<laughs>）的增强版本，这些标签嵌入在转录文本中。这些尖括号内的标签表示表情（大笑、叹息等），会被 Orpheus 的 tokenizer 视为特殊 token
* [`Jinsaryko/Elise`](https://huggingface.co/datasets/Jinsaryko/Elise) ——不带特殊标签的基础版本。

该数据集按每条样本包含一段音频和一条转录文本进行组织。在 Hugging Face 上，这些数据集具有如下字段： `音频` （波形）， `text` （转录文本），以及一些元数据（说话人姓名、音高统计等）。我们需要提供给 Unsloth 的是一个音频-文本对的数据集。

{% hint style="success" %}
与其只关注语气、节奏和音高，不如优先确保你的数据集有完整标注并且经过正确规范化。
{% endhint %}

{% hint style="info" %}
对于某些模型，例如 **Sesame-CSM-1B**，你可能会注意到使用 speaker ID 0 时不同生成结果之间的声音会有变化，因为它是一个 **基础模型**——它没有固定的声音身份。Speaker ID token 主要有助于保持 **一次对话内的一致性**，而不是跨不同生成结果保持一致。

要获得一致的声音，请提供 **上下文示例**，例如几段参考音频片段或先前的话语。这有助于模型更可靠地模仿所需的声音。没有这些，即使使用相同的 speaker 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 个样本，大小约 328 MB）。\`dataset\` 中的每个项目都是一个字典，至少包含： `dataset` 包含至少以下内容：

* `"audio"`：音频片段（波形数组以及采样率等元数据），以及
* `"text"`：转录字符串

Orpheus 支持如下标签： `<laugh>`, `<chuckle>`, `<sigh>`, `<cough>`, `<sniffle>`, `<groan>`, `<yawn>`, `<gasp>`等等。例如： `"我太想你了 <laugh>！"`。这些标签用尖括号括起，会被模型视为特殊 token（它们与 [Orpheus 期望的标签](https://github.com/canopyai/Orpheus-TTS) 如 `<laugh>` 和 `<sigh>`一致）。在训练期间，模型会学会把这些标签与对应的音频模式关联起来。带标签的 Elise 数据集已经包含其中许多标签（例如，其卡片中列出的“laughs”有 336 次、“sighs”有 156 次等）。如果你的数据集缺少这类标签，但你希望加入它们，可以手动为包含这些表现的音频转录文本加注。

**选项 2：准备自定义数据集** ——如果你有自己的音频文件和转录文本：

* 将音频片段（WAV/FLAC 文件）整理到一个文件夹中。
* 创建一个 CSV 或 TSV 文件，其中包含文件路径和转录文本列。例如：

  ```
  filename,text
  0001.wav,你好！
  0002.wav,<sigh> 我非常累。
  ```
* 使用 `load_dataset("csv", data_files="mydata.csv", split="train")` 来加载它。你可能需要告诉数据集加载器如何处理音频路径。另一种方法是使用 `datasets.Audio` 特性按需加载音频数据：

  ```python
  from datasets import Audio
  dataset = load_dataset("csv", data_files="mydata.csv", split="train")
  dataset = dataset.cast_column("filename", Audio(sampling_rate=24000))
  ```

  然后 `dataset[i]["audio"]` 将包含音频数组。
* **确保转录文本已规范化** （不要包含 tokenizer 可能不认识的异常字符，除非使用了情绪标签）。同时确保所有音频具有一致的采样率（必要时重采样到模型期望的目标采样率，例如 Orpheus 使用 24kHz）。

总之，对于 **数据集准备**:

* 你需要一个 **（音频，文本）列表** 对。
* 使用 HF `datasets` 库来处理加载和可选预处理（如重采样）。
* 把任何 **特殊标签** 包含在你希望模型学习的文本中（确保它们采用 `<angle_brackets>` 格式，以便模型将其视为独立 token）。
* （可选）如果是多说话人场景，你可以在文本中加入 speaker ID token，或者使用单独的说话人嵌入方法，但这超出了本入门指南的范围（Elise 是单说话人）。

### 使用 Unsloth 微调 TTS

现在，让我们开始微调！我们将用 Python 代码来演示（你可以在 Jupyter notebook、Colab 等环境中运行）。

**步骤 1：加载模型和数据集**

在我们所有的 TTS notebooks 中，我们使用以下设置启用 LoRA（16 位）训练并禁用 QLoRA（4 位）训练： `load_in_4bit = False`。这样通常可以让模型更好地学习你的数据集，并获得更高的准确率。

```python
from unsloth import FastLanguageModel
import torch
dtype = None # 自动检测。Tesla T4、V100 使用 Float16，Ampere 及以上使用 Bfloat16
load_in_4bit = False # 使用 4bit 量化以减少内存占用。可设置为 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 准备输入。对于文本转语音，一种方法是以因果方式训练模型：将文本和音频 token ID 连接起来作为目标序列。不过，由于 Orpheus 是一个输出音频的 decoder-only LLM，我们可以将文本作为输入（上下文），并将音频 token id 作为标签。实际上，如果模型配置将其识别为文本转语音，Unsloth 的集成可能会自动完成这一点。如果不会，我们可以这样做：

```python
# 对文本转录内容进行分词
def preprocess_function(example):
    # 对文本进行分词（保留诸如 <laugh> 之类的特殊 token）
    tokens = tokenizer(example["text"], return_tensors="pt")
    # 展平成 token ID 列表
    input_ids = tokens["input_ids"].squeeze(0)
    # 模型会在这些文本 token 之后生成音频 token。
    # 在训练时，我们可以将 labels 设为 input_ids（这样它会学习预测下一个 token）。
    # 但这样只覆盖了文本 token 预测下一个文本 token（它可能是音频 token 或结束符）。
    # 更复杂的方法：附加一个表示音频开始的特殊 token，然后让模型生成其余部分。
    # 为简单起见，使用相同的输入作为标签（模型会学习根据自身输出序列）。
    return {"input_ids": input_ids, "labels": input_ids}

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

{% hint style="info" %}
上面只是一个简化示例。实际上，要正确地微调 Orpheus，你需要把 *音频 token 作为训练标签的一部分*。Orpheus 的预训练大概率涉及将音频转换为离散 token（通过音频 codec），并训练模型根据前面的文本预测这些 token。对于新语音数据的微调，你同样需要为每个片段获取音频 token（使用 Orpheus 的音频 codec）。Orpheus 的 GitHub 提供了一个数据处理脚本——它会把音频编码为一串 `<custom_token_x>` token。
{% endhint %}

不过， **Unsloth 可能会将这一过程抽象掉**：如果该模型是一个带有相关 processor 的 FastModel，而且它知道如何处理音频，那么它可能会自动把数据集中的音频编码为 token。如果不会，你就必须手动将每段音频编码为 token ID（使用 Orpheus 的 codebook）。这一步超出了本指南的进阶范围，但请记住，仅使用文本 token 并不能让模型学到真实音频——它需要与音频模式相匹配。

我们假设 Unsloth 提供了一种直接传入音频的方法（例如，通过设置 `processor` 并传入音频数组）。如果 Unsloth 目前还不支持自动音频分词，你可能需要使用 Orpheus 仓库中的 `encode_audio` 函数来获取音频的 token 序列，然后将其用作标签。（数据集条目确实包含 `音素` 以及一些声学特征，这表明存在一条处理管线。）

**步骤 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, # 将此设置为一次完整训练。
        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`。如果使用多 GPU 设置，\`per\_device\_train\_batch\_size > 1\` 可能会导致错误；为避免问题，请确保 \`CUDA\_VISIBLE\_DEVICES\` 设为单个 GPU（例如，\`CUDA\_VISIBLE\_DEVICES=0\`）。按需调整。

**步骤 4：开始微调**

这将启动训练循环。你应该每 50 步看到一次 loss 日志（由 `logging_steps`设置）。训练可能会花一些时间，具体取决于 GPU——例如，在 Colab T4 GPU 上，3 小时数据跑几个 epoch 可能需要 1-2 小时。Unsloth 的优化会让它比标准 HF 训练更快。

**步骤 5：保存微调后的模型**

训练完成后（或者当你觉得已经足够时中途停止），保存模型。这里保存的 ONLY 是 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，如果基础模型没有完全微调，可能只会保存适配器权重）。如果你使用了 `--push_model` 在 CLI 中，或者使用 `trainer.push_to_hub()`，你可以直接把它上传到 Hugging Face Hub。

现在你应该已经在目录中拥有一个微调后的 TTS 模型。下一步是测试它，如果支持的话，你可以用 llama.cpp 将其转换为 GGUF 文件。

### 微调语音模型 vs. 零样本语音克隆

有人说，只需 30 秒音频就能用 XTTS 之类的模型克隆声音——无需训练。严格来说这是真的，但这并没有抓住重点。

零样本语音克隆在 Orpheus 和 CSM 等模型中也可用，它只是一种近似。它能捕捉说话者声音的整体 **语气和音色** ，但无法重现完整的表达范围。你会失去诸如语速、措辞、口头习惯，以及韵律细微差别等细节——这些才赋予声音以 **个性与独特性**.

如果你只是想要一种不同的声音，并且能接受相同的表达模式，零样本通常就够用了。但语音仍然会遵循 **模型的风格**，而不是说话者本人的风格。

对于任何更个性化或更具表现力的需求，你都需要使用 LoRA 之类的方法进行训练，才能真正捕捉某个人的说话方式。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://unsloth.ai/docs/zh/ji-chu-zhi-shi/text-to-speech-tts-fine-tuning.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
