声音进入 LLM 的路与图片相似又不同。相似的是范式——一个编码器把音频变成向量序列,一个 connector 对齐到 LLM,然后像文本一样处理;Whisper 的 encoder 之于语音,就像 CLIP ViT 之于图片。不同的是语音要双向:VLM 几乎只做理解(看图说话),语音模型从一开始就要既听又说——语音助手要用语音回答。生成侧要求把 LLM 的输出变回波形,而波形是每秒 16000–48000 个数的连续信号,不能像文本一样逐 token 生成。

这就是语音比图片更需要离散 token 的原因:神经 codec 把一秒音频压成几十到几百个离散 token,让 LLM 可以用生成文本的方式生成语音。语音分上下两篇:上篇(本篇)讲表示——声音在计算机里是什么、怎么变成编码器能吃的 mel 谱、Whisper 怎么把它变成特征、神经 codec 怎么把它变成离散 token(RVQ 是全篇的核心推导);下篇讲用法——语音理解、语音生成(TTS)、全模态与全双工对话的时延账。

本篇要回答的核心问题是:

一秒钟的声音在模型眼里是什么?1 语音为什么比图片更需要离散 token,RVQ 又是怎么用几个小码本表示高保真音频的?2

一、总览:两条路、三个层次

1. 声音进入 LLM 的两条路

声音进入 LLM 的两条路
路 表示 理解 生成 代表
连续特征 音频编码器(Whisper encoder 一类)的隐状态,经 connector 进 LLM 好——保留全部声学细节 不能直接生成;需外接 TTS Qwen-Audio / Qwen2-Audio、SALMONN、LLaMA-Omni(理解侧)
离散 token 神经 codec(EnCodec 一类)的量化码,或语义 token(HuBERT 聚类) 可以,但有信息损失 可以——LLM 自回归生成 token,codec 解码成波形 AudioLM、VALL-E、SpeechGPT、Moshi、GPT-4o(推测)
混合 理解用连续特征,生成用离散 token 好 好 Qwen2.5-Omni、Mini-Omni、GLM-4-Voice

VLM 几乎全在第一行(理解用连续特征),因为它们不生成图片。语音模型要生成,主流做法是让 LLM 输出离散 token(第二、三行)——但这是工程上的选择而不是唯一可行:下篇第四章的 F5-TTS / CosyVoice 用 flow matching 直接生成连续的 mel 谱,WaveNet 早就自回归地生成过波形;离散 token 赢在能复用 LLM 的整套训练与推理基础设施、序列短。这是核心问题后半的答案,第三章展开。

2. 三个层次的 token

音频 token 分三个层次,对应压缩的三个粒度:

音频 token 的三个层次
层次 来源 每秒 token 数 保留什么 丢什么
声学 token(acoustic) codec 的 RVQ 码(EnCodec:75 帧/秒 × 8 码本) 几百 音色、韵律、噪声——可以高保真重建 无(有损压缩)
语义 token(semantic) 自监督模型(HuBERT / w2v-BERT)的隐状态聚类 25–50 音素级内容、部分韵律 音色、细节——不能重建高保真波形
文本 token ASR 2–4(词) 内容 一切非语言信息

一段一分钟的语音:声学 token 约 3.6 万(EnCodec 8 码本)、语义 token 约 1500–3000、文本 200 词。语音 LLM 要在这三层之间选择或组合:AudioLM 的做法是先生成语义 token 再生成声学 token(内容与声音分开);Moshi 的做法是把语义与声学 token 一起以多流的方式生成——都在下篇。

3. 本文的章节安排

本文按”信号 → 特征 → 离散 token”的压缩顺序组织:先看声音本身是什么、怎么变成 mel 谱(每秒 16000 个数 → 100 个 80 维向量),再看编码器怎么把 mel 谱变成特征(Whisper、HuBERT),最后看 codec 怎么把它变成几个整数(RVQ)。

