背下来,和学会
训练的唯一目标是把损失压低。可是损失低有两种完全不同的来路:学会了产生这些文字的规律,或者干脆把这些文字背了下来。从一个数上看不出区别。这一章拿四段文字去试 GPT-2——一篇它没见过的新文章、一首程序员的打油诗、一份网上复制了无数遍的许可证、一段毫无意义的拉丁文——看它在哪里是「懂」,在哪里是「背」。
四段英文,分别交给 GPT-2,算平均每个字符的意外(比特/字符,越低说明它越「料得到」)。哪一段最低?
import this 打印的那首十九行的诗)。短句、重复的句式
C MIT 开源许可证的正文。法律套话
D 排版用的假文 Lorem ipsum。毫无意义的拉丁文
答案在本章末尾。
同一台模型,四段文字
除了比特/字符,再加一个直观的指标:第一名猜中率——逐个 token 看,模型排第一的那个候选,是不是恰好就是原文的下一个 token。
| 文字 | 比特/字符 | 第一名猜中率 | 同样的词,打乱顺序 |
|---|---|---|---|
| MIT 许可证正文 | 0.29 | 91.0% | 2.15 |
| 维基百科「ChatGPT」开头(新文章) | 0.96 | 39.4% | — |
| Python 之禅 | 1.03 | 48.7% | 2.09 |
| Lorem ipsum | 1.50 | 42.8% | 2.30 |
MIT 许可证那一行和别的不是一个量级:每个字符 0.29 比特,是新文章的三分之一还不到;十个 token 里有九个,它排第一的候选就是原文。这不是「法律文字比较规范」能解释的——规范的文字很多,它们都在 0.9 到 1 比特之间。
读到 Permission is hereby granted, free of,下一个是 ␣charge 的概率是 99.82%。这已经不是在「猜」了。
给它第一行,它能默写多少
更直接的测试:只给 MIT 许可证的第一行,让它每一步都挑最可能的那个 token,自己往下写。
Permission is hereby granted, free of charge, to any person obtaining a copy ← 给它的 ⟪of this software and associated documentation files (the "Software"), to deal⟫ ⟪in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,⟫ ⟦license,⟧ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
(原本的换行它写成了空行,这里略去。)从第一行之后,它一字不差地默写了 29 个词,在第 30 个词上多写了一个 license,,然后马上又接回了原文。这段文字它在训练里见过太多次了——每个用 MIT 协议的项目页面上都有一份——多到它在权重里存了一份近乎原样的拷贝。
同样的测试放到 Python 之禅上,给它前两行(「Beautiful is better than ugly. / Explicit is better than implicit.」),它写出的第三行是:
The only difference is that the implicit is better than the explicit. The only difference is that the implicit is better than the explicit. The only difference is that the implicit is better than the explicit. …
原文第三行是「Simple is better than complex.」。它没背下来,于是开始编,编着编着就原地打转了(第 14 章专门讲这种复读)。第 8 章会看到,连大 12 倍的 GPT-2 也没背下这首诗。背不背得下,取决于一段文字在训练数据里重复了多少遍,而不是它有多重要。
打乱顺序:它背的是句子,不是词
表的最后一列是个对照实验:同样那些词,顺序随机打乱,再让 GPT-2 读一遍。MIT 许可证从 0.29 涨到 2.15 比特/字符,翻了七倍多。
这说明它记住的不是「这篇文字里有哪些词」,而是这些词按这个顺序出现——每一个 token 都被前面的 token 精确地「召唤」出来。顺序一乱,召唤链就断了。
更耐人寻味的是 Lorem ipsum。这是一段排版用的假文,半拉丁半胡编,没有任何意思。可 GPT-2 读它只要 1.50 比特/字符,打乱之后是 2.30。一段毫无意义的文字,它也「料得到」——因为它在网上见过足够多遍(每个还没填内容的网页模板里都有)。低损失可以完全不涉及理解。
训练集和测试集之间那条缝
上一章那台 13 万参数的小网络,也有同样的现象,只是换了个名字。它在训练集上是 1.93 比特/字符,在没见过的测试集上是 2.40——中间那 0.47 比特,就是它记住了、但推广不出去的东西。如果给它更大的胃口——看前 16 个字符、23 万个参数——差距会更大:
| 模型 | 参数 | 训练集 | 测试集 | 差距 |
|---|---|---|---|---|
| 第 3 章:10-gram 计数模型(看 9 个字符) | — | 0.86 | 5.00 | 4.13 |
| 第 6 章:小网络,看 8 个字符 | 133721 | 1.93 | 2.40 | 0.47 |
| 同一种网络,看 16 个字符 | 232025 | 1.91 | 2.59 | 0.68 |
看 16 个字符的版本,在训练集上和看 8 个字符的差不多好(1.91 对 1.93),在测试集上却明显更差(2.59 对 2.40)。多出来的那些参数和上下文,主要被用来记住训练集里的特定片段了。这叫过拟合:在见过的数据上越来越好,在没见过的数据上原地不动甚至变差。第 3 章的 10-gram 是它的极端形态——几乎全靠背,背完就崩。
所以机器学习里有一条铁律:永远在模型没见过的数据上量它。这本书选一篇 2022 年之后才写的维基百科文章来量 GPT-2,原因就在这里——它保证 GPT-2 不可能背过。
为什么这条缝要紧
- 隐私。2021 年,Nicholas Carlini 等人发表了《Extracting Training Data from Large Language Models》,对象正是 GPT-2:他们让它大量生成文本,再用「模型对这段话异常有把握」作为信号去筛,从中找出了逐字背下来的训练数据——包括真实的人名、电话、邮箱和地址。所用的信号,就是这一章那个「异常低的比特数」。
- 版权。能逐字默写,就有可能逐字复现受版权保护的文字和代码。2023 年底《纽约时报》起诉 OpenAI 和微软,诉状里一个核心论点就是模型能大段复现它的文章。代码补全工具也因为同样的原因,加上了「与公开代码重复时不显示」的过滤选项。
- 被污染的考卷。如果一道评测题连同答案出现在了训练数据里,模型答对它可能只是在默写。这叫数据污染。评测一个模型,越来越多地要用它训练截止之后才出现的题目——和这本书选文章的道理一样。
MIT 许可证的正文在仓库的 scripts/probe-next/data/ 里(或者从任何一个 MIT 协议的项目里复制,去掉第一行的版权声明):
import math, random, torch
from transformers import AutoTokenizer, AutoModelForCausalLM
tok = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2").eval()
def bits_per_char(text):
ids = torch.tensor([tok.encode(text)])
with torch.no_grad():
nats = model(ids, labels=ids).loss.item() # 平均每个 token 的意外(单位 nat)
return nats * (ids.shape[1] - 1) / math.log(2) / len(text)
mit = open("mit-license.txt").read().strip()
words = mit.split()
random.Random(0).shuffle(words)
print(f"MIT 许可证原文 {bits_per_char(mit):.3f} 比特/字符")
print(f"同样的词,打乱顺序 {bits_per_char(' '.join(words)):.3f} 比特/字符")
first = mit.split("\n")[0] + "\n" # 只给第一行,让它自己往下写
out = model.generate(tok(first, return_tensors="pt").input_ids, max_new_tokens=40,
do_sample=False, pad_token_id=50256)
print(tok.decode(out[0]))
本机输出:
MIT 许可证原文 0.285 比特/字符 同样的词,打乱顺序 2.152 比特/字符 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute
拿你熟悉的「网红文本」去试:《独立宣言》开头、Apache 许可证、Lorem ipsum、某个热门开源项目的 README。比特数异常低、能默写下去的,就是它背过的。
pip install torch transformers && python 07.py
在线跑:Google Colab,把许可证文本直接粘成一个字符串即可。
- 考前背真题是人的过拟合:原题能拿满分,换个问法就不会。判断一个学生是不是真的会,办法和判断模型一模一样——出一道他没见过的题。
- 回测漂亮、实盘亏钱的量化策略是金融里的过拟合:参数调到和历史数据严丝合缝,等于把历史背了下来。《配平》那本书里讲的「样本外」,就是这一章的测试集。
- 模型的「训练截止日期」之所以要标出来,一半是为了告诉你它不知道什么,另一半是为了告诉评测者:哪些题它可能背过。
「模型在一段文字上的意外很低,说明它很理解这段文字。」
意外低可能只是见得多。GPT-2 读 MIT 许可证比读一篇新的维基百科文章低了三倍多,读毫无意义的 Lorem ipsum 也只要 1.50 比特/字符——它不「理解」一段乱写的拉丁文,它只是见过。
要区分「学会」和「背下」,只有一个办法:拿它没见过的东西去量。打乱顺序(MIT 从 0.29 涨到 2.15)、换成训练截止之后的文本、换个问法——能经得住这些的,才是学会了的那部分。
正确答案是 C:MIT 许可证 0.29 比特/字符,第一名猜中率 91.0%。
新文章 0.96、Python 之禅 1.03、Lorem ipsum 1.50。
《成列》第 18、19 章讲的是同一条铁律在数据分析里的样子:一份纯随机的数据,挑特征的步骤放在交叉验证外面,就能「背」出 98.9% 的准确率。《证伪》讲测试为什么必须是外部的、模型没见过的真相来源。《意外》第 13 章的「最短程序」给了另一个角度:背下来的模型像一个很长的程序,而真正的规律总是短的。
这一章的一句话
低损失有两种来路:学会了规律,或者背下了原文;从一个数上分不出来。在见过很多遍的文字上,模型会逐字背——那是隐私、版权和考卷污染的来源。要知道它学会了多少,只能拿它没见过的东西去量。
下一章把参数加上去:同一篇没见过的维基百科文章,交给四个尺寸的 GPT-2,从 1.24 亿到 15.6 亿个参数。每个字符的比特数只降了一成多——可在同一组模型里,「金的元素符号是什么」这道题,答案 Au 从第 1066 名一路升到了第 1 名。