上一篇 Qwen3-TTS 把「语音 token」当成黑箱,说它是「声音的拼音」——文字先变成一串离散的语音 token,再由声码器还原成波形。那篇讲了主干和训练,却对这串 token 本身一笔带过:它到底怎么造出来的?
这一步的「造币厂」,就是语音 tokenizer(也叫神经音频 codec、语音离散化器)。它是整个 TTS 的地基:tokenizer 一定,上面的语言模型好不好学、能不能边说边出声、还原得像不像,就都定了一大半。这篇把黑箱拆开——从设计上的根本矛盾讲起,一路看 EnCodec、SpeechTokenizer、Mimi、CosyVoice、X-Codec2 到 Qwen3-TTS,看它们各自站在哪、为什么这么选。
也算本站多模态系列里「离散化」这条线的深潜:离散 vs 连续 讲过「声音要先变成离散 token 才能被 LM 生成」,这篇专门拆那台把声音变成 token 的机器。
一、两个主人:语义还是声学
一个语音 tokenizer 要把连续的声波压成一串离散 token,绕不开一个根本矛盾:这串 token 到底该记住什么?
- 一路是语义 token:只关心「说了什么」。做法通常是拿自监督模型(HuBERT、WavLM 这类)抽特征,再做 k-means 聚类,把每帧归到一个「类别号」。它和音素、内容对得很齐,比特率低、语言模型好学;代价是丢掉了音色、语气、录音环境——你没法只凭它还原出原来那个人的声音。
- 另一路是声学 token:只关心「听起来怎样」。做法是训一个神经 codec,直接把波形编码、量化、再解码回去。它能高保真重建音色和韵律;代价是和内容没对齐——码里全是「怎么响」,不知道「说了啥」,而且(下文会看到)一帧要吐好几个码本,语言模型不好嚼。
这不是随口说的取舍。Google 的 AudioLM 把两种 token 摆在一起测过:声学 token(来自 SoundStream)重建质量好,但「音素判别」很差;语义 token(来自 w2v-BERT)反过来,音素判别极好,却几乎重建不出像样的音频。一个能听、一个能懂,偏偏很难兼得。 后面的故事,基本都是在想办法把这两个主人同时伺候好。
二、两条老路:声学 codec 与语义 SSL
先看这两路各自的代表作。
声学派:神经 codec 的 RVQ 家族。 2021 年 Google 的 SoundStream 立了范式:一个全卷积编码器把波形压成每秒几十帧的向量,再用残差向量量化(RVQ)离散化,最后解码器配 GAN 判别器还原波形。RVQ 是关键——单个码本容量有限,RVQ 就叠一串小码本:第一个量化主体,第二个量化「上一层的残差」,逐层由粗到细。SoundStream 在 24kHz 下每秒 75 帧、可配 8 个码本,覆盖 3–18kbps。
接棒的是 Meta 的 EnCodec(2022,卷积 + LSTM,最多 32 个码本,1.5–24kbps)和 Descript 的 DAC(2023,Snake 激活、码本利用率约 99%,44.1kHz 下约 8kbps)。VALL-E 用的正是 EnCodec:8 个量化器、每个 1024 条目。这些 codec 重建极好,却有个对语言模型不友好的硬伤:一帧要同时吐好几个码本(EnCodec 8 个、DAC 9 个),而单个自回归 LM 一步只能预测一个 token——数量对不上。VALL-E 只好让第一个码本自回归、其余码本另用非自回归模型补;MusicGen 则发明「延迟交错」把多条码本流摊平(这些补丁具体怎么打,第三节末尾细讲)。说到底,多码本是 RVQ codec 和自回归 LM 之间最大的一道坎——两边的结构就对不上:codec 一帧给出好几条并行的码本流,LM 却只会走一条一维序列。上面那些补丁都是绕道,绕不开的东西只有一个:把码本砍少(第五节的主角)。
语义派:自监督特征的 k-means。 另一条路根本不做 codec,而是拿 HuBERT、w2v-BERT、WavLM 这类自监督模型的中间特征,直接 k-means 聚类成离散「语义 token」。HuBERT 每秒 50 帧,聚类数常取 100、500、1000 不等;这些 token 和音素对得很齐,但基本不含音色。
那能不能两个都要?Google 的 AudioLM(2022)就是这么干的:先用 w2v-BERT 的语义 token(25Hz、1024 类)搭起长时结构,保证「说得通顺」;再用 SoundStream 的声学 token(12 层 RVQ)补上音质,保证「听着真」。两套 token、两级建模——能听又能懂,但要维护两条独立的 token 流。
三、量化的三种手艺:VQ / RVQ / FSQ
上面反复出现「码本」「量化」,这里花一节讲透。量化是 tokenizer 里唯一真正「离散化」的那一步:编码器每帧吐出一个连续向量,量化负责把它变成一个能进词表的整数编号。原理不难,但有三个绕不过去的坑——VQ、RVQ、FSQ 说白了就是对这三个坑的不同回答。
VQ:最近邻查表,和它的三个坑
最朴素的做法,像查字典:维护一本码本(K 个可学习的向量),给定编码器输出 z,找离它最近的那个码字,输出这个码字的编号——这就是 token;解码器则拿选中的码字代替 z 往下走。一帧的信息量就是 log₂K 比特(K=1024 → 10 比特)。
麻烦在于,这套东西有三个坑:
argmin 没法求导。 取最近邻这一步导数几乎处处为 0,梯度传不回编码器。解法是直通估计(STE):前向用量化后的值,反向假装量化是恒等函数,把解码器的梯度直接抄给编码器。
z_q = z + (quantize(z) - z).detach() # 前向 = quantize(z),反向梯度直通给 z码本自己怎么学? 要么用一个码本损失把码字拉向编码器的输出,要么用 EMA(滑动平均)更新——SoundStream、EnCodec 走的都是 EMA。同时还要一个承诺损失(commitment loss)反过来把编码器拉向码本,否则编码器到处乱跑、码本永远追不上;EnCodec 里它就是「量化器输入与输出之间的 MSE」。
码本塌缩。 少数码字被反复选中,其余的从没被选中 → 从没被更新 → 永远是死码:名义上 1024 条,实际可能只活了几十条。补丁一大堆(死码重置、k-means 初始化、码字分裂、熵惩罚)。DAC 的解法比较漂亮:把查表放到低维空间(8 或 32 维)做,再对码字做 L2 归一化(欧氏距离退化成余弦),利用率能拉到约 99%。
记住第三个坑——它就是后面 FSQ 出场的全部理由。
RVQ:容量不够,就叠残差
单码本一帧只有 log₂K 比特:K=1024 就是 10 比特,75 帧/秒也才 750bps,远不够高保真。想要 80 比特/帧?那得 2⁸⁰ 个码字——存不下,也搜不动。容量随比特数指数爆炸,这是单码本的死穴。
RVQ 的解法是级联一串小码本,每层只量化上一层剩下的残差:
r₀ = z
for i in 1..N:
kᵢ = argmin_k ‖r_{i−1} − e⁽ⁱ⁾_k‖ # 第 i 层在自己的码本里找最近的码字
rᵢ = r_{i−1} − e⁽ⁱ⁾_{kᵢ} # 减掉,剩下的交给下一层
z_q = Σ e⁽ⁱ⁾_{kᵢ} # 重建 = 各层选中码字之和
残差一层比一层小,所以是由粗到细的逐层逼近:第 1 层管大轮廓,后面的层修细节。比特数于是从「指数涨 K」变成「线性叠 N」——N × log₂K。
还有个关键小技巧:量化器 dropout——训练时随机只用前 n 层(n 从 1 到 N 均匀采样)。好处有二:一个模型同时服务多种码率(解码时少用几层就是低码率),而且逼出了层的重要性排序(第 1 层永远在,所以它必须最重要)。SoundStream 首创,EnCodec、DAC 都沿用。
(记住「第 1 层最重要」这个性质——下一节 SpeechTokenizer 把语义蒸馏进的,正是这一层。)
FSQ:干脆不要码本
2023 年 FSQ 的思路很反直觉:坑 2、坑 3 都是「码本要学」惹的祸,那就别学了。它原本是给图像 VQ-VAE 提的通用替代(论文副标题就叫 VQ-VAE Made Simple),整个机制四步说完:
- 投影到极低维:把编码器输出线性投影到 D 维,D 通常不到 10——这是和 VQ 最大的直觉反差:VQ 在几百维的空间里找最近邻,FSQ 只留几个维度;
- 限幅:每一维过一个 tanh 压进固定区间(论文的写法是 z ↦ ⌊L/2⌋·tanh(z),偶数档位时再加个小平移,细节从略);
- 取整:四舍五入——于是第 i 维只能落在 Lᵢ 个整数档位上;
- 拼编号:D 个整数按「进位制」拼成一个 id,基是 [1, L₁, L₁L₂, …],像读一个混合进位的数。
拿档位 [8, 5, 5] 举例:某帧投影、限幅、取整后得到 (5, 2, 3),那么 id = 5×1 + 2×8 + 3×40 = 141,词表总大小 8×5×5 = 200。隐式码本就是各维档位的笛卡尔积——一张 8×5×5 的固定网格,token 是网格点的编号。没有表、没有码字、也没有最近邻搜索。
训练上,round 不可导,解法和 VQ 的坑 1 一模一样——同一个 STE:x + sg(round(x) − x),前面那行 Python 换个函数名照用。更妙的是,连承诺损失也省了:tanh 已经把编码器输出捆在固定区间里,它想跑也跑不掉。
坑 3 则从根上消失。VQ 的码字是学出来的——会漂移,没被选中就得不到梯度、沦为死码;FSQ 的「码字」是构造出来的——固定网格点,不动、不需要梯度,自然没有「死」这回事。学习负担整个转给了编码器:码字不用学了,编码器得学会把输出摊满网格。这个差距论文里量过:VQ 的码本一超过 2¹¹,利用率就跌破 50%,再大也用不满 2¹⁰ 个码字;FSQ 到 2¹⁶ 依然≈100%。论文原话:FSQ「不会码本塌缩,也不需要 VQ 那套复杂机械(承诺损失、码本重置、码字分裂、熵惩罚等)」。
想要多大的词表,就加维度、加档位:容量 = 各维档位之积。论文给的启发式是每维至少 5 档,并附了张对照表——2¹⁰ 用 [8,5,5,5],2¹⁶ 用 [8,8,8,5,5,5]。
这也解释了 FSQ 为什么成了语音单码本设计的标配:单码本把全部赌注押在一个码本上,这个码本必须又大、又用得满——恰好 VQ 在这两点上都塌(2¹⁶ 只能用出千把个),FSQ 正好补上。CosyVoice 2/3 的 6561(它论文里的编号公式 μ = Σ h̄ⱼ(2K+1)ʲ 正是那套进位制)、X-Codec2 的 65536、Spark-TTS 的音色 token,都是这么来的。
一条公式,串起全文所有数字
三种手艺讲完,可以收成一条式子:
比特率 = 帧率 × 码本数 × log₂(码本大小)
它几乎能反推出本文提到的每个数字:
| 帧率 | 码本数 | log₂K | 算出来 | 论文值 | |
|---|---|---|---|---|---|
| SoundStream | 75 | 8 | 10 | 6,000bps | 6kbps |
| Mimi | 12.5 | 8 | 11(K=2048) | 1,100bps | 1.1kbps |
| BigCodec | 80 | 1 | 13(K=8192) | 1,040bps | 1.04kbps |
| MOSS-Tokenizer | 12.5 | 32 | 10 | 4,000bps | 4kbps |
于是全篇的取舍,都能落到三个旋钮上:帧率、码本数、码本大小。想要音质,三个都想往大拧;想让 LM 好学,帧率就得低(序列短)、码本数就得少(最好是 1,一帧一个 token)——于是只剩码本大小能加(所以 X-Codec2 敢用 65536),或者干脆把 tokenizer 本身做大(所以 MOSS 用 16 亿参数换质量)。
SoundStream 论文里有个特别直观的例子:同样是 6kbps,你可以选「8 个码本 × 1024」、「16 × 32」、甚至「80 × 2」——比特率一模一样,对 LM 的友好度却天差地别。量化这门手艺的全部张力,就在这一句里。
天差地别在哪?先算 token 总量(= 帧率 × 码本数):8×1024 每秒产 600 个 token,80×2 每秒 6000 个——同一个 4096 token 的上下文窗口,前者装得下 6.8 秒音频,后者只装 0.68 秒。再看单个 token 的含金量:1024 条目的 token 一个值 10 比特,抓得住一帧的粗轮廓;2 条目的一个只值 1 比特,LM 花一整步前向、只为预测一次抛硬币,而且第 1 层码本再也扛不住粗轮廓,「先建好第 1 层」的那类设计(马上会见到)全没了抓手。说到底,码本做大费的只是查表内存,流数做多费的却是步数、延迟和上下文——这 80 比特该折进码本大小,而不是流数。至于每帧欠下的这笔账到底怎么还,下一小节细算。
追问:LM 一步只出一个 id,N 个码本怎么办
读到这里你可能会问:自回归 LM 的一步,天生就是「一个隐状态 → 一个 softmax → 一个 id」,那 RVQ 一帧的 N 个 id 到底怎么生成?
先排除一个想当然的答案:把 N 个 id 打包成一个「联合 token」不行——词表会变成码本大小的 N 次方(1024⁸ 是 10²⁴ 量级),softmax 铺不开。所以只能是 N 个各自的分布,每个在自己的码本词表上。但这 N 个 id 之间又有依赖(第 2 层是第 1 层的残差),也不能天真地并行各采各的。业界给出了四种解法:
- 摊平:把「帧 1 码本 1、帧 1 码本 2……帧 2 码本 1……」排成一条序列,LM 老老实实一步一个。依赖关系完整,但序列长 N 倍、慢 N 倍——75Hz × 8 码本 = 每秒 600 步,基本没人真这么干,只当理论基线。
- 延迟交错(MusicGen):一步用 N 个头并行出 N 个分布,但把各条码本流错开一拍——第 k 层码本晚 k 步。这样预测某帧的码本 2 时,同帧的码本 1 已经在上一步生成、进了上下文,用一个小小的时间差近似补回帧内依赖。步数不变,代价是轻微近似。
- 先自回归,再并行填(VALL-E):主 LM 只自回归生成第 1 层(粗轮廓——量化器 dropout 逼出的「第 1 层最重要」在这儿兑现),其余 7 层交给一个非自回归模型,拿着完整的第 1 层序列一次性填出来。像先写完大纲再统一上色;代价是第二段不再流式。
- 主干+深度小模型(Qwen3-TTS 的 MTP、Moshi 的 Depth Transformer):主干每步只预测第 0 层,旁边挂一个小模块,在同一步内沿「深度」方向把其余残差层补出来——Qwen3-TTS 叫它 MTP(多 token 预测),Moshi 则是一个小号 Depth Transformer 配大号 Temporal Transformer,还叠了一手「声学延迟」(声学 token 比语义 token 晚 1–2 步,生成更稳)。主干仍是标准的一步一 id,多码本的负担卸给了小模型,流式也保住了——12Hz 那条线 97ms 首包就是这么来的。
收个账:码本数 = 每帧欠几笔账。①用时间还账,②用错位摊账,③④把账转给另一个模型——而单码本设计干脆让每帧只欠一笔,账本身消失了。「单量化器天然契合 LLM 自回归」那句话,说的就是这个。
一句话记住这节:单码本(VQ 或 FSQ)一帧只吐一个 token,对自回归 LM 最省心;RVQ 音质好,但要额外想办法摊平多码本。 这条「LM 友好度」的线,会贯穿后面所有的新设计。
四、关键一招:把语义塞进第 0 个码本
AudioLM 那种「两套 token」终归啰嗦。2023 年的 SpeechTokenizer 想了个漂亮主意:还是一个 RVQ codec,但训练时逼第一个码本去对齐语义——让 RVQ 第一层的输出去「蒸馏」HuBERT 的语义特征(最大化两者的余弦相似度)。于是第一层码本管「说什么」,剩下的层管「怎么响」。一个 codec,第 0 层是拼音,后面几层是录音。(编号习惯各家不同:SpeechTokenizer 叫它 RVQ-1,Qwen 叫「第 0 层码本」——说的都是同一个,最先、也最「懂内容」的那一层。)
这一招后来成了主流。Kyutai 的 Mimi(用在对话模型 Moshi 里,2024)就是它的现代版:12.5Hz、一共 8 个码本 = 1 个语义(蒸馏自 WavLM)+ 7 个声学 RVQ,全因果、可流式,码率才 1.1kbps。语义放第 0 层的好处很直接:语言模型先把第 0 层的语义序列建好,就先抓住了「内容」这条主线,音色细节交给后面的残差层去补。
(眼熟吗?上一篇 Qwen3-TTS 的 12Hz tokenizer——1 个语义 + 15 个声学——正是这门手艺。第六节我们再回到它。)
五、新潮流:更低的帧率、更少的码本
把这条线拉直,最近两年的语音 tokenizer 明显朝两个方向走,动机都是让自回归 LM 更好生成:帧率越低、码本越少,token 序列就越短、越像「写字」,LM 逐个往外蹦时就越省力。
方向一:把码本砍到 1 个。 既然多码本是摩擦,干脆只留一个。WavTokenizer(2024)用单个 VQ、每秒才 40–75 个 token(码本 4096),却宣称在 0.9kbps 上就超过了 DAC 在 9kbps 的音质。X-Codec2(2025,被 Llasa 用作 TTS 前端)走得更彻底:用 FSQ 单码本(大小 65536),把语义和声学特征拼在一起、一次量化成一个 token——Llasa 论文的原话是,单量化器「天然契合大模型从左到右的自回归机制」。BigCodec 也是单码本(VQ 8192、80Hz)。单码本 = 一帧一个 token = 一条能像文字一样建模的流。
方向二:帧率压到 12.5Hz,但把 tokenizer 做大。 另一些工作保留多码本、主攻低帧率。除了 Mimi,2026 年的 MOSS-Audio-Tokenizer 很有代表性:一个 16 亿参数、纯 Transformer(不用卷积)的 codec,在 300 万小时音频上从头训,12.5Hz、32 层 RVQ、码率可变(0.125–4kbps),流式,还统一了语音 / 音效 / 音乐。它不走单码本,而是用「低帧率 + 大模型」换质量。
顺带提一路「监督语义」的分支:阿里 CosyVoice 把量化器直接插进一个 ASR 编码器(而不是自监督特征),于是 token 天生就对齐内容(所以叫「监督语义」,区别于 HuBERT 那种自监督)。CosyVoice 1 是单码本 VQ(4096);CosyVoice 2 换成 FSQ、25Hz、码本 6561 且利用率 100%。这一路的特点是:语义对齐好、单码本、天生适合喂给 LM。
六、案例:Qwen3-TTS 的两把刀
有了这张全景,再看上一篇 Qwen3-TTS 的选择就清楚了:它没做一个「全能」tokenizer,而是给两个场景各配一把刀,正好落在地图的两个位置。
- Qwen-TTS-Tokenizer-25Hz:单码本(大小 32768)、25Hz,站在 Qwen2-Audio 上两阶段训练(先在 ASR 任务上续训、插入 VQ 层,再接 mel 解码器重建);波形还原用「分块 DiT + flow matching」这套扩散解码,首包约 138–150ms。它偏「单码本语义 + 扩散还原」,主攻长文本自然度。
- Qwen-TTS-Tokenizer-12Hz:16 层多码本 = 1 个语义(蒸馏自 WavLM)+ 15 个声学 RVQ、12.5Hz,用 GAN 训练,全因果、配一个轻量因果 ConvNet 解码器,首包 97ms。它就是第四节那门「语义塞进第 0 层」的手艺,主攻实时。
放回地图上:25Hz 那把刀站在「单码本、中等帧率、扩散解码」,12Hz 那把刀站在「低帧率、统一 RVQ、流式」——一个像 CosyVoice 的单码本语义路子,一个像 Mimi 的统一码本路子。两把刀,恰好各借了一条潮流。
七、一张地图,和写在最后
把这一路的名字摆到同一张图上——横轴帧率、纵轴每帧码本数——脉络就出来了:
再用一张表收束(数字均来自各自的论文 / 官方仓库):
| tokenizer | 年份 | 帧率 | 量化 | 语义/声学 | 谁在用 |
|---|---|---|---|---|---|
| SoundStream | 2021 | 75Hz | RVQ(8) | 声学 | AudioLM |
| EnCodec | 2022 | 75Hz | RVQ(≤32) | 声学 | VALL-E、MusicGen |
| DAC | 2023 | ~86Hz | RVQ(9) | 声学 | 通用 codec |
| w2v-BERT 语义 | 2021 | 25Hz(AudioLM 用法) | k-means | 语义 | AudioLM |
| SpeechTokenizer | 2023 | ~50Hz | RVQ(8,第 1 层蒸馏语义) | 统一 | 开源研究 codec |
| Mimi | 2024 | 12.5Hz | 1 语义 + 7 声学 | 统一·流式 | Moshi |
| WavTokenizer | 2024 | 40–75/s | 单 VQ(4096) | 统一 | 通用 codec |
| BigCodec | 2024 | 80Hz | 单 VQ(8192) | 声学 | 通用 codec |
| X-Codec2 | 2025 | 50/s | 单 FSQ(65536) | 统一 | Llasa |
| CosyVoice 2 | 2024 | 25Hz | 单 FSQ(6561) | 监督语义 | CosyVoice |
| MOSS-Tokenizer | 2026 | 12.5Hz | RVQ(32) | 统一·流式 | MOSS 音频 |
| Qwen 25Hz | 2026 | 25Hz | 单码本(32768) | 语义为主 | Qwen3-TTS |
| Qwen 12Hz | 2026 | 12.5Hz | 1 语义 + 15 声学 | 统一·流式 | Qwen3-TTS |
三个值得记住的收束:
- tokenizer 是 TTS 的地基。 它定了上面的语言模型好不好学、能不能边说边出声、还原得像不像——选错了地基,主干再大也补不回来。
- 主线是「统一」和「更省」。 一是把语义和声学揉进一套 token(语义放第 0 层),让一个 codec 既能懂又能听;二是把帧率压低、码本砍少(甚至到 1 个),让自回归 LM 生成得更顺。
- 没有免费午餐,所以有人干脆配两把刀。 单码本省心但音质天花板低,多码本音质好却难喂 LM,低帧率快却要更大的模型——每一处都是取舍。Qwen3-TTS 的两个 tokenizer,就是把这份取舍摆到明面上的答卷。
这也和本系列一以贯之的那句话呼应:离散 vs 连续 说「理解用连续、生成用离散」,Qwen3-TTS 说「一个全能表示满足不了所有目标,就给不同场景各配一个」。语音 tokenizer 这台造币厂,正是这句话最细的注脚。
参考
- Neural Discrete Representation Learning(VQ-VAE)(arXiv:1711.00937,VQ、直通估计与承诺损失的源头)
- SoundStream: An End-to-End Neural Audio Codec(arXiv:2107.03312,RVQ、量化器 dropout 与流式神经 codec 的开山)
- High Fidelity Neural Audio Compression(EnCodec)(arXiv:2210.13438,VALL-E 用的 codec)
- High-Fidelity Audio Compression with Improved RVQGAN(DAC)(arXiv:2306.06546)
- AudioLM: a Language Modeling Approach to Audio Generation(arXiv:2209.03143,语义 token + 声学 token 分层)
- HuBERT(arXiv:2106.07447,自监督语义 token 的代表)
- SpeechTokenizer(arXiv:2308.16692,第一层码本蒸馏语义,统一语义与声学)
- Moshi(含 Mimi tokenizer)(arXiv:2410.00037,12.5Hz、1 语义 + 7 声学、流式)
- WavTokenizer(arXiv:2408.16532,单量化器极致压缩)
- Llasa(用 X-Codec2 做单码本 TTS)(arXiv:2502.04128,单量化器契合 LLM 自回归)
- BigCodec(arXiv:2409.05377,低码率单码本)
- Finite Scalar Quantization(FSQ)(arXiv:2309.15505,不学码本、几乎不塌缩)
- CosyVoice 2(arXiv:2412.10117,监督语义 + FSQ 单码本、25Hz)
- MOSS-Audio-Tokenizer(arXiv:2602.10934,16 亿参数、12.5Hz、32 层 RVQ)
- Qwen3-TTS Technical Report(arXiv:2601.15621,两个 tokenizer 的来源)