本文的章节安排
章 主题 内容
二 音频的表示 波形是什么;分帧、FFT、mel 滤波器——用 40 行 NumPy 从一段合成语音算出 log-mel 谱(图)
三 编码器
  • Whisper 的 encoder-decoder
  • CTC 与 attention 解码
  • 自监督(HuBERT、w2v-BERT)与语义 token
四 神经 codec 与 RVQ
  • 为什么需要 codec
  • SoundStream / EnCodec 的结构
  • VQ 到 RVQ——一个 2 维手算例子 + 2000 个向量的实验(图)
  • 训练 RVQ 的四个技巧
  • 层次结构对生成的意义
五 动手(建议) EnCodec 的码本层数;配套代码
六 本文小结  
七 自测 5 道题

二、音频的表示

1. 波形:每秒 16000 个数

麦克风记录的是空气压力随时间的变化。采样把这条连续曲线每隔固定时间量一次:语音常用 16 kHz,即一秒量 16000 次,每次得到一个数(振幅,通常归一到 \([-1, 1]\))。所以一秒语音 = 一个长 16000 的数组,这就是波形(waveform)。它有两个让模型难受的性质:太长(一分钟 96 万个数,比任何 LLM 的上下文都长),以及”内容”不在单个数里——一个音是几百个连续的数组成的振动模式,模型看单个采样点什么都看不出。

2. 从波形到 mel 谱:三步

标准做法把波形变成”每 10 毫秒一个 80 维向量”,三步:

  1. 分帧:每 25 ms 切一段(400 个点),每隔 10 ms 切一次(步长 160 点)——相邻帧重叠 15 ms。一秒切出约 100 帧。人说话时声音的特性在 25 ms 内基本不变,所以一帧可以当”一个瞬间”。
  2. 每帧做傅里叶变换(FFT):把这 400 个点分解成”包含哪些频率、各多强”——一个音是几个频率的叠加,FFT 把它拆开。400 个点得到 201 个频点(0 到 8000 Hz),取幅度的平方(功率谱)。
  3. 合并成 mel 频带、取对数:人耳对低频敏感、对高频粗糙——100 Hz 与 200 Hz 听起来差很多,7000 Hz 与 7100 Hz 几乎一样。mel 刻度(\(m = 2595 \log_{10}(1 + f / 700)\))模拟这种感知;在 mel 轴上等距地放 80 个三角形滤波器,低频处窄而密、高频处宽而疏,201 个频点被合并成 80 个频带;再取对数(响度也是对数感知的)。

得到的就是 log-mel 谱:每秒约 100 帧 × 80 维。它是 Whisper 一类模型的输入;wav2vec 2.0、HuBERT 与 codec 编码器直接吃波形,用一维卷积自己学”滤波器”。三步各几行 NumPy:

def log_mel(wave, sr=16000, win=400, hop=160, n_mels=80):
    frames = np.stack([wave[i:i + win] * np.hanning(win) for i in range(0, len(wave) - win, hop)])   # ① 分帧:[T, 400],加窗让两端平滑
    power = np.abs(np.fft.rfft(frames, n=win)) ** 2                                                   # ② 每帧 FFT 取幅度平方:[T, 201]
    mel = power @ mel_filterbank(n_mels, win, sr).T                                                   # ③ 乘 80 个三角滤波器:[T, 80]
    return np.log(mel + 1e-10)                                                                        # ④ 取对数

(mel_filterbank 在配套代码里,十行:在 mel 轴上等距取 82 个边界点、换回 Hz、每三个相邻边界定义一个三角形。)对一段一秒钟的合成”语音”——基频 120 Hz 上下起伏的 11 个谐波,像一个拖长的元音,中间插一段 70 ms 的噪声模拟摩擦音——跑一遍:

  • 波形:16000 个采样点(16 kHz × 1 s)
  • 分帧:窗 400 点 = 25 ms,步长 160 点 = 10 ms → 98 帧
  • 每帧 FFT 得 201 个频点,乘 80 个 mel 三角滤波器 → 80 维;log-mel 形状 (98, 80)(真实 Whisper 输入:3000 帧 × 80)
  • 数据量:16000 个数 → 7840 个数,压缩 2.0 倍;一秒语音 = 98 个 80 维向量

