word2vec
不靠人工标注,仅凭「上下文」就能让词义从大量文本中自己浮现出来
一个词的意思,藏在它的上下文里
要让模型学出好的词向量,需要一个训练信号,这个信号从哪来?word2vec 的回答是 分布式假设 :
比如「猫」和「狗」都常出现在「喂___」「养了一只___」「___在睡觉」这样的语境里, 于是它们语义相近。这个假设妙在哪?只要让模型反复做 「根据上下文做预测」 这个任务,它就不得不把每个词放到合适的位置上, 好的词向量会作为 副产品 被逼出来。而且这个训练信号 完全免费、无需任何人工标注 :任何一段普通文本, 都天然告诉了我们「哪些词和哪些词做邻居」。
围绕这个想法,word2vec 给出了两种具体模型: CBOW 和 Skip-gram 。
CBOW:用周边词预测中心词
CBOW(Continuous Bag-of-Words,连续词袋) 的任务是: 用周边的词,预测中间被遮住的那个词 。
假设世界上只有一句话 I love you , 取窗口 (只看左右各一个词),任务就是 用周边的 I 和 you , 预测中心词 love 。整个流程是这样走的:
这里的 softmax 和交叉熵,都是 Softmax 回归 里学过的工具,直接复用。 注意一个区别:这里的「隐藏层」只是把取出的向量线性地求平均, 没有接激活函数 , 与《多层感知机》里「线性变换 + 激活函数」的隐藏层在结构上不同。这不是漏写了激活,CBOW 本来就不加。 CBOW 整体就是一个「输入周边词、输出中心词」的浅层分类网络。
下面把这条链路完整走一遍。为了能画出每一个数,这里把词表缩到只有 I / love / you 三个词、词向量维度取 2。 点「下一步」,看数据怎样从 one-hot 一步步变成概率。
关键转折:任务是手段,词向量才是目的
这里有个容易忽略却最重要的点: 我们其实根本不关心「预测中心词」这个任务本身 。 训练它,只是为了 逼着矩阵 学出好的表示 。
训练结束后,「预测中心词」这个任务本身不再需要,真正留下来使用的是 输入矩阵 ,一般取它的 每一行 作为对应词的词向量。它的形状正是 , 同样是 「按 token 的 ID 取对应行」 。 就是词嵌入里那张嵌入矩阵 , word2vec 只是给了它一种把语义训练进去的办法。
Skip-gram:反过来的对称版本
word2vec 还有一个与 CBOW 方向相反 的变体,叫 Skip-gram : 用 中心词去预测周边词 。结构与 CBOW 对称, 训练出来同样得到一张可用作词向量的矩阵。这里知道「还有这么一个对称的变体」即可, 细节不展开。
一个现实问题:全词表 softmax 太慢
上面为了讲清原理,输出层用的是「对 整个词表 做 softmax」。 但真实词表动辄几万、几十万个词,每训练一步都要在整个词表上算一遍概率, 计算量大到几乎跑不动。
所以真实的 word2vec 不会这么做,而是用 负采样(negative sampling) 等技巧提速,每步只更新「正确词」和少数几个随机抽来的「错误词」, 而不是整个词表。这里只需知道「全词表 softmax 只是讲原理用的,真实训练靠负采样提速」, 其细节不展开。
动手:训练一个词向量
自己从零实现 word2vec(含负采样)比较繁琐,实践里通常直接用现成库。 gensim 几行代码就能在一小段语料上训练出词向量,并查询最近邻。
想更直观地理解 word2vec 的来龙去脉,推荐阅读图解长文 The Illustrated Word2vec ; 原始论文见 Mikolov et al., 2013 。
训练好之后
有了训练好的词向量,能做的事不少,查近义词只是其中最简单的一种。 这些向量里还藏着一些相当惊人的几何性质;但与此同时, 它也有一个绕不开的先天缺陷。这两件事,是接下来要一起看清的。