如果把语言模型理解成“根据上下文预测下一个符号”,那么神经网络并不是唯一的实现方式。一个更离谱、也更有启发性的实验是:把操作系统里现成的 gzip 当成预测器。
Nathan Barry 提出的脑洞实验没有训练参数,也没有 Transformer。它只把语料放进压缩器能看到的上下文中,然后尝试不同的下一个字符,选择让压缩结果增长最少的那个。结果当然不像真正的语言模型那样流畅,但它确实能生成字符,也能从语料中表现出一些超出预期的结构信息。
这件事有趣的地方,不在于 gzip 即将取代大模型,而在于它让“预测”与“压缩”之间的关系变得非常直观。
压缩为什么可以用来预测
压缩器的目标是用更少的比特表示更多的数据。要做到这一点,它必须利用重复模式:已经出现过的字符串、常见的字符组合,以及在当前上下文中更可能出现的内容。
因此,可以把下一个字符的选择写成一个近似实验:
- 准备一段语料和提示词。
- 分别把候选字符追加到当前文本后面。
- 压缩每个候选结果。
- 选择压缩后长度最小,或新增长度最小的候选。
- 把选中的字符加入上下文,继续循环。
这里的“最可能”不是通过概率分布直接得到的,而是通过压缩代价间接估计的。某个字符让文本更容易被已有模式解释,它通常就更便宜。
这和语言模型的交叉熵、负对数概率有相似的精神:预测得越好,编码代价越低。但两者不能简单画等号。gzip 的字典、窗口、区块边界和启发式规则都会影响结果,它并不提供一个干净的神经语言模型概率分布。
一个可运行的最小实验
下面的脚本使用 Python 标准库里的 zlib,模拟一个极简的压缩驱动字符生成器。它会把语料、提示词和候选字符拼起来,比较压缩后的长度,并用贪心策略逐字符生成。
这段代码是一个可改造的实验模型,不是对原实验实现细节的复刻。为了让结果更稳定,脚本使用固定的候选字符集和较短的文本;真实实验可以换成更大的语料、完整字符集或 beam search。
#!/usr/bin/env python3
import zlib
CORPUS = """
Alice was beginning to get very tired of sitting by her sister on the bank,
and of having nothing to do. Once or twice she had peeped into the book her
sister was reading, but it had no pictures or conversations in it.
""".strip()
PROMPT = "Alice was "
CANDIDATES = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.!?'\n"
def compressed_size(text: str) -> int:
"""Return the size of zlib's compressed representation."""
return len(zlib.compress(text.encode("utf-8"), level=9))
def choose_next(context: str) -> tuple[str, list[tuple[int, str]]]:
scored = []
for candidate in CANDIDATES:
trial = context + candidate
# The corpus acts as the compressor's available history.
size = compressed_size(CORPUS + "\n" + trial)
scored.append((size, candidate))
scored.sort(key=lambda item: (item[0], item[1]))
best_size, best_char = scored[0]
return best_char, scored[:5]
def generate(prompt: str, steps: int = 120) -> str:
context = prompt
for _ in range(steps):
next_char, top = choose_next(context)
context += next_char
print(f"picked={next_char!r} size={top[0][0]} top={top}")
return context
if __name__ == "__main__":
result = generate(PROMPT)
print("\nGenerated text:")
print(result)
运行方式:
python3 gzip_lm.py
这个版本有一个重要限制:它每次都重新压缩整段文本,而且只比较最终压缩文件的总长度。压缩头和短文本的固定开销可能淹没候选字符的差异。实验时可以增加语料长度、使用更长的上下文,或者比较同一个压缩流中追加数据的增量代价。
它到底“知道”什么
压缩驱动的生成通常不会得到稳定、流畅的长文本。它更可能在局部表现出一些规律:
- 复现语料里出现过的短语或字符组合。
- 在特定上下文后偏向语料中常见的字符。
- 保留部分标点、空格和大小写模式。
- 在上下文足够接近时,产生看起来像续写的局部片段。
这些现象不代表 gzip 理解了故事、事实或语义。它更像是在利用重复模式和局部统计结构。若语料很小,生成内容可能只是记忆;若上下文超出压缩器窗口,较早的信息也会逐渐失效。
另一个边界是“压缩长度”并不总能准确反映候选字符的概率。不同压缩器可能采用不同字典策略,区块刷新也可能让相邻候选产生不稳定的分数。因此,实验结果应当被看作一种启发式测量,而不是严格的概率推断。
从这个脑洞实验得到的工程启发
这个实验最值得保留的结论,是语言建模可以从一个很朴素的问题开始:给定上下文,哪个候选让整体描述更短?
它也提醒我们区分三件事:
- 预测能力:能否在局部猜中下一个符号。
- 生成质量:能否长距离保持语法、主题和结构。
- 语义能力:是否真的建立了可泛化的世界模型。
传统压缩器在第一件事上可能比直觉中更有用,但并不自动拥有后两件事。神经语言模型通过参数化表示、训练目标和更大的上下文处理能力,把局部模式扩展成了更强的生成系统;gzip 则把问题压缩到了一个透明、便宜、容易复现的实验里。
什么时候值得尝试
可以把这个思路用于教学、压缩算法可视化、文本模式探索,或者快速验证“某个语料是否能解释一段候选文本”。如果目标是生产级文本生成、稳定的概率采样或事实可靠性,就不应把压缩器当成语言模型替代品。
实践时建议记录每一步的候选分数、压缩器版本、窗口大小和语料内容。还可以把贪心选择升级为 beam search,使用多个候选前缀保留路径,或者比较不同压缩算法的生成偏差。这样得到的不是一个更神秘的模型,而是一套更清楚的实验:观察“可压缩性”究竟能在多大程度上支持预测。