对应 Maps to:Stanford CS324 Winter 2023 Advances in Foundation Models,2/27 客座报告 Jack Rae(当时在 OpenAI,此前在 DeepMind 领导大语言模型研究,Gopher 的第一作者、Chinchilla 的作者之一):Compression for AGI。 依据说明:这次报告通过 Stanford MLSys Seminar 公开录像,但我没有拿到录像的文字稿或 slides,所以报告的具体内容只能依据二手材料:一篇中文的报告整理(智源社区,2023 年 3 月)和几篇引用了这次报告的文章。讲义里凡是”Rae 说……”的地方,都依据这些转述,并在用到具体数字(LLaMA-65B 的那笔账)时注明。论证本身是信息论和机器学习里成熟的内容,§2–§5 依据原始文献:Shannon 1948(信息论)、Shannon 1951(英语的熵)、算术编码(Witten, Neal & Cleary 1987)、Solomonoff(1964)与 Kolmogorov 复杂度、Rissanen 的最小描述长度(MDL,1978)、Blier & Ollivier 2018(The Description Length of Deep Learning Models)。§6–§7 补充了 Delétang et al. 2023(Language Modeling Is Compression)和 Huang et al. 2024(Compression Represents Intelligence Linearly)。文献里的数字都是我在原文里读到的。 配套代码 Code:
code/lecture11/(PyTorch + 标准库;3 个脚本 + 1 个模块。代码 01 约 5 秒,02 约 65 秒,03 约 4 秒) 预计阅读时间:3 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 用香农的码长 解释”预测”和”压缩”为什么是同一件事,以及交叉熵 = 平均码长。
- 说明算术编码的原理(不断缩小区间),以及它为什么能让任何一个语言模型变成一个真正的无损压缩器。
- 区分两种给数据编码的方式:两段式编码(先发模型、再发数据)和在线(prequential)编码(边学边编码,码长 = 训练损失曲线下的面积),并解释 Rae 为什么说”不需要发送权重”。
- 复述 Rae 的核心论断——“智能的目标是对有用信息的最大无损压缩”——以及支持它的论证链:MDL、奥卡姆剃刀、Solomonoff 归纳。
- 解释为什么在”每条数据只看一遍”的训练里,训练损失曲线就是泛化曲线,以及这和规模定律(第 4 讲)的关系。
- 用代码里的实验说出这个视角的边界:压缩什么数据、怎样给模型记账、压缩了没用的信息怎么办、为什么一个只会压缩的模型不会”回去抄”。
- 了解 2023 年之后的证据:大语言模型压缩图像和音频、压缩能力与基准分数近似线性相关。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面几讲再展开 Previewed only |
|---|---|
| 第 1 讲 §2.4:熵、交叉熵、bit/字符、困惑度 | induction head 与”复制”的机制(第 12 讲) |
| 第 1 讲 §2.5:n-gram 模型与数据稀疏 | 记忆与隐私:模型”压缩”了训练数据里的哪些具体内容(第 16 讲) |
| 第 3 讲 §1:BPE 分词(它本身就是一种压缩算法) | |
| 第 4 讲:规模定律、、单遍训练(one epoch) |
0.3 本讲地图 Map
§1 一个大胆的论断:Rae 的报告想说什么
§2 预测与压缩:香农码长、交叉熵、哈夫曼与算术编码 ← 代码 01 演示 1
§3 用语言模型压缩:算术编码实战;解码器必须有同一个模型;模型本身的代价 ← 代码 01 演示 2–4
§4 训练过程就是压缩器:在线编码、损失曲线下的面积、LLaMA 的账、
为什么更大的模型是更好的压缩器 ← 代码 02
§5 最小描述长度与奥卡姆剃刀:Kolmogorov、Solomonoff、MDL;用码长选模型 ← 代码 03 演示 1
§6 这个论证的边界:压缩"哪个世界"、没用的信息、复制、计算 ← 代码 03 演示 2、3
§7 2023 年之后:语言模型是通用压缩器吗;压缩与能力的相关性
§8–12 小结、常见误区、练习、延伸阅读、下一讲预告
0.4 记号 Notation(只列本讲新增的,其余见第 1 讲 §0.4)
| 符号 | 含义 | 例子 |
|---|---|---|
| 用分布 编码符号 的码长(bit) | → 1 bit | |
| 熵:最优平均码长 | 4 个等概率符号:2 bit | |
| 交叉熵:真实分布 、用模型 编码时的平均码长 | ||
| Kolmogorov 复杂度:能输出 的最短程序的长度 | 不可计算 | |
| 两段式码长:描述模型 + 用模型描述数据 | MDL 原则:选使它最小的模型 | |
| 在线(prequential)码长: | = 训练损失曲线下的面积 | |
| bpc / bpt | bit/字符、bit/token |
0.5 关于数字 · About the numbers
讲义里的实验数字都来自 code/lecture11/ 的实际输出(固定种子、PyTorch 单线程,同一台机器上重复运行逐字相同)。这一讲的压缩是真的:代码 01 把文本压成比特流,再解压回来逐字对照。和前几讲一样,第 1 讲的 base model 只有约 11 万参数、只见过一个模板生成的”迷你互联网”,所以它演示的是机制,不是大模型的量级。几处结果和我写代码前的预想不一样(完整清单在代码 README 的”关于实验结果”里),最重要的是:在我们的玩具世界里,bz2 比所有在线训练的神经网络都压得好(§4.4);以及用码长选模型时,两段式码长会因为模型编码得太浪费而选错(§5.3)。
1. 一个大胆的论断 · A bold claim
1.1 为什么”预测下一个词”能学会那么多
第 1 讲提出过一个问题:GPT-3 只被训练去做一件事——预测下一个 token——为什么它学会了翻译、问答、写代码、做算术?第 5 讲从”涌现”和”上下文学习”的角度讨论过这个问题。这一讲换一个角度:信息论。
Jack Rae 在 2023 年 2 月的这次报告里给出了一个非常简洁的答案:**预测下一个 token 就是在压缩数据,而压缩就是理解。**一个模型越能准确地预测文本,它就越能把文本压小;要把人类写的文字压到极致,就必须”理解”文字背后的世界——谁住在哪里、算术怎样算、代码怎样运行。据报告的整理,他的核心论断是:通用人工智能的基础模型,目标是对有用信息做最大限度的无损压缩。
这听起来像一个口号。这一讲要做的,是把它拆成几个可以检验的命题:
- 预测和压缩在数学上是等价的(§2,这是定理,不是口号)。
- 一个语言模型可以变成一个真正的压缩器,压缩率就是它的交叉熵(§3,代码 01 逐字节验证)。
- “训练”本身可以被看成压缩,而且不需要把模型参数算进成本(§4,Rae 论证的核心)。
- 最短的描述往往就是泛化得最好的描述(§5,奥卡姆剃刀的信息论版本)。
- 以及——这个论断在哪里会失效(§6)。
1.2 讲者的背景
Rae 在 DeepMind 领导过大语言模型的研究:2021 年的 Gopher(2800 亿参数)是他的工作,2022 年的 Chinchilla(第 4 讲的主角)他也是作者之一;做这次报告时他在 OpenAI。这个背景很重要:他是从”怎样把一个模型训练得更好”的工程经验出发,来找一个”为什么要这样做”的理论。规模定律(第 4 讲)告诉我们损失会怎样随规模下降;Rae 的报告想回答的是:为什么损失下降就意味着更聪明。
2. 预测与压缩 · Prediction is compression
2.1 香农的码长
1948 年,香农在《通信的数学理论》里证明了一件事:如果一个符号出现的概率是 ,那么给它编码最少需要
概率 0.5 的符号值 1 bit,概率 0.25 的值 2 bit,概率 0.01 的值 6.64 bit。直觉是:越常见的东西,越应该用短的码字——摩尔斯电码给最常见的字母 E 用了一个点,也是同一个道理。对整个信源取平均,最优的平均码长就是熵 (香农的信源编码定理:任何无损编码的平均码长都不能低于熵,而且可以任意接近它)。
这就把”预测”和”压缩”连在了一起。**一个预测器给真实发生的事情的概率越高,编码它花的 bit 越少。**反过来,一个压缩器也隐含着一个预测器:码字越短的东西,它”认为”越可能发生。
2.2 交叉熵就是码长
真实的分布 我们不知道,只能用一个模型 去近似。用 来设计码字,真实数据按 出现,平均码长是交叉熵:
第二个等号说明:用不完美的模型编码,要多花 个 bit——这多出来的部分,就是模型的”无知”。熵 是数据本身的不确定性,任何模型都消除不了;KL 是模型可以改进的部分。
第 1 讲 §2.4 定义过的”bit/字符”(模型在验证集上的平均交叉熵)因此有了一个非常具体的含义:用这个模型压缩这段文本,平均每个字符要多少 bit。语言模型训练时最小化的损失,字面意义上就是一个压缩率。
2.3 为什么需要算术编码
香农只证明了”存在”接近熵的编码。最常见的实际编码是哈夫曼编码(1952):给每个符号分配一个整数长度的码字,常见的短、少见的长。它的问题是”整数”:每个符号至少要 1 bit。
代码 01 演示 1 比较了两个信源:
| 信源 | 熵 | 哈夫曼平均码长 | 算术编码实测 |
|---|---|---|---|
| A:概率是 2 的幂(0.5, 0.25, 0.125, 0.125) | 1.7500 | 1.7500 | 1.7518 |
| B:很偏的分布(0.9, 0.05, 0.03, 0.02) | 0.6175 | 1.1500 | 0.6326 |
概率都是 2 的幂时,哈夫曼编码正好达到熵。但对信源 B,概率 0.9 的那个符号只值 0.15 bit,哈夫曼却必须给它至少 1 bit,平均码长比熵多了一倍多。语言模型的预测分布恰恰常常是”很偏”的:模型对下一个字符很有把握时,正确字符的概率可能是 0.99。
算术编码(arithmetic coding,Rissanen、Pasco 1976;Witten, Neal & Cleary 1987 给出了经典的整数实现)没有这个限制。它的想法是:把 区间看成”所有可能的消息”:
第 1 个字符:按概率把 [0, 1) 切成几段,真实字符 "t" 占 [0.30, 0.42) → 区间长度 0.12
第 2 个字符:在 [0.30, 0.42) 里再按 p(·|"t") 切,真实字符 "h" 占其中 70% → 区间长度 0.084
第 3 个字符:…… → 区间长度 = 所有概率的乘积
最后得到一个很小的区间,长度等于整条消息的概率 。要指明这个区间里的一个点,大约需要 个 bit——正好是模型在这段文本上的交叉熵之和,不再受”每个符号整数个 bit”的限制。实际实现用 32 位整数表示区间的上下界,区间落进左半或右半时就输出一个确定的 bit 并把区间放大一倍,所以不需要无限精度(codec_tools.py 里的 ArithmeticEncoder)。
最关键的一点:算术编码对模型没有任何要求——只要模型能在每个位置给出一个概率分布。n-gram 可以,神经网络可以,一个大语言模型也可以。
2.4 香农的另一个实验
香农本人在 1951 年做过一个著名的实验(Prediction and Entropy of Printed English):让人去猜一段英文的下一个字母,用猜测的结果来估计英语的熵。他的估计是每个字母大约 0.6 到 1.3 bit。这个实验和今天的语言模型做的是同一件事——只不过当年的”预测器”是一个人。从这个角度看,“语言模型的损失降到多少”一直就是一个关于”它离人类的语言理解有多近”的问题。
3. 用语言模型压缩 · Compressing with a language model
3.1 算术编码实战
代码 01 演示 2 把第 1 讲迷你互联网的验证集(14,085 个字符,每个字符原本 1 字节)用不同的模型做算术编码,再解压回来逐字对照:
| 压缩器 | 理论 bit/字符(交叉熵) | 实际 bit/字符 | 压缩后 | 压缩率 |
|---|---|---|---|---|
| 均匀分布(44 个字符) | 5.4594 | 5.4594 | 9,612 字节 | 1.5× |
| 1-gram(在训练集上数数) | 4.5498 | 4.5499 | 8,011 字节 | 1.8× |
| 3-gram | 1.3615 | 1.3621 | 2,399 字节 | 5.9× |
| 5-gram | 0.6453 | 0.6458 | 1,137 字节 | 12.4× |
| base model(TinyGPT,109k 参数) | 0.3555 | 0.3563 | 628 字节 | 22.4× |
| gzip -9 | — | 1.4785 | 2,603 字节 | 5.4× |
| bz2 -9 | — | 1.1905 | 2,096 字节 | 6.7× |
| lzma(xz)-9 | — | 1.4040 | 2,472 字节 | 5.7× |
所有模型都解压一致。几个观察:
- 实际码长和交叉熵只差千分之几 bit/字符(来自把概率量化成整数、以及结尾的两个 bit)。“交叉熵就是压缩率”在这里是字面意义上成立的。
- 模型越好,文件越小。base model 把验证集压成原来的约 1/22,比 bz2 还小 3 倍多。gzip、bz2、lzma 不”懂”这个世界的规律(谁住在哪里、评论的评分怎样由两个形容词决定),只能利用重复的片段和局部统计。
- 一个细节:base model 在这里是 0.356 bit/字符,比第 1 讲报告的验证集 0.409 更好。区别在于上下文:这里每个字符都有至少 48 个字符的上下文;第 1 讲的
bits_per_char把文本切成互不重叠的窗口,每个窗口开头的字符几乎没有上下文。同一个模型,“怎样给它上下文”也会改变压缩率。
3.2 解码器必须有同一个模型
怎样解压?解码器手里只有比特流。它从文本开头出发:用模型算出第一个字符的分布,看比特流落在哪一段,解出第一个字符;把这个字符交给模型,算出第二个字符的分布,再解……代码 01 演示 3 真的这样跑了一遍:前 600 个字符压成 215 bit,解码器”边解边预测”,结果与原文一致。
这有两个后果:
- 发送方和接收方必须有同一个模型——不只是同样的结构,而是逐位相同的参数和逐位相同的计算。一个浮点数的差别可能让某个概率被量化成不同的整数,从那一刻起解码就完全错了。这在实践中是个真问题:同一个模型在不同的 GPU、不同的 batch 大小下,浮点结果常常并不逐位相同(第 9 讲 §3.1、第 10 讲 §7.1 讨论过同类问题)。
- 解压和压缩一样慢:每解一个字符就要跑一次模型。用大语言模型做压缩器,速度比 gzip 慢几个数量级。
3.3 别忘了模型本身
还有一个更根本的问题:**如果接收方没有模型呢?**那就得把模型也发过去。代码 01 演示 4 用 base model 压缩它自己的训练文本(262,986 个字符,256.8 KB):
| 大小 | |
|---|---|
| 用 base model 编码训练文本(0.3503 bit/字符) | 11.2 KB |
| 加上模型参数(109,056 × 32 bit) | 合计 437.2 KB |
| 加上模型参数(× 16 bit) | 合计 224.2 KB |
| 加上模型参数(× 8 bit) | 合计 117.7 KB |
| 对照:bz2 -9 | 16.7 KB |
(顺便一提:base model 在训练文本上是 0.350 bit/字符,在验证集上 0.356,几乎一样——这个世界的文本高度规则,模型学到的是规律,而不是逐字背下训练集。)
模型本身比数据还大。这种”先发模型、再发用模型压缩的数据”的方式叫做两段式编码(two-part code):总码长 。对小数据集,它非常吃亏。如果”压缩 = 智能”要成立,就必须回答:**模型的大小算不算钱?**一个 650 亿参数的模型存下来要一百多 GB——“模型越大压缩越好”在两段式的记账下不再显然。
Rae 的回答是另一种记账方式。
4. 训练过程就是压缩器 · Training is compression
4.1 在线编码:不用发送权重
想象发送方和接收方约好了:用同一份训练代码、同一个随机种子初始化一个模型。然后:
发送方 接收方
用模型 θ₀ 给第 1 段数据编码 → 发送比特流 用 θ₀ 解出第 1 段数据
用第 1 段数据训练一步 → θ₁ 用第 1 段数据训练一步 → θ₁(和发送方逐位相同)
用 θ₁ 给第 2 段数据编码 → 发送比特流 用 θ₁ 解出第 2 段
…… ……
每一段数据都用”还没见过它”的模型来编码;接收方解出这段数据之后,做和发送方完全相同的一步训练,于是两边的模型始终相同。模型参数一个都不用发——接收方自己训练出来了。需要发送的只有:训练代码(很小),加上每一段数据的码长之和:
求和的每一项就是第 步训练时、更新之前的损失。所以:数据集的压缩后大小 = 训练损失曲线下的面积(横轴是 token 数)+ 训练代码的长度。这种编码在统计学里叫在线编码或预序列编码(prequential coding,Dawid 1984 的术语)。Blier & Ollivier(2018)专门研究了深度网络的描述长度,发现:用这种”增量”的编码方式,深度网络确实能把训练数据压得很好,即使把模型的代价考虑在内;而专门为最小化码长设计的变分方法反而给出很差的结果。
4.2 LLaMA 的账
据报告的整理,Rae 用 LLaMA 做了一笔账(以下数字来自对报告的转述,我没能核对原 slides,代码 02 演示 4 只核对算术):
- LLaMA-65B 的训练数据约 1.4 万亿个 token,按每个 token 约 4 字节算,原始文本约 5.6 TB。
- 训练损失曲线下的面积(整个训练过程中负对数似然之和)约 400 GB——相当于平均每个 token 2.29 bit(1.58 nats),也就是训练全程的平均损失。
- 初始化和训练 LLaMA-65B 的代码大约 1 MB。
- 所以 LLaMA-65B 把 5.6 TB 的训练数据”无损压缩”到了约 400 GB:约 14 倍。
就算改用两段式、把 65.2B 个 16 位参数(130 GB)也算进去,合计 530 GB,仍有约 10.6 倍——在这个规模上,参数的代价已经不重要了。Rae 由此得出:同样的数据、同样的训练代码,LLaMA-65B 的损失曲线比 LLaMA-33B 的低,所以 65B 是更好的压缩器——更大的模型更”智能”,在这个意义下是可以精确比较的。
4.3 迷你实验:一边学一边编码
代码 02 演示 1 在一个”更大的迷你互联网”上做了同样的事:同样的生成规则、同样的 60 个人,评论、故事、算术各 15,000 行,一共 1,731,979 个字符。4 种宽度的 TinyGPT 从随机初始化开始,按顺序读数据流,每 768 个字符先算损失(码长)、再做一步更新——每个字符只看一遍:
| 压缩器 | 参数个数 | 在线码长(bit/字符) |
|---|---|---|
| TinyGPT,宽度 16 | 8,832 | 1.7270 |
| TinyGPT,宽度 32 | 29,952 | 1.1190 |
| TinyGPT,宽度 64 | 109,056 | 0.8282 |
| TinyGPT,宽度 128 | 414,720 | 0.7198 |
| 自适应 3-gram(边数数边编码) | — | 1.2323 |
| 自适应 5-gram | — | 0.6293 |
| 自适应 8-gram | — | 0.5970 |
| gzip -9 | — | 0.9310 |
| bz2 -9 | — | 0.4371 |
| lzma -9 | — | 0.6873 |
在线码长随模型变大而单调变短:宽度 16 → 128,参数多了 47 倍,码长从 1.73 降到 0.72 bit/字符。这正是 Rae 的论点:大模型学得更快,损失曲线下的面积更小,而且不用为参数付钱。
演示 2 用两段式给同样 4 个模型记账(训练完的参数按 32 bit 存,再加上用训练完的模型编码整个数据流):
| 宽度 | 参数 × 32 bit(折合 bit/字符) | 数据 | 训练完的模型 | 两段式合计 | 在线码长 |
|---|---|---|---|---|
| 16 | 0.1632 | 0.8897 | 1.0529 | 1.7270 |
| 32 | 0.5534 | 0.5363 | 1.0898 | 1.1190 |
| 64 | 2.0151 | 0.5017 | 2.5168 | 0.8282 |
| 128 | 7.6630 | 0.4827 | 8.1457 | 0.7198 |
同一组模型,两种记账给出相反的排序:两段式偏爱小模型(宽度 128 的模型光参数就要摊上 7.66 bit/字符),在线编码偏爱大模型。“谁压缩得最好”这个问题,必须先说清楚”模型算不算钱、怎样算”。
4.4 一个和预想不同的结果:bz2 赢了
我原本期待在线训练的神经网络能在这个数据流上击败通用压缩器。实际上,bz2(0.44)比所有在线训练的神经网络和自适应 n-gram 都好。演示 3 把数据流分成十段,看每一段的边际码率:
| 数据流 | 宽度 16 | 宽度 32 | 宽度 64 | 宽度 128 | bz2 |
|---|---|---|---|---|---|
| 0–10% | 3.515 | 3.004 | 2.591 | 2.059 | 0.511 |
| 10–20% | 2.514 | 2.083 | 1.161 | 0.785 | 0.427 |
| 50–60% | 1.340 | 0.649 | 0.549 | 0.537 | 0.517 |
| 90–100% | 0.932 | 0.558 | 0.507 | 0.499 | 0.412 |
**学习曲线就是压缩曲线。**神经网络是从零开始学的,前 10% 的数据几乎都花在”学会这是什么语言”上(宽度 128 的模型要 2.06 bit/字符);它的边际码率一直在下降,最后 10% 降到 0.50 左右,但还没追上 bz2。原因有两个:这个世界的文本由几十种模板生成,信息量本来就很低(第 1 讲的 base model 充分训练后约 0.35 bit/字符),bz2 的块排序变换很擅长这种高度规则、大量相似片段的文本;而 173 万个字符对一个从零开始学的神经网络来说太少了——在线学习的压缩器要在足够长的数据流上才划算。LLaMA 读了 1.4 万亿个 token;我们只有 173 万个字符。
这个”失败”本身说明了 Rae 论证里一个容易被忽略的前提:“更大的模型是更好的压缩器”是在数据极多的条件下成立的。数据少的时候,学习的代价(损失曲线开头那一大块面积)会吃掉模型能力带来的好处。
4.5 为什么这暗示”更好的预测 = 更好的泛化”
在线编码还有一个很漂亮的性质。在 LLaMA 那样每条数据只看一遍(单遍训练,第 4 讲)的训练里,第 步的训练损失是模型在一批从没见过的数据上的损失——它同时也是测试损失。所以:
- 训练损失曲线就是泛化曲线。没有”训练损失低但测试损失高”的过拟合问题。
- 曲线下的面积小,意味着模型用更少的样本学会了更多——不只是最终状态好,学习过程本身也高效。这是”泛化能力”的一个很自然的定义。
Rae 的报告据整理强调了这一点:目标不是”更好地预测训练数据”(一个把训练集背下来的模型可以在训练集上做到 0 损失,但这对理解训练数据背后的世界毫无帮助),而是用最短的描述刻画产生这些数据的世界。在线编码把这两件事区分开了:背诵只在”见过之后”才有用,而在线码长只计”见过之前”的损失。
这也给第 4 讲的规模定律一个新的读法:损失随算力的幂律下降,就是压缩率随算力的幂律提高。规模定律之所以重要,是因为它在预测”更多的算力能把世界压缩得多好”。
4.6 报告的”总纲”
据整理,Rae 把这些归结成一个研究方向:明确目标任务,收集尽可能多的有效数据,然后用一切可用的方法把它无损压缩到极致——一个任务的最短有效描述,代表了对这个任务最深的理解。整理中的一个例子是翻译:用一本词典逐词查,描述很长、效果也差;加上语法规则和固定搭配,描述更短,效果也更好。更短的描述 = 抓住了更多规律 = 更好的泛化。下一节把这个直觉放到它的理论基础上。
5. 最小描述长度与奥卡姆剃刀 · MDL and Occam’s razor
5.1 Kolmogorov 与 Solomonoff
“最短的描述”可以被严格定义。Kolmogorov 复杂度 是能输出字符串 的最短程序的长度:一个由一百万个 “ab” 组成的字符串, 很小(“打印 ‘ab’ 一百万次”);一串真正随机的字符, 约等于它本身的长度——没有比”直接把它写出来”更短的描述。
Solomonoff 归纳(1964)把它变成了一个预测理论:给每个可能解释数据的程序一个先验概率 ,然后用所有与已观察数据一致的程序做贝叶斯预测。这是”奥卡姆剃刀”的数学版本:短的解释先验概率更高。Solomonoff 证明了这种预测器在一个很强的意义下是最优的——它犯的错误总量是有界的。Hutter 的 AIXI 理论把它推广到行动的智能体;他在 2006 年设立的 Hutter 奖正是奖励”把一段维基百科压得更小”的人,理由和 Rae 的报告一样:更好的压缩需要更多的理解。
问题是, 和 Solomonoff 归纳都不可计算(要遍历所有程序)。所以这个理论只能当作”北极星”:它告诉我们方向,但不能直接用。
5.2 最小描述长度(MDL)
Rissanen(1978)的最小描述长度原则是一个能用的近似:在一族模型里,选使”描述模型 + 用模型描述数据”总长度最小的那一个——也就是 §3.3 的两段式码长 。复杂的模型让 变小,但 变大;简单的模型反之。MDL 在两者之间找平衡,这正是防止过拟合的思想:一个把训练集全部背下来的模型, 几乎是 0,但 和数据本身一样长——没有任何压缩。
§4 的在线编码是 MDL 的另一种实现:它不需要显式地给模型编码,模型的”复杂度”体现为学习的代价——复杂的模型开头学得慢,损失曲线开头那一段面积更大。
5.3 迷你实验:用码长选模型
代码 03 演示 1 用这个想法给 n-gram 模型选阶数 (1 到 10)。四种选法:训练集损失、验证集损失、在线码长(自适应 n-gram 一边数数一边编码)、两段式码长(朴素地把计数表也发过去:每个非零计数记一条,上下文和字符 bit,计数用 Elias γ 码)。训练集 30,015 个字符时:
| 训练集 | 验证集 | 在线码长 | 两段式 | |
|---|---|---|---|---|
| 3 | 1.345 | 1.443 | 1.652 | 2.467 |
| 4 | 0.707 | 0.905 | 1.161 | 2.839 |
| 5 | 0.548 | 0.822 | 1.103 | 3.722 |
| 7 | 0.423 | 0.796 | 1.140 | 5.968 |
| 9 | 0.385 | 0.817 | 1.240 | 8.709 |
| 10 | 0.387 | 0.848 | 1.305 | 10.258 |
(加粗的是每一列的最小值。)换三种训练集大小,每种选法选出的 :
| 训练集字符数 | 按训练集损失 | 按验证集损失 | 按在线码长 | 按两段式码长 |
|---|---|---|---|---|
| 10,016 | 9 | 5 | 5 | 2 |
| 30,015 | 9 | 7 | 5 | 3 |
| 262,986 | 10 | 10 | 8 | 4 |
- 训练集损失总是偏爱大 ——上下文越长,越能背下训练集。它不能用来选模型。
- 验证集损失在中间某处最好,而且最好的 随数据量增加:1 万个字符时 5,3 万个时 7,26 万个时到 10 还在下降。数据越多,越复杂的模型越划算。
- 在线码长完全不看验证集,却在 1 万个字符时正好选中 5,3 万个时选 5(验证集最好是 7),全部数据时选 8——偏保守,但跟着数据量一起变大,方向是对的。大 的模型开头学得慢(大部分上下文都没见过),这段学习的代价被计进了码长:这就是奥卡姆剃刀。
- 两段式码长选的 太小(2–4)。原因是我们的计数表编码很浪费,模型的代价被高估了;更聪明的模型编码会小得多。MDL 的结论依赖”模型怎样编码”——这是两段式 MDL 在实践中最麻烦的地方,也是在线编码的一个优点:它回避了这个问题。
6. 这个论证的边界 · Where the argument breaks
“压缩 = 智能”是一个有力的视角,但把它当作定义会遇到几个问题。
6.1 压缩的是哪个世界
代码 03 演示 2 把同一份验证集做了几种变换(bit/字符;均匀分布是 5.46):
| 数据 | base model | gzip | bz2 | lzma |
|---|---|---|---|---|
| 原样 | 0.355 | 1.478 | 1.190 | 1.404 |
| 每一行倒过来写 | 10.734 | 1.474 | 1.169 | 1.402 |
| 每一行内部打乱字符 | 11.595 | 5.048 | 4.763 | 4.744 |
| 随机字符 | 15.438 | 5.629 | 5.579 | 5.732 |
把文本倒过来写,信息量一点没变,bz2 几乎不受影响;base model 却要 10.7 bit/字符——比什么都不知道的均匀分布还差一倍。它”自信地猜错”:它的预测建立在”迷你互联网是正着写的”这个先验上。随机字符没有任何规律,base model 要 15.4 bit/字符,代价最大。
所以”压缩 = 智能”要加一个前提:压缩的是我们关心的那个世界的数据。一个强的先验在它的世界里是巨大的优势,在别的世界里是负担。这也是 Solomonoff 归纳里那个”通用”先验的意义:它对所有可计算的世界都不太差。大语言模型之所以能”通用”,靠的是训练数据极其广泛(§7.1)。
6.2 有用的信息与没用的信息
无损压缩要求把每一个 bit 都编码下来,包括没用的那些。一张照片的像素噪声、一段日志里的时间戳、一串随机生成的 ID——它们的信息量很大(不可压缩),压缩它们花掉了大部分的码长,但”理解”它们没有任何意义。强化学习里有一个类似的问题,叫”嘈杂的电视”:一个以”减少预测误差”为目标的智能体,可能会盯着一台播放雪花噪声的电视不放。
Rae 的表述里有一个关键的限定词:有用的信息。但”什么是有用的”本身就不是压缩能回答的问题——它需要一个目标、一个任务、一个价值判断。这也是为什么 Rae 的”总纲”第一步是”明确目标任务”:压缩是在一个给定的数据分布上衡量理解的尺子,而选择哪个分布,是尺子之外的事。
6.3 复制、上下文与”回去抄”
代码 03 演示 3 构造了一种特别的数据:每一行是一个 20 字符的随机串,空一格,再把同一个串写一遍(bit/字符;随机串的熵是 ):
| 压缩器 | 第一次出现 | 第二次出现 |
|---|---|---|
| base model | 13.508 | 13.835 |
| 自适应 8-gram | 5.576 | 2.243 |
| gzip | 4.982 | 0.794 |
| lzma | 4.975 | 0.400 |
第一次出现谁也压不了。第二次出现时,gzip、lzma 只要 0.4–0.8 bit/字符:它们的核心机制(LZ77)就是”这一段前面出现过,回去抄”。自适应 8-gram 也部分学会了。但 base model 第二次出现的代价和第一次几乎一样:它不会回去抄。第 5 讲已经发现,这个模型在训练中没有形成 induction head——“找到前面出现过的同一段,照着往下写”的注意力电路。
这个对比提醒我们两件事。第一,“损失低”不等于”什么都会”:base model 在它的世界里是最好的压缩器,却缺少一个 gzip 都有的能力。第二,大语言模型在上下文里学习新规律的能力(第 5 讲),从压缩的角度看就是”在当前文档里找到可以复用的结构”——induction head 可以说是神经网络版的 LZ77。第 12 讲会专门训练一个形成了 induction head 的小模型,看看这个电路长什么样。
6.4 计算与速度
Kolmogorov 复杂度和 Solomonoff 归纳不在乎计算量:最短的程序可能要运行一万年。而现实中的智能是有资源限制的:一个能在一秒钟里给出答案的系统,和一个要算一万年的系统,不是一回事。用大语言模型做压缩器也有同样的问题:压缩和解压都要跑一遍模型,比 gzip 慢几个数量级(§3.2)。所以”更好的压缩 = 更好的智能”至少要加上”在同样的计算预算下”——这恰好又把问题带回了第 4 讲的规模定律:在给定的算力下,怎样得到最低的损失。
6.5 压缩能力不等于一切能力
最后,压缩衡量的是”预测数据”的能力。它不直接衡量:按指令行事(第 7 讲:base model 在续写文档,不是在完成任务)、做出决策、在世界上采取行动、诚实(一个完美的压缩器会同样准确地预测真话和谎话,取决于训练数据里有什么)。Rae 的论断说的是基础模型——“基础”二字很关键:压缩可能是通用能力的基础,但从”基础”到”有用的助手”,还有第 6、7 讲的整个适配与对齐。
7. 2023 年之后 · Since 2023
Rae 的报告之后,“语言模型就是压缩器”这个视角得到了更多直接的实验检验。这里只列和本讲直接相关、比较确定的几条。
7.1 语言模型是通用压缩器吗
Delétang et al.(2023,Language Modeling Is Compression,DeepMind)把这件事做成了系统的实验:用大语言模型加算术编码去压缩不是文本的数据。主要训练在文本上的 Chinchilla 70B,把 ImageNet 的图像块压到原始大小的 43.4%,把 LibriSpeech 的语音样本压到 16.4%,分别好于专门的压缩格式 PNG(58.5%)和 FLAC(30.3%)。这支持了”大语言模型是通用的预测器”——它们能在上下文里认出新模态数据的规律(§6.3 说的上下文学习)。
但同一篇论文也强调了 §3.3 的问题:如果把模型本身的大小算进去,这些大模型在这些(相对小的)数据集上就不再是好的压缩器——两段式记账下,模型越大,越需要海量的数据才能把参数的代价摊薄。这和我们代码 02 演示 2 的结论一致。论文还指出,压缩的视角为规模定律、分词和上下文学习提供了新的理解;以及反过来,任何压缩器都可以变成一个生成模型(例如用 gzip 来采样)。
7.2 压缩能力与基准分数
Huang et al.(2024,Compression Represents Intelligence Linearly)比较了 31 个公开的大语言模型:用它们压缩外部文本语料的效率,和它们在 12 个基准(知识与常识、编程、数学推理)上的平均分,几乎是线性相关的。他们由此主张压缩效率可以作为一种不需要人工标注的评测指标。这是对 Rae 论断的一个直接的经验支持。需要注意的是:相关性是在”同样被训练来做下一个 token 预测”的一族模型之间观察到的;它说明在这一族模型里,压缩得好的通常也更强,但不说明压缩是能力的定义(§6 的那些边界仍然存在)。
7.3 用码长评测模型
在实践中,“在一份没见过的语料上的 bit/byte”已经成了比较语言模型的标准指标之一(例如 Pile 上的 bit/byte)。用 byte 而不是 token 作单位,是为了让不同分词器的模型可以公平比较——这本身就是压缩视角的产物:分词器不同,“每个 token 多少 bit”不可比,“每个字节多少 bit”可比。第 4 讲 §11 提到的推理时计算、思维链也给压缩视角带来了新问题:一个在回答前”想”了很久的模型,它的”码长”该怎样算?这还没有公认的答案。
7.4 哪些结论仍然成立
仍然成立:预测与压缩的等价(定理);交叉熵就是码长;单遍训练的损失曲线是泛化曲线;更低的损失在同一族模型里通常对应更强的能力。需要谨慎的:“压缩 = 智能”作为定义;“模型越大压缩越好”在两段式记账下不成立;压缩衡量不了有用性、对齐和行动。比较稳妥的说法是:压缩是衡量一个模型对它所学世界的理解程度的一把好尺子,而不是智能的全部。
8. 小结 · Summary
- 预测就是压缩:概率 的符号最优码长 ;用模型 编码真实分布 ,平均码长是交叉熵 。模型的损失字面意义上就是它的压缩率。
- 算术编码让任何能给出概率分布的模型变成真正的无损压缩器,码长几乎等于交叉熵;哈夫曼编码受”每个符号至少 1 bit”的限制。base model 把迷你互联网的验证集压到 0.356 bit/字符(22 倍),比 bz2 小 3 倍多。
- 解码器必须有逐位相同的模型和计算;模型本身的代价在两段式记账下会压倒一切(base model 的参数比它的训练文本还大)。
- 在线编码:发送方和接收方运行同一份训练代码,码长 = 训练损失曲线下的面积 + 代码长度,不用发送参数。据转述,Rae 用它算出 LLaMA-65B 把 5.6 TB 压到约 400 GB(14 倍)。我们的实验里在线码长随模型变大而变短(1.73 → 0.72 bit/字符),两段式则相反。
- 单遍训练的训练损失就是泛化损失,曲线下的面积小 = 学得快 = 泛化好;规模定律就是压缩率的规模定律。
- MDL 与奥卡姆剃刀:Kolmogorov 复杂度、Solomonoff 归纳是不可计算的理想;MDL 是可用的近似。在线码长不看验证集,就能选出接近最优的 n-gram 阶数;朴素的两段式会因为模型编码太浪费而选错。
- 边界:压缩的是哪个世界(倒过来写的文本让 base model 比均匀分布还差);有用的信息 vs 噪声;不会”回去抄”(没有 induction head);计算资源;压缩不等于有用、对齐、行动。
- 2023 年之后:大语言模型能压缩图像和音频(Chinchilla 70B:ImageNet 43.4%、LibriSpeech 16.4%),但算上模型大小就不划算;31 个模型的压缩效率与基准分数近似线性相关。
9. 常见误区 · Common pitfalls
- “交叉熵只是一个训练目标,和压缩是两回事。” 用算术编码,交叉熵就是实际的码长,只差千分之几 bit/字符(代码 01 演示 2)。
- “把模型在训练集上的损失降到 0,就是最好的压缩。” 那是背诵:模型本身就成了数据的一份拷贝。在线编码只计”见过之前”的损失;两段式编码要把背下数据的模型也算进去。
- “更大的模型总是更好的压缩器。” 在在线编码、数据很多时是这样;在两段式记账、数据较少时正好相反(代码 02 演示 2)。在小数据上,学习的代价也可能让它输给 bz2(演示 1)。
- “一个好的压缩器对什么数据都好。” 压缩器的好坏取决于它的先验和数据是否匹配。base model 在倒过来写的文本上比均匀分布还差一倍(代码 03 演示 2)。
- “压缩得越好就越智能,所以压缩是智能的定义。” 无损压缩也要编码噪声和没用的信息;它不衡量按指令行事、决策、诚实;它忽略计算成本。它是一把好尺子,不是定义。
- “Kolmogorov 复杂度给出了最优的压缩器,只是我们还没找到。” 它是不可计算的——不存在一个程序能对所有字符串算出它。我们只能用 MDL、在线编码这类可计算的近似。
- “用验证集选模型和用 MDL 选模型是一回事。” 它们常常选得相近(代码 03 演示 1),但 MDL 不需要留出数据;而且两段式 MDL 的结论依赖于模型怎样编码。
- “在线码长就是最后的损失。” 它是整条损失曲线下的面积——开头学得慢的那一段占了很大比例(代码 02 演示 3 的第一行)。
10. 练习 · Exercises
练习 1(码长) 模型给真实的下一个字符分别打出概率 0.9、0.5、0.01,编码它各要多少 bit?一个模型对 99% 的字符给出 0.9、对 1% 给出 0.01,平均码长大约是多少?
参考答案
,, bit。平均约 bit/字符。少数”意外”贡献了接近三分之一的码长——这就是为什么语言模型的损失对”自信地猜错”很敏感(代码 03 演示 2 里 base model 在倒过来写的文本上要 10.7 bit/字符)。
练习 2(交叉熵的分解) 真实分布是 4 个等概率的符号,模型用的是 (0.5, 0.25, 0.125, 0.125)。熵、交叉熵、KL 各是多少?多出来的 bit 意味着什么?
参考答案
bit。 bit。 bit。每个符号多花 0.25 bit,是因为模型”以为”第一个符号更常见,给了它短码、给了其他符号长码;这多出来的部分就是模型的无知,换一个更好的模型可以消除。熵的 2 bit 则是数据本身的不确定性,谁也消除不了。
练习 3(把符号打包) 信源 B 是 (0.9, 0.05, 0.03, 0.02)。如果把每两个符号打包成一个”超级符号”(16 种),再做哈夫曼编码,平均每个原始符号要多少 bit?和熵 0.6175、单个符号的哈夫曼 1.15 比较。
参考答案
用代码 01 的 huffman_lengths 对 16 个两两乘积的概率做哈夫曼编码,平均每个原始符号约 0.769 bit:比 1.15 好得多,但离 0.6175 还有距离。打包得越长越接近熵,但码表的大小随打包长度指数增长。算术编码相当于”把整条消息打包成一个符号”,却不需要码表——这是它在实践中胜出的原因。
练习 4(压缩率) base model 的实际码长是 0.3563 bit/字符,bz2 是 1.1905。原始文本每个字符 8 bit,两者的压缩率各是多少?
参考答案
倍, 倍——和代码 01 演示 2 表里的 22.4×、6.7× 一致(表里按整字节算,略有差别)。
练习 5(LLaMA 的平均损失) 据转述,LLaMA-65B 的损失曲线下面积约 400 GB,训练了 1.4 万亿个 token。平均每个 token 的损失是多少 nats?LLaMA 论文里 65B 模型训练结束时的损失大约在 1.5 nats 附近,两者为什么能对得上?
参考答案
bit/token nats/token。这是整个训练过程的平均损失,包括开头损失很高的那一段;由于训练的绝大部分 token 是在损失已经降到 1.5–1.8 附近之后才看到的,平均值只比最终损失高一点。(这也说明:数据越多,开头”学习的代价”在总码长里占的比例越小。)
练习 6(两段式什么时候划算) 宽度 128 的模型有 414,720 个参数,按 32 bit 存。假设训练好的模型比 bz2 每个字符少 0.14 bit,数据流至少要多长,两段式编码才能赢 bz2?
参考答案
参数要 bit。每个字符省 0.14 bit,需要 个字符,约 9500 万个字符——是代码 02 数据流的 55 倍。这就是”大模型要配大数据”在压缩视角下的一个算术版本。
练习 7(改一行代码,预测结果) 把代码 02 的学习率 LR 从 3e-3 改成 1e-3。你预测 4 个模型的在线码长会怎样变?顺序会变吗?
参考答案
学习率变小,模型学得更慢,损失曲线开头那一大块面积会更大,在线码长大概率都变长(具体数字以你运行的结果为准)。大模型是否仍然更好不一定:在很短的数据流上,学得慢对所有模型都不利,模型之间的差距可能缩小。这个练习说明在线码长衡量的是”模型 + 训练配方”的整体,而不只是模型结构——Rae 的论证里,“训练代码”本身就是压缩器的一部分。
练习 8(改一行代码,预测结果) 在代码 03 演示 1 里,把 n-gram 的平滑参数 k 从 0.01 改成 1。你预测按验证集选出的 会变大还是变小?在线码长呢?
参考答案
k 变大,没见过的上下文和低计数的上下文会被”拉平”得更厉害,大 的模型(上下文大多只见过几次)受伤最重,所以验证集和在线码长选出的 大概率都会变小(具体数字以你运行的结果为准)。这说明”最好的模型复杂度”依赖于”怎样从数据里估计模型”——平滑、正则化本身也是模型的一部分。
练习 9(概念:单遍训练) 为什么说在每条数据只看一遍的训练里,训练损失就是测试损失?如果训练了 4 个 epoch,在线码长该怎样定义?
参考答案
单遍训练时,第 步的 batch 是模型从没见过的,它在这个 batch 上的损失就是一个对”新数据”的损失估计。训练多个 epoch 时,第二遍看到的数据模型已经见过,损失会偏低,不再是泛化的估计;在线码长只能按”每条数据第一次出现时的损失”来算——后面几个 epoch 的训练相当于”不发新数据、只让接收方多训练几步”,不增加码长,但也不改变已经发送的数据的码长。这也是为什么在线编码的视角天然偏爱”多数据、单遍”的训练方式。
练习 10(概念:嘈杂的电视) 一个视频模型学会了完美地预测摄像头的传感器噪声(假设这种噪声有某种可学的规律)。它的压缩率因此提高了很多。它”更智能”了吗?Rae 的表述怎样回应这个问题?
参考答案
按”压缩率 = 智能”的字面定义,它更智能了;但直觉上,学会传感器噪声的规律对”理解视频里发生了什么”没有帮助。Rae 的表述里有”有用的信息”这个限定——压缩的是哪些数据、哪些信息值得压缩,取决于我们关心的任务(“总纲”的第一步是明确目标任务)。这说明压缩是衡量理解的尺子,但尺子要量什么,需要在尺子之外决定(§6.2)。
练习 11(概念:确定性) 你用一个大语言模型压缩了一个文件,在一张 GPU 上压缩,在另一种型号的 GPU 上解压,结果解出来的是乱码。最可能的原因是什么?怎样避免?
参考答案
两张 GPU 上的浮点计算不是逐位相同的(不同的 kernel、不同的累加顺序),某个位置的概率分布差了一点点,量化成整数频率表时就可能不同;从那个字符开始,解码器的区间和编码器不一致,后面全部错乱。避免方法:用确定性的推理(固定 kernel、固定累加顺序、甚至用整数或定点运算),或者把概率量化得更粗、让小的浮点误差不影响量化结果(仍然不能完全保证)。这也是用神经网络做实用压缩器的一个工程难点。
11. 延伸阅读 · Further reading
原课相关(2/27 Compression for AGI)
- Jack Rae. Compression for AGI. Stanford MLSys Seminar, 2023 年 2 月(公开录像)。
- Rae et al. Scaling Language Models: Methods, Analysis & Insights from Training Gopher. 2021. arXiv:2112.11446
信息论与 MDL(§2、§5 的依据)
- Shannon. A Mathematical Theory of Communication. 1948.
- Shannon. Prediction and Entropy of Printed English. 1951.
- Witten, Neal & Cleary. Arithmetic Coding for Data Compression. Communications of the ACM, 1987.
- Rissanen. Modeling by Shortest Data Description. Automatica, 1978.
- Grünwald. The Minimum Description Length Principle. MIT Press, 2007.
- Blier & Ollivier. The Description Length of Deep Learning Models. NeurIPS 2018. arXiv:1802.07044
2023 年之后
- Delétang et al. Language Modeling Is Compression. ICLR 2024. arXiv:2309.10668
- Huang et al. Compression Represents Intelligence Linearly. 2024. arXiv:2404.09937
12. 下一讲预告 · Next lecture
这一讲从外部衡量模型:它把数据压得多好。但压缩率不告诉我们模型内部在做什么——代码 03 演示 3 里,base model 明明是迷你互联网最好的压缩器,却不会”回去抄”。为什么?它内部到底学到了什么算法?
第 12 讲:机制可解释性——逆向工程 Transformer 学到的算法(1/30 客座 Stella Biderman)打开模型:残差流、注意力的 QK 和 OV 电路、logit lens;训练一个只有注意力层的小模型,看 induction head 怎样在训练中突然形成,并用消融和激活修补证明它就是”复制”的来源;看一个在模加法上 grokking 的网络怎样用傅里叶变换做加法;以及一个特征多于维度时,模型怎样把它们”叠加”在一起,稀疏自编码器又怎样把它们分开。