卷 I · 一张表CH 01深度 1/20

它只会做一件事:给下一个词打分

你每天让它写代码、改文案、解释报错。可是在最底下,它只做一件事:读完前面所有的字,给「下一个 token」打一张分数表——50257 个候选,每个一个概率,加起来正好是 1。这一章把那张表真的打印出来看一眼,然后把整本书的地基埋下去:你见过的一切本事,都是为了把这张表打准,被迫在它肚子里长出来的零件。

50257 个候选Paris 排第 5跨领域对照表
◉ 这一章在 GPT-2 的哪一段

▷ 先猜一下

把一句话 The capital of France is 喂给 GPT-2——OpenAI 在 2019 年公开权重的那台模型,最小的版本有 1.24 亿个参数。它会给「下一个 token」的每个候选打一个概率。

问:␣Paris 在这张表里拿到多少概率、排第几?(␣ 表示前面带一个空格。)

A 第 1 名,超过 50%。这是一道常识题 B 第 1 名,20% 上下。它知道答案,只是不太肯定 C 在前五名里,但不是第 1 名,3% 上下 D 前一百名都排不进去。它根本不知道法国首都是哪

顺手再猜一个:如果 Paris 不是第一,那排第一的是什么?答案在本章末尾。

每章开头都有这么一题。你此刻在做的事——看一段话、猜下一个是什么、等着被告知对错——正是这台模型在训练时每一步做的事。这本书读到最后,你会知道这不是比喻。

把那张表打印出来

先把话说准。模型读的不是「字」也不是「词」,是 token:一段文字被切成的小块。The capital of France is 被切成 5 块:The、␣capital、␣of、␣France、␣is(␣ 表示前面带一个空格——空格是 token 的一部分,下一章细说)。GPT-2 的「字典」里一共有 50257 个这样的块,叫做词表。

把这 5 个 token 送进模型,跑一遍——这一遍叫前向传播,就是把输入从第一层算到最后一层——出来的是 50257 个数,每个数对应词表里的一个 token,代表「下一个是它」的概率。下面就是这张表的前十名,本机实测:

The capital of France is ▍ 之后,GPT-2 small 的候选表(前十名) The ␣capital ␣of ␣France ␣is 下一个 token 概率 意外 ' the' 8.46% 3.56 比特 ' now' 4.79% 4.38 比特 ' a' 4.62% 4.44 比特 ' France' 3.24% 4.95 比特 ' Paris' 3.22% 4.95 比特 ' in' 2.66% 5.23 比特 ' also' 2.64% 5.24 比特 ' not' 2.38% 5.39 比特 ' home' 2.33% 5.42 比特 ' still' 1.55% 6.01 比特 熵 8.65 比特 · 本机实测,全部 50257 个候选加起来 = 1

几件值得停下来看的事:

  • Paris 排第 5,只有 3.22%。排第一的是 ' the'(8.46%)。
  • 这张表很「平」。50257 个候选里,概率超过 1% 的只有 14 个;前十名加起来也只占 35.9%,剩下的六成多散落在五万个候选身上。
  • 每一行右边那个朱红的数,是这个候选的「意外」:−log₂ p,单位比特。概率越小,真出现时越意外。Paris 那一行是 4.95 比特。这个数第 4 章会变成全书最重要的那个数——训练时要压低的,就是它。

这张表有多「平」,可以用一个数概括:熵,也就是「平均意外」。它是 8.65 比特——相当于模型在大约 403 个同样可能的选项里犹豫。(2 的 8.65 次方约等于 403。熵的来历《意外》那本书讲了一整卷,这里只需要知道:熵越高,表越平,模型越拿不准。)

对照一下,同一台模型读到 The Eiffel:

The Eiffel ▍
  ' Tower'      96.99%    ⟦0.04 比特⟧
  ' tower'       2.59%
  ' Towers'      0.13%
  熵 0.24 比特:几乎没有犹豫

同一台机器,两张表,一张平得像在乱猜,一张尖得像在背书。表的形状不是模型固定的性格,是上下文决定的。上下文越能确定下一个是什么,表就越尖。

为什么排第一的是「the」

直觉会说:「它不知道法国首都是巴黎。」这个判断下得太快了。换个角度问:在互联网上,「The capital of France is」后面最常跟着什么?

是 the city of Paris、a city of over two million people、now ……、also ……。把「Paris」直接接在 is 后面,只是这句话众多写法中的一种。模型给 ' the' 最高分,不是因为它觉得答案是「the」,而是因为它在做一件和「回答问题」完全不同的事:它在猜这段文字最可能怎么接下去。

这是理解这台机器的第一个台阶。它没有「问题」和「答案」的概念,只有「前文」和「下一个」。它不是在回答你,它是在续写。

一句话,就是从表里反复抽

一张表只给一个 token。一段话是这样来的:

读入前文           →  出一张表  →  从表里挑一个 token  →  把它接到前文末尾
  ↑                                                              │
  └──────────────────────── 再来一遍 ─────────────────────────────┘

