首页
/ 在 TTS 中使用 Tortoise:高表现力语音合成与声音克隆的模型结构与推理配置全解析

在 TTS 中使用 Tortoise:高表现力语音合成与声音克隆的模型结构与推理配置全解析

2026-09-08 11:40:21作者:齐添朝

Tortoise 是 🐸TTS 仓库中一个以"表现力"和"声音克隆"见长的 TTS 系统,其核心是一条由 GPT 式自回归声学模型、扩散模型与 UnivNet 声码器串联的三级流水线。本文以 docs/source/models/tortoise.md 为主线,结合仓库内 模型实现tortoise 专用层实现 的源码证据,讲解它的整体工作原理、三种调用方式(Python 模型级、🐸TTS API、命令行)、TortoiseConfig / TortoiseArgs 全部配置参数,以及预设(preset)与采样参数在推理时的真实作用。读完你既能照抄示例完成声音克隆推理,也能理解每个旋钮在源码流水线中落在哪一步。

一、Tortoise 是什么:一段"绕远路"但表现力极强的合成流水线

与 VITS 这类一步到位的并行(parallel)TTS 不同,Tortoise 采用的是从源码结构上清晰可见的三级级联设计(模型在 TTS/tts/models/tortoise.py 中按此结构初始化对应子网络):

  1. GPT 式自回归声学模型(UnifiedVoice):把输入文本与参考说话人音频的 condition 一起,自回归地"逐 token"预测离散化的声学 token(TTS/tts/layers/tortoise/autoregressive.py 中的 UnifiedVoice,其推理封装 GPT2InferenceModel 基于 Hugging Face GPT2PreTrainedModel 实现,见同一文件的 GPT2InferenceModel 定义);
  2. 扩散模型(DiffusionTts):将上一步得到的离散声学 token + 文本/说话人嵌入条件,通过迭代去噪生成梅尔频谱帧(TTS/tts/layers/tortoise/diffusion.pydiffusion_decoder.py);
  3. UnivNet 声码器:把梅尔频谱还原为最终波形(TTS/tts/layers/tortoise/vocoder.py 中的 UnivNet,配置默认指向 VocConf.Univnet)。

除此之外,推理流水线中还有一个起"裁判"作用的 CLVP(CLIP 变体,条件潜在变量评分器)TTS/tts/layers/tortoise/clvp.py),用于对自回归模型产生的多个候选样本按与文本的匹配程度打分排序。原文档对它的定位很直白:

Tortoise is a very expressive TTS system with impressive voice cloning capabilities... The important downside is that Tortoise is very slow compared to the parallel TTS models like VITS.

也就是说,表现力与克隆能力是它的卖点,推理速度则是其明显短板。需要澄清一点:这里"慢"是相对 VITS 等并行模型而言的架构性代价——自回归解码逐 token 生成 + 扩散模型多步去噪 + 一次合成数十个候选再筛选,天然就无法做到并行 TTS 那样的低延迟。仓库实现同样保持了这一约束,且只实现了推理路径(forward()train_step()eval_step() 均直接 raise NotImplementedError("Tortoise Training is not implemented")源码见),因此本文所述均针对推理/克隆场景。

二、快速上手:三种加载 Tortoise 的方式

原文档给出了三种等价用法,下面按当前仓库源码的实际签名整理(注意:仓库当前版本的 synthesize() 参数名是 voice_dirs,与早期文档中的 extra_voice_dirs 不同,详见 TTS/tts/models/tortoise.py 的 synthesize 定义)。

1. Python 模型级调用

from TTS.tts.configs.tortoise_config import TortoiseConfig
from TTS.tts.models.tortoise import Tortoise

config = TortoiseConfig()
model = Tortoise.init_from_config(config)
model.load_checkpoint(config, checkpoint_dir="paths/to/models_dir/", eval=True)

# 随机音色合成
output_dict = model.synthesize(text, config, speaker_id="random", voice_dirs=None, **kwargs)

# 用某个说话人的参考音频克隆音色
output_dict = model.synthesize(text, config, speaker_id="speaker_n", voice_dirs="path/to/speaker_n/", **kwargs)

这里 load_checkpoint() 有它自己的特殊性:Tortoise 不是单文件权重,而是一个多模型权重集合。从 load_checkpoint 实现 可以看到它会在 checkpoint_dir 下按固定文件名查找以下文件:

