全面理解word2vec


(mmforever) #1
  1. 为什么要用词向量
  2. 什么是word embedding
  3. 向量空间模型
  4. Word2Vec简介
  5. CBOW和Skip-Gram
  6. CBOW模型
  7. Skip-gram模型
  8. 加速策略(一):Hierarchical Softmax
  9. 加速策略(二):Negative Sampling
  10. word2vec词向量性质观察

为什么要用词向量

自然语言处理系统通常将词汇作为离散的单一符号,例如 “cat” 一词或可表示为 Id537 ,而 “dog” 一词或可表示为 Id143。这些符号编码毫无规律,无法提供不同词汇之间可能存在的关联信息。换句话说,在处理关于 “dogs” 一词的信息时,模型将无法利用已知的关于 “cats” 的信息(例如,它们都是动物,有四条腿,可作为宠物等等)。可见,将词汇表达为上述的独立离散符号将进一步导致数据稀疏,使我们在训练统计模型时不得不寻求更多的数据。而词汇的向量表示将克服上述的难题。

NLP 中最直观,也是目前最常用的词表示方法是 One-hot Representation,它是将词符号化的一种方式,也就是每个词的向量大小就是词典大小,一个向量只有一个位置为1,其他位置为0,当然,这样的向量不包含任何语义信息。

**而为什么一般不应该用one hot编码?**对于one hot编码,有多少个字,就得有多少维向量,假如有1万字,那么每个字向量就是1万维(常用的字可能不多,几千个左右,但是按照词的概念来看,常用的词可能就有十几万了),这样可能会造成维数爆炸,计算上受不了,于是就出来了连续向量表示,比如用100维的实数向量来表示一个字,这样就大大降低了维度,当然,更深层的原因是,wors2vec的字、词向量,能够包涵语义信息,向量的夹角余弦能够在某种程度上表示字、词的相似度等等。

什么是word embedding

那如何将语义融入到词表示中?Harris 在 1954 年提出的分布假说( distributional hypothesis)为这一设想提供了理论基础:上下文相似的词,其语义也相似。而基于基于分布假说的词表示方法,根据建模的不同,主要可以分为三类:基于矩阵的分布表示、基于聚类的分布表示和基于神经网络的分布表示。而word embedding一般来说就是一种基于神经网络的分布表示。

举个例子,给出一个文档,文档就是一个单词序列比如 “A B A C B F G”, 希望对文档中每个不同的单词都得到一个对应的向量(往往是低维向量)表示。比如,对于这样的“A B A C B F G”的一个序列,也许我们最后能得到:A对应的向量为[0.1 0.6 -0.5],B对应的向量为[-0.2 0.9 0.7] (此处的数值只用于示意),这个向量就是我们说的word embedding。

需要注意的是和下面要讲的word2vec和word embedding的联系和区别,word embedding 是一个将词向量化的概念,或者说是一种基于神经网络的分布表示,为区别one-hot的词向量,可翻译成词嵌入。而word2vec是谷歌提出一种word embedding 的工具或者算法集合

向量空间模型

向量空间模型 (VSMs)将词汇表达(嵌套)于一个连续的向量空间中,语义近似的词汇被映射为相邻的数据点。向量空间模型在自然语言处理领域中有着漫长且丰富的历史,不过几乎所有利用这一模型的方法都依赖于分布式假设,其核心思想为出现于上下文情景中的词汇都有相类似的语义。采用这一假设的研究方法大致分为以下两类:基于计数的方法 (e.g. 潜在语义分析, Glove), 和 预测方法 (e.g. 神经概率化语言模型,word2vec).

简而言之:基于计数的方法计算某词汇与其邻近词汇在一个大型语料库中共同出现的频率及其他统计量,然后将这些统计量映射到一个小型且稠密的向量中。预测方法则试图直接从某词汇的邻近词汇对其进行预测,在此过程中利用已经学习到的小型且稠密的嵌套向量。

而在这篇文章中,我们主要讨论的是神经概率化语言模型,Glove或者其他之后有机会再讲。

