压缩即预测
文章摘要
Annie Sexton 这篇约 3700 字、配了大量交互式演示的长文,从压缩的基础一路讲到 LLM,论证一个核心命题:压缩器和大语言模型在最底层解决的是同一个问题。
压缩到底是什么。 文章先用「代码压缩(minification)」做反例:它把人类可读的变量名缩成单个字母、去掉空白和注释,文件确实小了很多,但你几乎不会在数据压缩领域听到有人提它。原因是 minification 只是扔掉机器不需要的语法,而「真正的」压缩依赖冗余。作者用一个由连续重复字符构成的字符串演示了游程编码(run-length encoding):把每一段连续重复替换成「字符 + 重复次数」,28 个字符的 224 位缩到 12 个字符的 96 位,小了 57%。
压缩器的三个「器官」。 现代压缩工具大致由三部分组成:变换(transforms)、模型(models)和熵编码器(entropy coders)。变换是让数据更容易压缩的预处理步骤,游程编码就是一种变换;值得注意的是变换不一定缩小数据,有时它反而是用来制造更多冗余的,冗余越多后面能压得越狠。模型根据每个符号(字母、数字、token、甚至二进制码)的频率描述数据的形状——现在你可以把它想成一张符号到概率的映射表,但它可以复杂得多。熵编码器几乎总是最后一步,产出最终的压缩产物:一段原始比特流。
文章特别强调中间那个环节:数据模型把一组概率交给熵编码器,概率进去,压缩比特流出来。
用概率压缩数据:算术编码。 作者选算术编码来讲解,因为它最能说明「更好的概率带来更好的压缩」。想法是用一个数字表示整个数据集。以字符串 “ABABAAC” 为例,统计出 A 概率 0.571、B 概率 0.286、C 概率 0.143,把 0 到 1 的区间按概率切成三段。然后对字符串里的每个符号,把当前区间收缩到该符号所占的那一段——注意收缩后仍然用同样的概率去切分这个新的、更小的区间。走完所有符号后得到一个极小的区间 [0.38730, 0.38855),最终数字可以是这个区间里任何一个数,理想情况下应该是所需比特最少的那个:0.3876953125。原字符串按 8 位 ASCII 要 56 位,这个数字只要 10 位。作者特别说明这不是浮点数而是二进制小数——浮点数也是二进制小数,但宽度固定,不管需不需要都得付 32 或 64 位的代价,而这个只需要 10 位。解压时,解压器拿到同样的概率表重建 [0,1) 区间,看这个魔法数字落在哪一段就记下哪个符号,然后把区间收缩到那一段,如此反复。
概率如何影响压缩。 作者换了一个 A 占绝对主导(概率 0.833)的 12 字符字符串对比:原来 7 个符号的串压到平均 1.38 比特/符号,新的 12 个符号的串压到 0.82 比特/符号。结论是:数据分布越偏斜(某些符号概率越高),压缩比越好。
这个「平均比特/符号」就是熵——不是物理里的熵,而是香农熵,不过作者指出它的数学形式和热力学里的吉布斯熵公式几乎一样。她用一个直观的例子解释:「昨天我在市区散步时看到一只动物,那是一只 ___」。如果答案是常见的鸟,你可能一次就猜中;如果是熊,大概要猜好几次。给定各动物的概率(1/2、1/4、1/8、1/16、1/16),可以用一棵是/否决策树算出平均需要多少次猜测。把动物当成符号、把「是/否」换成 1 和 0,猜测次数就正好等于表示它所需的比特数——常见动物得到更短的码字,罕见的得到更长的。这种给符号分配码字的方式就是另一种熵编码器:哈夫曼编码,gzip 和 Brotli 都在用它;与算术编码把数据编成单个数字不同,哈夫曼是为每个符号造码字。
但哈夫曼有个问题:当概率不是整齐的二分之一时怎么办?树上每条路径都是整数次「猜测」,所以必须取整,而取整意味着为不需要的比特付费。精确答案由公式给出:所需比特数 = −log₂(概率)。把动物的概率代进去,得到的比特数和决策树的猜测次数完全一致。所有符号的 −log₂(概率) 的平均值就是熵。
关于熵,最重要的一点是:它是地板——这是给定数据所能达到的最小平均比特数,再也压不动了。(作者注明这只适用于无损压缩;JPEG、MP3 这类有损压缩通过扔掉不会被察觉的细节可以压得更小,但同样依赖模型和概率。)
那为什么没有一个万能的「上帝压缩器」?因为熵是相对于一组特定概率而言的。如果我们能让概率分布更偏斜,就能压得更狠。
上下文很重要。 到目前为止用的模型只关心符号频率(出现次数除以总符号数)。但上下文能极大改变一个符号的概率:在整个英语里字母 U 的概率约 0.028,但紧跟在 Q 后面时飙升到约 0.999。换算成比特:−log₂(0.028) ≈ 5.158 比特,而 −log₂(0.999) ≈ 0.001 比特。用单个上下文决定符号概率叫做 order-1 模型,回答的是「给定某个上下文,某符号的概率是多少」;可以扩展到 order-2、order-3……也就是看前 N 个符号。代价是模型不再是一张概率表,而是一整组表,每个前导符号一张。作者用 “TO BE OR NOT TO BE” 这个 18 符号字符串做了对比:无上下文时压到约 47 位、平均 2.59 比特/符号;用 order-1 模型压到约 21 位、平均 1.16 比特/符号——砍掉了一半以上。加上下文给了我们更强的概率,换句话说,它帮我们预测下一个符号。
「还有什么东西特别擅长预测呢?」
语言建模与压缩。 作者引用了 DeepMind 2023 年那篇论文,主张语言建模和压缩是同一件事的两种视角。LLM 常被形容为「花哨的自动补全」,这基本属实:你提交 prompt,它成为模型用来返回下一个可能词的概率分布的上下文,然后选一个附加到上下文里,如此循环。
关键的转折在这里:熵编码器虽然产出最终比特流,但里面没有任何可调的东西——它们是固定的、确定性的、无损的。想要更好的压缩,你必须改进模型,让每个符号获得更高的概率。换句话说,你需要一个更好的预测器。而说到预测,LLM 基本上就是天花板。
用 LLM 做压缩和用它生成文本几乎一样,只有一处不同:我们不选下一个词。因为我们不是要生成新文本,我们已经知道下一个词是什么了。流程是:基于前面的 token,模型说「这些是我认为接下来的 token 以及它们的概率」;然后看真实的下一个符号是什么;模型给它分配的概率就决定了它的比特代价(同样是 −log₂(概率))。模型训练得好,它认为概率最高的 token 就会是真实的下一个符号。
反过来,模型没训练好就要付出代价:如果上下文是「The rain in」,一个训练不佳的模型可能给「Bermuda」0.82 的概率而给真实答案「Spain」只有 0.02——前者只要 0.29 比特,后者要 5.64 比特。在算术编码里这体现为区间的坍缩:编码低概率符号会让区间变得极小,而最终数字必须落在区间里,区间越小需要的精度越高,精度越高就是更多位数、更多比特。作者用一组对比图展示:好模型的区间在三层之后仍然很宽,最终编码成 9 比特;坏模型的区间收缩成一条细缝,需要 28 比特。
即便是今天看来很烂的老模型,压缩比也相当可观。文中用狄更斯《双城记》开头那段著名文字做测试:order-1 模型压到 434 比特(原始的 24%),GPT-2 压到 176 比特(原始的 10%)。
现实世界里的压缩。 那既然 LLM 这么会压,为什么不到处用?因为压缩工具的目标不是「尽可能压小」,而是「在特定资源约束下尽可能压小」。以 HTTP 响应为例:浏览器发 Accept-Encoding: gzip, br 告诉服务器自己能解哪些格式,服务器挑一个压缩响应。浏览器收到后用一个内置的小模型解出 HTML、CSS、JS,开销极小。如果换成 LLM,浏览器和服务器都需要一份可能几个 GB 的模型副本——这是为好压缩率付的高昂代价,而且还没开始跑呢;压缩和解压会消耗大量资源,把页面加载速度拖到无法使用的地步。「想象一下:每一个样式表、每一个脚本、每一个 JSON 载荷都要跑一次 LLM 来压缩和解压。恶心。」对压缩 HTTP 响应这种小任务,LLM 是滑稽的杀鸡用牛刀——把模型体积算进去,你为了省几 KB 要发几个 GB。但即使你压缩的数据集远大于 LLM 本身,所需的天文数字级算力仍然让它不切实际。
一枚硬币的两面。 结论部分把观点收拢:把数据压到它的熵,在今天已经是个已解决的问题——1970 年代末发明的算术编码能落在极限之内几个比特的范围,如今熵编码器竞争的是速度和内存,不是压缩率。真正的开放问题是我们能把熵做到多小。更好的模型、更好的预测器能拉低这个数字。LLM 在这件事上极其出色(先不算开销),但真正有意思的是:它们被训练的目标正是最小化这个「每符号比特数」——在 LLM 里这叫交叉熵(cross-entropy),但底层公式是同一个。所以在压缩里熵衡量的是我们能把东西缩到多小,而在语言建模里它是我们为了让模型更会预测而去降低的数字。归根结底,LLM 和压缩算法都是预测器,是同一套底层数学的两种表达。压缩即预测,而 LLM 就是压缩器。
HN 评论精华
这条帖子拿到 668 分、295 条评论。讨论质量相当高,主线有三条:这个命题的历史渊源(大量指路)、它对「LLM 能否产生新想法」之争的意义,以及一场关于「压缩等价于预测是否隐含了过强假设」的精彩辩论。
-
ssivark 贡献了本帖最长也最有价值的反对意见,值得完整转述:「不对,这里有更多的微妙之处,而且这个区别很重要。压缩在功能上等价于预测,前提是数据分布恰好代表了所有未来的问题。一旦你要的是泛化,故事就完全变了——因为测试分布可能任意地不同,哪怕支撑集相同。举例:你在训练数据里观察到一个罕见的边缘情况,(有损)压缩完全可以直接忽略它;但如果你恰恰想在空间的那个部分获得泛化能力——要么因为有对手在测试你,要么因为你出于设计自由选择在那个角落里搭东西——那你要的就不只是数据压缩,而是在一个峰值恰好落在那个角落的测试分布上的良好预测性能。假设训练数据分布恰好就是你唯一会在乎的分布,在『压缩=预测』这个论断里承担了大量隐形工作量,而这句话被不加思索地当成宣言反复念叨,让我很不爽。训练数据分布没有任何天然性可言,尤其当数据生成过程是探索性的、而下游使用是利用性的时候。」
这条引来了一整串高质量反驳。porphyra 的角度最简洁:这怎么就否定「压缩即预测」了呢?如果未来数据不同而你没能泛化,那么预测失败就意味着你压得更差、必须花更多比特存储新信息;反过来,如果未来数据和你见过的一样,你就能预测得很好、压缩得更好。canjobear 一句话:压缩仍然是预测,只是预测可能不准。pornel 说得更直接:你只是假设了一个有缺陷的预测器实现,然后抱怨它有缺陷——这并不否定「预测器就是压缩器」这个大前提;预测器不必给你某种语料平均概率,它们可以逐符号调整概率,甚至在完全确定时给某个符号概率 1(那就是 0 比特)。「如果你有一个已经知道你要发送的全部数据的魔法先知预测器,你就有完美的压缩。」variadix 补充:一个能从已观察数据泛化到潜在未来数据的压缩器就是更好的压缩器;而且压缩器本来就要处理非平稳数据源,所以说压缩器只建模平稳分布也不对。srean 给了一记技术性的回击:如果测试分布可以任意不同,那所有机器学习算法都完蛋了;而且「泛化」这个技术术语的定义本身就建立在测试和训练分布相同的假设之上。bjt 从压缩这一侧给了个类比:你说的现象在压缩里也能观察到——LAME 把声音编成 mp3 时并不给所有频率同等权重,它扔掉人类听觉范围之外的数据来给范围之内的留更多空间。
AnotherGoodName 给出了最有说服力的经验反驳:破纪录的文件压缩器全都非常擅长泛化。原因在于压缩比赛的计分方式是把程序体积(包括任何训练数据或字典)加上压缩后数据体积——如果不这么算,你完全可以直接把测试数据打包成字典,程序只负责打印它。所以他们实际测量的是柯尔莫哥洛夫复杂度。这意味着训练数据对顶级压缩器帮助不大:比如 Fabrice Bellard 的 nncp 目前是 enwik9 的纪录保持者,它是一个边跑边学的 LLM,出厂只有 628 KB,却能在数 GB 规模的数据集上取胜,因为它能即时生成训练数据。没有任何预训练 LLM 在这个计分方式下接近它。「所以真的不用太担心。我们都说 AI 就是压缩,而我们会驳回上面那种评论,是有原因的——它们站不住脚。没人在运输显著的字典或预训练数据。想赢压缩比赛?你最好有能即时学习、天然可泛化的原始代码。」
-
第二条主线是「LLM 能不能有新想法」。throwaway_7274 说这个视角是对付「LLM 不可能有新想法,它们只是下一个 token 预测器」这类论证的有用直觉来源:如果你把训练理解成在一个巨大的参数化压缩算法族上做优化,那么「新」「想法」从这个过程里涌现出来突然就显得合理多了。他还补了一句好玩的:这个关系是双向的,你可以自己试试——zstd 是个相当烂的语言模型 :) glial 表示这对他并不直观:「新想法」几乎按定义就是不在训练集里的东西,能否展开讲讲?AnotherGoodName 的回应最有力:预测正是让计算机下出人类永远想不到的、极具创造性的国际象棋和围棋着法的东西;更一般地说,预测让你能朝某个目标寻路。「如果有人说『LLM 不可能有新想法因为它们只是在预测』,你只能笑——预测本身就在使能新想法,因为你用这些预测去寻路抵达目标。」nonameiguess 提出了一个很少被讨论的区分:想法不是事实。无论 LLM 还是人类,都不能仅凭思考产生新的知识(而非想法),物理调查和实验是必需的——纯数学是例外,因为它完全存在于想法的领域,这大致就是康德那个古老的分析/综合二分。
-
pjankiewicz 提出了一个方向性的警告:LLM 是压缩器,但压缩器不是 LLM;混淆这两者会让你以为可以用压缩器做 LLM 做的事,而你不能。但 Legend2440 和 davmre 都指出这在技术上其实可以做到。davmre 给了具体做法:给定上下文(对 LLM 而言包括整个预训练数据集加 prompt),对每一个可能的下一个 token 压缩「上下文 + 该 token」;和已有上下文共同压缩得最好的那些 token 就是「最不令人意外」的续写,选一个然后迭代。「你可以很容易地用 gzip 这样生成文本。文本不会很好,因为 gzip 的压缩没有 transformer + SGD 那么精妙,但原理是一样的。」
-
第三条主线是指路,几乎构成了一份阅读清单。sheeeeesh 推荐 Grant Sanderson(3Blue1Brown)关于同一主题的系列视频《Compression is Intelligence》。sethev 立刻联想到 Hutter Prize——一个从 2005 年起就在运行的比赛,前提正是压缩与智能密切相关。andai 指路 Bellard 的《用神经网络做无损数据压缩》,adamgordonbell 补上了 LLM 版本 ts_zip 和 LLMZip 论文。woliveirajr 列了一串兔子洞:偏序匹配预测(PPM)、柯尔莫哥洛夫复杂度、归一化信息距离、归一化压缩距离。vrighter 顺着说,这正是他认为二者本是同一件事的理由——把一个(有损的)机器学习到的马尔可夫链塞进 PPM 里替换掉从数据完美学出来的那个,算法其余部分零改动。brumar 补了最小描述长度(MDL)。farfatched 指出这正是剑桥那门《Information Theory, Inference, and Learning Algorithms》课程的主旨,并引用了 MacKay 那段话:为什么要统一信息论和机器学习?因为它们是一枚硬币的两面;1960 年代信息论学者、计算机科学家和神经科学家同在控制论这一个领域里研究共同问题;大脑是终极的压缩与通信系统,而数据压缩和纠错码的顶尖算法用的是和机器学习一样的工具。(书有 CC 授权版本,讲座在 YouTube 上。)
-
adamgordonbell 还讲了一个香农的好故事作为注脚:香农想测量普通英文文本里到底包含多少信息。他 1948 年的理论说这个数字必然存在,但他无法计算,因为英语里的模式跨越几十个字母,没有任何方程或频率表能一次性捕捉它们。于是他不去计算,而是拿一个人做实验——他取一段受试者没读过的小说文字,用一张卡片盖住,只露出已经猜对的部分,然后让受试者说出下一个字母;猜错就继续问,直到说对为止,记下用了几次,揭示该字母,把卡片右移一位,然后对下一个字母重复整个过程。产出的不是一串字母,而是一串猜测次数。
-
deepsun 提了个合理的扩展:为什么只有 LLM?所有统计模型都是压缩器,「模型」和「压缩器」可以说是同义词;他还指出文章完全没提 embedding(通常被视为一种压缩方法)和自编码器的 encoder 部分。variadix 补了压缩理论的一层:了解非 LZ 压缩器的工作原理后这一点就没那么惊人了;而一个不那么显然的结论是,LZ 压缩器也隐式地在做同样的事——它能发出的每个符号(字面量、匹配等)的长度可以转换成该 LZ 压缩器所诱导的概率分布,因为编码一个符号所需的比特数和它的信息量相关。duskwuff 补充了实现层面的常见设计:很多压缩器先用 LZ 做第一步,再用熵编码器表示常量数据和偏移-长度对——gzip 的 Deflate 用哈夫曼,xz 的 LZMA 用预测范围编码器,Zstandard 两种都能用,LZ4 这类高速压缩器则干脆跳过熵编码阶段来换速度;bzip2 是个有趣的反例,它用 Burrows-Wheeler 变换而不是 LZ 做第一遍,而这也正是它慢的主要原因之一。
-
j-pb 提出了一个常被漏掉的第三面:索引。文章里用来解释压缩的决策树同时也是一种查找索引;在这三种情形里,你做的都是识别(可复用的)结构。自索引的简洁数据结构就是第三面的好例子。「所以这是个三位一体:压缩、预测、索引。」
-
最后一条题外话来自 kailanb,很多人会心一笑:与内容无关,但他很高兴看到这个站点的 cookie 同意默认是最低限度的——他条件反射地点了「全部拒绝」,然后才发现那本来就是默认,反而把他搞懵了。