🔊文本转语音(TTS)微调指南

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

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

使用 Unsloth,你可以微调 任何 TTS 模型(transformers 兼容),速度比采用 Flash Attention 2 的其他实现快 1.5 倍,且内存占用减少 50%。

Unsloth 支持任何 transformers 兼容的 TTS 模型。 即使我们暂时还没有对应的 notebook 或上传版本,它也仍然受支持,例如:你可以尝试微调 Dia-TTS 或 Moshi。

零样本克隆能捕捉语气,但会缺少节奏和表达,往往听起来机械且不自然。微调则能实现更准确、更逼真的声音复现。 在此阅读更多.

微调 Notebooks:

我们还把 TTS 模型(原始版和量化版)上传到了我们的 Hugging Face 页面.

选择并加载 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 位加载:

运行此代码时,Unsloth 会下载模型权重;如果你偏好 8 位,可以使用 load_in_8bit = True,或者若要完整微调则设置 full_finetuning = True (确保你有足够的 VRAM)。你也可以将模型名称替换为其他 TTS 模型。

注意: Orpheus 的 tokenizer 已经包含用于音频输出的特殊 token(后面会详细介绍)。你 需要单独的 vocoder——Orpheus 会直接输出音频 token,这些 token 可以解码成波形。

准备你的数据集

至少,TTS 微调数据集应包含 音频片段及其对应的转录文本 (文本)。我们使用 Elise 数据集 ,它是一个约 3 小时的单说话人英语语音语料。它有两个变体:

  • MrDragonFox/Elise ——一个增强版,其中将 情绪标签 (如 <sigh>、<laughs>)嵌入到转录中。这些尖括号标签表示表情(大笑、叹息等),并会被 Orpheus 的 tokenizer 视为特殊 token

  • Jinsaryko/Elise ——基础版本,转录中不含特殊标签。

该数据集按每条样本对应一个音频和一段转录来组织。在 Hugging Face 上,这些数据集通常包含如下字段: audio (波形), text (转录文本),以及一些元数据(说话人名称、音高统计等)。我们需要向 Unsloth 提供一个音频-文本对的数据集。

对于某些模型,例如 Sesame-CSM-1B,你可能会注意到使用说话人 ID 0 时,不同生成结果之间存在声音变化,因为它是一个 基础模型——它没有固定的声音身份。说话人 ID token 主要有助于保持 一次对话中的一致性,而不是在不同生成结果之间保持一致。

要获得一致的声音,请提供 上下文示例,例如几个参考音频片段或之前的语句。这有助于模型更可靠地模仿目标声音。没有这些,哪怕使用相同的说话人 ID,出现变化也是正常的。

选项 1:使用 Hugging Face Datasets 库 ——我们可以使用 Hugging Face 的 datasets 库加载 Elise 数据集:

这会下载该数据集(约 1.2k 个样本,大小约 328 MB)。其中的每一项 数据集 都是一个字典,至少包含:

  • "audio":音频片段(波形数组及采样率等元数据),以及

  • "text":转录字符串

