Phonon TTS发布:开源端侧语音合成,离线也能跑
Gradium推出的Phonon TTS模型能在手机、笔记本等设备上离线运行,支持英法德西葡五语。它在Hugging Face开源TTS榜单中,德语和西班牙语清晰度排名第一,首字延迟表现优异。文章介绍其技术原理、Transformers集成方法及实际应用场景。
语音模型为什么应当留在设备本地
眼下大多数语音AI都跑在云端。你说出的每句话,都要先传到服务器再传回来。速度慢,隐私也是隐患。Phonon 走的是相反的路:直接在设备上运行。文字不必上传,断网照样能用。
还有一点更关键:模型权重已经开源。Phonon 31m 采用 Apache 2.0 协议,商业产品可以免费使用。其余多语言模型使用 CC BY-NC 协议,面向非商业项目。如果要把这些多语言模型用于商业产品,授权费按用户数量计算,与生成语音的时长无关。
让小型设备发出好声音,一直是道难题。模型缩小,音质常常跟着下降;音质上去了,模型又大得装不进手机存储。这次我们想两边都顾上。
今年四月,Phonon 首次对外公布。此后六个月,合作伙伴与早期用户给了不少反馈。模型体积缩小了,覆盖的语言增多了,还新增了语音克隆和语音设计两种定制声音的方式。
Phonon 的技术基础来自 Pocket TTS 系列。它靠流匹配(flow matching)生成音频,与多数 TTS 系统的建模思路不一样。小型模型能保住高音质,靠的正是这套方法,但训练也因此变得棘手。起初数值不稳定,试过好几种技巧之后,才在五种语言上都达到合格水平。
本次发布的内容
Phonon 有 65 MB 和 75 MB 左右两个版本。英语、法语、德语、西班牙语和葡萄牙语都能说。内置声音超过 100 种,也可以在 Gradium Studio 里创建自己的声音。
电话号码、日期、代码这类内容,很多 TTS 系统念得磕磕绊绊。Phonon 把这些细节处理顺了。
手机、笔记本、单板计算机,甚至浏览器标签页里,它都能跑。Hugging Face 空间里可以直接试听。Phonon 31m 仅支持英语,但 Apache 2.0 协议意味着随便用。Phonon 40m 覆盖全部五种语言,仅限非商业用途。
演示案例与早期项目
发布时,朋友和团队一起做了几个演示。Apple Watch 关掉 Wi-Fi,Phonon 一步步教你冲咖啡。iPhone 没有信号,它帮你数健身动作。Autonomous 公司那台叫 Lamp 的机器人戴着分院帽,用角色音说话。浏览器里,一款离线恐龙跑酷游戏被改成了 Rex 主持的电台节目。此外还有玩家在本地机器上跑的科幻游戏配音,以及逐页朗读普鲁斯特作品的项目。
Hugging Face 团队完成了 Transformers 集成,开发者从第一天起就能在 Transformers 中调用 Phonon。
社区的动作也不慢。有人做出 ExecuTorch 转换版,让 Phonon 在浏览器里调用访客的 GPU 来运行。Micdrop 这类空间也在它之上搭建新功能。
发布头两周,Phonon 下载量突破 20 万次,目前在 Hugging Face 上的热度仍在上升。
Hugging Face Open TTS 榜单上的成绩
Hugging Face Open TTS Leaderboard 是个新项目。Hub 上汇聚了 9000 多个 TTS 模型,开发者能在这个榜单上集中对比开源模型,还能并排试听。榜单评估 23 种语言,指标覆盖清晰度(输出文本的词错误率)、速度(逆实时因子与首字延迟)和自然度(英语用 UTMOS,韩语用 KOMos)。固定声音与语音克隆分开评估,后者额外考察说话人相似度。榜单另设“Listen”标签页,方便大家自行比较、投票。
清晰度上,Phonon 在德语和西班牙语排第一,其他参与评估的语言也都进入前三。要知道,部分竞争对手的模型体量是它的 100 倍。
速度更是 Phonon 的长处。CPU 流式基准测试中,它的首字延迟最短。
推理实现的细节
我们为 Phonon 单独写了一套推理框架 ptts。目的是让它在各类设备上都跑得顺,因此支持十多种后端:CPU(Intel、Arm、WebAssembly)、GPU(CUDA、Vulkan、Metal、WebGPU),以及 NPU(Apple Core ML、Snapdragon®)。
从 v5.21 版本起,Phonon 也被收录进 🤗 Transformers,作为参考 PyTorch 实现。ptts它侧重单设备上的单流运行,Transformers 则侧重 GPU 吞吐量:比如批量生成、全图torch.compile,适合大规模合成语音数据集。由于是原生 PyTorch,它既能作为集成进自家技术栈的基础,也是研究模型本身的好起点。
Pocket TTS系列已经进了Transformers,Phonon则是叠在上面的模块化模型,只描述差异部分的代码大约200行。
Phonon以自回归方式生成音频潜变量:每步输出一个潜变量,这个潜变量又成为下一步的输入,训练损失同样基于它来算。潜变量既是模型的输入,也是输出和目标,作用类似语言模型里的文本token。把音频映射成潜变量的编解码器,相当于分词器。Transformers里也是这个路子:音频分词器与文本分词器一起放进processor。声音不是录音,而是说话人潜变量的张量。Phonon从不用音频分词器直接编码音频,分词器只保留解码部分,声音全部预计算好,放在gradium/phonon-voices。
from transformers import AutoProcessor, GradiumPhononTTSForConditionalGeneration
model_id = "gradium/phonon-31m-en"
processor = AutoProcessor.from_pretrained(model_id, device_map="auto")
model = GradiumPhononTTSForConditionalGeneration.from_pretrained(model_id, device_map="auto")
print(f"{model.num_parameters():,}") # 30,788,354
print(f"{processor.audio_tokenizer.num_parameters():,}") # 10,305,313
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
path = hf_hub_download("gradium/phonon-voices", "voices/LMDIYE0Oa377Qh6Z.safetensors")
voice = load_file(path)["speaker_wavs"][0].T # (num_frames, 512)提示词的布局是声音位置、文本,再加一个开启音频的token,这套聊天模板随检查点一起发布,和其他多模态模型在Transformers里的做法一致。写一段标准对话,在声音该出现的地方留一个空项,代码在不同检查点之间通用。processor会把声音占位符展开成每帧一个位置,对文本做归一化和分词,然后打包成输入:audio
conversation = [
{"role": "user", "content": [{"type": "audio"}, {"type": "text", "text": "Hello from Phonon."}]}
]
inputs = processor.apply_chat_template(
conversation,
tokenize=True,
return_dict=True,
add_generation_prompt=True,
processor_kwargs={"audio_prompt_latents": [voice]},
).to(model.device)
# prompt: <|audio_prompt|> × num_frames, "Hello from Phonon.", <|audio_bos|>generate模型返回潜变量,processor再把它解码成音频:
outputs = model.generate(**inputs) # audio_latents: (batch_size, num_frames, 32)
audio = processor.decode(outputs.audio_latents, outputs.audio_latents_mask)
processor.save_audio(audio, "phonon.wav")更多细节可以翻文档 🤗。
接下来做什么
Phonon能用任何声音说话。经说话人同意,拿一段短录音就能克隆一个声音;也可以在Gradium Studio里用纯文本描述一个新声音。下载声音文件、加载进Phonon,全程都在设备上跑。我们也在和设计伙伴一起训练定制Phonon模型。内存紧张的话,模型可以做得更小;需要韩语、阿拉伯语或日语,我们也能训练;产品有专属词汇表,还能在此基础上微调。有需求可以联系我们聊聊。
感谢Hugging Face团队,也感谢头两周里试用Phonon的每一个人。这是我们的第一个开源发布。试试看,告诉我们你做出了什么。