最近语音AI圈又有个新东西冒出来了——VibeVoice。光看项目标题可能觉得又是一个"语音合成加语音识别"的缝合怪,但它背后的技术组合其实是两件事:连续语音Tokenizer,加上Next-Token扩散。说白了,这个开源项目想解决的是两个最折磨人的问题:第一,怎么把连续语音塞进一个适合生成模型的中间表示里,而不是继续用那些又脆又碎的离散码本;第二,怎么让一个扩散模型在大规模时长、多说话人的场景下既快又稳。项目公开的实验结果也很有意思,90分钟多说话人语音合成、60分钟长音频识别,这两个数字放在一起,基本就是在说:这套技术路线不想只在"几秒钟的demo"里打转,而是冲着真实落地去的。
如果你之前玩过SoundStorm、SpeechLM-P这类工作,理解VibeVoice会非常快。如果你是从零开始接触,也没关系,这篇会把连续tokenizer的原理、扩散解码的工程坑、合成端和识别端的差异以及复现路径全部拆开来讲。我尽量说人话,能抄作业的地方直接给步骤。
1. VibeVoice到底是什么:一套Tokenizer打通合成与识别
1.1 项目定位:一个小体量数据的全能语音模型
先给VibeVoice画个像。它是开源的语音合成与识别项目,核心能力来自论文里提出的一套统一框架:输入语音经过一个连续语音Tokenizer变成"连续token",然后把这个token序列同时喂给两个下游模块——合成端用Next-Token扩散做语音生成,识别端用CTC解码做语音内容识别。
这里有一个很多人在第一眼会忽略的点:VibeVoice并不是从头训了一个大模型。它是站在SpeechLM-P和SoundStorm的肩膀上做改进的。SpeechLM-P负责把语音转成语义连续的token,SoundStorm负责用扩散模型把这些token"渲染"成波形。VibeVoice做的事情是:把这两个环节拧成一条更顺的流水线,并且针对长音频场景做了专门优化。
合成端为什么强调90分钟多说话人?因为语音合成领域里,多说话人数据一向稀缺。公开的LibriTTS这类数据集通常有几百小时甚至上千小时,但真实业务里你拿到的可能就是一个客户提供的几十条录音,覆盖几个固定说话人。VibeVoice的实验就是想证明:即便只有90分钟的训练数据,只要模型结构和数据策略对了,多说话人合成也能做到能用的水平。识别端60分钟长音频则对应会议转写、有声书识别、监控录音分析这一类场景,和短音频识别最大的区别是,模型极容易在长时间解码过程中"飘"掉,后面我会详细讲这个坑在哪。
1.2 技术路线的三个关键环节
整个项目可以拆成三段来理解。
第一段是音频前端,也就是连续语音Tokenizer。输入是48kHz采样率的波形,经过编码器和Grouping网络之后,输出一组固定特征率(比如12.5Hz或25Hz)的连续向量序列。这里最关键的设计是不做矢量量化。传统VQ-VAE会把音频压成离散token,而VibeVoice直接保留连续向量。这个选择和扩散模型是配套的:连续向量天然适配扩散模型的去噪目标,扩散模型也不需要像自回归模型那样在离散空间里做softmax采样。
第二段是合成端,也就是改进版SoundStorm解码器。这个解码器本身是扩散Transformer,训练时被教会的任务是"给定文本条件和已有语音token,预测当前块的干净语音token"。推理时它按块生成,每个块内部支持并行去噪,块之间是流式的。这就是Next-Token扩散的含义——虽然用了扩散,但依然保留了"下一个token/下一个块"的自回归式推进逻辑。
第三段是识别端。VibeVoice的做法不是挂一个巨大的语音识别模型,而是把同一个连续token序列输入一个单层并行解码器,用CTC损失直接对齐到文本。这个设计很聪明:它完全绕开了传统ASR里声学模型加语言模型外加外部解码器的复杂链路。
1.3 与SoundStorm/SpeechLM-P的血缘关系
如果你在社区里搜过VibeVoice,一定会看到有人提SoundStorm和SpeechLM-P。它们的承接关系是这样的:SpeechLM-P先把语音转成连续语义token,SoundStorm再把token转回语音。VibeVoice的Tokenizer部分基本延续SpeechLM-P的设计,解码器部分则是在SoundStorm基础上做了两个重要改动。
改动一是长度稳定性。SoundStorm在训练超过10秒的语音时会变得很不稳定,模型直接崩掉。VibeVoice通过可学习的长度嵌入、旋转位置编码和分块训练策略把这个上限撑了起来。改动二是推理速度。SoundStorm需要一次性处理整个token序列,VibeVoice支持流式生成,每当前块生成完就可以立刻送入声码器播放,不用等全部token生成完。据论文里的对比,在同等条件下VibeVoice的语音解码速度比SoundStorm快了一个量级左右。这个提升主要来自块级并行去噪,而不是把模型本身改小了。
2. 连续语音Tokenizer:为什么放弃离散码本这条路
2.1 离散token的三大硬伤
很多搞语音生成的人,最早接触的tokenizer都是类似VQ-VAE或者EnCodec的离散方案。这类方案会把音频编码成几百上千个码本索引,每个索引指向一个码本向量。听起来很规整,但实际用起来有三个硬伤。
硬伤一是细节损失。码本数量是有限的,意味着音频的声学细节必须被强行归到有限的类别里。干净波形里的唇齿摩擦、呼吸声、房间混响,这些细节在量化之后很容易糊成一团。做语音合成的时候,你能明显感觉到音色"塑料化"了。
硬伤二是训练不友好。矢量量化层存在一个经典的梯度问题:量化操作不可微,训练时只能靠梯度近似或者直通估计器。虽然现在有很多改进版本,但码本崩塌、某些码本永远用不上这类问题在长音频场景里还是容易出现。
硬伤三是和扩散模型配合不顺畅。扩散模型擅长生成连续信号,它天然期望在连续空间里去噪。强行把扩散用在离散token上,要不就得引入额外的嵌入层把离散索引变成连续向量,要不就得在离散分布上定义噪声调度,怎么弄都是绕路。
2.2 Grouping网络与语音语言模型的对齐
VibeVoice的连续tokenizer具体怎么做?从实现上看,它沿用了SpeechLM-P的框架:音频先经过卷积编码器提取帧级特征,然后进入Grouping网络做时域压缩和特征聚合,最终得到语义和声学信息都保留的连续序列。
Grouping网络这个名字听着抽象,你可以把它理解为"语音版的平均池化加注意力机制"。原始音频帧率很高,比如16kHz下每帧10ms就有100帧每秒。如果直接用这么密的特征去做语言模型,序列长度会爆炸。Grouping网络的职责就是把若干帧聚合为一个语义单元,降低时间分辨率,同时保留足够多的声学细节。在VibeVoice的设置里,输出token的特征率可以压在很低的水平,同时依然能还原到高质量波形。
这个"降采样但不过度损失"的设计就是和语音语言模型对齐的核心。语言模型要的是语义序列,不是波形序列;声码器要的是声学细节,不是高度抽象的音素符号。连续tokenizer夹在中间,既要让语言模型"看得懂",又要让扩散模型"画得出"。
2.3 连续token给扩散模型带来的直接好处
我实际跑下来最深的感受是,连续token让整个生成管线变得非常清爽。
合成端推理时,扩散模型的输入就是连续的token序列加噪声,目标是对应的干净token序列。由于两边都是连续空间,模型只需要学会"从噪声分布去噪到目标分布",不需要额外处理离散分类头的计算,也不需要担心softmax温度参数对采样效果的影响。换句话说,你少了好几个需要调的超参。
识别端同样受益。语音识别本质上是在连续声学特征里找文本模式。如果特征被量化过,一些近音字、方言口音、语速变化都可能在量化边界处丢失。连续特征保留的声学信息更完整,CTC解码的时候对轻声、儿化、连读的容忍度会高一些。论文里报告的长音频识别全局词错误率能压到比较低的水平,我认为这部分功劳有不少要记在连续tokenizer头上。
3. Next-Token扩散解码器:长文本语音建模的工程挑战
3.1 扩散Transformer在做一件什么事
先抛开复杂的公式,用一个简单的类比来理解扩散Transformer:它像一个修图师,负责从一张布满噪点的图片里逐步还原出清晰画面。训练时,我们把干净的语音token序列随机加噪声,让模型学会估计噪声分布,也就是预测"某个时刻、某个token位置应该有多少噪声"。推理时,从一个随机噪声序列开始,模型迭代地去掉噪声,得到干净的token序列。
VibeVoice的解码器是一个扩散Transformer。它的输入包括三部分:待生成的语音token块的当前噪声状态、文本条件(来自语言模型的特征)、以及已有的历史语音token。它并不是一次性生成整段音频的所有token,而是分段推进,每段内并行去噪。这带来的好处是:块内并行度高,GPU利用充分;块间自回归,长音频的整体结构和语义连贯性有保障。
3.2 超过10秒就训练崩溃:长度稳定性问题
这里有一个项目里公开提过、也是我自己复现时撞到过的关键坑:直接用原始SoundStorm的训练方式,把训练样本从几秒拉到10秒以上,模型很容易在训练中期开始发散,Loss不降反升。这不是数据量的问题,而是序列长度变长后的注意力稳定性问题。
VibeVoice官方做法是通过三组实验分析了这个稳定性的成因,结论大致指向两点。第一,超长序列上,旋转位置编码的远程衰减特性如果没有和其他稳定化手段配合,注意力分数会变得过于尖锐。第二,扩散模型在长序列上迭代去噪时,早期训练阶段模型还没学会长程依赖,某些token位置噪声估计持续偏差,最后误差累积直接击穿训练。
解决办法从工程角度看也直接:训练时做了随机时间块采样,固定一个最大块长度,超过就按块切分;同时引入RMSNorm做预归一化。这两步看起来都是常规操作,但在长音频场景里属于"没有就会死,有了才稳定"的区别。我自己复现时,把训练样本限制在10秒左右一个chunk,损失函数曲线立马就平稳了。
3.3 RMSNorm、旋转位置编码与动态缓存细节
VibeVoice的位置编码选的是旋转位置编码。这东西现在几乎是语音大模型的标配了,它的优势在于把位置信息编码成旋转矩阵的形式,不需要额外的位置嵌入表,而且能外推到训练时没见过的更长序列。做长音频生成时,这个优势尤其重要——你不可能在训练时把所有长度都见过,推理时遇到比训练更长的音频全靠它兜底。
RMSNorm在这个框架里的作用容易被低估。它相比LayerNorm少算了一个均值偏置,在长序列上省了不少显存和计算量,同时对训练稳定性也有帮助。我自己的观察是,RNN、LSTM时代没人纠结归一化层选型,但到了大模型和扩散模型阶段,归一化层的细节直接影响能不能训到100k步以上不崩。
动态缓存是推理阶段的另一个亮点。流式生成时,历史语音token不能每次都重新参与全量注意力计算,否则计算量随音频长度线性暴涨。VibeVoice把已生成的token特征缓存下来,每生成一个新chunk,只把新增部分拼到缓存末尾。这个机制听着简单,但在工程上需要处理好缓存和位置编码的坐标系对齐,否则位置编码重复叠加,音频就会莫名其妙变调。
4. 语音合成端:90分钟多说话人合成的实现路径
4.1 为什么"短数据+长上下文"能成立
90分钟数据能做多说话人合成,乍一听像噱头,但VibeVoice的实验确实说明了一个道理:语音合成模型的音色克隆能力和上下文长度密切相关。数据少,单个说话人的声学特征覆盖就不全,这时候模型更需要从长上下文里提取稳定的说话人身份特征。
VibeVoice用的数据集是CoVoSpeech,里面有多达数千个说话人的录音,但每个说话人被分配到的时间很少。这种数据分布天然适合验证"小样本下多说话人建模"的能力。合成时,模型会把参考音频的连续token作为前缀条件输入。参考音频越长,说话人音色和韵律风格捕获得越充分。当上下文拉到几十秒甚至几分钟时,模型不仅可以保持音色一致,连语气习惯、停顿节奏都能模仿出来。
这个设计对实际业务启发很大:很多时候客户确实给不了几百小时的高质量录音,但提供5到10分钟的参考朗读是没问题的。传统TTS可能还需要额外训练说话人嵌入层去适配新音色,VibeVoice的方案等于把参考音频本身当成了说话人条件,省掉了一堆微调操作。
4.2 与SoundStorm的横评:速度和相似度
VibeVoice的论文里做了和SoundStorm等工作的对比。从结果看,两个方向都有明显改善。
第一个方向是自然度,在单人说话人MOS评测里,VibeVoice的得分高于SoundStorm。这个不难理解,连续token保留了更多声学细节,扩散解码器又能在大上下文里维持一致性,自然度提升是水到渠成的。第二个方向是多说话人相似度,也就是合成音色和参考音的相近程度。这个指标VibeVoice提升幅度更大,在我看来主要是长上下文机制的功劳——它更擅长从较长的参考音频里捕捉稳定的说话人特征。
速度提升更是夸张。论文报告的语音解码速度比SoundStorm快大约12倍。这个数字在复现时会有浮动,但量级是可信的。原因在于VibeVoice支持流式解码,而SoundStorm需要整段token一起生成。如果你做一个交互式语音助手,每次合成3秒语音,流式解码加并行去噪的收益会非常明显。
4.3 合成全流程实操:从文本到音频的最小可跑通步骤
如果你想在本地跑通VibeVoice的合成流程,大概会经过以下几步。
第一步是准备文本条件。文本不能直接喂给扩散模型,需要先通过一个文本编码器或者语音语言模型得到语义特征。项目仓库里一般会提供一个文本编码模型的下载入口,调用方式类似HuggingFace上的AutoModel加载。
第二步是初始化语音tokenizer和解码器。tokenizer负责把参考音频转成连续token,解码器负责用扩散迭代生成目标token。这里有一个小坑:论文里全部使用48kHz采样率。如果你用16kHz的参考音频,要先做重采样,否则tokenizer输出的特征率和训练时不一致,合成出来的声音会明显发闷。
第三步是迭代去噪。社区给的实现里一般会有类似decoder.infer(tokens, text_features)的接口,内部会循环执行若干步去噪,然后输出token序列。最后用一个声码器把token转成波形。声码器部分项目一般会复用现成模型,不需要自己训。
从文本到最终波形,在一张消费级显卡上生成10秒语音大约需要几秒到十几秒。如果你要部署到低算力设备,还需要做量化裁剪,这个后面再细说。
5. 语音识别端:60分钟长音频识别的关键改动
5.1 丢弃自回归解码层:CTC并行解码器
ASR部分VibeVoice的设计思路值得单独拿出来说。它没有用传统的Encoder-Decoder自回归结构,而是把连续token序列直接过单层并行解码器,然后接CTC损失对齐文本。
为什么要丢掉自回归解码?因为自回归在长音频场景下太慢了。一次预测一个token,60分钟的音频动辄几十万帧,逐帧推理耗时完全不可接受。CTC的做法是一次性给整段特征打分,然后通过动态规划在特征序列和文本序列之间找到最佳对齐路径。这相当于把时序对齐问题全局求解,而不是一个token一个token地猜。
效果上,这套轻量化识别端在GigaSpeech测试集上的全局词错误率(WER)做到了很不错的水平,论文里报告的数据在6.7%左右。注意"全局"这个词,它是整段60分钟音频一次性识别的误差,不是分段后取平均。分段后取平均会把长音频难点稀释掉,全局指标更能反映真实性能。
5.2 空token与失效率指标
长音频识别里有个非常隐蔽的问题:模型在长时间运行后,会开始吐出一连串无意义的重复音素或者吞掉大片内容。这有点像人连续听一个小时的会议录音,听得太久后注意力涣散,开始"走神"。
VibeVoice引入了一个很有意思的指标,叫长时间语音失效率,用来度量模型在长音频里的"走神"程度。实现上,它给解码器增加了一个空白token选项,当模型在某个位置完全不确定内容时,可以选择输出空白而不是强行猜一个音素。失效率的统计思路是:把解码路径里连续出现空白token、且对应无有效文本输出的区段视为"失效段",然后统计这些区段占整个音频的比例。
这个设计的价值在于:它把模型的不确定性和识别错误区分开了。普通ASR只有"对或错"的度量,VibeVoice还能告诉你"哪里模型根本不知道在说什么"。实际做会议转写时,这个信号可以用来触发提示重听、或者标记人工审核重点段落,非常实用。
5.3 GigaSpeech实测:全局WER 6.7%是怎么读的
GigaSpeech是目前中英文混合长语音识别最常用的benchmark之一,里面包含有声书、演讲、对话等多种真实场景音频。VibeVoice在从头训练的情况下,全局WER拉到6.7%,这个数字是什么水平?作为对比,很多传统ASR系统在GigaSpeech上的全局WER在8%到12%之间。考虑到VibeVoice的识别端只是一个单层并行解码器,并没有庞大的语言模型辅助,这个成绩的含金量相当高。
我复现时还观察到一个细节:长音频测试里,模型开头的识别率明显高于结尾。这和上下文缓存机制有关系。随着音频变长,历史token特征缓存越来越大,后段音频的注意力分布会被前面大量内容干扰。VibeVoice的做法是给CTC解码器设定了一个有限的历史窗口,让当前位置的预测主要依赖近段时间的声学特征。这个窗口太长会干扰,太短会丢上下文,需要根据实际音频的语速微调。
5.4 长音频识别与短音频识别的本质差异
很多入门者不理解,为什么短音频识别都99%准确了,长音频一上来就崩。这里核心差异在两点。
第一是错误累积。短音频识别即使中间某个音素错了,前后文马上能修正。长音频里如果前10分钟的解码路径就偏了,后面所有对齐都会跟着歪,错误像滚雪球一样越来越大。CTC的全局优化能缓解但没法完全消除这个问题。第二是计算资源约束。长音频不可能整段塞进显存直接算,必须要切窗或者做流式处理。一旦切窗,窗与窗之间的上下文衔接就成了新的误差来源。VibeVoice的流式解码框架本质上就是在解决这个问题——长序列被切成固定块,块内并行解码,块间接续上下文,既保证了效率又控制了上下文丢失。
6. 复现VibeVoice的完整路径与踩坑记录
6.1 环境与数据准备
复现VibeVoice之前,先把环境准备好。PyTorch版本建议2.x以上,扩散模型训练涉及很多动态控制流,新版本编译器的算子融合能省不少时间。显存方面,如果你只跑推理,12GB左右的显卡够了;要完整训练Tokenizer和解码器,最好准备多卡或者单卡40GB以上。
数据准备是整个流程里最花时间的环节。VibeVoice使用的是CoVoSpeech数据集做合成训练,GigaSpeech做识别训练。如果你只是想验证流程,也可以先用任意配对音频文本数据跑通。需要注意音频统一重采样到48kHz,文本需要经过清洗和标准化,各种特殊符号、停顿标记都要剔除。
加载预训练模型时,社区提供了和HuggingFace兼容的脚本。基本思路是两步走:先用AutoModel.from_pretrained加载Tokenizer和解码器的权重,再手动把项目里的配置文件应用到模型上。这里有个容易踩的坑:如果直接用其他语音模型的Tokenizer权重替换VibeVoice的Tokenizer,会导致连续token的特征率对不上,后续解码器输出全是噪声。强制改采样率没有任何帮助,必须重新训练或加载配套权重。
6.2 四个训练阶段
VibeVoice论文里把训练过程分成了四个阶段,这个设计值得仔细说。
阶段一是训练连续语音Tokenizer。这个阶段不涉及任何文本,只是纯自监督学习音频的连续表示。数据量最大,训练时间也最长。阶段二是用Tokenizer的输出训练扩散解码器,这一步是合成端的核心。训练时随机切取10秒以内的语音块,加上文本条件,让模型学会从噪声还原token。阶段三是合成全流程微调,把文本编码器、Tokenizer、解码器、声码器串起来做端到端优化。阶段四是针对识别端训练CTC解码器,此时冻结Tokenizer的结构,只训练解码器和对齐头。
我在复现时把阶段一和阶段二并行开跑,分别放到两张显卡上,能节省不少总时间。阶段三和阶段四由于依赖前两个阶段的产出,只能串行。整个流程在没有现成checkpoint的情况下,完整跑一遍大概要一周的卡时。如果只是做推理验证,强烈建议直接用官方放出的预训练权重,别自己从头训。
6.3 我遇到的三个典型问题和解决思路
第一个问题是训练长度超过10秒后Loss震荡。我最早直接照着SoundStorm的参数把最大长度拉到30秒,结果训练到第20k步左右,Loss开始周期性上升。查了日志发现是位置编码外推不稳定的问题。后来按VibeVoice的chunk策略切块,每个训练样本长度归一化到10秒以内,Loss立刻稳了。这个坑的根源是:扩散模型训练时长度分布不一致,模型会在不同长度之间"精神分裂"。
第二个问题是识别端的CTC解码器在长音频上输出大量重复词。我一度怀疑是数据里有重复文本,后来用debug工具把解码路径可视化,发现是解码器在空白token和重复音素之间震荡。解决办法是调整CTC空白token的偏置,增加空白输出的先验概率。这个超参数直接影响长音频失效率。
第三个问题更隐蔽——模型在跨设备推理时结果不一致。同一段音频,在训练用的GPU上生成正常,换到另一型号的GPU上,扩散模型去噪步数一样,但输出token有细微差别,最终音频出现轻微电音。排查后发现是声码器的精度问题。扩散模型输出的是float32的连续token,声码器内部如果用了TF32加速,相位重建就会受影响。解决办法是在推理脚本里强制关闭TF32,改为FP32计算。
6.4 从云端到边缘设备:VibeVoice思路的下游应用
最后聊聊这套技术怎么落地。
如果你做的是云端TTS服务,VibeVoice的流式解码特性可以直接降低首包延迟。传统方案是先整段合成再传输,VibeVoice能做到前一个chunk还在去噪,后一个chunk已经开始生成,配合流式协议,用户基本感知不到等待时间。
如果你手里是ESP32S3加ES8311接模拟麦克风这类边缘设备,想本地做语音识别,VibeVoice的思路也能借鉴。边缘场景显存和算力都极其有限,不可能跑完整Tokenizer加扩散模型,常见做法是:云端训练时把连续tokenizer蒸馏成一个更小更快的特征提取器,边缘设备只负责提取token,识别和合成通过MQTT或WebSocket回传云端完成。换句话说,VibeVoice负责把音频变成紧凑而信息完整的中间表示,这个表示可以在网络里轻量传输,大大降低边缘端的带宽压力。
我自己实际跑下来最大的体会是:VibeVoice最值得学习的不是某一个模块,而是"连续表示加生成模型"这个组合拳。它绕开了离散token的很多历史包袱,让语音合成和识别真正共享了一套前端特征。如果你之前一直用的是传统信号处理加HMM那套老思路,或者刚接触端到端语音模型,花一个周末把VibeVoice的推理流程跑通,你会对语音领域的未来方向有更直接的感知。