Word2Vec简介

最简单的理解,其实word2vec是只有一个隐层的全连接神经网络, 用来预测给定单词的关联度大的单词。或者说就是一个语言模型。

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘800’%20height='446’>)

下面看图说下整体步骤是怎么样的:

  1. 在输入层,一个词被转化为One-Hot向量。
  2. 然后在第一个隐层,输入的是一个 W*x+bx 就是输入的词向量, Wb 是参数),做一个线性模型,注意已这里只是简单的映射,并没有非线性激活函数,当然一个神经元可以是线性的,这时就相当于一个线性回归函数。
  3. 第三层可以简单看成一个分类器,用的是Softmax回归,最后输出的是每个词对应的概率

举个例子:

如果我们的语料仅仅有这3句话: “the dog saw a cat”, “the dog chased the cat”, “the cat climbed a tree”. 那么单词字典只有8个单词: “the”, “dog”, “saw”, “a”, “cat”, “chased”, “climbed”, “tree”.

那么V=8, 输入层的初始就可以是:

[1, 1, 1, 1, 1, 1, 1, 1] 代表: [“the”, “dog”, “saw”, “a”, “cat”, “chased”, “climbed”, “tree”]

输入[“”, “dog“, “”, “”, “”, “”, “”, “”] 可以表示为: [0, 1, 0, 0, 0, 0, 0, 0]

输入[“”, “”, “saw“, “” , “”, “”, “”, “”] 可以表示为: [0, 0, 1, 0, 0, 0, 0,0]

如果是在字典中有的, 就用1表示

W 的大小是NxV的, 通过训练完毕后得到权重 W_1和W_0 ,当只要输入单词, 就能预测出最符合这个上下文的下一个单词时,这时候的 W_1和W_0参数的精度最高。

CBOW和Skip-Gram

Word2vec是一种可以进行高效率词嵌套学习的预测模型。其有两种变体是现在比较常用的,分别为:连续词袋模型(CBOW)及Skip-Gram模型。从算法角度看,这两种方法非常相似,其区别为CBOW根据源词上下文词汇(’the cat sits on the’)来预测目标词汇(例如,‘mat’),而Skip-Gram模型做法相反,它通过目标词汇来预测源词汇。

Skip-Gram模型采取CBOW的逆过程的动机在于:CBOW算法对于很多分布式信息进行了平滑处理(例如将一整段上下文信息视为一个单一观察量)。很多情况下,对于小型的数据集,这一处理是有帮助的。相比之下,Skip-Gram模型将每个“上下文-目标词汇”的组合视为一个新观察量,这种做法在大型数据集中会更为有效。

下面详细说说两种模型:

CBOW(Continuous Bag-of-Words)

普通的CBOW模型通过上下文来预测中心词,抛弃了词序信息,注意不同的论文有不同的描述,但本质都是一样的,下面具体分层来说,第一种结构或角度:

输入层: 2m×n 个节点,上下文共2m 个词的n维向量,注意一开始为随机初始化

投影层:n 个节点,上下文共 2m 个词的词向量直接相加后求得的平均值;

投影层到输出层的连接边:输出词矩阵 U_{|V|×n}};

输出层: |V|个节点。第 i 个节点代表w_iii 的概率。

对于这里来说输入层的就是输入的词向量,我们看到,输入层每个周围词用n个节点表示,n就是词向量的大小,一开始随机初始化,在之后作为模型参数学习更新,最终得到合适的词向量。

在gensim 和 google的 word2vec实现中,也是这样的结构,就是输入层初始化的时候直接为每个词随机生成一个n维的向量,并且把这个n维向量作为模型参数学习,最终得到该词向量。

另一种结构或者角度讲解为:

输入层: 2m×|V|个节点2m个词的one-hot 编码表示

输入层到投影层到连接边:输入词矩阵 V:n×|V|维;

投影层: n 个节点,上下文共 2m 个词的one-hot 表示和输入词矩阵相乘后得到的词向量求和再平均的值,也就是 V*x的求和再平均 ;

