词怎么变成向量:从 one-hot 到 Word2Vec

1 分钟阅读
·

没有机器学习背景的工程师补大模型原理的第二站:one-hot 为什么装不下语义,Word2Vec 怎么用「预测上下文」把词的含义学进几百维向量,以及负采样这个工程近似为什么必须存在。

本文是「零基础学大模型原理」系列的第 2 篇。这个系列记录一个没有任何机器学习背景的工程师,在 ChatGPT 发布之后从零补原理的过程。

上一篇讲了神经网络的训练过程:定义损失函数、用反向传播计算梯度,再反复更新参数。但例子里的输入是房价和 XOR 这样的数字。回头再翻 GPT-3 论文,第二节里反复出现 token、embedding 这些词。语言模型的输入是文本,计算过程需要数字,因此还要处理「词怎么变成数字」。更具体地说,「猫」和「狗」语义相近,怎样把这个关系编码到数字中?这篇笔记补的就是这一环。前置内容基本是上一篇,知道损失函数和梯度下降在做什么就够了。

one-hot 编码的两个限制

最直接的办法是给词编号。词表里有五万个词,就给每个词一个序号,再将它展开成一个五万维向量:序号对应的位置为 1,其余位置为 0。这叫 one-hot 编码,许多入门资料都从这里讲起。

第一个问题是维度高。五万维向量只有一个位置非零,若按稠密向量存储和计算,绝大部分开销都落在零上,词表扩大后成本还会继续增加。第二个问题是它不表示词之间的语义关系。任意两个不同词的 one-hot 向量点积都是 0,欧氏距离也都相等。「猫」和「狗」的距离,与「猫」和「微积分」的距离相同。对模型而言,每个词都是无关的符号,「这两个词经常可以互换」这类信息没有编码位置。

词表外的新词无法表示,一词多义也无法区分。前两个限制已经说明需要另一种编码。

分布式假设:上下文与词义

一种语言学假设认为,词的含义可以从它经常出现的上下文中推断。英文里有句更早的表述:You shall know a word by the company it keeps(Firth, 1957)。拿一个陌生词举例:连续读十句带「螃蟹」的句子,如果「蒸」「壳」「钳子」「吃」反复出现在附近,即使没有定义,也能大致推断它指什么。机器也可以利用这种信息:不必人工标注每个词的含义,只需从词周围经常出现的词中学习表示。

相应地,一个词的表示不再只占一个维度,而是分布在几百个维度中。单独查看某一维通常无法解释其含义,整体向量则由词的使用分布训练得到。这也是「分布式表示」的含义。

Word2Vec 如何训练词向量

2013 年 Mikolov 在 Google 发表的两篇论文将这个想法实现为可训练的方法。对工程背景的读者来说,Word2Vec 的训练任务很直接:模型根据文本中的共现关系预测词,词向量是训练中得到的参数。

它有两种结构:

  • CBOW:用上下文预测中间的词。「今天 天气 很 好」遮住「天气」,模型根据「今天、很、好」预测中间的词。
  • Skip-gram:用中间的词预测窗口内可能出现的上下文词。

以 Skip-gram 为例,模型维护两张查找表:每个词作为中心词时有一个向量,作为上下文词时有另一个向量,初始值是随机数。对于一对「中心词 + 真实上下文词」,模型计算两个向量的点积。点积越大,模型给这对词的分数越高。随后定义损失、反向传播并更新向量,使训练语料中的共现词对获得更高分。在大量语料上重复这一过程后,使用上下文相似的词通常会得到相近的向量。

我当时特意停下来想过这为什么会奏效。损失函数要求真实共现的词对得到更高分,却没有直接规定「猫」必须靠近「狗」。如果「猫」和「狗」的上下文有大量重合,例如都能与「养」「喂」「毛」共现,训练会让它们分别与相近的上下文向量匹配。在给定语料、目标函数、向量维度和训练结果下,这可能使两个词的向量接近,但不是目标函数保证的结论。想通这一点后,「词向量里装着语义」对我来说才对应到具体的训练机制。

还有一个当时只记了结论的点:这个任务是自监督的。训练样本(中心词、上下文对)从原始文本中自动构造,不需要人工标注。可用语料规模不再受标注量直接限制,但训练规模仍受数据、算力和训练时间约束。后来我才慢慢理解「不用人工标注」具体改变了什么。