Orpheus 支持如下标签: <laugh>, <chuckle>, <sigh>, <cough>, <sniffle>, <groan>, <yawn>, <gasp>等等。例如: "I missed you <laugh> so much!"。这些标签用尖括号括起来,模型会将它们视为特殊 token(它们与 Orpheus 期望的标签<laugh><sigh>相匹配。在训练期间,模型会学会把这些标签与相应的音频模式关联起来。带标签的 Elise 数据集本身已经包含了许多这类标签(例如,在数据卡中列出了 336 处 “laughs”、156 处 “sighs”等)。如果你的数据集缺少这类标签,但你想加入它们,可以在音频中包含这些表达时手动标注转录文本。

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

  • 将音频片段(WAV/FLAC 文件)整理到一个文件夹中。

  • 创建一个 CSV 或 TSV 文件,包含文件路径和转录文本列。例如:

  • 使用 load_dataset("csv", data_files="mydata.csv", split="train") 来加载它。你可能需要告诉数据集加载器如何处理音频路径。另一种方法是使用 datasets.Audio 特性按需加载音频数据:

    然后 dataset[i]["audio"] 将包含音频数组。

  • 确保转录文本已规范化 (不要使用 tokenizer 可能不认识的异常字符,除非是使用了情绪标签)。还要确保所有音频的采样率一致(如有必要,请重采样到模型期望的目标采样率,例如 Orpheus 的 24kHz)。

总之,对于 数据集准备:

  • 你需要一个 (音频,文本)列表 对。

  • 使用 HF datasets 库来处理加载以及可选的预处理(如重采样)。

  • 在文本中包含任何 特殊标签 ,即你希望模型学习的内容(确保它们采用 <angle_brackets> 格式,这样模型会把它们当作独立 token)。

  • (可选)如果是多说话人场景,你可以在文本中加入说话人 ID token,或使用单独的说话人嵌入方案,但这超出了本基础指南的范围(Elise 是单说话人)。

使用 Unsloth 微调 TTS

现在,让我们开始微调!我们会用 Python 代码演示(你可以在 Jupyter notebook、Colab 等环境中运行)。

步骤 1:加载模型和数据集

在我们所有的 TTS notebooks 中,我们启用 LoRA(16 位)训练,并禁用 QLoRA(4 位)训练,方式如下: load_in_4bit = False。这样通常能让模型更好地学习你的数据集,并获得更高的准确率。

如果内存非常有限,或者数据集很大,你可以流式加载或分块加载。这里,3 小时的音频很容易装入 RAM。如果使用你自己的数据集 CSV,也可以类似地加载。

步骤 2:进阶 - 为训练预处理数据(可选)

我们需要为 Trainer 准备输入。对于文本转语音,一种方法是以因果方式训练模型:将文本和音频 token ID 连接为目标序列。不过,由于 Orpheus 是一个只解码器的 LLM,会输出音频,我们可以把文本作为输入(上下文),把音频 token id 作为标签。实际上,如果模型配置将其识别为文本转语音,Unsloth 的集成可能会自动完成这一步。如果没有,我们可以这样做:

上述方法只是简化版。实际上,要正确微调 Orpheus,你需要把 音频 token 作为训练标签的一部分。Orpheus 的预训练很可能是把音频转换为离散 token(通过音频 codec),并训练模型根据前面的文本预测这些 token。对于新的声音数据微调,你同样需要为每个音频片段获取音频 token(使用 Orpheus 的音频 codec)。Orpheus 的 GitHub 提供了一个数据处理脚本——它会把音频编码为 <custom_token_x> token 序列。

不过, Unsloth 可能会把这些细节抽象掉:如果模型是一个 FastModel,并且关联了一个知道如何处理音频的 processor,那么它可能会自动把数据集中的音频编码为 token。如果没有,你就必须手动把每个音频片段编码成 token ID(使用 Orpheus 的 codebook)。这一步超出了本指南的基础范围,但请记住,单纯使用文本 token 并不能教会模型真实的音频——它必须与音频模式相匹配。

我们假设 Unsloth 提供了一种直接输入音频的方法(例如,通过设置 processor 并传入音频数组)。如果 Unsloth 还不支持自动音频分词,你可能需要使用 Orpheus 仓库中的 encode_audio 函数获取音频的 token 序列,然后把它们作为标签。(数据集条目确实包含 phonemes 以及一些声学特征,这表明它有一条处理流程。)

步骤 3:设置训练参数和 Trainer

我们做 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:保存微调后的模型

训练完成后(或者当你觉得已经足够时中途停止),保存模型。这里保存的只是 LoRA 适配器,而不是完整模型。若要保存为 16bit 或 GGUF,请继续向下看!

这会保存模型权重(对于 LoRA,如果基础模型没有完全微调,可能只保存适配器权重)。如果你在 --push_model 中使用 CLI 或 trainer.push_to_hub(),你可以直接把它上传到 Hugging Face Hub。

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

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

人们常说,只需 30 秒音频,就能用像 XTTS 这样的模型克隆声音——无需训练。从技术上讲,这是真的,但这并没有抓住重点。

零样本语音克隆,在 Orpheus 和 CSM 等模型中也可用,本质上是一种近似。它能捕捉说话人声音的整体 语气和音色 ,但无法完整再现其表达范围。你会丢失诸如说话速度、措辞、发声习惯,以及韵律细微差别等细节——这些才构成了一种声音的 个性和独特性.

如果你只想要一个不同的声音,并且可以接受相同的表达方式,那么零样本通常已经足够了。但语音仍然会遵循 模型的风格,而不是说话者的风格。

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

最后更新于

这有帮助吗?