投影层到输出层的连接边:输出词矩阵 U:|V|×n 维;

输出层: |V|个节点,先经过softmaiii个节点代表w_iii的概率,后得到最大概率词的one-hot表示。

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1066’%20height='1312’>)

然后词向量就是神经网络里的参数,生成词向量的过程就是一个参数更新的过程。首先将one-hot向量转换成低维词向量的这一层中,某个词的one-hot表示可看成是 1*|V| (|V|是词总数)的矩阵,与这个系数矩阵 V:n×|V|(维),相乘之后1*n的向量,这个向量就是这个词对应的词向量了。那V:n×|V|维 的矩阵,每一列就对应了每个单词的词向量。在神经网络中,训练不断更新这个矩阵。

另外,对于投影层到输出层的输出词矩阵 U:|V|×n 的每一行,也是对于每个单词的词向量。然后词向量具体怎么得到呢?这类似一个查表,比如输入节点有个词x的one-hot是 [1,0,0,…,0],他与输入词矩阵相乘 U*x^T 就得到该词的词向量(因为one-hot 里值是 1 的那个位置,对应的 n个权重被激活,其实就是从一个 n*|V| 的随机词向量矩阵里,抽取某一列)。或者在输出节点若得到词x的one-hot是 [1,0,0,…,0],它和输出词矩阵 x*V 也得到该词的词向量。这也就是该词对应的输入向量和输出向量,一般我们只用输入向量。

然后两种结构看下来,我们发现其实都是一样的,只是第二种结构的输入变成了一个one-hot表示,之后做一个类似查表操作得到词向量,学习原来输入向量变成学习现在的权重矩阵。

Skip-Gram模型

Skip-gram模型其实和CBOW模型很相似,都是相关词预测其他词,只是变成了由中心词来预测上下文的词。在结构上也是这样,两种角度,其中对于CBOW第二种结构或角度的情况如下图:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘540’%20height='617’>)

以上是输入层是one-hot,输出层也是one-hot的结构,具体里面的参数含义和CBOW相对应,这里不再讲解。

我们知道,Word2vec 本质上是一个语言模型,它的输出节点数是 V 个,对应了 V 个词语,也是一个多分类问题,但实际当中,词语的个数非常非常多,直接softmax来计算会给计算造成很大困难,所以需要用技巧来加速训练,下面就介绍word2vec对应的两个加速技巧hierarchical softmax和negative sampling。注意:这两个技巧只是加速训练的技巧

Hierarchical Softmax

对于原始的模型来说,由于使用的是softmax()函数,时间复杂度为 O(|V|)(|V|表示词典的总词数) ,因此计算代价很大,对大规模的训练语料来说,非常不现实。因此Mikolov 提出2个技巧,其中一个是Hierarchical Softmax。

简单来说,Hierarchical Softmax是一种对输出层进行优化的策略,输出层从原始模型的利用softmax计算概率值改为了利用Huffman树计算概率值。

Huffman树是二叉树,在叶子节点及叶子节点的权给定的情况下,该树的带权路径长度最短(一个节点的带权路径长度指根节点到该节点的路径长度乘以该节点的权,树的带权路径长度指全部叶子节点的带权路径长度之和)。直观上可以看出,叶子节点的权越大,则该叶子节点就应该离根节点越近。因此对于模型来说就是,词频越高的词,距离根节点就越近。

而一开始我们可以用以词表中的全部词作为叶子节点,词频作为节点的权,构建Huffman树,作为输出。从根节点出发,到达指定叶子节点的路径是唯一的。Hierarchical Softmax正是利用这条路径来计算指定词的概率,而非用softmax来计算。

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘289’%20height='161’>)

