Word2vec基础
词嵌入的假设,是通过一个词所在的上下文可以获得词的语义甚至语法结构,有相似上下文的词在向量空间中是邻近的点。
背景概念
Word2Vec要解决的根本问题,说到底是自然语言处理的问题。一般认为自然语言处理模型氛围两大派系,分别是形式文法和统计语言模型
统计语言模型
简单地说,可以用统一地形式化表示:
上述表示可以看做是用来计算词构成一个句子的概率模型,这就是统计语言模型的本质。但是概率的计算是个大问题,数据稀疏、参数过多,很难实际运用。前辈们研究了各种模型。例如n-gram、n-pos、决策树模型、最大熵模型等
n-gram
基于马尔科夫假设,即认为每个词只与前面n−1个词(上下文)有关。例如bigram:
至于n的选择,在NLP任务中,普遍认为只需使用2或3,而概率的计算实质为:
神经网络语言模型(NNLM)
Distributed Representation的代表,每个输入词都是一个实数向量,而模型包含输入层、投影层、隐藏层和输出层。这个直接读Bengio2003的论文《A Neural Probabilistic Language Model》
Log-Bilinear模型
虽然与Word2Vec关系紧密,但与本文话题暂时不相干,所以暂不展开
词向量
具体地,词如何用向量来表示?通常认为有两种基本思路,一种称为One-hot Representation,而另一种叫做Distributed Representation。
One-hot的表示方法,非常稀疏, 每个词就是一个很长的向量,向量的维度就是词表的大小,而只有该词对应位置的数字是1,其他都是0。这种表示方法无法捕捉词与词之间的相似关系,且容易发生维数灾难。
Distributed的表示方法,Hinton在1986年提出,大致思想是用较低维度的实数向量来表示词,而用词之间的距离(例如余弦距离)来表示相似度。普遍认为这种方法能够较好地捕捉词与词之间的语义相似度。
许多模型或方法可以用来获取词向量,除了word2vec使用的模型外,LSA矩阵分解模型、pLSA潜在语义分析模型、LDA文档生成模型,也均可用来估计词向量。
数据结构
Huffman编码
Huffman编码是一种用于无损数据压缩的熵编码(权编码)算法。由大卫·霍夫曼在1952年发明。该方法通过一种变长编码表来对符号进行编码,而变长编码表是通过评估符号出现概率的方法得到,目标是出现概率高的符号使用较短的编码,出现概率低的符号则使用较长的编码。这种方式能有效降低编码之后的字符串的平均长度。
在谈论数据结构的时候,Huffman编码又称为最优二叉树,表示一种带权路径长度最短的二叉树。带权路径长度,指的就是叶子结点的权值乘以该结点到根结点的路径长度。
因此最短长度问题被转化为构建一个由字符(每个字符都是叶子结点)的出现频率作为权值所产生的Huffman树的问题。
构造Huffman树
算法描述:假设有n个权值,则构造出来的Huffman树有n个叶子结点。若n个权值分别为({w_1,w_2,dots,w_n})
- 将({w_1,w_2,dots,w_n})当作n棵树(每棵树1个结点)组成的森林
- 选择根结点权值最小的两棵树,合并,获得一棵新树,且新树的根结点权值为其左、右子树根结点权值之和。词频大的结点作为左孩子结点,词频小的作为右孩子结点
- 从森林中删除被选中的树,保留新树
- 重复2、3步,直至森林中只剩下一棵树为止
模型概要
CBOW
CBOW(Continuous Bag-of-Words Model):在已知上下文,例如(w_{t−2},w_{t−1},w_{t+1},w_{t+2})的前提下,如何预测当前词(w_t)。学习的目标函数是最大化对数似然函数:
Skip-gram
Skip-gram(Continuous Skip-gram Model):在已知当前词(w_t)的前提下,预测其上下文,例如(w_{t−2},w_{t−1},w_{t+1},w_{t+2})。目标函数形如:
模型比较
CBOW和Skip-gram均为三层神经网络(输入层、投影层和输出层),而上下文的词的具体个数是可定义的。窗口大小为5时,两个模型的网络结构如下:
同样用于计算概率值,从模型的计算方式看,skip-gram想要预测更多(上下文),一次会更比CBOW慢一些,但有观点认为对低频词效果更好一些。
算法策略
然而,上面的公式计算很不切实际,因为计算(log p(w_{t+j}|w_t))的梯度与W的大小直接相关。为了降低复杂度,Word2Vec使用了Hierarchical Softmax和Negative Sampling两种求解策略。普遍认为Hierarchical Softmax对低频词效果较好;Negative Sampling对高频词效果较好,向量维度较低时效果更好。
Hierarchical Softmax
作为一种计算高效的近似方法,Hierarchical Softmax被广泛使用。Morin和Bengio首先将这种方法引入神经网络语言模型。该方法不用为了获得概率分布而评估神经网络中的(W)个输出结点,而只需要评估大约(log_2(W))个结点。层次Softmax使用一种二叉树结构来表示词典里的所有词,(V)个词都是二叉树的叶子结点,而这棵树一共有(V−1)个非叶子结点。
对于每个叶子结点(词),总有一条从根结点出发到该结点的唯一路径。这个路径很重要,因为要靠它来估算这个词出现的概率。以下图为例,白色结点为词典中的词,深色是非叶子结点。图中画出了从根结点到词(w_2)的唯一路径,路径长度(L(w_2)=4),而(n(w,j))表示从根结点到词w2的路径上的的第(j)个结点。
在层次Softmax模型中,叶子节点的词没有直接输出的向量,而非叶子节点其实都有响应的输出向量。
求目标词的概率
在模型的训练过程中,通过Huffman编码,构造了一颗庞大的Huffman树,同时会给非叶子结点赋予向量。我们要计算的是目标词w的概率,这个概率的具体含义,是指从root结点开始随机走,走到目标词w的概率。因此在途中路过非叶子结点(包括root)时,需要分别知道往左走和往右走的概率。例如到达非叶子节点n的时候往左边走和往右边走的概率分别是:
以上图中目标词为(w_2)为例:
到这里可以看出目标词为(w)的概率可以表示为:
其中(θ_{n(w,j)})是非叶子结点(n(w,j))的向量表示(即输出向量);h是隐藏层的输出值,从输入词的向量中计算得来;(sign(x,j))是一个特殊函数定义:
此外,所有词的概率和为1,即:
参数更新公式
其中(j=1,2,cdots,L(w)-1)
Negative Sampling
Hierarchical Softmax的另一个替代方法叫做Noise Contrastive Estimation(NCE),Gutmann和Hyvarinen介绍了这种方法,而Mnih和Teh将该方法应用到了语言模型中。但是如果我们的训练样本里的中心词(w)是一个很生僻的词,那么就得在霍夫曼树中辛苦的向下走很久了。
比如我们有一个训练样本,中心词是(w),它周围上下文共有(2c)个词,记为(context(w))。由于这个中心词(w),的确和(context(w))相关存在,因此它是一个真实的正例。通过Negative Sampling采样,我们得到(neg)个和(w)不同的中心词(w_i,i=1,2,..neg),这样(context(w))和(w_i)就组成了neg个并不真实存在的负例。利用这一个正例和neg个负例,我们进行二元逻辑回归,得到负采样对应每个词(w_i)对应的模型参数(θ_i),和每个词的词向量。
从上面的描述可以看出,Negative Sampling由于没有采用霍夫曼树,每次只是通过采样neg个不同的中心词做负例,就可以训练模型,因此整个过程要比Hierarchical Softmax简单。
不过有两个问题还需要弄明白:1)如何通过一个正例和neg个负例进行二元逻辑回归呢? 2) 如何进行负采样呢?
负采样的方法
现在我们来看看如何进行负采样,得到neg个负例。word2vec采样的方法并不复杂,如果词汇表的大小为V,那么我们就将一段长度为1的线段分成(V)份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词w的线段长度由下式决定:
在word2vec中,分子和分母都取了3/4次幂(调参)如下:
在采样前,我们将这段长度为1的线段划分成M等份,这里(M>>V),这样可以保证每个词对应的线段都会划分成对应的小块。而M份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从(M)个位置中采样出neg个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。
在word2vec中,(M)取值默认为(10^8)。(M)足够大,所以每个词被采样到的概率近似于线段的长度。
预处理技巧
以下根据源代码来看下两个初始化环节
词典构建
拿到sentences以后首先是创建词典,即从文本序列中创建一个词典。词典数据是一个词对应一个出现次数,词典的规模是可以定义的,有一个参数叫做max_vocab_size即是用来限定最大词典量的。因为这是创建词典的过程,一般希望出现频率较高的词被保留,而频率较低的词被略去。所以统计的过程中,如果当前词汇数超过max_vocab_size,则需要按略去词频低于min_reduce的词,初始化的时候,min_reduce为1。随着实际词典的增大,min_reduce有可能会递增。
SubSampling
全部扫过一遍词典后,需要再次考虑删去总体频率较低的词(出现次数少于min_count的长尾词)。不过,最重要的还是Subsampling。
SubSampling有时候被称作DownSampling,也曾有人译作亚采样,实际是对高频词进行随机采样,关于随机采样的选择问题,考虑高频词往往提供相对较少的信息,因此可以将高于特定词频的词语丢弃掉,以提高训练速度。Mikolov在论文指出这种亚采样能够带来2到10倍的性能提升,并能够提升低频词的表示精度。
采样参数sample表示采样百分比,默认是1e-3,Google推荐的是1e-3至1e-5。
其中(f(w_i))是词(w_i)的词频,t是阈值。而这个是Mikolov论文里的说法,实际Word2Vec的代码,以及后续gensim的实现,都采用了如下公式来表示词(w_i)被丢弃的概率:
其中:(N_W)是参与训练的单词总数,包含重复单词,实质即词频累加;(v_{wi})是词(w_i)的词频。在gensim的实现中,对(sample<1)和(sample geq 1)的情况区分对待,具体可去看gensim版源码。
Word2vec模型细节
CBOW
One-word context
从CBOW模型的最简单版本开始介绍——One-word context。即假定context(预测目标单词的上下文信息)只有一个单词,也就是说One-word context 模型是在只要一个上下文单词(one context word)的情况下来预测一个目标单词(one target word)的生成概率
如图描述的就是One-word context定义之下的神经网络模型。这里我们假设文本词汇量的大小为(V),隐藏层的大小为(N),相邻层的神经元是全连接的。输入层是一个用one-hot方式编码的单词向量(x=(x_1,...,x_V)),其中只有一个(x_i)为1,其余均为0。 从输入层到 隐藏层的权重值可以用一个(V imes N)维的矩阵(W)来表示:
其中(W)矩阵的每一行代表的是一个与输入层相关的单词的(N)维向量表示形式(v_ω)。那么假设我们给定了一个输入单词(a context),其单词向量的第(k)个元素(x_k=1),其余均为0,则有
从上式我们可以看出,(h)向量完全是从(W)矩阵第(k)行复制过来的,(v_{ωi})即为输入单词(ω_I)(上下文单词)的一种向量表示
分析完输入层到隐藏层之后,我们再看隐藏层到输出层,同样连接权重用一个新的
(N imes V)矩阵(W^{'}={ω^{'}_{ij}})来表示如下:
通过这些权重,我们可以为词表中的每一个单词都计算出一个得分(mu_j):
其中(v^{'}_{wj})即为矩阵(W^{'})的第(j)列向量
经过以上讨论之后,我们可以使用一种对数-线性分类模型softmax函数来计算单词的后验分布(是多项式分布)
其中,(y_j)表示输出层第(j)个神经元的输出。结合上面三式,有训练目标:
(v_ω)和(v^{′}_ω)是单词的两种向量表示形式。其中(v_ω)实际上是权重矩阵(W)(input->hidden)的某一行向量,(v^{′}_ω)则是权重矩阵(W^{′})(hidden->output)的某一列向量。分别称为“输入向量和“输出向量”
Multi-word context
基于multi-word context的CBOW模型就是利用多个上下文单词来推测中心单词target word的一种模型
其隐藏层的输出值的计算过程为:首先将输入的上下文单词(context words)的向量叠加起来并取其平均值,接着与input→hidden的权重矩阵相乘,作为最终的结果,公式如下:
其中(C)为上下文单词的个数,(ω_1,dots,ω_C)为上下文单词,(v_ω)为单词(ω)的输入向量。损失函数为:
Skip-gram
与CBOW模型正好相反,Skip-Gram模型是根据中心单词(target word)来预测其上上下文信息(context words)
我们仍然使用(v_{ωI})来表示输入层上唯一的那个单词的输入向量,因此,我们对于隐藏层的输出值(h)的计算公式与第一节公式相同,表示如下:
上式显示,(h)向量其实就是input->hidden权重矩阵(W)的某一行结合输入单词(ω_I)的向量拷贝。在输出层,与CBOW模型的输出为单个多项式分布不同的是,SG模型在输出层输出了C个多项式分布。每个输出都使用相同的hidden->output矩阵计算:
其中,(ω_{c,j})表示输出层的第(c)个panel的第(j)个单词(何为panel?就是输出层的表示每个上下文单词的神经元的组合,图中一种有C个context words,所以总共有C个panel);(ω_{O,c})实际上表示的是输出上下文单词(output context words)的第(c)个单词;(ω_I)是唯一的输入单词;(y_{c,j})为输出层的第c个panel上的第j个神经单元的概率输出值;(mu_{c,j})表示的是输出层第c个panel的第j个神经元的输入值;由于输出层的所有panels共享同一权重矩阵(W^{′}),因此:
其中,(v_{wj}^{'})为词汇表第(j)个单词(ω_j)的输出向量;同样,它也是取自于hidden→output权重矩阵(W^{′})的一列