左:波形的前 50 ms,锯齿状的周期振动,橙色阴影标出一帧 25 ms 的范围;中:80 个 mel 三角滤波器里每隔 8 个画一个,低频处窄而密、6000 Hz 处宽;右:log-mel 谱热力图,横轴 0–1 秒、纵轴 80 个 mel 频带,底部几条随时间起伏的亮横纹是谐波,0.55–0.62 秒处一条竖直的亮带是那段噪声

怎么读右图:横轴是时间、纵轴是频率(mel 频带)、颜色是响度。底部随时间起伏的亮纹是那 11 个谐波——它们跟着基频一起上下弯,这就是”音调”在谱图上的样子;0.55 秒处那条贯穿所有频带的竖线是噪声(噪声在所有频率上都有能量)。语音识别模型看到的就是这样一张”图”——这也是为什么 Whisper 的 encoder 前两层是卷积:它把 mel 谱当图像处理。

三、编码器

1. Whisper

Whisper(Radford 等 2022)是语音的”CLIP”——一个在 68 万小时(v3 用 500 万小时含伪标签)多语言、多任务数据上训的 encoder-decoder Transformer,任务是转写(ASR)与翻译。encoder 输入 30 秒的 log-mel(3000 帧 × 80;large-v3 改为 128 个 mel 频带),过两层卷积——第一层步长 1、第二层步长 2(两层都是 2 会变成 750 帧;HF modeling_whisper.py 里 conv1 stride 1、conv2 stride 2)→ 1500 帧、20 ms 一帧,加正弦位置编码,32 层 Transformer(large 的 1.55B 是 encoder + decoder 合计,encoder 约 0.64B);decoder 是标准的自回归文本 decoder,输出转写。

它的 encoder 输出(1500 × 1280)是语音理解任务最常用的特征——就像 CLIP ViT 的 patch 特征。04-08第七章算过它的成本:30 秒固定 1500 个位置(不足 30 秒 pad),是 VLM 里 576 个 patch 的 2.6 倍。Qwen2-Audio 用 Whisper-large-v3 的 encoder,再 pool 到 25 Hz(每秒 25 个特征,30 秒 750 个),进 LLM。

2. CTC 与 attention 解码

ASR 的两种解码:CTC(Connectionist Temporal Classification)让 encoder 的每一帧独立输出一个字符或”空白”,去重去空白得到转写——不需要 decoder、并行、流式友好,但假设帧间条件独立,语言模型能力弱;attention decoder(Whisper)自回归地生成文本,每步 attend 到全部 encoder 输出——准确但非流式(要等 encoder 看完一整段)。工业 ASR 常用 RNN-T(Transducer)作为两者的折中——流式且有隐式语言模型。语音 LLM 里 Whisper encoder 的 attention 特征被 LLM 消费,LLM 本身就是 decoder。

3. 自监督编码器:HuBERT 与 w2v-BERT

对应视觉的 DINOv2:不用标签,在海量无标注语音上学表示。wav2vec 2.0 用对比目标(预测被 mask 的帧的量化表示);HuBERT 用 k-means(L2 第七篇)聚类得到的伪标签做 masked prediction——遮住一段 mel 谱,让模型预测被遮部分属于哪个簇——迭代地用自己的隐状态重新聚类;w2v-BERT 结合两者。它们的隐状态(尤其中间层)编码音素级内容——对隐状态做 k-means(500–2000 类)得到的离散码就是语义 token,AudioLM、SpeechGPT 用它做内容表示。它们不编码音色(说话人信息在 k-means 中被丢掉)——这是”语义”的含义。

