第 7 天 · 熵一路上涨,复杂度却先升后降
一句话
论文用二维元胞自动机模拟奶油混入咖啡,用压缩后的文件大小估计熵和“表观复杂度”。结果显示:熵单调上升,复杂度先升后降;而且粒子之间必须有交互,复杂度才会明显出现。
论文讲了什么
结论先行:封闭系统的熵(entropy)只增不减,但系统“看起来有多复杂”会先升后降。这篇论文把这个直觉变成一个可以在电脑上跑的实验。
问题。把奶油倒进黑咖啡:开始时上白下黑,结构很简单;最后完全混匀,也很简单;中间出现卷须和漩涡,看起来最复杂。第 6 天 Aaronson 的博客文章提出了这个猜想。难点是“复杂”缺少可计算的定义。柯氏复杂度(Kolmogorov complexity)不可计算,而且它把纯随机噪声算成最复杂的东西,和直觉相反。
方法。作者做了 3 件事:
- 建两个模型。n×n 网格上每格是奶油(1)或咖啡(0),初始时奶油占上半部分。交互模型每一步随机选一对相邻格子,两格的值不同就交换。非交互模型让每个奶油粒子独立做随机游走,一个格子可以容纳多个粒子。
- 用压缩估计熵。把精细状态(每个格子的原始值)用 gzip 压缩,压缩后的字节数近似熵。
- 定义表观复杂度(apparent complexity)。先做粗粒化(coarse-graining):把网格切成 g×g 的小块,取块内平均值,再按阈值分成 3 档。然后压缩粗粒化后的图,压缩后的字节数就是复杂度估计值。粗粒化抹掉微观噪声,只留下宏观结构。
关键结果。
- 两个模型的熵都单调上升,最后趋于平稳。
- 交互模型的复杂度先升后降,峰值出现在混合过程的中段。
- 3 档阈值会在边界制造假复杂度:边界附近的格子在两档之间来回跳。作者改用 7 档,并按每行的多数值修正(adjusted coarse-graining)。修正后,非交互模型几乎不出现复杂度,交互模型仍然先升后降。
- 换用其他压缩程序,曲线的形状保持一致。
- 规模实验:网格边长 n 增大时,最大熵约按 n² 增长,最大复杂度约按 n 增长,到达复杂度峰值的时间约按 n² 增长。
附录给出一个分析论证:非交互模型的复杂度在整个过程中保持很低。作者也承认:交互模型里复杂度必然上升这一点,目前还没有严格证明。
关键概念
- 柯氏复杂度(Kolmogorov complexity)
- 能输出某个字符串的最短程序长度。它衡量“最少要多少信息才能描述这个对象”。它不可计算,而且纯随机串的值最大。
- 表观复杂度(apparent complexity)
- 论文用的复杂度指标:先粗粒化,再估计结果的柯氏复杂度。纯有序和纯随机的状态得分都低,有宏观结构的状态得分高。
- 粗粒化(coarse-graining)
- 把 g×g 小块取平均并分档,相当于站远一点看图。它去掉单个粒子的随机细节,只保留观察者关心的宏观形状。
- 用压缩近似信息量(compression as a proxy)
- 柯氏复杂度算不出来,就用 gzip 压缩后的字节数代替。压缩得越小,说明数据里的规律越多。这是一个上界估计,不是精确值。
- 交互模型(interacting model)
- 每格只能放 1 个粒子,粒子只能和邻居交换位置。粒子之间互相阻挡,于是出现了非交互模型里没有的宏观结构。
历史位置:为什么值得读
它回答的是一个物理学家的追问。热力学第二定律说熵会增加,但没有说复杂结构从哪来、何时消失。2011 年 Sean Carroll 在一次会议上提出这个问题,Aaronson 写博客回应,提出“复杂动力学第一定律”的猜想(第 6 天)。这篇论文是两人带着学生 Ouellette 做的第一次量化实验。
它把“复杂”从口号变成了可测量的量。此前有几种候选定义:精巧度(sophistication)、逻辑深度(logical depth)、光锥复杂度(light-cone complexity)。作者讨论后选了最容易计算的方案:粗粒化加压缩。这个选择不完美,但给出了可以复现、可以改进的基线。
它为什么出现在 Ilya 的清单里。清单前半段反复出现同一条线索:学习就是压缩。第 4、5 天讲最小描述长度,第 26 天讲柯氏复杂度。这篇论文补上了“结构”这一面:真正值得学习的,是去掉噪声之后剩下的宏观规律。神经网络泛化,本质上也是学到数据的粗粒化结构,而不是记住每个像素。
学前准备
- 先读第 6 天:复杂动力学第一定律的博客文章
- 知道 gzip 是无损压缩,压缩率反映数据的规律性
- 理解二维随机游走:每步随机向上下左右走一格
- 会用 NumPy 操作二维数组,会用 matplotlib 画曲线
在原仓库怎么学
这一天不需要下载数据集,全部数据由模拟生成。建议按下面的顺序学:
- 读 README.md 的目录结构,它按论文第 3 到第 9 节组织。先对照论文读一遍,知道每节对应哪段代码。
- 读 implementation.py。它实现了交互和非交互两个模型,以及 3 档和修正后两种粗粒化测量。先看模型的单步更新函数。
- 运行 train_minimal.py。默认参数跑交互模型;加
--compare-models对比两个模型;加--compare-methods对比两种粗粒化;加--scaling跑规模实验。 - 打开 notebook.ipynb 一步步复现曲线。画图函数在 visualization.py,包括熵与复杂度曲线、网格快照、规模分析和压缩器对比 4 类图。
- 做 exercises/ 的 5 个练习:01 自己写粗粒化和复杂度测量;02 对比交互与非交互模型;03 规模分析;04 修正粗粒化;05 对比不同压缩器。卡住时再看 exercises/solutions/。
- 最后用 paper_notes.md 和 CHEATSHEET.md 自查理解。
今天再看
“学习即压缩”这条线索在大语言模型时代更强了。语言模型的预训练损失就是对下一个 token 的编码长度。2023 年 DeepMind 的论文直接把语言模型当压缩器用,压缩率超过 gzip 这类通用工具。
这篇论文的度量方法已经显得粗糙。gzip 只能发现局部重复,发现不了“漩涡”这种几何规律。如果换成神经网络压缩器,表观复杂度的曲线很可能会变。复杂度峰值的位置也依赖粗粒化尺度 g,而 g 由人来选。
核心问题仍然开放。什么样的数据“有结构”?模型应该记住什么、忽略什么?今天讨论数据质量和合成数据时,人们问的还是这篇论文的问题:怎样区分有意义的结构和随机噪声。
容易卡住的地方
- 分清两条曲线:熵压缩的是精细状态,复杂度压缩的是粗粒化状态。两者用同一个压缩器,区别只在输入,所以形状不同才是重点。
- 小网格看不到清楚的峰值。建议先用 n = 50 并多跑几次取平均,因为 n = 10 时随机波动会盖住先升后降的趋势。
- 非交互模型也“出现”复杂度时,先别怀疑代码。这通常是 3 档阈值的边界伪影,加上 7 档和行多数修正后会消失。
- 模拟步数随 n² 增长。网格边长翻倍,步数约乘 4,所以先用小网格调通代码,再跑规模实验。
延伸阅读
- 原论文:Quantifying the Rise and Fall of Complexity in Closed Systems: The Coffee Automaton
- 原仓库第 7 天目录:07_coffee_automaton/ —— 代码、笔记本、练习与答案(英文)
- The First Law of Complexodynamics(Aaronson 博客) —— 本文的出发点,即第 6 天的原文
- Language Modeling Is Compression —— 把语言模型当压缩器用的实验
- Kolmogorov complexity(Wikipedia) —— 柯氏复杂度的定义和不可计算性