写 100 个 token,就是把这个圈跑 100 遍。每一遍,模型都把「原来的前文 + 它自己刚写的那些」整个重新读一次(第 15 章会讲它怎么偷懒、不真的全部重算)。这种「自己写的东西马上变成自己的输入」的方式,有个名字叫自回归。

「从表里挑一个」这一步有很多种挑法:永远挑第一名,或者按概率抽签,或者只在前几名里抽。挑法不同,写出来的东西天差地别——第 14 章会看到,永远挑第一名的那种,写着写着就开始原地复读。

而你在聊天框里看到的「对话」,也只是这个圈的一种用法。试试把一个问题原样喂给 GPT-2:

What is the capital of France? ▍
  '\n'       33.84%     ← 换行
  ' The'      5.25%
  ' It'       4.07%
  ' France'   2.70%
  ' What'     2.65%

它最想写的是一个换行。因为在它读过的文字里,一个孤零零的问句后面,最常见的是另一个问句——FAQ 页面、考试题、论坛标题列表。它不觉得自己是被提问的那个人,它觉得自己在续写一页问题清单。第 16、17 章会讲「助手」这个角色是怎么后来被训练出来的;现在只要记住:对话也是续写,只不过续写的是一份写成对话格式的文档。

一件小事,怎么长出那么多本事

如果它真的只会「猜下一个」,为什么它能写代码、能翻译、能解释你的报错?

因为「猜下一个」这件事,想做到极致,就必须把产生这段文字的那个世界也猜出来。想猜准一段 Kotlin 代码的下一个 token,得知道括号要配对、变量要先声明、这个函数刚才返回的是什么类型;想猜准一本推理小说最后一页的下一个词,得知道凶手是谁;想猜准一个人在论坛里的下一句话,得猜他在生什么气。

没有人给 GPT-2 写过一行「语法规则」或「事实库」。训练只给了它一个要求:在几十亿个 token 上,把下一个 token 猜得尽量准。为了满足这个简单到近乎无聊的要求,它在内部长出了一堆没人要求过的零件——本书卷 III 会把其中几个拆出来看:一个专门盯着「上一个 token」的注意力头、一个会「现学现卖」的复制电路、一些把「打篮球的人」接到「篮球」上的权重。

这个结构——目标极其简单,优化压力极其巨大,于是内部长出复杂的、没人设计过的机制——不是 AI 独有的。学会认它,你会在很多地方看见同一张脸:

你以为它是其实它是章
聊天机器人在「回答你」在续写一份「用户:…… 助手:……」格式的文档,并且被训练成只写助手那一半16–17
手机输入法的联想是个小功能和 GPT 是同一个函数:给下一个词打分。差别只在上下文多长、模型多大3
压缩软件和 AI 是两个领域猜得准就是压得小。GPT-2 拿来当压缩器,比 zlib 小 3 倍多4
程序是人一行行写出来的这段程序是「搜」出来的:梯度下降在 1.24 亿个旋钮组成的空间里找6
查数据库是「找到存好的那一条」注意力是一次模糊的查询:按相似度给所有行加权,再把它们的值混在一起9
模型里的知识是一条条存好的记录没有记录,只有分散在几千万个权重里的倾向;删不掉某一条11
「现学现卖」需要真正理解至少有一大块是一个两步的复制电路,关掉 4 个注意力头它就不会了12
一直挑最可能的词,写出来的就是最好的话一直挑最可能的,会掉进原地复读14
它说得这么肯定,一定是知道训练从来没有奖励过「我不知道」;流畅和知道是两件事18
眼睛这样精密的器官,一定是被设计出来的是「活下来、生下来」这个简单目标被优化了几亿年,顺带长出来的本章
一家公司复杂的部门结构是老板画出来的是「赚钱」这个目标在竞争压力下长出来的;KPI 也会被同样精确地钻空子(见《打分》)本章

最后两行不是跑题。进化、市场、梯度下降,是同一类过程:给一个简单的打分,然后施加巨大的选择压力。它们的产物都有两个共同特点:内部机制远比目标复杂;而且会精确地满足你写下的那个目标,而不是你心里想要的那个。这本书讲的是第一个特点在大模型里长什么样;第二个特点在第 17、18 章会回来找你。

为什么拿 GPT-2 来拆

你可能会问:2026 年了,为什么拆一台 2019 年的模型?

理由和生物学家用果蝇一样:它小到能在笔记本上完整地跑、完整地改,又大到已经长出了那些有意思的零件。这本书里的每一个数字,都是在本机上把 GPT-2 真跑出来的:我用 numpy 从头写了一台 GPT-2(只做前向计算,权重直接读 OpenAI 公开的文件),和 Hugging Face transformers 库的官方实现逐个对照——同一段输入,logits(就是 softmax 之前的那 50257 个原始分数)最大只差 0.0032,最可能的那个 token 100% 一致;分词器和 OpenAI 自己的 tiktoken 在 234347 个字符上逐 token 相同。