四、神经 codec 与 RVQ

1. 为什么需要 codec

要让 LLM 生成语音,需要一个离散的、序列不太长的、能重建波形的表示。mel 谱是连续的且每秒 100 帧;波形每秒 16000 个点;语义 token 离散但不能重建音色。神经 codec 填这个空:把波形压成每秒几十帧、每帧几个离散码,且能高保真解码回波形。

2. SoundStream / EnCodec 的结构

SoundStream(Zeghidour 等 2021)与 EnCodec(Défossez 等 2022)的结构相同:

  • 编码器:一维卷积网络,对波形逐级下采样(EnCodec 24 kHz:步长 2, 4, 5, 8 共 320 倍 → 75 帧/秒),每帧一个 128 维的连续向量。
  • 量化器:残差向量量化(RVQ,下一节),把每帧的 128 维向量变成 \(N_q\) 个离散码(\(N_q = 8\),每个码本 1024 项 = 10 bit)。
  • 解码器:卷积网络镜像上采样,从量化后的向量重建波形。
  • 训练:重建损失(波形 L1 + 多尺度 mel 谱损失)+ 对抗损失(多个判别器判断真假)+ 感知特征匹配 + RVQ 的 commitment 损失。

比特率:75 帧/秒 × 8 码本 × 10 bit = 6 kbps——比 MP3 的 128 kbps 低 20 倍,主观质量接近。

3. RVQ 的推导

先说清”量化”。编码器每帧输出一个 128 维的连续向量 \(z\)——128 个小数。要把它变成一个整数(token),办法是准备一张”字典”:\(K\) 个预先选好的 128 维向量 \(\{e_1, \ldots, e_K\}\),叫码本(codebook),每个叫码字(codeword)。向量量化(VQ)就是查字典:找离 \(z\) 最近的码字,用它的编号 \(k^*\) 代替 \(z\):

\[k^* = \arg\min_k \lVert z - e_k \rVert, \qquad \hat z = e_{k^*}\]

传输或建模时只需要整数 \(k^*\),解码时查表得到 \(\hat z\)——它不等于 \(z\),差的那部分是量化误差。这与 L2 第七篇的 K-Means 是同一件事:码本就是簇中心,量化就是”归到最近的中心”,训练码本就是跑 K-Means。

问题是精度。\(K = 1024\) 时每帧 10 bit,用 1024 个点去覆盖 128 维空间里所有可能的 \(z\),误差太大——一个 2000 个 16 维向量的 toy 上,64 个码字的 VQ 重建误差是原方差的 31%。要高保真需要 \(K\) 巨大(\(2^{80}\) 一类)——存不下也查不了。

残差向量量化把量化分成多级:

\[r_0 = z, \quad k_i = \arg\min_k \lVert r_{i-1} - e^{(i)}_k \rVert, \quad r_i = r_{i-1} - e^{(i)}_{k_i}, \quad i = 1, \ldots, N_q\] \[\hat z = \sum_{i=1}^{N_q} e^{(i)}_{k_i}\]

第一个码本量化 \(z\) 本身,第二个码本量化第一级的残差(\(z\) 减去第一级选中的码字,即”还没被表示的部分”),第三个量化第二级的残差……每级一个独立的码本(\(K = 1024\))。重建是各级码字之和。

2 维手算一遍:\(z = (3.2, 1.1)\),第 1 级码本有三个码字 \(\{(3, 1), (-3, 1), (0, -2)\}\),第 2 级码本 \(\{(0.2, 0.1), (-0.2, 0.1), (0, -0.1)\}\)——注意第 2 级的码字都很小,它们是为残差准备的。

RVQ 两级量化的手算过程
级 面对的向量 码本里最近的码字 编号 剩下的残差
1 \(z = (3.2, 1.1)\) \((3, 1)\) 0 \((0.2, 0.1)\)
2 \(r_1 = (0.2, 0.1)\) \((0.2, 0.1)\) 0 \((0, 0)\)

