从符号到坐标
在上一篇里你学会了把文本切成 token。但像「狗」这样的 token 仍然只是一个符号——一个没有内部结构的标签。最古老的解决办法是独热编码:给每个词在一个巨大向量里分配专属的一格,一个 1 周围全是 0。在五万词的词表里,「狗」和「猫」就变成了五万维的向量,彼此完美而无用地正交。机器没有任何办法知道它们都是动物;任意两个不同的词之间,距离都一样、都无限远。
词嵌入的飞跃,是把那个稀疏、毫无意义的格子,换成一个简短而稠密的实数向量——比如 300 个数——从数据中学出来。每个词都成了 300 维空间里的一个点,而关键的承诺是:距离反映意义——「狗」和「猫」彼此挨近,「狗」和「民主」相距甚远。一个嵌入正是如此:一张从离散符号到连续空间的、学习得来的地图,在那里几何承载着信息。
这也悄悄解决了前面阶梯里的一个问题:维度灾难。独热向量随词表一起膨胀,并且永远稀疏;而一个稠密的 300 维嵌入是一份紧凑的摘要,模型的其余部分真的能用它来计算。维度更少,每个维度承载的意义更多。
「观其伴,知其词」
这些坐标从哪来?没有人手工标注。答案立足于分布假说——语言学家 J. R. Firth 在 1957 年的名言:「观其伴,知其词。」出现在相似上下文里的词,往往意义相近。从没人告诉过你「wug」是什么意思,但读到「我喂了那只饿坏的 wug」和「那只 wug 蜷在火炉旁」,你已经怀疑它是只宠物了。这个假说断言:意义会从上下文里渗出来。
早期的 NLP 其实已经用了一半这个想法。词袋和 n 元计数,再经 TF-IDF 打磨,会注意到哪些词共同出现,但它们把每个词当作独立的一列,从不把这种共现压缩进一套共享的几何里。嵌入则把分布假说当真,并把它变成一个学习目标:安排好这些向量,使一个词的位置能预测它的邻居。
word2vec 究竟是怎么学的
2013 年,Mikolov 与他在 Google 的同事发布了 word2vec,它让嵌入既便宜又出奇地好。最流行的变体 skip-gram 玩一个简单的猜谜游戏。在文本上滑动一个窗口;在每个位置取中心词,去预测它周围的词。「The cat sat on the mat」——给定「sat」,模型应当让「cat」「on」「the」变得可能。没有人提供标签;文本给自己打标签。这就是自监督学习——从原始数据中变出监督信号。
机制上它是可能最浅的神经网络:每个词有一个输入向量和一个输出向量,模型用两者的点积给一个上下文词打分。点积高意味着「这两个属于一起」。这些分数经过一个 softmax 变成概率,训练则通过普通的梯度下降微调这些向量,使真正的邻居得高分、随机词得低分。在经过数百万个窗口之后,那些总是结交相似伙伴的词,会漂移到空间的同一片区域。嵌入并不是输出;它是副产品——输入矩阵的那些行。
一个实用技巧让它得以扩展:与其每一步都在整个五万词的词表上算 softmax(代价高昂),word2vec 采用负采样——把真正邻居的分数推高,把少数几个随机「负」词的分数压低。几年后 GloVe(斯坦福,2014)从另一个方向得到了相似的向量:它不扫描局部窗口,而是对一个全局的词共现计数矩阵做分解。两条路,一个目的地——它们都是表示学习的不同口味。
# skip-gram, in one breath
for (center, context) in slide_window(corpus):
# raise the true neighbor, lower a few random words
score = dot(vec_in[center], vec_out[context])
neg_scores = [dot(vec_in[center], vec_out[w]) for w in sample_negatives()]
loss = -log_sigmoid(score) - sum(log_sigmoid(-s) for s in neg_scores)
update(loss) # tiny gradient-descent step
# the embedding you keep = the rows of vec_in向量算术:可以加减的意义
这就是登上头条的结果。取「国王」「男人」「女人」的向量,计算 国王 − 男人 + 女人。离这个结果点最近的词是「女王」。同样的把戏给出 巴黎 − 法国 + 意大利 ≈ 罗马,以及 walked − walk + swim ≈ swam。这让人觉得模型*理解*了性别、首都和动词时态。可究竟发生了什么?
诚实的解释是几何的,而非魔法的。因为训练把每个词与它的上下文绑在一起,某个一致的*差异*——「男→女」的位移,或「国家→首都」的位移——会在许多词对上呈现为大致相同的方向和长度。从「男人」指向「女人」的箭头,几乎与从「国王」指向「女王」的箭头平行。于是减去「男人」、加上「女人」,就让你沿着那条性别方向滑动;落在「女王」附近便是回报。关系变成了空间中的方向。
要找最接近的词,用的不是直线距离,而是余弦相似度——向量之间的夹角,它忽略长度,只问「这俩指向同一个方向吗?」两个几乎指向同一方向的词会被判为相似,哪怕其中一个向量更长。这种基于夹角的搜索,是「帮我找相关词」背后的引擎,也正是后来驱动对整篇文档做向量检索的同一个想法。
几何在哪里失灵
word2vec 和 GloVe 有一个你必须刻进脑子里的硬限制:每个词永远只得到一个向量。于是「bank」——河岸的与银行的——被强行塞进同一个浑浊的点,成为它所有义项的一个模糊平均。没有任何办法让上下文把它磨锐。让这些向量得以存在的那件事(一次性从所有上下文里学习),恰恰也是阻止它们消歧任何单一用法的那件事。
这些是静态嵌入:「bank」在「river bank」和「central bank」里的坐标完全相同。修复之道——让每一次出现都有一个由其实际句子塑造的向量——正是接下来几篇要经由语言建模、并最终经由基于注意力的模型所搭建的目标,在那些模型里,一个词的表示每次都从它的邻居重新算出。word2vec 是那道门;上下文嵌入是门后的那个房间。
还有一道值得正面直视的伦理棱角。因为嵌入吸收了人类文本的统计规律,它也一并吸收了其中的偏见:word2vec 有个著名例子,把「男人→程序员」与「女人→家庭主妇」摆在一起。捕捉「国王→女王」的那套几何,用同一套机器捕捉刻板印象——它根本没有「哪些方向无害、哪些有害」的概念。有用的表示与不想要的偏见,是由完全相同的过程学来的,这也正是为什么每一个现代大语言模型至今仍继承着这个问题。
为什么这个想法比算法本身活得更久
今天你很少会从零训练 word2vec,而这正是关键所在。它持久的馈赠不是那个具体算法,而是一个信念:意义可以是一个向量——正确的着手方式,是学出一个稠密空间,让几何替你干活。这个信念如今贯穿了下游的一切:每一个进入 Transformer 的 token 都先变成一个嵌入,而句子、图像、用户的「嵌入」驱动着全行业的搜索、推荐与检索。
- Token 本身仍只是符号——孤立时毫无意义,又被独热的稀疏性所拖累。
- 分布假说说上下文揭示意义:相似的伙伴意味着相似的义项。
- word2vec/GloVe 把它变成一个自监督游戏,给每个词产出一个稠密向量。
- 于是距离与方向编码了相似性与关系——向量算术与余弦搜索。
- 但每个词只有一个静态向量,无法处理歧义——这正是接下来几篇要解开的悬念。