第一篇论文标题里的 Efficient 不是客套。相较于更早的神经语言模型,例如 Bengio 2003 年那类含非线性隐藏层的模型,Word2Vec 使用更简单的结构,CBOW 和 Skip-gram 的投影层是线性的。论文报告其训练复杂度比当时的神经语言模型低几个数量级,并展示了在十几亿词语料上训练词向量的结果。这使得「在大语料上学词向量」能够被更多工程实践采用,也解释了它在 2013 年之后的传播。

向量空间中的词类比

词向量训练完成后,可以用向量运算检查其中是否出现某些词关系。Mikolov 论文中的一个例子是:取 king 的向量,减去 man 的向量,再加上 woman 的向量,在词表中搜索最近邻,结果可能是 queen。

我第一次看到这个等式时没有理解它的含义。可以将 man 到 king 的差向量与 woman 到 queen 的差向量作比较:若两者接近,向量空间中就保留了这两组词对的部分关系。同样,man 到 woman 与 king 到 queen 的差向量也可能接近。词关系有时可以表现为向量偏移,但这种线性关系只是训练语料和模型中观察到的性质,并不适用于所有词对。国家和首都也常被用作测试,例如比较 China 到 Beijing 与 France 到 Paris 的差向量。

词向量空间中的语义方向与词对关系

实际向量通常有三百维左右,图中只是二维投影。投影会改变距离和方向,因此不能据图判断高维空间中的精确几何关系。论文将国家-首都、动词时态、形容词比较级等类比分组测试,而不是只展示一个词对。我当时还下载了官方放出的预训练向量(GoogleNews-vectors-negative300,纯英文词表),亲手查了几组近邻,cat 的最近邻里确实有 cats、dog、kitten、pet。这个结果是我对预训练向量做的检查,不代表所有词或所有模型都会得到同样的近邻。

负采样:以局部二分类替代完整 softmax

按 Skip-gram 的完整写法,给定中心词,模型对某个输出词作为上下文词的条件概率使用覆盖整个词表的 softmax:

其中, 是中心词向量, 是上下文词向量。问题在分母:词表 动辄几十万,每处理一个训练样本都要计算词表中所有输出词与中心词的点积并归一化,成本很高。

第二篇论文提出负采样。它不再计算完整 softmax,而是为每对真实共现词构造一个二分类任务:从噪声分布中随机抽几个词,例如 5 个,作为负例;模型让真实词对得到高分,让抽到的负例得到低分。负例是按词频变换后的分布抽样,抽中的词不一定在当前中心词的上下文窗口外。每次更新只涉及中心词、一个正例和几个负例的向量,点积次数从与词表大小同量级降为几个。Jay Alammar 那篇图解将它概括为反复判断「这个词是不是当前中心词的上下文词」。

说实话,负采样的理论依据,即它的二分类目标与完整 softmax 目标的关系,我当时没有深挖,只把它理解为可训练的工程替代方案。后来在这个领域里,我多次见到将计算量过大的目标改写为局部可计算目标的做法。这是第一次遇到。

同期还有另一条路线,顺带记一笔:GloVe(斯坦福,2014)先构建全局的词-词共现统计矩阵,再优化词向量。它与 Word2Vec 的训练目标和实现不同。我当时只看了结论,没有细读,也没有在这里比较两者效果,先记下备查。

静态词向量不能区分一词多义

照例记一个疑问。这套方案为每个词保存一个固定向量,但「苹果」在「吃苹果」和「苹果公司」中含义不同,「bank」在河岸和取款机旁的含义也不同。单个静态词向量不能直接为这些上下文给出不同表示,只能将不同用法混合在同一个向量中。

当时我只想到一个模糊方向:词的表示需要和具体句子绑定,不能让一个词只对应一个向量。但具体怎么做毫无头绪,读到的资料里也没有现成答案。这个问题先放在这里,后来学序列模型时才回头接上。

另一个小疑问是:向量维度为什么常取 300 这个量级,多一点或少一点影响多大。当时只查到「经验上调出来的」,没有更深的原因。

本篇参考资料


489 字 · 42 段落
ximing

Written by ximingFollow onGitHub

相关文章