重建 \(\hat z = (3, 1) + (0.2, 0.1) = (3.2, 1.1)\),这个向量的”token”是两个整数 \((0, 0)\)。两级各 3 个码字,能表示 \(3 \times 3 = 9\) 种组合;\(N_q = 8\) 级、每级 1024 时是 \(1024^8 = 2^{80}\) 种——用 8 个小码本得到一个巨大的等效码本,且每级的搜索只是 1024 次比较。代码就是一个循环:

def rvq_encode(z, codebooks):
    r, codes, z_hat = z.copy(), [], np.zeros_like(z)
    for cb in codebooks:                                                # 每级一个码本 [K, d]
        k = ((r[:, None] - cb[None]) ** 2).sum(-1).argmin(1)            # ① 在这一级码本里找离残差最近的码字
        codes.append(k)
        z_hat += cb[k]                                                  # ② 累加到重建
        r = r - cb[k]                                                   # ③ 剩下的残差交给下一级
    return np.array(codes), z_hat

训练也简单:第 1 级码本对全部 \(z\) 做 K-Means,第 2 级对第 1 级的残差做 K-Means,依此类推。2000 个 16 维向量、8 级、每级 64 个码字:

单码本 VQ,K = 64(6 bit):相对重建误差 0.313
RVQ 8 级、每级 K = 64:只用前 n 级解码的相对误差 n=1:0.314  n=2:0.161  n=3:0.089  n=4:0.051  n=5:0.030  n=6:0.018  n=7:0.010  n=8:0.006
每级之后残差的均方根:1.25  0.90  0.66  0.50  0.38  0.30  0.23  0.18(逐级变小:后面的码本在越来越小的尺度上精修)
等效码本大小 64^8 = 2^48,每个向量 48 bit

左:横轴解码时用的码本级数 1–8,纵轴相对重建误差(对数轴),从 0.31 沿直线降到 0.006,灰色虚线是单码本 VQ 的 0.31;右:柱状图,每级之后残差的均方根从 1.25 递减到 0.18

为什么它有效:右图——每一级的残差 \(r_i\) 的范数递减(每级去掉了一部分),后面的码本在越来越小的尺度上精细化;左图——每多用一级,误差大约减半,8 级把误差压到单码本的 1/50,而码本总大小只是单码本的 8 倍。这天然形成由粗到细的层次:第 1 个码本编码最主要的信息(内容与大致音色),后面的码本编码细节(高频、微弱的噪声)。只用前 \(n\) 个码本解码,得到的是一个低比特率、质量较差但内容完整的重建——EnCodec 用同一个模型支持 1.5 / 3 / 6 / 12 kbps(2 / 4 / 8 / 16 码本)就是靠训练时随机丢弃后面的码本(quantizer dropout)。

上面的 toy 里编码器输出是固定的,真实 codec 的编码器与码本一起训,多出四个问题(第九篇 VQ-VAE 详述):(1)\(\arg\min\) 是”选一个编号”,对 \(z\) 的微小变化输出不变或跳变,没有可用的导数,梯度传不回编码器——用 STE把梯度直通过去;(2)码本用 EMA(滑动平均,见第一篇 DINOv2)更新而不是梯度——每个码字慢慢移向被分到它的那些 \(z\) 的均值,其实就是在线的 K-Means;(3)加 commitment 损失 \(\lVert z - \text{sg}(\hat z) \rVert^2\)(sg = 停止梯度)让编码器的输出主动靠近码字,别乱跑;(4)用 K-Means 初始化码本、并对长期没被选中的码字随机重置,防止码本坍缩——只有少数码字被用到、其余闲置(上面 toy 的 64 个码字每级都用满了,真实训练里不加对策常常只剩几十个活着)。

4. 声学 token 的层次结构对生成的意义

