mlx-audio 中的 Confucius4-TTS:基于 MLX 的多语言零样本语音克隆全解
【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apple's MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audio
Confucius4-TTS 是网易有道开源的多语言、跨语言零样本语音克隆(zero-shot voice cloning)TTS 模型,mlx-audio 将其完整移植到 Apple 的 MLX 框架上,推理路径完全去除 torch/transformers 依赖。读完本文,你将掌握 Confucius4 在 mlx-audio 中的四阶段管线结构(w2v-bert 语义特征 + CAMPPlus 说话人嵌入 → T2S GPT-2 语义建模 → S2A 条件流匹配 → BigVGAN v2 声码器)、generate()全部采样参数、fp32/int8 两种精度模型的用法,以及如何用仓库自带脚本把原始 PyTorch 权重量化转换为 MLX 格式。
一、整体架构:四阶段级联管线
Confucius4 的推理链路在 confucius4.py 的模块 docstring 中被明确概括为:
w2v-bert semantic features + CAMPPlus speaker embedding → T2S (GPT-2) → S2A conditional flow-matching (DiT + WaveNet) → BigVGAN v2 vocoder
对应到源码实现,四个组件分别在独立文件中:
| 阶段 | 作用 | 实现文件 | 权重文件 |
|---|---|---|---|
| w2v-bert-2.0 Conformer 编码器 | 从 160 维 fbank 提取第 17 层隐状态作为语义条件 | w2vbert.py | w2vbert_mlx.safetensors |
| CAMPPlus 说话人编码器 | 从参考音频提取 192 维说话人向量 | 复用 xvector.py 的CAMPPlus | campplus.safetensors |
| T2S(GPT-2) | 文本 + 语义条件自回归生成语义 token 序列 | t2s.py | t2s_model.safetensors |
| T2S 前缀编码器 | 文本投影 + ECAPA-TDNN 说话人编码器 | prefix.py | 同t2s_model.safetensors |
| S2A 流匹配估计器 | DiT(13 层、U-Net 跳跃连接)+ WaveNet,条件流匹配求解 mel | s2a.py | s2a_mlx.safetensors |
| BigVGAN v2 声码器 | 80 维 mel → 22050 Hz 波形,256× 上采样 | vocoder.py | bigvgan_mlx.safetensors |
从源码结构看几个值得注意的设计细节:
- w2v-bert 只保留前 17 层:w2vbert.py 中
NLAYERS = 17,hidden17()方法只跑 feature_projection 加 17 个 Conformer 层(ffn1 → 带 relative-key 的自注意力 → 因果 depthwise conv_module → ffn2 → final LayerNorm),与 confucius4.py 中调用self.w2v.hidden17(feats)的接口一一对应。每层注意力的相对位置键实现为mx.clip(r - l, -64, 8) + 64的距离嵌入查表(w2vbert.py)。 - T2S 是标准 GPT-2 结构:24 层、20 头、hidden 1280(t2s.py),词表用
BOS=8192 / EOS=8193作为语义 token 流的首尾标记,语义 embedding 之上叠加独立学习的 semantic position embedding。 - S2A 使用欧拉法求解流匹配 ODE:
solve_euler()中条件/无条件各拼一条 batch,按d = (1+cfg)*cond_d - cfg*uncond_d做 CFG(s2a.py),每步之后会把参考区间T_ref的 mel 重新置零,保证只有目标段被生成;mel[:, :, T_ref:]的切片在 confucius4.py 中完成,即参考前缀只作条件、不输出。 - 长度由 T2S 决定:S2A 的
build_mu()内部长度调节器按out_len = T * 1.72把语义 token 序列插值展开为 mel 帧序列(s2a.py),所以整段音频的时长在语义生成阶段就已确定。
二、快速上手:零样本语音克隆
使用入口是mlx_audio.tts.utils.load_model。按模块 README 的标准用法:
from mlx_audio.tts.utils import load_model model = load_model("mlx-community/Confucius4-TTS-mlx") results = list(model.generate( text="Xin chào, đây là giọng nói được nhân bản.", ref_audio="reference.wav", # 任意采样率;内部解码并重采样到 16 kHz lang="vi", )) audio = results[0].audio # mx.array sample_rate = results[0].sample_rate # 22050生成结果是GenerationResult的生成器(generate是yield单条结果),用仓库内置的音频 I/O 工具保存:
import numpy as np from mlx_audio.audio_io import write write("output.wav", np.array(results[0].audio), results[0].sample_rate)GenerationResult除了audio/sample_rate,还携带token_count(语义 token 数)、audio_duration、real_time_factor、processing_time_seconds等字段,可直接用于推理性能统计(见 confucius4.py)。
generate() 参数详解
对照 confucius4.py 的函数签名,全部参数与默认值如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
text | 必填 | 待合成的文本 |
ref_audio | 必填 | 参考音频路径,任意采样率,内部解码 + 重采样为 16 kHz 单声道 |
lang | "vi" | 目标语言代码,决定语言指令 token |
temperature | 0.8 | 采样温度,传入 T2S 的_sample中做logits / temperature |
top_k | 30 | 先截断到 top-k 再做 top-p |
top_p | 0.8 | 核采样阈值 |
repetition_penalty | 10.0 | 重复惩罚(注意默认值远大于 1,防止语义 token 循环) |
seed | 0 | 随机种子;同时作用于 numpy 采样 RNG 与 S2A 的初始噪声z |
采样逻辑在 t2s.py 的_sample中,顺序是:先对已生成过的 token 施加重复惩罚(正值除以惩罚、负值乘以惩罚),再除以温度,然后 top-k 截断、top-p 截尾、softmax 后用np.random.default_rng(seed)抽样。
语言支持
lang接受六个代码:zh、en、vi、ja、ko、th(中、英、越南、日、韩、泰)。从源码看,语言代码的作用是查 confucius4.py 中的LANGUAGE_TOKEN表,得到一句中文语言指令(如"请用越南语朗读接下来的文字"),与"You are a helpful assistant. "前缀和待合成文本拼接后经 tokenizer 编码,再进入 T2S 的文本投影。传入未知代码时回退为英文(LANGUAGE_TOKEN.get(lang, LANGUAGE_TOKEN["en"]),confucius4.py)。
三、可选模型与精度
模块 README 列出两个模型:
| 模型 | 精度 | 说明 |
|---|---|---|
mlx-community/Confucius4-TTS-mlx | fp32 | 全精度权重 |
mlx-community/Confucius4-TTS-mlx-int8 | int8 | 体积约缩小 60%,在 Apple Silicon 上更快 |
从源码结构看,int8 模型与 fp32 模型共用同一套加载/推理代码:ModelConfig中的quant_bits(默认 8)与quant_group_size(默认 64)控制量化状态(confucius4.py)。T2S 的_cw与 w2v-bert 的_lin在检测到权重旁存在.scales伴生张量时自动走mx.quantized_matmul,否则退回普通矩阵乘(t2s.py),因此精度差异对上层generate()完全透明。
四、权重转换:从 PyTorch 到 MLX
仓库自带转换脚本,一条命令完成:
python -m mlx_audio.tts.models.confucius4.convert --out ./confucius4-model加--quantize int8(或int4)可在转换时量化 T2S 主干的 matmul。脚本实现在 convert.py,要点如下:
- torch 只在转换阶段出现。脚本 docstring 明确写着 "torch is used ONLY here (conversion), never at inference";推理路径只用 mlx + numpy +
tokenizers(Rust 实现的 BPE 分词器)。 - 量化范围有讲究。
--quantize只量化 T2S 24 层中的attn.c_attn / attn.c_proj / mlp.c_fc / mlp.c_proj四类主干 matmul(GPT-2 的 Conv1D 权重会先转置成[out,in]布局再按 group 64 量化),而semantic_head、norm、embedding 保持 fp32——代码注释指出 8-bit 量化 semantic_head 会明显劣化 token 选择的保真度(convert.py)。w2v-bert 则量化每层的 ffn 与自注意力线性层,feature_projection因输入维度 160 不是 group 64 的倍数而保持 fp32(convert.py)。 - 转换产物是一个完整模型目录,共 8 个条目(与 confucius4.py
__init__的加载清单一一对应):
| 文件 | 来源 | 说明 |
|---|---|---|
t2s_model.safetensors | netease-youdao/Confucius4-TTS同名文件 | T2S 主干 + 前缀编码器权重 |
s2a_mlx.safetensors | s2a_model.pt | 转换时折叠weight_norm(_fold_weight_norm) |
bigvgan_mlx.safetensors | nvidia/bigvgan_v2_22khz_80band_256x | 同样折叠 weight_norm |
w2vbert_mlx.safetensors | facebook/w2v-bert-2.0 | 只取 feature_projection + 0~16 层 |
w2v_stats.npz | wav2vec2bert_stats.pt | 第 17 层隐状态的 mean/std,用于归一化条件向量 |
campplus.safetensors | funasr/campplus的campplus_cn_common.bin | 经 mlx-audio 自带 CAMPPlus 清洗后保存 |
fbank_filters.npz | 转换期预计算 | kaldi-mel 80 维滤波矩阵 + povey 窗 |
checkpoints/tokenizer.json | netease-youdao/Confucius4-TTS | 运行时用Tokenizer.from_file无 torch 加载 |
另外还会写出一份config.json(model_type: confucius4、sample_rate: 22050、quant_bits、quant_group_size),供load_model构建ModelConfig。
五、推理路径实现细节(torch-free)
理解"参考音频到底怎么被用掉",是理解这个模型的关键。generate()的前半段(confucius4.py)一次参考音频输入产出三种条件:
- 16 kHz 单声道波形:
load_audio(ref_audio, sample_rate=16000)解码并重采样。源码注释解释了为什么必须重采样——下游 fbank、CAMPPlus、参考 mel 全部假设 16 kHz,若不重采样,44.1/48 kHz 参考音频会被当作 16 kHz 误读,mel 频率轴偏移sr/16000倍,输出乱音。 - 语义条件向量:160 维 SeamlessM4T 风格 fbank(features.py,povey 窗、逐帧去直流 + 0.97 预加重、80 kaldi-mel、逐 mel 箱 CMVN、stride-2 叠帧)→ w2v-bert 第 17 层隐状态 → 用
w2v_stats.npz的 mean/std 归一化 → 经prefix.cond_emb的 ECAPA-TDNN 压缩成(1,1,1280)条件嵌入。 - 风格与音色条件:CAMPPlus 192 维说话人向量(
style)用于 S2A 的流匹配条件;_ref_mel()(confucius4.py)用 numpy 在 22050 Hz 上重算 80 维 slaney-norm mel(n_fft=1024, hop=256,滑窗用 numpy 纯实现),作为 S2A 的前缀参考 mel。
随后文本经 tokenizer 编码、prefix.text_emb投影(4096 维 embedding → SiLU → 1280 维,叠加文本位置嵌入),T2S 以 KV cache 做 prefill + 自回归采样(max_new默认 512),得到语义 codes 与 LM 隐状态latent;S2Abuild_mu构造(T_ref + target)长度的条件序列;26 步欧拉积分(mx.linspace(0, 1, 26)、cfg=0.7,confucius4.py)从噪声z积分到 mel;最后 BigVGAN v2(上采样率[4,4,2,2,2,2]、anti-aliased snakeβ 激活)还原 22050 Hz 波形并裁剪到 ±1。
六、使用前提与限制
- 运行环境:MLX 框架,即 Apple Silicon(macOS)。模型输出采样率为 22050 Hz(
ModelConfig.sample_rate = 22050)。 - 模型来源:fp32 与 int8 模型分别对应
mlx-community/Confucius4-TTS-mlx与mlx-community/Confucius4-TTS-mlx-int8;若需自定义转换,运行上文convert命令即可在本地生成等价目录。 - 参考音频:任意采样率均可,但内部统一按 16 kHz 处理特征;参考片段长度决定
T_ref,过长时 S2A 的 RoPE 频率表会自动线性外推延长(s2a.py 的_extend_freqs),无需担心超过 4096 帧的限制。 - 依赖边界:推理路径不含 torch/transformers;torch 仅在
convert.py中用于一次性权重转换。
小结
mlx-audio 的 Confucius4 移植把"多语言零样本语音克隆"完整搬进了纯 MLX 生态:load_model一行加载,generate(text, ref_audio, lang)三参即得 22050 Hz 波形;采样参数(temperature/top_k/top_p/repetition_penalty/seed)全部暴露且默认值经过调校;convert.py支持 fp32/int8/int4 三档产物,量化只作用于主干 matmul 以保护语义 token 选择质量。相关实现集中在 mlx_audio/tts/models/confucius4/ 目录,配合模块内 README 可作为后续深入源码的入口。
【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apple's MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考