语言模型就是压缩器(真的,不是比喻)
这一章只做一件事:拿同一台模型走两条完全不同的路,然后把两个数字并排放。它们会相差 1.6 个比特——整条消息一共 1.6 个。
一台 3 阶字符模型,训练在 2053 个字符上。用它压一段没见过的 222 字符文本。gzip 在同一段文字上压到 136 字节。这台模型压到多少?
把两件事接起来
到这里,两块拼图都在桌上了:
- 第 8 章:算术编码每一步只需要「当前这一步的概率表」,而这张表可以每一步都不一样——只要解码方能算出同一张。
- 第 10、11 章:一台模型的好坏,就是它给正确答案分配的概率有多高,量化成交叉熵。
把它们拼起来:
编码:
for 每个字符 x_i:
p = 模型.预测(已编码的历史) # 一张概率表
算术编码器.编(x_i, p) # 按这张表切区间
解码:
for i in 1..n:
p = 模型.预测(已解码的历史) # 【同一张表】
x_i = 算术解码器.解(p)
把 x_i 追加到历史里
# 两边的历史永远一样,所以两边算出的 p 永远一样。
# 【模型本身完全不需要传输】——只要收发双方装的是同一台模型。
而这台压缩器的输出长度是多少?第 8 章证明过:−log₂ P(整条消息)。而那正好是交叉熵乘以长度。
所以「模型的交叉熵」和「压缩后的比特数」是同一个数。
这不是推理,是可以当场验证的。下面这台真的跑了这两条路:
那两个数字
在 3 阶那一档,两条路给出的是:
| 走哪条路 | 做的事 | 结果 |
|---|---|---|
| 预测路 | 把模型每步给正确答案的概率取 −log₂,加起来 | 352.4 比特 |
| 压缩路 | 拿同一台模型驱动算术编码器,数输出了几位 | 354 比特 |
| 差 | 收尾开销 + 概率量化的零头 | 1.6 比特 |
左边那个数字,计算过程里完全没有出现「编码」两个字——它只是一串对数相加。
右边那个数字,计算过程里完全没有出现「交叉熵」四个字——它只是数了数输出缓冲区里有多少位。
而它们相差 1.6 个比特,还是整条消息一共 1.6 个。
「模型有多准」和「文件能压多小」不是两件相关的事,是同一个数字的两种读法。
更准确地说:
压缩后的比特数 = −log₂ P_模型(数据)
= 交叉熵 × 数据长度
= 「模型对这份数据有多惊讶」
# 一台模型看到数据不惊讶 → 它压得小
# 一台模型看到数据很惊讶 → 它压得大
# 「不惊讶」= 「预测得准」= 「压得小」,三句话是一句话。
和 gzip 比一比
| 压 222 字节的那段文字 | 字节 | 压缩比 | 它知道什么 |
|---|---|---|---|
| 原文 | 222 | 1.0 | — |
| gzip -9 | 136 | 1.6 | 只有这 222 字节本身 |
| 3 阶模型 + 算术编码 | 45 | 4.9 | 另外 2053 个字符的先验 |
三倍。而且这不是因为算法更聪明,是因为它见过东西。
这一点必须说清楚,否则这个对比是不公平的:
模型压出 45 字节,前提是解压的一方也装着同一台模型——也就是说,那 2053 个字符的训练语料的「知识」,必须已经在解码方手上。
如果你要把模型也一起传过去,账就完全不同了。
所以这个对比的正确读法是:
- 如果你只需要传一次 222 字节 → gzip 赢(它自带一切)。
- 如果你要传几千万段这样的文字,而模型只传一次 → 模型完胜,而且差距会越拉越大。
这正是现实中语言模型的经济学:几百 GB 的权重是一次性的固定成本,之后每一次推理都在享受那个成本带来的先验。
而「模型本身要多少比特」这个问题不是可以回避的记账技巧——它是第 13 章的主题,而且它有一个精确的名字。
这个等号在现实里长什么样
一、Hutter Prize。2006 年设立的一个奖,规则很简单:把 1 GB 的维基百科文本压到最小,奖金按你比上一个记录省了多少发。
它的设立理由写在官网上,大意是:「压缩这个语料,等价于理解它。」而事实站在这一边——榜上前列的方法全是「神经网络预测下一个字节 + 算术编码」。一个纯压缩比赛,事实上变成了一个语言模型比赛。
二、用压缩率当模型评测指标。2023 年 DeepMind 有一篇论文(Language Modeling Is Compression)直接把大语言模型当通用压缩器用,去压文本、图片、音频。结论是:在这些模态上,一台没见过图片和音频的语言模型,压得比 PNG 和 FLAC 还好。
这件事听上去很怪,但按上面那个等号是必然的:只要模型能给字节序列分配好概率,它就能压——不管那些字节原来是什么。
三、用压缩当分类器。有一个很土的方法:判断文本 A 属于哪一类,就看「把 A 和类别 C 的样本放在一起压」比「单独压 A」省了多少。省得越多,说明 A 和 C 越像。
2023 年有篇论文(“Low-Resource” Text Classification: A Parameter-Free Classification Method with Compressors)用 gzip + kNN 做文本分类,在一些低资源数据集上打平甚至超过了 BERT。这篇论文引发了不少争议(有人指出它的 kNN 评估方式偏乐观),但核心洞察是站得住的:压缩率是一种相似度度量,而且是一种不需要训练的相似度度量。
那么「理解」呢
这个等号成立之后,有一个很诱人的推论摆在那里:「压缩 = 预测 = 理解」。
前两个等号我们已经证明了。第三个呢?
诚实的说法是:它有很强的一半,也有明显的另一半。
要把一段文本压得很小,你必须捕捉它的规律。
想把一本物理教材压到极小,你的模型得「知道」F = ma——因为知道这个,它才能在看到 F = m 的时候把 a 的概率推到很高。想把一本小说压得小,你的模型得跟得上人物关系和情节。
压缩率是一个无法作弊的指标:你不能靠说漂亮话把文件变小。这一点比几乎所有 AI 评测基准都硬。
三个具体的缺口:
- 它奖励表层统计。把「的、了、是」的分布学得极准,能省下大量比特,而这和理解无关。大部分比特花在最没意思的地方。
- 它不区分「知道」和「能用」。一个能完美预测数学教材下一个字符的模型,不一定能解一道新题。预测文本和执行推理是两个不同的任务,只是前者的训练信号恰好也能推动后者一部分。
- 它对「重要」和「不重要」一视同仁。这正是第 1 章那个分工的延续——信息论不碰意义。一段文本里那句真正改变结论的话,和一句客套话,在比特账上没有区别。
所以更稳妥的说法是:压缩是理解的必要条件的一个可测量的代理,而不是理解本身。而「用力优化一个不完美的代理会发生什么」——那是第 23 章。
这一点值得单独说清,因为它是整个方案能成立的关键,也是初学者最容易怀疑的地方。
「编码时模型看的是原文的历史,解码时哪来的原文?」
答案:解码时看的是「已经解出来的部分」,而它和原文的对应部分逐字相同。
编码第 i 个字符时,模型看到的上下文 = 原文[0 .. i−1] 解码第 i 个字符时,模型看到的上下文 = 已解出[0 .. i−1] # 而只要前面 i 个字符都解对了,这两个字符串就完全相同。 # 于是两边算出的概率表逐位相同,算术编码器就能对上。 # 数学归纳法:第 0 个对了,第 i 个也就对了。
代价是解码必须严格串行——第 i 个字符解出来之前,第 i+1 个的概率表算不出来。这也是为什么这类压缩器又慢又不能并行,而 LZ77 可以。压得好和解得快,在这里是一对真实的矛盾。
这本书的引擎里那个 arithDecodeStreaming 函数就是干这件事的——它每解出一个符号就回调一次,让调用方把符号喂回模型。如果你把它写成「先解完再算概率」,程序会在第二个符号就解错。
如果你选了 A 或 B,那是把「一台只读过两千个字符的玩具模型」和「工业级压缩算法」放在同一个尺度上比了。但它们比的根本不是同一件事:gzip 比的是算法,模型比的是它见过多少东西。
而这正是这一章的重点:压缩这门手艺,在某个点上停止了关于算法的竞争,变成了关于知识的竞争。那个点大约就在算术编码被发明出来、模型可以随意插拔的时候。
这一章的一句话
352.4 和 354。一个来自纯预测,一个来自纯压缩,中间隔着 1.6 个比特。「模型有多准」和「文件能压多小」不是两件事——它们是同一个数字。
下一章补上这一章故意欠下的那笔账:模型本身要多少比特?
这个问题会把我们带到一个比熵更根本的量——柯尔莫哥洛夫复杂度,「能生成这个数据的最短程序有多长」。它是奥卡姆剃刀的数学形式,它给了「随机」一个终极定义,而且它不可计算。三件事我们都会讲清楚,还会给你一台真的在算这笔账的机器。