RVQ 的层次结构决定了语音生成的形态:第 1 个码本的 token 序列(75/秒)承载内容,可以由一个自回归模型精确地生成;后面 7 个码本是”细化”,彼此依赖弱、可以并行生成——这正是 VALL-E 的 AR + NAR 设计(下篇第二章)。以及 AudioLM 的三阶段:语义 token → 粗声学 token(前几个码本)→ 细声学 token。

5. 语义 token 与声学 token 的分工

声学 token 保留一切但序列长(75 × 8 = 600/秒)且”内容”埋在细节里,LLM 直接在它上面建模语言内容效率低。语义 token 短(25–50/秒)、内容清晰,但不能重建。所以很多系统两者都用:LLM 在语义 token(或文本 + 语义 token)上做内容建模,再用一个较小的模型从语义 token 生成声学 token 并解码。近年的 codec(SpeechTokenizer、Mimi)试图把两者合一:让 RVQ 的第一个码本被蒸馏成语义 token(用 WavLM / HuBERT 的表示做目标),后面的码本承载声学细节——一个 codec 同时给出语义与声学,Moshi(下篇第三章)用的 Mimi 就是这样(12.5 Hz、8 码本、1.1 kbps,第一码本语义蒸馏)。

五、动手(建议)

  • RVQ 的层次:用 encodec(或 transformers 的 EncodecModel)对一段 10 秒语音编码,分别用 2 / 4 / 8 个码本解码(对应 1.5 / 3 / 6 kbps),听三段重建,并用一个 ASR(Whisper-small)转写测 WER、用 UTMOS 打分。看第一个码本承载了多少内容、后面的码本改善了什么。
  • 语义 vs 声学:用 HuBERT-large 的第 9 层特征做 k-means(500 类)得到语义 token,用一个开源的 unit vocoder 解码,与 EnCodec 重建对比——语义 token 的重建应该内容对但音色变了。
  • mel 谱:把配套代码里的合成波形换成你自己录的一句话(soundfile 读 wav、重采样到 16 kHz),看谱图上元音的谐波与辅音的噪声。

该看的:2 码本的 WER 是否已接近 8 码本(内容在第一码本)而 MOS 差很多;语义 token 重建的说话人相似度是否低。不引用任何未跑过的数字。

配套代码:第二章的 mel 谱与第四章的 VQ / RVQ 实验、两张图由 multimodal/04_audio_mel_and_rvq.py 产生(mel / rvq),纯 NumPy,CPU 几秒。

六、本文小结

音频编码器与 codec 的规则小结
项 规则 备注
波形 16 kHz:一秒 16000 个数 太长、单个数无意义
log-mel 25 ms 窗 / 10 ms 步分帧 → FFT 功率谱 → 80 个 mel 三角滤波器 → log 一秒 ≈ 100 帧 × 80 维;Whisper 输入 3000 × 80
Whisper encoder-decoder,68 万小时;encoder 输出 1500 × 1280 / 30 s 语音的 CLIP;理解任务的特征来源
解码 CTC(并行、流式、弱 LM)vs attention(准、非流式);RNN-T 折中 语音 LLM 里 LLM 就是 decoder
自监督 HuBERT / w2v-BERT 的隐状态 k-means → 语义 token(25–50/秒) 有内容无音色,不能重建
两条路
  • 连续特征(理解好、不能说)
  • 离散 token(能说、理解有损)
  • 混合
语音要双向 → 需要离散 token
三层 token
  • 声学(几百/秒,可重建)
  • 语义(25–50/秒,内容)
  • 文本(2–4/秒)
一分钟:3.6 万 / 3000 / 200
VQ 码本 = 字典,量化 = 查最近码字 = K-Means 归类 单码本精度不够(toy:误差 31%)
RVQ \(r_i = r_{i-1} - e^{(i)}_{k_i}\),\(N_q\) 个 1024 码本 ≈ \(2^{10 N_q}\) 等效码本
  • 由粗到细,每级误差约减半
  • quantizer dropout 支持多比特率
  • STE + EMA + commitment + 重置