今天的模型比它大几十到几千倍,换了几个零件(第 19 章逐个对照真实的配置文件),但骨架是同一副:切 token → 查向量 → 十几到上百层「注意力 + MLP」→ 一张表 → 抽一个 → 再来一遍。拆懂了这一台,你就拆懂了那一族。

⌨ 自己跑一遍

十几行。装好 torch 和 transformers,第一次运行会自动下载 GPT-2 的权重(548 MB):

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

tok = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2").eval()

prompt = "The capital of France is"
ids = tok(prompt, return_tensors="pt").input_ids
with torch.no_grad():
    logits = model(ids).logits[0, -1]        # 最后一个位置:给「下一个 token」的打分
p = torch.softmax(logits, -1)                # 50257 个数,加起来是 1
H = -(p * torch.log2(p)).sum()
print(f"词表 {p.numel()} 个候选,概率之和 {p.sum():.4f},熵 {H:.2f} 比特")
for rank, i in enumerate(p.topk(10).indices.tolist(), 1):
    print(f"{rank:2d}  {tok.decode(i)!r:10}  {p[i]:.2%}")

本机输出:

词表 50257 个候选,概率之和 1.0000,熵 8.65 比特
 1  ' the'      8.46%
 2  ' now'      4.79%
 3  ' a'        4.62%
 4  ' France'   3.24%
 5  ' Paris'    3.22%
 6  ' in'       2.66%
 7  ' also'     2.64%
 8  ' not'      2.38%
 9  ' home'     2.33%
10  ' still'    1.55%

把 prompt 换成你自己的句子,看看表在哪里尖、在哪里平。一个好玩的实验:把一句中文喂进去——GPT-2 几乎没见过中文,你会看到一张平得惊人的表。

pip install torch transformers && python 01.py

不想装环境:打开 Google Colab 新建一个笔记本,torch 和 transformers 都是现成的,粘进去直接运行。

▸ 在现实里
  • 手机输入法上方那一排候选词,就是一张候选表的前三名。它和 GPT 的差别是量,不是质:输入法看前面一两个词,GPT-2 看前面 1024 个 token,今天的模型看几十万个。
  • 编辑器里的灰色代码补全,是从这张表里一路挑最可能的那个,挑到一个换行或者一个右括号为止。
  • 聊天产品里的「重新生成」按钮:同样的前文,再从表里抽一遍。抽到的第一个 token 不一样,后面的一整段就分岔了。
  • 有的 API 会把这张表的前几名连同概率一起返回给你(OpenAI 的接口里叫 logprobs)。做分类、做打分时,读这张表往往比读模型写出来的那句话更可靠——表里有它的犹豫,写出来的句子里没有。
✗ 这个直觉是错的

「它先在心里想好答案,再把答案一个字一个字写出来。」

每一步,它只产出一张表。并没有一个「已经想好的完整答案」放在某处,等着被逐字誊写出来。刚才那张表里排第一的是 ' the'——如果它真的抽中了 the,答案就要等它写完「the city of」之后,在另一张表里才轮到 Paris 出场。

这件事有一个很实用的推论:它每写一个 token,只做固定那么多计算(一次前向)。所以让它「一步一步写出推理过程」不是礼貌用语——写出来的每个中间 token,都是多给它的一份计算,而且会变成它后面每一步都能读到的前文。直接要答案,等于逼它在一次前向里算完。

◇ 揭晓

正确答案是 C:' Paris' 排第 5,概率 3.22%。
排第一的是 ' the'(8.46%),后面是 ' now'、' a'、' France'。

A 「这是常识题」——对人是。但这台模型没有被出过题,它只见过网页上的句子,而网页上这句话有一百种接法,直接接 Paris 只是其中一种。这个选项的直觉来自把它当成了「问答机」。 B 「知道但不肯定」——方向对了一半。它确实「知道」一点:Paris 在 50257 个候选里排进了前五,比随便一个城市名高得多。错在以为它的不确定是「对答案没把握」,其实大部分不确定是「对句式没把握」。 D 「根本不知道」——太悲观了。第 8 章会看到,把句子补成「…is the city of」,换成大 12.5 倍的 GPT-2 XL,Paris 的概率从 11.08% 涨到 82.45%;而稍微改一下提示词的写法(第 16 章),这台小模型自己就能把 Paris 推到 63.71%。知识在里面,只是这句话没把它叫出来。

这一章的一句话

大模型的全部接口是一张给下一个 token 的概率表;它不是在回答你,是在续写。你见过的一切本事,都是为了把这张表打准,被迫在它肚子里长出来的零件。

下一章去看这张表的「行」到底是些什么东西。一个提前剧透:' strawberry' 在 GPT-2 眼里是一个符号,编号 41236——它从来没有见过这个词里面那几个字母。这就是为什么「strawberry 里有几个 r」能难倒一台会写代码的模型。