嵌入 — 让「意义」变成可以计算的东西
这一章讲三件事: 为什么必须有这一步、那串数是怎么被学出来的、 以及由此得到的那个「意义空间」长什么样。 作者自己说,嵌入是这本书的中心思想1;中文版导读序也把这一节评为 「我见过的对这一概念最好的解释」2。
1. 先看现象:它似乎「懂」词的意思
这一节回答:为什么必须有这一章。
你大概注意到过这类事:
- 你说「苹果公司」和说「Apple 公司」,它知道是一回事;
- 你打错一个字,它照样明白你要什么;
- 你换个说法重问,它给的答案基本一致。
它看起来「懂意思」。可上一章刚说过,网络里只有数字。
那问题就来了:「猫」这个词,在网络里到底是哪个数字?
2. 最笨的办法为什么不行
最直接的想法是编号: 给字典里五万个常用词各分一个号。
这不是假想 —— GPT-2 里 the 真的就是 914,cat 真的就是 35423。
但编号有个致命缺陷:编号是任意的。
914 = the 915 = ? 它们之间有关系吗?没有。
3542 = cat 3543 = ? 它们是近义词吗?不是。
图说:编号只是标签。网络拿到一堆毫无结构的标签,只能死记硬背。
死记硬背意味着:没见过的组合它什么都推不出来 —— 这正是第 01 章说的那条死路。
3. 嵌入是什么
一句话:
不给一个数,给一串数;并且让「意思相近的词」,这串数也相近。4
这串数就叫这个词的嵌入。实际用的很长:GPT-2 是 768 个数,GPT-3 是 12 288 个数。 把它压到平面上画出来,你能亲眼看到植物类的词聚成一堆、动物类的词聚成另一堆。
这串数是从哪来的? 靠一条朴素到近乎废话的观察:
出现环境相似的词,意思就相近。
书里的例子很直白:alligator(短吻鳄)和 crocodile(鳄鱼)
经常出现在几乎可以互换的句子里,所以它们在这个空间里靠得近;
而 turnip(芜菁)和 eagle(鹰)不会出现在相似的句子里,就离得远。
这套嵌入是从互联网上五十亿个词里学出来的——听着多,其实只是 ChatGPT 训练用量的百分之几 (那边是几百亿个词,见第 04 章)5。
主走查:三个词,各给三个数
下面这三串数是为演示编的,不是真实数值。 真实的一串有 768 个或 12 288 个, 这里砍到 3 个,只是为了能写在纸上看清楚。
| 词 | 它的那串数 |
|---|---|
| 猫 | [0.82, 0.11, −0.05] |
| 狗 | [0.79, 0.15, −0.02] |
| 桌子 | [−0.30, 0.64, 0.41] |
现在把它们逐位相减,看差多少:
猫 − 狗 = [ 0.03, −0.04, −0.03] 三个位置都差不到 0.05 → 很近
猫 − 桌子 = [ 1.12, −0.53, −0.46] 头一个位置就差了 1.12,是上面那行的三十多倍 → 很远
图说:「差得小」就是「意思近」。这就是「意义变成了距离」这句话的全部内容。
注意这里没有任何一处写着「猫和狗都是动物」。 那句话谁也没告诉它; 它只是让猫和狗那两串数长得像,于是「是不是同类」就变成了「差多少」。
4. 机制:不直接教「相似」,让它在别的事情上被迫决定
这一节是本章的核心,也是最容易读漏的一步。
先想想难在哪
你想让网络知道「哪些词意思相近」。可是 —— 这件事没有标准答案可以拿来训练。 谁来标?标多少?「苹果」和「梨」有多近,近 0.7 还是 0.8?没人说得清。
作者的解法是绕过去。 他先用图像把这个机制讲透,因为图像更容易看清楚。
拿认数字的网络来演示
上一章那个认手写数字的网络,最后输出 10 个值,对应「这是 0 到 9 各自的可能性」6。 最后一道工序叫 softmax,作用是「逼出一个明确的赢家」—— 把一堆数变成一组加起来等于 1 的可能性,并且放大最大的那个。
现在做一件事:在 softmax 之前把网络拦下来,看看那一层的数。
| 拦在哪 | 拿到什么 | 有什么用 |
|---|---|---|
| 最后一层(softmax 之后) | 「这是 4」 | 只剩结论,信息全被压平了 |
| softmax 之前 | 代表 4 的那个数最大,但其他数里也有信息 | 这就是一张「签名」:每个 4 的签名都略有不同,和 8 完全不同 |
| 再往前一层 | 500 个数 | 更细的签名,更好用7 |
这些数就可以直接拿来当嵌入。
关键概念:相似度是「顺带」学到的
作者把这一步的思路点得很清楚8:
我们不直接去表示「哪个图像接近哪个图像」, 而是找一个有明确训练数据的任务(这里是认数字), 然后利用一个事实:网络为了完成这个任务,内部不得不做出相当于「哪些东西接近」的判断。
用大白话说:
想要「哪些东西意思相近」 ← 没有标注数据,没法直接训
↓ 换一个问题
「这张图是几?」 ← 有海量标注数据,好训
↓ 训完之后
从网络中间层把数截出来 ← 它为了答对上面那题,
内部不得不把长得像的东西放到一起
图说:用一个能训的任务,换一个不能训的能力。这是全书最漂亮的一招。
换到词上,一模一样
词这边,那个「能训的任务」是填空:
给定 the ___ cat,各个词填进去的可能性分别是多少?
或者给定 ___ black ___,两侧的词分别可能是什么?9
- 输入:词的编号 ——
the ___ cat就是914, 3542; - 输出:一个约五万个数的列表,给出每个词填进去的可能性;
- 嵌入:同样在网络「得出结论之前」拦截,把中间那层的数取出来。
接上第 3 节那条走查,把这三步走实(数是编的,只有 914 和 3542 是书里的):
① 输入 `the ___ cat` → 编号 914, 3542
② 输出 五万个数:cat 0.11 / dog 0.09 / black 0.07 / … 剩下的分掉 0.73
③ 嵌入 不要 ② 那五万个数,回退一层,取那 500 个数
图说:第 ③ 步取到的 500 个数,就是第 3 节那种东西 ——
只不过每个词是 500 个数,不是 3 个。
「猫」和「狗」的那 500 个数逐位相减,差还是很小;「猫」和「桌子」差得很大。 500 个数只是比 3 个数分得更细,别的什么都没变。
过去十年出现的一系列系统 —— word2vec、GloVe、GPT、BERT (这四个名字你以后会反复撞见;此刻只需要知道它们是四种不同的「把词变成数」的做法)—— 用的网络结构各不相同,但最终都是用几百到几千个数来表示一个词10。
这些数单看是几乎没有信息的,一堆看不出意思的数字。 它们的意义完全体现在彼此的距离上。
判断(我们的,不是书里的): 这解释了嵌入为什么必然继承语料里的偏见, 而且不是靠清洗数据就能完全解决的:嵌入学的就是「哪些词出现在相似环境里」。 语料里若某个职业词长期和某个性别词一起出现,它们在空间里就是近的 —— 这不是数据标错了,是嵌入的定义本身。 如果错,会错在: 如果通过改造训练目标(而不是清洗数据)能有效解开这类关联, 那问题就不在定义层面,而在方法层面。
5. 从词到整段:ChatGPT 实际在做的
书里紧接着有一句很关键 、很多人读漏的话:
比起用一串数表示一个词,还可以做得更好 —— 可以对一段词、甚至整个文本块这样做。 ChatGPT 内部就是这样处理的。
也就是说,ChatGPT 会把「到目前为止的全部文字」变成一个嵌入, 然后据此给出下一个标记的可能性11。
于是全书的第一条链条终于接上了:
整段文字 ──嵌入──> 一串数 ──神经网络──> 另一串数 ──> 五万个候选标记的可能性
↑
「意思相近 = 数值相近」这条约定
让上面这条流水线里的每一步计算都变得有意义
图说:这就是为什么说嵌入是全书的枢纽——没有它,后面全部无从谈起。