文件名 加载到的子网络 说明
autoregressive.pth self.autoregressive GPT 式自回归模型(以 strict=False 加载以兼容 Transformers 版本差异)
diffusion_decoder.pth self.diffusion 扩散解码器
clvp2.pth self.clvp 候选音频打分器
vocoder.pth self.vocoder UnivNet 声码器
mel_norms.pth self.mel_norm_path 梅尔归一化统计量(format_conditioning 会用到,见 torchmel 转换 相关逻辑)

若设置了 config.model_dir 也可以不传 checkpoint_direval=True 时会额外调用 autoregressive.post_init_gpt2_config(self.args.kv_cache) 并让所有子网络进入 eval() 模式。用于"随机音色"的 RLG 权重(rlg_auto.pthrlg_diffuser.pth)则是在 get_random_conditioning_latents 中从 models_dir 惰性加载的。

2. 通过 🐸TTS 高级 API 调用

from TTS.api import TTS
tts = TTS("tts_models/en/multi-dataset/tortoise-v2")

# 克隆 `lj` 声音(来自你本地 voices 目录里的 lj 文件夹),并用自定义推理设置覆盖默认值
tts.tts_to_file(text="Hello, my name is Manmay , how are you?",
                file_path="output.wav",
                voice_dir="path/to/tortoise/voices/dir/",
                speaker="lj",
                num_autoregressive_samples=1,
                diffusion_iterations=10)

# 用预设(preset)合成同一音色
tts.tts_to_file(text="Hello, my name is Manmay , how are you?",
                file_path="output.wav",
                voice_dir="path/to/tortoise/voices/dir/",
                speaker="lj",
                preset="ultra_fast")

# 完全随机音色生成
tts.tts_to_file(text="Hello, my name is Manmay , how are you?",
                file_path="output.wav")

这套高层 API 在 TTS/api.py 中定义(tts_to_file 见该文件 第 290 行),并由 TTS/utils/synthesizer.pySynthesizer 负责把 voice_dir 等参数转发到模型层的 synthesize(..., voice_dirs=...) 调用链(见 synthesizer 中 voice_dir 处理模型 synthesize 调用)。注意 voice_dir 指向的是存放多个音色子目录的父目录,而 speaker="lj" 会从其中找到 lj/ 子目录并读取里面的参考音频。

3. 命令行调用

# 克隆 `lj` 音色
tts --model_name tts_models/en/multi-dataset/tortoise-v2 \
--text "This is an example." \
--out_path "output.wav" \
--voice_dir path/to/tortoise/voices/dir/ \
--speaker_idx "lj" \
--progress_bar True

# 随机音色生成
tts --model_name tts_models/en/multi-dataset/tortoise-v2 \
--text "This is an example." \
--out_path "output.wav" \
--progress_bar True

无论走哪条路径,speaker_id="random"(或不指定音色)都等价于"随机声音生成"——此时仓库实现并不加载任何参考音频,而是由 RandomLatentConverter 从纯噪声向量生成一对随机的 conditioning latents(见 get_random_conditioning_latents,对应模型 TTS/tts/layers/tortoise/random_latent_generator.py)。

三、TortoiseConfig:推理设置与采样策略配置

TortoiseConfig 继承自 TTS/tts/configs/shared_configs.pyBaseTTSConfig,全部字段和默认值如下(源码第 8-87 行):

参数 类型 默认值 含义
model str "tortoise" 模型名,用于 setup_model 按名定位实现模块(见 TTS/tts/models/init.py),无特殊需求勿改
model_args TortoiseArgs TortoiseArgs() 模型结构参数,见下一节
audio TortoiseAudioConfig TortoiseAudioConfig() 采样率配置:sample_rate=22050(自回归/condition 用)、diffusion_sample_rate=24000output_sample_rate=24000
model_dir str None 存放全部 Tortoise 模型权重的目录,见上文 load_checkpoint
temperature float 0.2 自回归模型的采样温度,越大越"有创造性",但稳定性下降
length_penalty float 1.0 束搜索中的长度惩罚。它作为序列长度的指数参与对分数的除法:>0 鼓励更长输出,<0 鼓励更短输出
repetition_penalty float 2.0 重复惩罚参数,1.0 表示不惩罚
top_p float 0.8 核采样(nucleus sampling)概率阈值:只保留累计概率达 top_p 的最小 token 集合参与采样
cond_free_k float 2.0 无分类器引导(conditioning-free diffusion)的平衡旋钮,取值 [0,∞)。公式:output = cond_present_output*(cond_free_k+1) - cond_absent_output*cond_free_kcond_free_k 越大,输出越受"无条件信号"主导
diffusion_temperature float 1.0 注入扩散模型的噪声方差,[0,1]0 相当于取扩散网络"均值预测",听感会平淡、发糊
num_autoregressive_samples int 16 从自回归模型采样的候选条数,随后全部交由 CLVP 打分筛选。因为 Tortoise 是概率模型,样本越多越可能出"惊喜"
diffusion_iterations int 30 扩散步数,[0,4000]。步数越多理论上越精细,但源码注释指出超过 250 步基本看不出提升
sampler str "ddim" 扩散采样器:ddimdpm++2m

