上一章《生成模型家族图谱》刚选完五条路线。但它们只在文字上有用吗?
把同一套零件摊开、逐个拨到另外三种模态上看一遍,答案是:
真正要重写的只有四个零件,其中 tokenizer 最吃领域知识。
它们为什么非改不可,背后是
这门课从头到尾讲的是同一台机器:把一串向量变得有用。 注意力、残差、LayerNorm、反向传播、AdamW、扩散、训练循环—— 但你可能已经发现,这些章的例子几乎全是文字。 那么换成像素、换成波形、换成帧,这台机器还转不转?
这个问题的答案是让人喘一口气的,而且它本身就值一条第一性原理: 核心机制大部分是通用的。这不是巧合——因为它们本来就只处理「一串向量」, 从不关心那串向量是从哪儿来的。
同一套厨房设备做中餐和做西餐:灶、火候控制、尝味调整全都一样用。
真正要换的只有两样——刀工(怎么把食材切成能被加工的小块)
和最后装盘的样子(端出来的是什么)。
这一章要做的就是:把四个灶台并排摆开,让你亲眼看见哪几个零件根本没动。
下面这张数据流图从头到尾只有一张。点上面的四个按钮切换模态—— 绿框四种模态一字不改, 粉框必须换掉, 琥珀框介于两者之间。 点任意一个格子,下面会摊开它在四种模态下分别是什么。
互动 · 拨到不同模态,看哪里断了、哪里没变
互动 · 打开四种模态:到底有几个零件必须换
同一台机器四种模态共用:注意力、残差 + LayerNorm、训练循环; 扩散和重建损失在图像 / 音频 / 视频上共用,到了文本要换一条路(离散的字没法直接加噪声,见第 4 节)。 换模态要动的只有四个零件:tokenizer(怎么切)、输出头(吐出来是什么)、 位置编码(1D / 2D / 3D)和评测指标。
变的是零件,不变的是零件之间的接法。 节点本身没变,但同一套注意力撞上的序列长度差了几十倍—— 这笔账在数学一节、第 7 节和最后一节都会回来算。
每个位置都有三串数:Q(我想找什么)、 K(我挂出来什么)、V(我携带的内容)。 Q 和 K 逐个做点积,点积越大 = 越像 = 越该看它; softmax 把这些分数压成「加起来正好等于 1」的配比, 再按这个配比把大家的 V 混起来——所以「加权平均」平均的就是 V。 整段流程里没有一个地方提到过「字」或「像素」,完整式子在数学一节那张卡里。 (这里让 Q 和 K 共用同一张投影表,只是为了直接看相似度——投影就是乘一张表, 把一串数换成另一串数;真实注意力里 Q、K、V 各有各的可学投影表。)
互动 · 同一段注意力代码,四种真实输入
互动 · 为什么必须除以 √dk:不除,softmax 会一直很硬
四张热力图来自同一个函数:每种模态先切块,每块压成一串数(这里用的是降采样 + 一张随机投影表),再算相似度。 结果是文本和音频很尖(一行最大权重平均 0.70 / 0.66,「自己」所在的对角线最亮);图像和视频摊得很开(0.32 / 0.34)。 差别不在算法,在数据自带的结构。
扩散只做一件事:把信号一步步加噪,再一步步去噪。 它不关心信号是图像的一行、一段波形、还是视频里的一条时间切片—— 只要它是连续的数,公式就一个字都不用改。
前向:把原信号 x0 和一份高斯噪声
ε 按比例混起来,混合比例由第几步决定
(这条曲线叫余弦调度:按一条余弦曲线决定每一步保留多少信号——
扩散那一章讲过)。
反向:ε 是训练时我们自己加进去的,所以已知;生成时它是网络猜出来的——
「倒过来解」就是用猜到的 ε 从 xt 解回 x0。
所以整个扩散的灵魂只有一个问题:怎么把 ε 猜准。
而那跟模态无关,只跟「这个模态的数据长什么样」有关。
互动 · 真的加噪、真的去噪
把鼠标移到上面那张卡里的 t 或 ε 符号上——这两个滑块会分别亮起来。
t 是「第几步」,ε 是模型要猜的那份噪声。
互动 · ε 猜得准不准,四种模态的还原误差一模一样吗
「扩散不变」有个前提:信号得是连续的数。
文本的第一个反应是不同意——词是离散的,加高斯噪声加不出一个「半懂不懂的词」。
所以文本走的是另外两条路:① 先查嵌入表把词变成向量,在向量空间里加噪;
② 干脆改用离散扩散(那是另一条线,见
扩散语言模型)。
这一格是这套机制上唯一一个「到了文本就得换」的裂缝——记住它,
第 5 节里它和 tokenizer 一起构成了模态之间最不通用的一环。
上一节说「注意力不用改、连续信号上的扩散也不用改」。这句话在数学上可以再压缩成一个字母:n。
不管喂进去的东西是字、是像素、还是波形,到了这一步永远是一个 n × d 的矩阵
——d = 每个 token 被压成的那串数的长度(四种模态可以取同一个值)——
算子一模一样,只有 n 不一样。而注意力要建的那张表是 n × n:
模态之间真正的数学差别,就是这个 n 带来的账。
公式里还会出现 dk:那是 Q / K 的长度——多头注意力里常取 d 除以头数;
本页的演示取 dk = d。
互动 · 四种模态的 n 和注意力矩阵,随「内容时长」一起变
同一套数据流,量纲全在 n 上
互动 · 公式里每个符号,就是图上的一块
同一套机制,代价却是平方。下面四条曲线就是四种模态的 token 数: 横轴是内容时长,纵轴是 token 数。视频那条一路上扬,文本几乎趴在地上。
注意力这个算子对模态一无所知——它只认 n(序列多长)和 d(每个 token 的向量长度)。
所以「一套机制四种模态」在数学上就是一句话:同一个函数,喂不同的 n。
这句话的另一面是代价:函数没变,但 n 一变,算它的账就换了量纲——
第 7 节那条 62 GB 的视频账,根就在这里。
互动 ·
前面两节都在说「不用改」。这一节说必须改、而且最贵的那一环。 原因一句话:四个零件里,tokenizer 最需要「懂这个领域」。 它决定的是「什么算一个单位」——这是一个关于世界的问题,不是关于算法的问题。
先补一句音频的底:声音在电脑里是每秒 16000 个数(采样率 16 kHz); 频谱帧是把每 25 毫秒切一小段、看这一小段里各个音高有多响,每 10 毫秒切一次—— 换算下来每秒 100 帧。后面的「帧」和「码」指的都是这种切出来的单位。
| 模态 | 怎么切 | 切法是谁定的 | 切错了会怎样 |
|---|---|---|---|
| 文本 | BPE / 子词 | 从数据里统计出来(哪些字常一起出现就合并) | 还能纠回来——切碎了模型照样能从上下文猜出词 |
| 图像 | 16×16 的块(patch) | 人定死的(一个网格,和内容无关) | 相邻像素被拦腰截断;网格一挪,同一个块里的内容就换了 |
| 音频 | 频谱帧 | 时间和采样率定死的(25 ms 窗 / 10 ms 跳) | 「帧」和时间强绑定:10 秒就是 1000 帧,压缩不了就是算不动 |
| 视频 | 时空块 | 人定死的(时间 × 空间一起切) | 一刀切下去,帧与帧之间的运动只能靠模型自己补 |
一个自然会问的问题:图像和视频的切法是「人定死的」,那有没有人像 BPE 那样从数据里学切法? 有——按内容自适应切块的方法一直有人做,但主流仍是固定网格:它便宜,而且和硬件的对齐方式一致。
互动 ① · 文本的切法,真的切一遍
改这句话试试。下面用的是真的贪心最长匹配(BPE 的最朴素形式): 它在词表里找「能匹配上的最长片段」,匹配不上就退到单字符。
互动 ② · 拖一个位移:patch 序列立刻面目全非
下面是一条真实的 1-D 信号(128 个点)。绿色的线是卷积的输出, 粉色的线是切块(每 8 个点取平均 = 一个最朴素的 patch)的输出。 拖动位移,看两条线分别对「挪一点点」有多敏感。
互动 ③ · 二维的切块:网格线一挪,块里装的东西全变了
互动 ④ · 音频为什么必须先压缩:采样点 → 帧 → 码
卷积的归纳偏置是「平移等变」:把信号挪 3 个点,输出也跟着挪 3 个点,
数值一模一样。所以它不关心你从哪开始切。
切块没有这个性质——它默默假设「第 0 个点、第 8 个点、第 16 个点……是天然的分界」,
而这条分界是切的人规定的,不是世界规定的。
这也解释了 ViT 那个著名的麻烦:换输入分辨率就得给
把四种模态并排放在一起(图像和视频合成一列——它们共用同一套视觉主干)。 这张表就是这一章的骨架——请横向读「…变了吗」那三行。
互动 · 把上面那张表画成一张热力图
点画布上任意一格:这里会说明那个零件在该模态上是「换 / 半换 / 没变」。
注意力里唯一要动的开关:要不要遮住「未来」
| 零件 | 文本 | 图像 / 视频 | 音频 |
|---|---|---|---|
| 怎么切 token | BPE / 子词(~10 万词表) | 16×16 的 patch;视频再乘时间块 | 25 ms / 10 ms 的频谱帧;或神经编 |
| 主流架构 | decoder-only Transformer | DiT(扩散)/ U-Net;视频加时序注意力 | 自回归 + 声码器;或 encoder-decoder |
| 损失函数 | 交叉熵(预测下一个 token) | 重建(MSE / LPIPS)或扩散的 ε-MSE;视频再加光流 | 重建(mel L1)+ 对抗;识别用 CTC / 交叉熵 |
| 评测指标 | 困惑度 / 准确率 | FID / CLIP score / IS(图);FVD(视频) | WER(识别)/ MOS(合成)/ FAD |
| 1D(第几个词) | 2D(图)/ 3D(帧 + 行 + 列) | 1D(时间) | |
| 注意力变了吗 | —(它就是基准) | 没变 | 没变 |
| 扩散变了吗 | 要换(离散 → 走 |
没变 | 没变 |
| 重建损失变了吗 | 文本没有「重建」(除非用自编码目标) | 没变 | 没变 |
| 代表工作 | GPT / Llama | ViT / DiT / Stable Diffusion;Sora 一类的时空 DiT | Whisper / AudioLM / EnCodec |
「注意力没变」指的是那个算子(缩放点积 + softmax):换成模态,变的只是
要不要加因果掩码——文本和自回归音频要(不能偷看未来),图像与视频扩散不要,
不加因果掩码就是每个位置都能看到所有位置。
表里那排缩写,一个一句:LPIPS 比图像的感知相似度;mel L1 是频谱上的绝对误差;
CTC 是语音识别的对齐损失;CLIP score / IS 是图文匹配分 / 生成图的多样性与清晰度;
WER 错字率;MOS 人类打分;FAD / FVD 是把 FID 搬到音频 / 视频上;
光流是相邻两帧之间每个像素怎么移动;声码器是把频谱或编码还原成声音波形的模块。
顺带说清这一章和下一章的分工:这一章讲的是
接上之后哪些零件根本没动;而
「机制通用」是真的,但它不等于「换模态很轻松」。下面三件事是这一章必须留下的诚实标注。
互动 · 同样「10 秒的内容」,token 数差多少个数量级
拖「内容时长」:下面每种模态的 token 数、以及 n×n 那张表的显存,都会跟着重算。
互动 · 视频不是「图像的堆叠」:时序一致性
| 边界 | 为什么它是独立的难题 |
|---|---|
| ① 视频不是「图像的堆叠」 | 把 300 张图分别生成、再拼起来,你会得到 300 张互不相干的图: 同一个人的脸在第 1 帧和在第 2 帧会变成两个人。 时序一致性(temporal consistency)是一个独立的难题—— 它要求模型记住「上一帧是什么」,而这一点在单张图的任务里根本不存在。 这也是 FVD 这个指标存在的原因:FID 只看单帧,看不出画面在抖。 |
| ② 音频的序列长得离谱 | 10 秒、16 kHz = 160,000 个采样点。
这段内容写成文字可能只有 30 个词,序列长度差三个多数量级;
而这 160,000 个点要先被压成 1,000 帧才喂给注意力(上面那个互动),
但注意力的代价随长度平方增长。所以音频必须靠
「切帧 + 神经编 |
| ③ 指标之间不可比 | 困惑度、FID、WER、MOS、FVD——它们连量纲都不是一回事。 FID 是「两组特征分布的距离」,WER 是「错字率」, MOS 是「人类打分」。所以「哪个模态更难」这个问题 不能靠比数字回答,只能靠比「离可用还差多少」—— 判断「做成了没有」,看这个模态自己的指标有没有接近人类水平,或者超过原有的专用方法。 |
文本有整个互联网(数万亿 token);图像有数十亿张带标注的图; 音频的高质量配对数据(音频 + 转写)大概几十万小时——Whisper 用的是 68 万小时弱标注数据; 而视频带动作 / 声音标注的公开集要少近两个数量级(AudioSet 约 5,800 小时)。 六条暗线(拆一个方法时该问的六个问题)里,C 是参数账本:在文本上问的是「算力烧多少」, 到了音频与视频就变成「数据从哪来」——这是模态之间最现实的一道墙。
这一章属于七条线里的第二条——而且它是那条线最实用的一次应用。
| 暗线 | 这一章的回答 |
|---|---|
| A 信息流动 | 四种模态最终都塌缩成同一个形状 n × d。
差别全在「n 是怎么来的」:文本的 n 由内容决定(可变、可逆),
图像/音频/视频的 n 由分辨率、采样率、帧率决定(固定、不可逆)。
图像:H×W×3 → (H/p)·(W/p) × d;
音频:16000·s / hop × d;
视频:fps·s/2·(H/p)·(W/p) × d(每 2 帧一组)。
这里 p 是块的边长、hop 是每次往前跳多少个采样点、fps 是每秒多少帧。
这也解释了 |
| B 什么被牺牲了 | 先验被榨到了极限。为了让引擎通用,把它里面所有领域知识都删掉了——
代价是它必须假设「所有位置都可能相关」,于是复杂度变成 O(n²),
序列一长就崩(第 7 节那张账本就是它)。 而删掉的那点先验并没有消失,只是被挪到了 tokenizer 里,变成一笔人工设计费。 |
| C 参数账本 | 同一套注意力,四种模态的代表性输入:文本 512 个 token(一章文字的典型输入)、
一张图 196 个、10 秒音频 1,000 帧、10 秒视频 29,400 个(每 2 帧一组)。 注意力矩阵是 n²:从 0.5 MB(文本)一路到
1.7 GB(视频;fp16 = 每个数 2 字节,单头 = 只算一个注意力头)。拉到 1 分钟,这张表就装不下了
(第 7 节那张账本是这个数)——这时候真正的约束不是参数,是显存。
真跑视频的模型靠两件事把它压下来:FlashAttention
不把整张表算出来存下,再加上把视频压进潜空间、按时间分块。 |
这一章只验证了四种模态。往外推要谨慎:换到图结构(点云、分子)这类没有网格的输入, 「位置」就不是 1D / 2D / 3D 能编下的了——第五个要换的零件可能就在这时冒出来。
它接住了上一章的什么:《生成模型家族图谱》刚选完五条路线, 换个模态还成立吗—— ViT给了「图像能不能当序列」的第一个证据, 注意力给了那个唯一的共用算子, 扩散与DiT给了「同一套加噪去噪也能画图」, 分词给了文本侧的切法。
它给下一章留了什么:这里核对的是「哪些零件能复用」,还有一个接口问题没回答: 两套各自练好的模型,怎么接在一起? 下一章(《多模态模型》)接着做这件事—— 让只会读字的模型,长出一双眼睛。
换模态时:注意力、残差 + LayerNorm、训练循环四种模态完全共用;
扩散和重建损失在图像 / 音频 / 视频上共用,到了文本要换一条路——
真正要重写的只有 tokenizer(怎么切)、
输出头(吐出来是什么)、
所以呢:这一章的价值不是「又教了三个模态」,而是让你知道自己学的那些东西
值多少钱——你一路学下来的引擎,是通用的;
最需要每次重学的,是「这个领域里,什么算一个单位」。
而那恰好也是最没法从通用规律里推出来的那件事。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。