上一个单元结尾提到,tokenizer 把文本切成 token ID 之后,嵌入层会把每个 ID 变成一个 1,536 维的向量。
「向量」这个词可能让你觉得很抽象。这一章节的目标是让你对它建立直觉:向量是什么、为什么要用向量来表示词、以及 1,536 维到底意味着什么。
从一个属性到多个属性
假设你要描述一种水果,只允许用一个数字。你可能会选「甜度」,用 0 到 10 的评分:
苹果 → 7
柠檬 → 2
西瓜 → 9
一个数字就是一维空间中的一个点。你可以把它画在一条数轴上:柠檬在左边(不甜),西瓜在右边(很甜),苹果在中间。
但一个数字远远不够。苹果和梨的甜度可能都是 7,但它们明显不同。你需要更多维度来区分它们——比如加上「酸度」:
苹果 → (甜度 7, 酸度 3)
柠檬 → (甜度 2, 酸度 9)
西瓜 → (甜度 9, 酸度 1)
梨 → (甜度 7, 酸度 2)
两个数字组成一个二维向量。你可以把它画在一个平面坐标系上:横轴是甜度,纵轴是酸度。每种水果就是平面上的一个点。苹果和梨在甜度轴上靠近,但在酸度轴上有点距离——两个维度合在一起才能把它们区分开。
如果再加上「水分」「硬度」「大小」,就变成了五维向量。五个维度没法画在纸上了,但数学上完全一样——每种水果是五维空间中的一个点,用五个数字来定位。
词向量:用数字描述词的含义
同样的思路可以用在词上。
如果我们能用一组数字来描述一个词的含义——每个数字代表含义的某个方面——那含义相近的词,它们的数字组合就应该相近。
比如用三个维度(实际不会这么少,但为了便于理解):
[皇室程度, 性别倾向, 年龄]
国王 → [0.9, 0.8, 0.7]
王后 → [0.9, -0.8, 0.7]
男孩 → [0.0, 0.8, -0.8]
女孩 → [0.0, -0.8, -0.8]
这组数字捕捉了词之间的关系:国王和王后在「皇室程度」上都很高,但在「性别倾向」上相反。男孩和女孩同样如此。国王和男孩在「性别倾向」上相近,但在「皇室程度」和「年龄」上不同。
更有意思的是,这些关系可以做算术:
国王 − 男性 + 女性 ≈ 王后
用向量运算来理解:从「国王」的位置出发,减去「男性」的方向,加上「女性」的方向,得到的点应该落在「王后」附近。这种关系在实际训练出来的词向量中确实成立——2013 年 Google 的 Word2Vec 论文因为发现了这个现象而引起了极大的关注。
1,536 维意味着什么
三个维度太少,不够描述人类语言中词义的复杂性。实际的语言模型使用几百到几千个维度。Sophia 使用 1,536 维。
1,536 维意味着每个 token 被描述为一组 1,536 个数字。你无法想象一个 1,536 维的空间(人类的空间直觉止步于三维),但数学上没有任何区别——所有在二维、三维空间中成立的运算(加法、减法、距离计算、角度计算),在 1,536 维空间中完全一样。
这 1,536 个维度各自代表什么含义?答案是:我们不知道,也不需要知道。
与上面「皇室程度」「性别倾向」这样的人为标签不同,模型自己学出来的维度没有人类可读的标签。某个维度可能同时编码了词性、语义角色、情感色彩的某种混合——这些信息以一种人类难以解读的方式分布在 1,536 个维度上。
这没有关系。重要的不是每个维度代表什么,而是向量之间的关系——含义相近的词离得近,含义不同的词离得远。模型通过训练自动学会了怎么在这个高维空间中组织词义。
嵌入层:一张查找表
回到 Sophia 的实际实现。嵌入层(embedding layer)就是一张表,大小是 65,536 × 1,536:
- 65,536 行,每一行对应词表中的一个 token。
- 1,536 列,每一列是一个维度。
当 token ID 为 3847 时,嵌入层做的事情就是:去第 3847 行,把那 1,536 个数字取出来,作为这个 token 的向量表示。
# 伪代码
embedding_table = 一个 65536 × 1536 的数字表
token_id = 3847
vector = embedding_table[token_id] # 取第 3847 行,得到一个长度为 1536 的向量
没有任何计算——就是查表。这张表的每一个数字都是模型的参数,在训练过程中被梯度下降一点一点调整到合适的值。
训练开始时,表里的数字全是随机的——每个 token 的向量都是随机的一团噪声。随着训练的进行,含义相近的 token 的向量会被逐渐推到高维空间中相近的位置,而含义不同的 token 会被推开。
从 token 到序列
一段文本经过 tokenizer 之后变成一个 token ID 序列。嵌入层把每个 ID 查成一个向量,整个序列就变成了一个向量序列:
文本:"机器学习很有趣"
tokenizer 编码 → [3847, 2190, 1523, 892, 4401]
嵌入层查表 →
[0.12, -0.45, 0.78, ..., 0.33] ← token 3847 的向量(1536 个数字)
[0.56, 0.23, -0.11, ..., 0.67] ← token 2190 的向量
[0.89, -0.34, 0.45, ..., -0.12] ← token 1523 的向量
[-0.23, 0.67, 0.90, ..., 0.45] ← token 892 的向量
[0.34, 0.12, -0.56, ..., 0.78] ← token 4401 的向量
5 个 token 变成了一个 5 × 1,536 的数字矩阵——5 行(每行一个 token),1,536 列(每列一个维度)。
这个矩阵就是 Transformer 层要处理的输入。从这里开始,模型的所有计算都发生在向量和矩阵的世界里——不再有文字,只有数字。
但目前这些向量有一个局限:每个 token 的向量是独立查出来的,没有考虑上下文。「银行」这个 token,无论它出现在「去银行取钱」还是「河岸银行」里,查到的向量都是同一个。让 token 的表示能感知上下文——这是注意力机制的工作,第四单元会讲。
下一章节先回答一个基础问题:两个向量之间「有多像」怎么计算?这个计算是注意力机制的核心。