上图是一个已根据词频构建好的Huffman树,各叶子节点代表词表中的各个词,非叶子节点共 |V|−1 个。以词 w_2 为例,从根节点到该叶子节点的路径长度 L(w_2)=4 ,各个节点依次被记为 n(w_2,1) 、n(w_2,2)、n(w_2,3) 和 n(w_2,L(w_2)) 。对于每个非叶子节点 n(w,j) ,虽然不是词表中的词,但也引入所谓的“输出词向量” u_{n(w,j)} ,是需要学习的参数,为什么要引入它?下面讲述。

从根节点出发,走到指定叶子节点 W 的过程,就是一个进行 L(w)−1 次二分类的过程:路径上的每个非叶子节点都拥有两个孩子节点,从当前节点 n(w,j)n(w,j) 向下走时共有两种选择,走到左孩子节点 ch(n(w,j)) 就定义为分类到了正类,走到右孩子节点就定义为分类到了负类。

以CBOW模型为例,即输入层是 \hat{v}_t 。用二项Logistic回归模型对每一次分类过程建模:从当前节点 n(w,j)n(w,j) 走到下一节点,那么走到左孩子节点的概率为

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘90’%20height='31’>)

走到右孩子节点的概率为

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘242’%20height='42’>)

将上面两个式子统一起来,那就是

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1256’%20height='124’>)

双线括号的意思是,当括号内为真则输出1,为假则输出-1

现在计算输出词为 W 的概率:这对应于一条从根节点 n(w,1) 走到叶子节点 n(w,L(w)) 的路径,概率计算式为下式:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1514’%20height='382’>)

平均时间复杂度为 O(log|V|) ,相比于使用softmax()函数有很大提高。

所以简单来说,应用Hierarchical Softmax就是把 N 分类问题变成 log(N)次二分类

Negative Sampling

第二种加速策略是Negative Sampling(简写NEG,负采样),这是Noise-Contrastive Estimation(简写NCE,噪声对比估计)的简化版本:把语料中的一个词串的中心词替换为别的词,构造语料 D 中不存在的词串作为负样本。本质上就是一个预测全部分类的变成预测总体类别的子集的方法。在这种策略下,优化目标变为了:最大化正样本的概率,同时最小化负样本的概率。对于一个词串 (w,c) ( c 表示 w 的上下文),用二项Logistic回归模型对其是正样本的概率建模:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘209’%20height='33’>)

所以全部正样本的似然函数为

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘153’%20height='54’>)

同理,全部负样本的似然函数为

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘155’%20height='50’>)

需要最大化前者同时最小化后者,也就是最大化下式:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘344’%20height='55’>)

取对数得到对数似然:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘434’%20height='168’>)

由于使用SGD,所以只需要知道对一个正样本 (w,c) 的目标函数。式中 NEG(w)(w,c) 的负样本的中心词集合:

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘373’%20height='54’>)

当然既然是负采样,那么怎么负采样是一个比较重要的问题,NEG(w)中一般包涵5到10个词,现在这里先不详细解释,之后有机会再讲。
注意:因为都是来自论文《word2vec Parameter Learning Explained》的笔记,所以对于Negative Sampling和Hierarchical Softmax的讲解直接参考了前人的博客Hierarchical Softmax与Negative Sampling

word2vec词向量性质观察

查看某个词在embedding里的最近邻居可以看到单词间的语义接近关系,将vector构成的空间降维,可以更高效地查找最近单词,但降维过程中要保持邻居关系(原来接近的降维后还要接近),比如我们可以把学习向量映射到2维中以便我们观察,其中用到的技术可以参考 t-SNE 降纬技术。当我们第一次发现这样的诱导向量空间中,展示了一些特定的语义关系,这是非常有趣的,比如文字中 male-femalegender 甚至还有 country-capital 的关系, 如下方的图所示

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1505’%20height='527’>)

另外,我们能得到的不仅是单词的邻接关系,由于将单词向量化,可以对单词进行计算可以通过计算进行语义加减,语法加减。

最后,对于word2vec的论文有很多,推荐几篇我觉得比较好的论文,这篇文章也很多都是这些论文的笔记:

《Distributed Representations of Sentences and Documents》

《Efficient estimation of word representations in vector space》

《word2vec Parameter Learning Explained》