语义蒸馏 第一码本蒸馏 HuBERT / WavLM(SpeechTokenizer、Mimi) 一个 codec 同时给语义与声学

七、自测

  1. 16 kHz 一分钟语音在三层 token 下各多少个?文本呢?

    答案

    声学 token 几百 / 秒 → 约 3.6 万;语义 token 25–50 / 秒 → 约 3000;文本 2–4 / 秒 → 约 200。语音输入比同样内容的文本贵 15 倍以上。

  2. 一段 3 秒、16 kHz 的语音,按 25 ms 窗 / 10 ms 步分帧、80 个 mel 频带,log-mel 谱是多大的矩阵?相比波形压缩了几倍?

    答案

    3 秒 = 48000 个点,帧数 ≈ (48000 − 400) / 160 ≈ 297 帧,矩阵 297 × 80 = 23760 个数;48000 / 23760 ≈ 2 倍。mel 谱不是为了压缩数据量,是为了把”振动模式”变成”每个瞬间有哪些频率”这种模型能读的形式。

  3. 为什么语音比图片更需要离散 token?只用连续特征能做什么、不能做什么?

    答案

    语音要双向——既要听也要说;连续特征(Whisper encoder → 投影)理解好但 LLM 无法”生成”连续特征去合成语音;离散 token 让生成侧也能用 next-token,代价是理解有损。混合方案两边各取。

  4. RVQ 用 8 个 1024 大小的码本,等效码本多大?为什么不用一个大码本?

    答案

    \(2^{10 \times 8} = 2^{80}\) 等效码字;一个 \(2^{80}\) 的码本无法存储与查找,RVQ 由粗到细逐级量化残差 \(r_i = r_{i-1} - e^{(i)}_{k_i}\),每级只查 1024 个。quantizer dropout 还让同一模型支持多比特率。

  5. \(z = (5.1, -2.0)\),第 1 级码本 \(\{(5, -2), (-5, 2), (0, 0)\}\),第 2 级码本 \(\{(0.1, 0), (-0.1, 0), (0, 0.1)\}\)。两级的编号各是多少?重建是什么?

    答案

    第 1 级最近的是 \((5, -2)\),编号 0,残差 \((0.1, 0)\);第 2 级最近的是 \((0.1, 0)\),编号 0,残差 \((0, 0)\)。重建 \((5, -2) + (0.1, 0) = (5.1, -2.0)\),token 是 \((0, 0)\)。

  1. 一个每秒 16000 个数的波形,先按 25 ms 窗、10 ms 步切成约 100 帧,每帧 FFT 后合并成 80 个 mel 频带取对数——一秒语音变成 100 个 80 维向量(log-mel 谱),像一张”时间 × 频率”的图;编码器(Whisper)再把它变成每 20 ms 一个的特征向量。详见第二章、第三章。 ↩

  2. 因为语音模型要生成——VLM 只需理解,连续特征进 LLM 就够;而把 LLM 的输出变回每秒上万个采样点的波形,最省事的方式是让 LLM 生成一个短的离散序列(codec token,每秒几十帧、每帧几个码),再用 codec 解码器还原波形——不是唯一可行(flow matching 生成连续 mel、自回归波形都能做),而是与 LLM 的训练 / 推理栈最兼容。RVQ 逐级量化残差,用 8 个 1024 项的小码本得到 \(2^{80}\) 的等效表示能力,每级误差约减半,且自然分层(第一码本内容、后面细节)——这个层次直接决定了下篇 VALL-E 的 AR + NAR 结构与 Moshi 的多流建模。详见第四章。 ↩

这篇对你有用?

本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/speech-and-omni-models-audio-encoders-codecs-and-duplex.html)的前提下,欢迎各种形式的转载、翻译或商业引用。


COMMENTS

评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。

×