需要理解的一点是:这些 config 字段如何进入实际采样。在 inference_with_config 中,temperature / length_penalty / repetition_penalty / top_p / cond_free_k / diffusion_temperature / sampler 会先被收集成一个 settings 字典,若调用方传了 preset 则用预设覆盖,随后再被用户显式传入的 kwargs 覆盖(settings.update(kwargs)),最终展开成 inference(**settings) 调用。因此优先级是 显式 kwargs > preset > config 默认值

四、TortoiseArgs:三个子网络的完整结构参数

原文档通过 autoclass:: TTS.tts.models.tortoise.TortoiseArgs 自动生成 API 文档,对应实现位于 TortoiseArgs 定义。它是一组纯数据类字段,被 Tortoise.__init__ 逐项拆解去构造三个子网络。以下按源码注释中的分组整理全部默认值。

4.1 通用/推理相关参数

参数 默认值 说明
autoregressive_batch_size 1 自回归批次大小;为 None 时由 pick_best_batch_size_for_gpu() 按显存自动挑选(空闲 >14GB→16,>10GB→8,>7GB→4,否则 1,见 源码
enable_redaction False 是否开启 Wav2Vec 对齐的自动消音(敏感词抹除),开启时会加载 Wav2VecAlignmentTTS/tts/layers/tortoise/wav2vec_alignment.py
high_vram False 高显存模式:为 True 时四个子网络常驻 GPU;否则通过 temporary_cuda 上下文管理器用完即搬回 CPU,显存换延迟
kv_cache True 自回归推理是否启用 KV Cache 加速
ar_checkpoint / clvp_checkpoint / diff_checkpoint None 各子网络独立的 checkpoint 路径(实际加载仍由 load_checkpoint 统一完成)
num_chars 255 文本 token 数上限相关常量
vocoder VocConf.Univnet 声码器类型,来自 TTS/tts/layers/tortoise/vocoder.py 中的 VocConf/VocType 枚举

4.2 UnifiedVoice(自回归模型)结构参数

参数 默认值 说明
ar_max_mel_tokens 604 自回归最大梅尔 token 数
ar_max_text_tokens 402 自回归最大文本 token 数
ar_max_conditioning_inputs 2 最大 condition 输入条数
ar_layers 30 Transformer 层数
ar_model_dim 1024 模型维度
ar_heads 16 注意力头数
ar_number_text_tokens 255 文本词表大小
ar_start_text_token 255 文本起始 token id
ar_checkpointing False 是否启用激活 checkpointing(省显存)
ar_train_solo_embeddings False 是否单独训练 embedding

4.3 DiffusionTts(扩散解码器)结构参数

参数 默认值 说明
diff_model_channels 1024 UNet 风格主干通道数
diff_num_layers 10 层数
diff_in_channels 100 输入通道(对应 100 维梅尔)
diff_out_channels 200 输出通道
diff_in_latent_channels 1024 潜在 condition 通道
diff_in_tokens 8193 音频 token 词表大小
diff_dropout 0 dropout 比例
diff_use_fp16 False 是否 fp16
diff_num_heads 16 注意力头数
diff_layer_drop 0 层 dropout
diff_unconditioned_percentage 0 无条件输入比例(与 cond-free 训练相关)

4.4 CLVP(候选评分器)结构参数

参数 默认值 说明
clvp_dim_text 768 文本编码维度
clvp_dim_speech 768 语音编码维度
clvp_dim_latent 768 共享潜在空间维度
clvp_num_text_tokens 256 文本词表大小
clvp_text_enc_depth 20 文本编码器深度
clvp_text_seq_len 350 文本最大序列长度
clvp_text_heads 12 文本编码器注意力头数
clvp_num_speech_tokens 8192 语音 token 词表大小
clvp_speech_enc_depth 20 语音编码器深度
clvp_speech_heads 12 语音编码器注意力头数
clvp_speech_seq_len 430 语音最大序列长度
clvp_use_xformers True 是否使用 xformers 加速(TTS/tts/layers/tortoise/xtransformers.py
duration_const 102400 扩散 condition 采样时长常量(102400 / 24000Hz ≈ 4.27s)

这些结构参数在 Tortoise.__init__TTS/tts/models/tortoise.py#L322-L394)中被原样传递到 UnifiedVoiceDiffusionTtsCLVP 和声码器的构造函数中。若你自行加载第三方权重,务必保证 TortoiseArgs 与权重训练时的结构一致,否则 load_state_dict 会失败。

五、源码级理解:一次 Tortoise 推理到底发生了什么

调用 synthesize()inference_with_config()inference() 后,主干逻辑在 inference 实现 中。可把它拆成四个阶段:

阶段 1:文本与 condition 编码。 文本先经 VoiceBpeTokenizer 分词(BPE,词表文件位于 TTS/tts/utils/assets/tortoise/tokenizer.json,内部先用英文清洗器 english_cleaners 预处理、空格替换为 [SPACE],见 TTS/tts/layers/tortoise/tokenizer.py),得到 token id 序列;源码强制要求其长度 < 400,否则抛错提示把文本分段。若提供了参考音频,则调用 get_conditioning_latents 提取一对(自回归条件、扩散条件)latent——参考音频会被重采样到 22.05kHz 和 24kHz 两个版本(见 audio_utils.load_required_audio),前者用于自回归条件(截取 cond_length=132300,即 6 秒),后者切成约 4.27s(duration_const)片段算梅尔供扩散模型用。

阶段 2:自回归候选生成与 CLVP 筛选。num_autoregressive_samples 指定的条数范围内,用 Hugging Face generate API 做自回归采样(top_p/temperature/length_penalty/repetition_penalty/max_mel_tokens 全部作用于这一步)。每个候选要先经过 fix_autoregressive_output 修补:把 stop token 替换为静音 token(83),再在尾部补上 45, 45, 248 三个特判 token——注释特别说明不做这一步会导致输出末尾出现生硬的 "BLAH" 类音爆。然后所有候选经 CLVP 对(文本, 音频 token)打分,torch.topk 取出最好的 k 条(k=1 只保留一条,见 inference)。

阶段 3:扩散去噪。 对入围候选,用自回归模型的最后一层隐藏态(而不是离散 token)作为条件,送入 DiffusionTts。采样器由 load_discrete_vocoder_diffuser 构造,它本质是一个 SpacedDiffusion(训练 4000 步的 beta 线性调度 + space_timesteps 采样到目标步数),支持 ddim / dpm++2m 两种采样器,是否启用 cond-free 由 cond_free / cond_free_k 控制。去噪输出的 100 维梅尔经 denormalize_tacotron_mel 还原(归一化常量见 audio_utils.py)。

阶段 4:声码与可选消音。 梅尔交给 UnivNet 声码器转波形;若 enable_redaction=True,再经 Wav2VecAlignment.redact() 依据文本对齐信息抹除敏感词。最终输出采样率为 24kHz。

同时也要注意到该实现为低显存/非高 VRAM 场景做了大量工程化:四个大模型默认都留在 CPU,只有推理到哪一步才通过 temporary_cuda源码)临时搬到 GPU,用毕再搬回。这就是 high_vram 参数存在的原因——它直接决定速度与显存的取舍。

六、声音克隆实操:voice 目录、参考音频与 latent

6.1 参考音频的组织方式

load_voiceTTS/tts/layers/tortoise/audio_utils.py#L117-L130)对 voice_dir(即 extra_voice_dirs)下的目录结构有固定约定。每个音色是一个子目录,get_voices 会扫描其下所有 .wav.mp3.pth 文件(get_voices),典型结构:

path/to/voices/dir/
└── lj/                     # 音色名 = 文件夹名
    ├── clip_1.wav
    └── clip_2.wav

规则如下:

  • 若该音色目录里只有一个 .pth 文件,它会被直接视为预计算好的 conditioning latents(load_voice 返回 (None, torch.load(path)),此时跳过重计算,速度最快);
  • 否则目录内所有 .wav/.mp3 都会被当作参考音频加载,load_required_audio 会把每个音频重采样成 (22.05kHz, 24kHz) 的成对张量交给模型现场提取 latents;
  • 传入多个参考音频时,自回归条件在样本间做平均,扩散条件则按 latent_averaging_mode(0/1/2,见 get_conditioning_latents 参数说明)决定是截取约 4.27s 平均、还是全片段分块平均、抑或逐样本平均后叠加。

仓库内自带的 TTS/tts/utils/assets/tortoise/ 目录目前只有 tokenizer.json(BPE 词表),并不包含示例参考音频。原文档示例中克隆 lj 声音指的是在你本地自备的 voices 父目录中放置 lj/ 子目录与参考 wav,而不是仓库内置资源。

6.2 随机音色(不提供任何参考音频)

speaker_id="random" 时,load_voice 返回 (None, None),代码转而走 get_random_conditioning_latents:惰性加载 RandomLatentConverter(自回归侧 1024 维、扩散侧 2048 维),对常数张量 [0.0] 做一次前向得到一对随机 latents。这也是"随机声音生成"真正的实现来源——每个新 seed 都是一副新嗓子

七、推理预设(preset):速度与质量的旋钮组合

原文档示例中 preset="ultra_fast" 的取值不是魔法字符串,而是硬编码在 inference_with_config 中的一组现成组合。下表汇总全部预设及其覆盖的参数:

preset num_autoregressive_samples diffusion_iterations sampler / 其他
single_sample 8 10 ddim
ultra_fast 16 10 ddim
ultra_fast_old 16 30 cond_free=False
very_fast 32 30 dpm++2m
fast 5 50 ddim
fast_old 96 80 (沿用默认 ddim)
standard 5 200 (默认采样器)
high_quality 256 400 (默认采样器)

直观规律:候选样本数越多、扩散步数越多,质量上限越高但耗时越长——从 ultra_fast 的 16 样本/10 步,到 high_quality 的 256 样本/400 步,正是"表现力上限"与"缓慢"这枚硬币的两面。预设仅覆盖采样数量与步数类设置,而 temperature 等仍来自 config;若同时显式传参(如 API 示例里的 num_autoregressive_samples=1, diffusion_iterations=10),显式参数会覆盖预设,因为代码在 settings.update(presets[...]) 之后又执行了 settings.update(kwargs)

inference() 本身还暴露了一组与 config 默认值略有差异的兜底默认值(如 temperature=0.8diffusion_iterations=100max_mel_tokens=500half=True),但走 synthesize() 正常入口时会被 config 值覆盖,无需担心。需要额外说明的两个参数:

  • max_mel_tokens:限制输出长度,单位约 1/20 秒,合理区间 (0,600];
  • k:最终返回几条候选(默认为 1,即只保留 CLVP 评分最高的一条);
  • use_deterministic_seed:传入整数可复现同一条音频(deterministic_state 会同时种下 torch 与 random,源码)。

八、实践建议与限制清单

  • 适用场景:需要高自然度、强情感表现力或 few-shot 声音克隆的场景;对实时性有硬要求的场景应选择 VITS 等并行模型。
  • 显存策略:默认 high_vram=False 下 CPU/GPU 反复搬运较慢;显存充足(源码按 >7GB/>10GB/>14GB 分级)时建议设 high_vram=True 并把 autoregressive_batch_size 交给自动探测。
  • 文本长度:单次推理文本 token 数必须 < 400,超长文本需要自行切分(这也与 fix_autoregressive_output 中"语音太长找不到 stop token"的警告一致)。
  • 音质细节:关闭 cond-free(cond_free=False)会明显损失真实感,不建议;diffusion_temperature=0 会让声音平淡发糊。
  • 可复现性:固定 use_deterministic_seed 可复现结果;synthesize() 返回字典中的 deterministic_seed 记录了本次实际使用的种子。
  • 训练支持:该模型的 forward/训练接口均未实现(仅推理),若需要训练请关注 XTTS 等其他路径。

九、参考资料与致谢

本模型的 🐸TTS 移植工作由 @manmay-nakhashi 完成。Tortoise 本身的设计思路来自社区公开研究:原始 Tortoise 实现及其加速版、UnivNet 声码器(基于位置可变卷积的 GAN 声码器)、Latent Diffusion(以潜在空间为对象的扩散建模)以及 DALL-E 中的对比学习方法(CLVP 即受其启发,代码注释也标注源自 DALLE-pytorch,见 TTS/tts/layers/tortoise/clvp.py)。如需深入,建议按以下顺序阅读仓库源码:先通读 Tortoise 模型入口,再分别研究 autoregressive.pydiffusion.pydiffusion_decoder.pyclvp.pyvocoder.py,即可把本文描述的每一环对应到具体实现。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389