1. 从一句反直觉的断言说起:为什么"压缩"能等同于"智能"
第一次看到"压缩即智能"这个说法,大多数人的反应是困惑——压缩不就是把文件变小吗,跟智能有什么关系?我当初也是这个反应。但如果你真的动手写过压缩算法,或者认真琢磨过预测模型的工作原理,你会发现这两件事在数学底层几乎是同一件事。
这个系列我打算用几章的篇幅,把"智能的本质是压缩"这条线索从头到尾捋一遍。第一章先解决最核心的问题:信息熵到底在度量什么,它和"理解"之间是什么关系,以及为什么一个足够好的世界模型,本质上就是一个足够好的压缩器。
适合谁来读?如果你写过一点代码,对机器学习有模糊的认知,但总觉得"熵""KL散度""最小描述长度"这些词飘在空中落不了地,那这篇就是写给你的。我不打算堆公式,而是尽量用"你手上正在做的事"来解释这些概念为什么非出现不可。
先说结论,后面慢慢展开:一个系统对世界的"理解程度",可以用它预测下一个观测值时节省了多少信息量来衡量。预测得越准,需要的存储越少,压缩率越高——而预测得准,恰恰就是我们说一个系统"聪明"的日常含义。
这个等价关系不是哲学比喻,它有严格的数学形式。接下来我会一层层把它拆开。
2. 信息熵:把"意外程度"变成一个可以计算的数
2.1 从"今天下雨吗"这个最朴素的问题入手
假设你住在一个常年干旱的地方,一年365天里只有5天下雨。我问你"明天会下雨吗",你几乎可以闭着眼睛回答"不会"。这个回答几乎不携带任何信息,因为结果高度可预测。
换一个地方,一年里有一半时间在下雨。同样的问题,你的回答就变得有价值了——它真的在传递信息。
香农的天才之处在于,他把这种"意外程度"变成了一个可以精确计算的量。一个事件发生的概率是 p,它携带的信息量就是 -log(p)。概率越接近1,信息量越接近0;概率越小,信息量越大。
这个定义不是拍脑袋来的。它必须满足几个直觉上合理的要求:必然发生的事件信息量为零;两个独立事件的信息量应该可以相加;信息量应该连续依赖于概率。满足这三条的数学形式,本质上只有 -log(p) 这一种(差一个常数倍)。所以熵不是一个"发明",而是一个"发现"——它是唯一合理的选择。
2.2 熵是"平均意外程度",不是"混乱程度"
很多人把熵通俗地解释成"混乱程度",这个说法在热力学里勉强能用,但在信息论里会误导人。熵的准确定义是:在你知道概率分布的前提下,对下一个符号进行最优编码时,平均每个符号需要的最少比特数。
注意这里有两个关键限定。第一,"你知道概率分布"——熵是相对于一个模型而言的。第二,"最优编码"——它衡量的是理论下界,不是某个具体算法的表现。
举个能算的例子。假设一个信源只发两种符号 A 和 B,A 出现的概率是 0.9,B 是 0.1。那么:
- A 的信息量 = -log2(0.9) ≈ 0.152 比特
- B 的信息量 = -log2(0.1) ≈ 3.322 比特
- 平均熵 = 0.9 × 0.152 + 0.1 × 3.322 ≈ 0.469 比特
也就是说,虽然每个符号理论上可以用1个比特表示(0和1),但因为这个信源高度偏向A,实际上平均只需要0.469比特就能无损编码。省下来的那一半多,就是从"可预测性"里榨出来的。
2.3 为什么这个数字是压缩的物理下限
这里有个容易被忽略的点:熵给出的下界是无损压缩的下界。你不可能用比熵更少的比特无损地表示这个信源,这是香农信源编码定理保证的。
我实测过一个很直观的对比。拿一段英文文本,用最朴素的定长编码(每个字符8比特)存储,和用基于字符频率的霍夫曼编码存储,后者通常能压到原来的55%到65%。而英文文本的字符级熵大约在4.0到4.5比特/字符之间,也就是说理论极限能压到50%出头。霍夫曼编码之所以达不到极限,是因为它按单个字符编码,没有利用字符之间的相关性——比如"q"后面几乎总是"u"。
这就引出了下一节的核心:要突破单符号熵的限制,你必须建模符号之间的关系。而建模关系,就是"理解"的开始。
3. 条件熵与互信息:预测能力如何被量化
3.1 从"独立编码"到"看着上文编码"
单符号熵假设每个符号独立出现。但真实世界的数据几乎从不独立——文字有语法,图像有空间连续性,音频有频谱结构。当你利用这些结构时,你实际上是在做条件编码:给定前面的内容,预测下一个符号。
条件熵 H(X|Y) 度量的是:在已知 Y 的情况下,X 还剩下多少不确定性。如果 Y 能完美预测 X,条件熵为0;如果 Y 对 X 毫无帮助,条件熵等于原来的熵。
互信息 I(X;Y) = H(X) - H(X|Y),正好度量了"知道 Y 之后,关于 X 的不确定性减少了多少"。这个量是对称的,也是非负的。
3.2 一个能亲手验证的小实验
你可以用一段文本做这个实验,感受一下条件熵的威力。
第一步,统计单个字符的频率分布,算出字符级熵。英文大概在4.1比特左右。
第二步,统计每个字符在"前一个字符已知"条件下的分布,算出条件熵 H(X_i | X_{i-1})。这个值会降到大约3.3比特。
第三步,把条件扩展到前两个、前三个字符。条件熵会继续下降,但边际收益递减。到前五个字符左右,英文的条件熵会逼近1.0到1.5比特/字符这个区间。
这个逐步下降的过程,就是"模型在变聪明"的过程。每多利用一点上下文,就多榨出一点可预测性,压缩率就更高一点。一个能利用无限长上下文的模型,理论上能把英文压到接近其真实熵——而那个真实熵,恰恰就是"英语这门语言的全部规律"的信息量。
3.3 预测即压缩的严格对应
现在可以把这个对应关系说清楚了。任何压缩算法都可以拆成两部分:一个概率模型(对下一个符号给出概率分布)和一个编码器(根据概率分布把符号映射成比特)。
给定概率模型,最优编码器的平均码长就是模型预测分布的交叉熵。模型预测得越准,交叉熵越低,压缩率越高。反过来,任何压缩器都隐含一个概率模型——你从它的输出反推,总能得到一个对数据的概率分布。
所以"预测"和"压缩"不是类比关系,是同一件事的两种说法。一个模型对世界的理解越深,它对下一个观测的预测分布就越接近真实分布,它能达到的压缩率就越接近真实熵。
这里有个容易踩的坑:很多人以为"压缩率高"就等于"模型好"。但如果你的模型过拟合了训练数据,它在训练集上压缩率极高,在新数据上却崩掉。真正衡量理解程度的,是在未见过的数据上的压缩率。这一点后面讲世界模型时会反复用到。
4. 最小描述长度:奥卡姆剃刀的数学化
4.1 "最简单的解释最好"到底怎么算
奥卡姆剃刀说"如无必要,勿增实体"。这句话在哲学上很漂亮,但没法直接用来做工程决策——什么叫"必要"?多复杂算"增实体"?
最小描述长度(MDL)原理给了它一个可计算的版本。核心思想是:最好的模型,是能把"模型本身"加上"用这个模型编码数据"的总长度压到最短的那个。
总描述长度 = L(模型) + L(数据|模型)
第一项是描述模型本身需要的比特数,第二项是用这个模型编码数据需要的比特数。模型越复杂,第一项越大;但模型越强,第二项越小。最优模型在这个权衡中找到平衡点。
4.2 为什么这一项"模型本身的长度"如此关键
这一项是很多人忽略的。举个极端例子:假设你有一个模型,它把整段数据直接背下来了,预测时查表就行。那么 L(数据|模型) = 0,压缩率完美。但 L(模型) 等于数据本身的长度——你只是把数据从左边口袋挪到了右边口袋,总长度没变。
这就是过拟合的本质:你把复杂度从"数据拟合项"转移到了"模型描述项",总账没省。
真正有价值的模型,是能用很短的描述捕捉到数据中大量规律的那个。牛顿力学用几个公式描述了行星运动,L(模型) 极短,L(数据|模型) 也极短,总长度远小于把每颗行星每个时刻的位置都存下来。这就是"理解"的经济学定义。
4.3 两段式编码与贝叶斯的内在联系
MDL 和贝叶斯推断在数学上是等价的。L(模型) 对应先验概率的负对数,L(数据|模型) 对应似然的负对数,总描述长度最小化等价于后验概率最大化。
这个等价关系不是巧合。贝叶斯推断说"根据数据更新对模型的信念",MDL 说"找总描述最短的模型",两者都在做同一件事:在"模型复杂度"和"数据拟合度"之间找最优权衡。
我个人的经验是,理解了这个等价关系之后,调模型正则化项的时候心里就有底了。L2 正则化本质上是在说"我偏好权重小的模型,因为描述它们需要的比特少";L1 正则化是在说"我偏好稀疏的模型,因为零权重不用编码"。正则化系数就是在调 L(模型) 和 L(数据|模型) 之间的兑换率。
5. 世界模型:智能体内部的压缩器
5.1 智能体为什么必须"压缩"世界
现在把视角拉到一个在环境中行动的智能体。它每时每刻接收大量感官输入,如果它把每个输入都原样存储,很快就会淹没。它必须做取舍:哪些信息值得保留,哪些可以丢弃。
这个取舍的标准是什么?保留那些能帮助预测未来的信息,丢弃那些对预测无用的信息。这恰恰就是压缩的定义。
一个智能体内部的世界模型,本质上就是一个压缩器:它把高维、嘈杂、冗余的感官流,压缩成一个低维、干净、可预测的内部状态。这个内部状态不需要还原原始输入,它只需要保留对决策有用的部分。
5.2 预测误差就是"压缩没压好"的信号
如果世界模型压缩得好,它对下一个观测的预测就应该准,预测误差就小。预测误差大,说明模型没有捕捉到数据中的某些规律,压缩没做到位。
这就给了一个非常干净的学习信号:最小化预测误差,就是在最大化压缩,就是在提升对世界的理解。
这个视角统一了很多看起来不同的方法。自编码器最小化重构误差,是在压缩;预测编码网络最小化下一帧预测误差,是在压缩;对比学习拉近正样本、推远负样本,本质上也是在学一个能把数据压缩到低维流形的表示。
5.3 从"记住"到"理解"的分界线
一个只会记忆的系统和一个真正理解的系统,区别在哪里?
记忆系统:L(模型) 很大,L(数据|模型) 很小,总长度不省。 理解系统:L(模型) 很小,L(数据|模型) 也小,总长度大幅节省。
用日常语言说:记忆是把答案抄下来,理解是找到生成答案的规律。抄下来的答案只能应对见过的问题,找到的规律能应对没见过的问题。
这就是为什么泛化能力和压缩能力是同一件事的两面。一个模型如果在训练集上压缩得很好,但在测试集上压缩得差,说明它学到的是记忆而不是规律。真正学到规律的模型,在未见数据上同样能压缩得好。
实操提示:如果你想评估一个表示学习模型到底学到了什么,别只看下游任务的准确率。试试用它压缩一批新数据,看压缩率如何。压缩率在训练集和测试集之间的差距,比准确率更能暴露过拟合。
6. 把理论落到代码:一个最小可运行的压缩实验
6.1 用字符级n-gram模型做条件压缩
光讲理论不够,我们动手写一个能跑的实验,亲眼看到"模型越强,压缩越好"。
思路很简单:用不同阶数的n-gram模型对一段文本做算术编码,比较压缩率。阶数越高,模型利用的上下文越长,理论上压缩率越好。
import math from collections import defaultdict def build_ngram_model(text, n): """构建n-gram频率模型,返回条件概率表""" counts = defaultdict(lambda: defaultdict(int)) for i in range(len(text) - n): context = text[i:i+n-1] next_char = text[i+n-1] counts[context][next_char] += 1 # 转成概率,加平滑避免零概率 probs = {} vocab_size = len(set(text)) for context, next_counts in counts.items(): total = sum(next_counts.values()) + vocab_size probs[context] = { c: (cnt + 1) / total for c, cnt in next_counts.items() } return probs, vocab_size def cross_entropy(text, model, n, vocab_size): """计算模型在文本上的交叉熵(比特/字符)""" probs, _ = model total_bits = 0.0 count = 0 for i in range(len(text) - n): context = text[i:i+n-1] next_char = text[i+n-1] if context in probs and next_char in probs[context]: p = probs[context][next_char] else: p = 1.0 / vocab_size # 未见上下文,退化为均匀分布 total_bits += -math.log2(p) count += 1 return total_bits / count跑这段代码,你会看到交叉熵随 n 增大而下降。n=1 时大约4.1比特,n=3 时降到3.0左右,n=5 时接近2.5。每降一点,就意味着模型多理解了一点语言结构。
6.2 交叉熵、困惑度与压缩率的换算
这三个指标说的是同一件事,只是单位不同。
- 交叉熵(比特/符号):最优编码下平均每个符号的比特数。
- 困惑度:2的交叉熵次方,可以理解为"模型在每一步平均在多少个候选之间犹豫"。
- 压缩率:交叉熵除以原始编码的比特数(比如8比特/字符)。
如果交叉熵是2.5比特/字符,原始是8比特/字符,压缩率就是31.25%。困惑度是 2^2.5 ≈ 5.66,意思是模型平均在5到6个字符之间犹豫。
我习惯用困惑度来调模型,因为它对数值变化更敏感,训练时曲线更好看。但跟人解释压缩效果时,用压缩率更直观。
6.3 实测中的几个意外发现
第一个意外:平滑参数对结果影响巨大。我一开始用最简单的加一平滑,n=5时压缩率反而不如n=3。原因是高阶n-gram遇到大量未见上下文,平滑把概率摊得太均匀,反而增加了编码长度。后来换成插值平滑(高阶和低阶加权混合),高阶模型才真正发挥出优势。
第二个意外:训练集和测试集的压缩率差距,比准确率差距更能反映过拟合。我试过一个n=8的模型,训练集上压缩到1.8比特/字符,测试集上却是3.5。而n=4的模型训练集2.6,测试集2.9。虽然后者训练集表现差,但泛化好得多。这印证了前面说的:真正衡量理解的是在未见数据上的压缩。
第三个意外:文本长度对结论有影响。短文本上高阶模型因为上下文稀疏,表现反而不稳定。要观察到清晰的"阶数越高压缩越好"的趋势,至少需要几十万字符的语料。
7. 常见误解与边界条件
7.1 "压缩即智能"不等于"压缩率越高越智能"
这是最需要澄清的一点。压缩率是在特定数据集上衡量的。一个模型在某个数据集上压缩率极高,可能只是因为它过拟合了这个数据集,或者这个数据集本身规律简单。
真正的智能体现在跨分布的压缩能力:在一个分布上训练的模型,能不能在另一个相关但不同的分布上同样压缩得好。这对应的是泛化,是迁移学习,是"举一反三"。
所以更准确的说法是:智能体现在用尽可能短的模型描述,在尽可能广的数据分布上达到尽可能低的压缩率。三个"尽可能"缺一不可。
7.2 有损压缩与无损压缩对应不同的"理解"
前面讨论的主要是无损压缩。但有损压缩同样重要,甚至更贴近生物智能。
生物系统不会无损记住所有感官输入,它做的是有损压缩:丢弃细节,保留对生存有用的结构。什么该丢、什么该留,取决于任务。这就是为什么"理解"总是相对于某个目标而言的——没有绝对的理解,只有对完成某类任务足够好的理解。
在工程上,这意味着设计表示学习目标时,必须想清楚下游任务是什么。一个为图像分类训练的压缩器,和为一个为图像生成训练的压缩器,保留的信息完全不同。
7.3 计算复杂度:理论下界不等于实际可达
香农熵给出的是理论下界,但达到这个下界可能需要无限的计算资源。实际中我们总是在"压缩率"和"计算成本"之间权衡。
一个能完美预测的模型如果推理一次要跑三天,那它在实践中毫无价值。这就是为什么工程上大量使用近似方法:变分推断、采样、蒸馏。它们牺牲一点压缩率,换取可接受的计算成本。
理解这个权衡很重要,因为它解释了为什么"理论上最优"的方案经常不是"工程上最优"的方案。做研究可以追理论极限,做产品必须算总账。
8. 这一章留下的线索
把这一章的核心串起来:信息熵度量了在已知模型下编码数据的最少比特数;条件熵和互信息刻画了利用上下文能省多少;MDL把"模型复杂度"和"数据拟合度"统一到一个总长度里;世界模型就是智能体内部的压缩器;预测误差就是压缩没压好的信号。
这条线索往下走,自然会遇到几个问题:如果世界模型是压缩器,那它的最优结构应该长什么样?为什么现在的深度网络能压缩得这么好?压缩目标和强化学习的目标怎么统一?
下一章我打算聊"预测编码与自由能",把"最小化预测误差"这个信号从信息论一路推到神经科学和强化学习。那里面有个很漂亮的结论:一个系统如果持续最小化它的预测误差,它就在隐式地最大化它对世界的压缩,也就走在通往"理解"的路上。
如果你手头有代码环境,强烈建议把第6节那个n-gram实验跑一遍。亲眼看到交叉熵随模型变强而下降,比读十遍公式都管用。我当初就是跑完这个实验,才真正相信"压缩"和"理解"不是比喻,是同一件事。