在目前的机器学习或者深度学习系统中,向量化已经成为不可避免的趋势,如同万物皆对象一般的万物皆向量,词可以用词向量(Word Embedding),句子可以使用句向量,甚至文章都有篇章级别的向量,但是至于这些向量的表示效果,有好有坏,这篇文章就先介绍一下词向量的相关内容。

one-hot编码词向量

从最基本的词向量开始,最容易想到的表示方法就是one-hot编码表示法,只需要一个包含所有需要的词的词表,假设词表的长度为n,则对于每一个词的表征向量均为一个n维向量,且只在其对应位置上的值为1,其他位置都是0,如下图公式所示: $$ {w^a} = \left[ {\begin{array}{c} 1\ 0\ \vdots \ 0 \end{array}} \right],{w^{at}} = \left[ {\begin{array}{c} 0\ 1\ \vdots \ 0 \end{array}} \right],…,{w^{zebra}} = \left[ {\begin{array}{c} 0\ 0\ \vdots \ 1 \end{array}} \right] $$ one-hot向量非常直观的将每个单词表示为完全独立的实体,一目了然,但是这样的表征方法存在以下问题:

  1. 有序性问题:无法反映文本的有序性。语言并不是一个完全无序的随机序列,特定的一系列词按特定的顺序组合在一起才能组成一个有意义的句子;
  2. 语义鸿沟:无法通过词向量来衡量相关词之间的相似程度,因为任意两个向量的距离是相同的;
  3. 维度灾难:高维情形下将导致数据样本稀疏,距离计算困难,加重了下游任务计算负担。

虽然one-hot向量不太好用,但是依然可以拿来构造句向量,为了更好的评价所构造的句子合理性,就引入了统计语言模型(如N-gram模型)和神经网络语言模型(Neural Network Language Model,NNLM)。

统计语言模型

如何计算一段文本序列在某种语言下出现的概率?统计语言模型给出了解决这一类问题的基本框架,一段文本序列$S = {w_1},{w_2},…,{w_T}$的概率可以表示为: $$ P(S) = P({w_1},{w_2},…,{w_T}) = \prod\limits_{t = 1}^T {p({w_t}|{w_1},{w_2},…,{w_{t - 1}})} $$ 也就是将序列的联合概率转化为一系列条件概率的乘积。这样,要求出一段文本的联合概率,仅需要计算出每个词或文本在给定词$w_t$下的条件概率: $$ p({w_t}|{w_1},{w_2},…,{w_{t - 1}}) $$ 但是当文本过长时,模型参数空间实在是太大了,而且理论上来讲,大部分文章开头的词和中间的词也没啥相关性,导致原始模型在实际中并没有什么用。所以为了提高运算效率,增加可解释性,更多情况是使用当前文本的前n个文本来计算条件概率,也就是最常见的N-gram模型,它做了n-1阶的马尔可夫假设,认定词的出现就与其前n-1个词相关: $$ p({w_t}|{w_1},{w_2},…,{w_{t - 1}}) \approx p({w_t}|{w_{t - n + 1}},…,{w_{t - 1}}) $$ 其中,当n=1时,也就是假设一个词的出现不依赖于其他任何词,模型被称为unigram模型,而n=2时,即假设一个词的出现仅仅依赖于其上一个词时,这个模型又称为bigram模型,同样的当n=3时,假设一个词的出现依赖于该词的前两个词,这时模型称为trigram模型,再同理,n=4,5…不好意思,后面还真就没这么多了。

为什么呢,主要有以下几个问题:

  1. 由于模型复杂度和参数空间的指数型增长,当n>3时,参数空间已经非常稀疏,就别说处理更长程的上下文了,同时n从3到4的增加对性能提升并不明显,在tradeoff之后一般n不会超过3;
  2. 使用one-hot向量表征,向量长度对应于整个词表的长度,可能会面临维度灾难问题;
  3. 统计语言模型没有解决文本本身的表征问题,只是解决了文本之间的转移概率的问题;
  4. 当出现语料库没有的词汇时,句子的出现概率为0,不合理,需要引入平滑(smoothing)算法解决。
神经网络语言模型

由于Ngram等统计语言模型的问题,提出了NNLM模型和word embedding的概念。NNLM模型的基本思想可以概括如下:

  1. 假定词表中的每一个word都对应着一个连续的特征向量;
  2. 假定一个连续平滑的概率模型,输入一段词向量的序列,可以输出这段序列的联合概率;
  3. 同时学习词向量的权重和Ngram概率模型里的参数。

NNLM模型的目标是找到一个合适的参数来拟合一个词序列的条件概率$p({w_t}|{w_1},{w_2},…,{w_{t - 1}})$,其结构如下图所示,是一个3层的神经网络:

img

第一层为输入层,将输入的N−1个one-hot词向量${w_{t - n + 1}},…,{w_{t-1}}$,通过维度为$V \times {\rm{D}}$的矩阵C映射为分布式的词向量,其中V是词典的大小,D是Embedding向量的维度。这里的矩阵C就是要学习的词向量,因为n个one-hot表征的词向量输入到神经网络中,进行的操作是$Y = W\times X$,等效于将n个词向量从Embedding层中提取出来的查表操作: $$ \left( {\begin{array}{c} 1&0&0&0\ 0&1&0&0 \end{array}} \right)\left( {\begin{array}{c} {w_{11}}&{w_{12}}&{w_{13}}\ {w_{21}}&{w_{22}}&{w_{23}}\ {w_{31}}&{w_{32}}&{w_{33}}\ {w_{41}}&{w_{42}}&{w_{43}} \end{array}} \right) = \left( {\begin{array}{c} {w_{11}}&{w_{12}}&{w_{13}}\ {w_{21}}&{w_{22}}&{w_{23}} \end{array}} \right) $$ 第二层是隐藏层,组成就是简单的全连接网络和tanh激活函数;

最后一层是输出层,也是一层全连接网络结合softmax,将Embedding层输出的N−1个词向量映射为一个长度为V的概率分布向量。

NNLM通过引入连续词向量和平滑的概率模型,可以在连续空间内对序列概率进行建模,从而降低了数据稀疏性和维度灾难出现的概率,同时以条件概率$p({w_t}|context)$为学习目标去更新词向量的权重,也增加了可解释性。

但仍然存在如下问题:

  1. 模型使用的全连接神经网络,只能处理定长序列;
  2. 模型参数大小,与输入长度相关,长度太大时训练耗时太久。
Word2Vec模型

针对NNLM存在的第一个问题,Mikolov等人提出了一种RNNLM模型,用递归神经网络代替原始模型里的前向反馈神经网络,并将Embedding层与RNN里的隐藏层合并,解决了变长序列问题;对于第二个问题,将原始的NNLM模型的训练拆分为两步:1. 先训练出连续的词向量;2. 基于训练好的词向量去训练Ngram神经网络模型。而NNLM模型的计算瓶颈主要是在第二步的softmax,如果只是想得到word的词向量,可以对第二步的神经网络模型进行简化,Word2Vec模型就应运而生。

Word2Vec模型主要有两个训练方案,两个加速手段,训练方案为Continues Bag-of-Words(CBoW)和Skip-gram,加速手段为hierarchical softmax(层次softmax)和Nagative Sampling(负采样)。

任务举例

训练word2vec的目标并不是得到词的向量,词的向量其实只是学习到的“副产品”。真正训练的目标是:给定一个词,计算当这个词出现时,它前后的n个词出现的概率,下图是n=2窗口的场景:

word2vec-fake-target

CBoW

CBoW比较类似于NNLM,其思路就是利用上下文词预测中心词:输入中间词的前后共C个词,预测中间词,在这个过程中训练出词向量矩阵,表现为每个输入的多个上下文词(多个学生)从中心词标签(老师)那里获得知识。其模型结构如下图所示:

img

其中:

  • 图中${x_{1k}},…,{x_{Ck}}$ 表示第 k 个中心词的前后C个上下文的 one-hot 向量;
  • 将 one-hot 向量输入存放词向量的矩阵${W_{V \times N}}$进行查表,V为词表的大小,N为词向量的维度;
  • 将查表得到的上下文向量直接进行求和,再通过一个$N \times {\rm{V}}$的矩阵${W’}$映射到输出层。

学习结束后,可以选择输入层到隐层的矩阵$W$或者隐层到输出层的矩阵${W’}$作为词向量矩阵,若选择后者,每一列就是对应单词embedding后的结果。

以例子的结果,最终计算公式为:$P(fox|quick,brown,jumps,over)$,中间向量计算如下,其中C=2n: $$ {v_{mid}} = \frac{1}{{\rm{C}}}\sum\limits_{i = 1}^{\rm{C}} {{W^T}{x^{(i)}}} = \frac{1}{{\rm{C}}}\sum\limits_{i = 1}^{\rm{C}} {{v_i}} $$ 从隐层到输出层与另一个矩阵${W’}$相乘,若中心词为第j个词,那么输出的向量为${t_j} = {v^T}{u_j}$,其中${u_j}$是${W’}$的第j列,得到的结果送入Softmax进行概率归一化: $$ p({w_j}|{w_1},…,{w_C}) = {y_j} = \frac{\exp ({v_{mid}^T}{u_j})}{\sum\nolimits_{k = 1}^{v_{mid}} {\exp ({v_{mid}^T}{u_k})} } $$ 最大化似然等价于最小化负的对数似然,上式取对数有: $$ \log p({w_j}|{w_1},…,{w_C}) = {v_{mid}^T}{u_j} - \log \sum\nolimits_{k = 1}^V {\exp (v_{mid}^T{u_k})} $$ 对比NNLM有以下三个不同点:

  1. 替换NNLM模型中的隐藏层,为更简单的投影层(只对输入的向量做累加操作);
  2. 直接将Embedding layer的查表之后累加求和(NNLM是将输出结果拼接);
  3. 将下文单词纳入上、下文环境,真正考虑了Context(NNLM的输入严格来说为上文文本)。
Skip-gram

CBoW模型是从上下文到中心词预测中学习到词向量的表达,反之亦可,这就是利用中心词预测上下文词的Skip-gram模型,表现为每个输入的中心词(学生)从多个上下文词标签(老师)那里获得知识。模型结构与CBoW几乎反转,有输入层、投影层(其实是多余的恒等投影,加上是便与CBoW模型对比)和输出层:

word2vec-skip-gram

以例子的结果,计算公式为:$P(quick,brown,jumps,over|fox) = P(quick|fox) \cdot P(brown|fox) \cdot P(jumps|fox) \cdot P(over|fox)$

Skip-gram模型的预测概率过程用极大似然估计表示为: $$ \prod\limits_{t = 1}^T {\prod\limits_{ - n \le j \le n,j \ne 0} {P({w^{(t + j)}}|{w^{(t)}})} } $$ 等价最小化对数似然函数: $$

  • \sum\limits_{t = 1}^T {\sum\limits_{ - n \le j \le n,j \ne 0} {\log } } P({w^{(t + j)}}|{w^{(t)}}) $$ 最后Softmax概率归一化之后: $$ P({w_j}|{w_i}) = \frac{{\exp (v_i^T{u_j})}}{{\sum\nolimits_{k = 1}^V {\exp (v_i^T{u_k})} }} $$ 其中,${v_i}$是来自矩阵$W$的词向量,${u_j}$是来自矩阵${W’}$的向量。

上式取对数可得: $$ \log p({w_j}|{w_i}) = v_i^T{u_j} - \log \sum\nolimits_{k = 1}^V {\exp (v_i^T{u_k})} $$ Skip-gram模型的本质是计算输入word的input vector与目标word的output vector之间的余弦相似度,并进行softmax归一化。

模型对比
  1. CBOW训练速度更快。当窗口大小为2时,CBOW需要计算4个one-hot向量乘W矩阵计算均值并反向传播,会更新 Context(w) 的词向量,计算量为1次前向传播和1次反向传播,而skip-gram需要分别计算4个词的前向和反向传播;
  2. Skip-gram对于低频词(小数据集场景下)更加准确。CBOW通过学习上下文来预测这个词,相当于是完形填空,旨在预测最常见或者说概率最大的词汇,而skip-gram为了预测上下文,对训练数据的利用更高效(构造的数据集多),可以学习到更多低频词信息。
  3. Hierarchical Softmax试图用词频建立一棵哈夫曼树,那么经常出现的词路径会比较短。树的叶子节点表示词,共词典大小多个,而非叶子结点是模型的参数,比词典个数少一个。要预测的词,转化成预测从根节点到该词所在叶子节点的路径,是多个二分类问题。本质是把 N 分类问题变成 log(N) 次二分类;
  4. Negative Sampling把原来的 Softmax 多分类问题,直接转化成一个正例和多个负例的二分类问题。
Word2Vec提速方案

W2V的计算瓶颈主要在Softmax上面,每计算一个词的概率都要对词典里的V个词计算相似度,然后进行归一化,消耗时间巨大,因此提出了两个提速方案:层次Softmax和负采样。

层次Softmax

Hierarchical Softmax方案是通过构造一个Huffman树,将归一化概率问题转化为一系列二分类的条件概率相乘。构造的Huffman树以词表为根结点,词为叶节点,中间节点仅仅是辅助作用,没什么实际的含义。叶节点的权值就是词频,带权路径长度指的是词频乘以路径的大小,带权路径最小这一条件使得构造出来的霍夫曼树中,高频词离根结点更近,而低频词离根结点更远。如下所示: img

层次Softmax通过构造一颗二叉树,将目标概率的计算复杂度从最初的O(V)降低到了O(logV)的量级。但一个word出现的条件概率的变化,会影响到其路径上所有非叶节点的概率变化,间接地对其他word出现的条件概率带来不同程度的影响,而且当遇到生僻词的时候,在哈夫曼树中也要走很久,计算量也很大。

负采样

将多分类训练方法转化为二分类,避免了Softmax分母求和计算,也就是给定$w_c$和$w_i$两个词,$w_i$在$w_c$窗口内的概率。正样本就是中心词$w_c$和窗口范围内每个词$w_i$组成的词对,负样本是中心词$w_c$和词库中随机抽取的k个词,k一般是5到20。

负样本选择方案:

  • 完全随机取样:可能取到停用词或者高频无意义词汇;
  • 每个词概率均等采样:取到的词对于文本可能没有代表性;
  • 带权采样:$P({w_i}) = \frac{f{(w_i)^{3/4}}}{\sum\nolimits_{j = 0}^n {(f{(w_j)^{3/4}})}}$,其中${f({w_i})}$是${w_i}$在文中出现的次数,3/4次幂处理是经验值,一定程度上提升低频词的权重。

具体执行方法是将所有的词按频率映射到0-1区间,随后按照特定距离等切分线段,映射到原有区间,当采样时,随机生成等距区间的随机数,即为负样本,若采样到本体,跳过。最后仅需要从采样结果中计算归一化概率分布,从而大大简化计算过程。

word2vec的局限性
  1. 在模型训练的过程中仅仅考虑context中的局部语料,没有考虑到全局信息;
  2. 对于中文而言,分词会严重影响词向量的质量,因此,从某些方面来说word2vec对中文不是那么友好;
  3. 词向量模型的embedding矩阵在训练完成后便已经是固定了的,对于同一个词,在任意一个句子和环境下的词向量都是固定的,无法解决歧义等问题。
W2V代码细节
  • sigmoid近似计算:缓存预先计算好的sigmoid在区间[-6, 6]之间的值,小于-6为0,大于6为1,避免指数级别计算影响。

  • 低频词处理:建立词典时通常会设置一个min_count的阈值参数,剔除出现次数少于min_count次的词。

  • 高频词的处理:通常认为高频词(的、是等副词)往往只提供较少的信息,而且这些词对应的词向量在众多样本的训练过程中也不会发生明显的变化。因此对出现频率高于特定值t的词,进行下采样,按$$1 - \sqrt {\frac{t}{{f(w)}}}$$概率进行抛弃,f(w)表示该词在文本出现的占比,一方面可以提高的训练速度,另一方面也可以提升低频词的表示精度。

FastText

Facebook提出的类似于CBOW结构的快速词向量计算和文本分类工具。

模型结构
  1. 输入层:词和子词(subword)的n-gram的特征向量;

  2. 隐藏层:所有词的向量叠加求平均之后经过线性变换到隐藏层;

  3. 输出层:Hierarchical Softmax输出文本类别。

模型特点
  • 字符级的n-gram:除了每个单词的词向量外,还为每个单词的n-gram字符添加一个向量,作为额外的特征,对于低频词生成的词向量效果会更好。因为它们的n-gram可以和其它词共享;对于训练词库之外的单词,仍然可以构建它们的词向量。我们可以叠加它们的字符级n-gram向量;
  • 分层softmax:利用哈夫曼树构建,根据目标类别的多少自上而下构建,数目越多的类越在顶部;
  • 各种提速的trick,如提前算好exp的取值之类;
  • 核心思想:将整篇文档的词及n-gram向量叠加平均得到文档向量,然后使用文档向量做softmax多分类。
与CBOW的异同点
  • 相似:输入都是多个经向量表示的单词,输出都是一个特定的target,隐含层都是对多个词向量的叠加平均。
  • 不同:CBOW的输入是目标单词的上下文,fastText的输入是多个单词及其n-gram特征,这些特征用来表示单个文档;CBOW的输入单词被onehot编码过,fastText的输入特征是被embedding过;CBOW的输出是目标词汇,fastText的输出是文档对应的类标。
GloVe

GloVe(Globel Vectors)算法,其实就是SVD分解与Word2Vec的结合。

统计共现矩阵

在介绍GloVe的思想之前,先定义一个共现矩阵X,该矩阵中的${X_{ij}}$表示第j个单词出现在以第i个单词为中心,长度为n的窗口中的次数。将长度为n的窗口遍历整个语料库,则得到了共现矩阵X。

对于大型语料库,我们可以认为统计的词共现矩阵X可以很好的描述词与词之间的相关性。但是,就像之前我们说过的,这样大的一个共现矩阵在实际使用中将会面临复杂的维度灾难问题,因此需要想办法词向量进行降维,比如之前的SVD对词-文档共现矩阵进行降维就是这样一种思想。

对于word2vec,我们每次训练词向量,都是针对于局部语料进行预测,这就使得模型的训练过程中是很难考虑到整个语料库的全局信息的。我们通常将其称为一种预测模型,其目标是不断提高对其他词的预测能力,即减小预测损失,从而得到词向量。既能通过训练的方式得到固定维度的词向量表征,又能够使得到的词向量能够充分考虑到语料库的全局特征?

简单来说,Glove相对于Word2Vec,需要提前统计词共现矩阵,并将其整合到代价函数之中,使得训练结果对于该统计是有一定的重建能力的。我们将其称为一种统计模型,其目标是优化减小重建损失,即降维之后的向量能尽量表达原始向量的完整信息。

GloVe与Word2Vec的区别就在于GloVe考虑到了文本的全局特征,直观上来说比Word2Vec更合理。在语料库足够大的情况下GloVe的效果通常会更好一些,可以尝试的做法是将GloVe和Word2Vec进行拼接等操作。

与其他词向量对比
词向量 不同点
Glove 利用全局信息,收敛更快,需要统计固定语料信息,滑动窗口构建共现矩阵,统计全部语料在固定窗口内词共现频次,没有直接利用共现矩阵,通过词频特性,将词向量和词频联系起来,损失函数最小平方损失函数
Word2Vec 局部语料库训练,特征提取基于滑窗,损失函数带权重的交叉熵,可以进行在线学习
fastText 考虑subword,可有监督学习文本分类,利用了层次softmax进行加速,引入字符级n-gram
LSA 基于全局语料采用SVD进行矩阵分解
ELMOs 动态词向量,解决了上述静态词向量不考虑词序,不能识别的一词多义问题
各种词向量
特点
  • bag-of-words(one-hot、tf-idf、textrank):维度灾难、语义鸿沟;

  • 矩阵分解(LSA):利用全局语料特征,但SVD求解计算复杂度大;

  • 基于NNLM/RNNLM的词向量:词向量为副产物,存在效率不高等问题;

  • word2vec、fastText:优化效率高,但是基于局部语料;

  • glove:基于全局预料,结合了LSA和word2vec的优点;

  • elmo、GPT、bert:动态词向量,可以随上下文变动;

Quiz
衡量词向量的方式

词向量的“similarity”跟通常意义的近义词或相似词有本质上的区别,词向量更多的含义是“同位词”,即上下文相近的词。

  • 把词向量作为下游任务的输入,如NER,分类等任务;
  • 相似度度量,数据集的余弦相似度应该更高;
  • 词汇类比任务,king – queen = man - woman;
  • 聚类任务,词聚类观察是否准确。
Word2vec得到的词向量为什么能够表征词语之间的语义近似关系

word2vec得出的词向量其实就是训练后的一个神经网络的隐层的权重矩阵,经过模型训练后,词义相近的词语就会获得更为接近的权重,因此可以用向量的距离来衡量词的相似度。

词向量平均做分类的优劣

优势:分类模型简单,速度快,训练参数量少,在语句少的场景下,效果好;

劣势:在长语句场景下,效果较差,词越多,信息量越杂,简单的做平均的话,重要的词信息会在平均的过程中被削弱。

字向量和词向量

字向量可以解决未登录词的问题,避免分词造成的影响;

词向量包含的语义空间更大,更加丰富,如果语料足够的情况下,词向量可以学到更多的语义信息。

词向量如何做优化

引入attention机制增强敏感词重要词的权限,或使用self-attention重新调整语句词的分布,提高模型的学习能力,或者直接使用预训练模型bert来做学习。

Word2vec 和 TF-IDF 在计算相似度时的区别
  • 前者是稠密向量,后者是稀疏向量;
  • 前者维度低很多,计算更快;
  • 前者可以表达一定程度的语义信息,后者不行;
  • 前者可以通过计算余弦相似度计算两个向量的相似度,后者不行。
Word2vec是否需要加入正则

正则化用来避免过拟合的问题,word2vec训练任务不存在过拟合问题,反而需要尽量高的拟合度以有效表达语义。

参考
  1. 【深度学习】Word2Vec | 细语呢喃 (hrwhisper.me)
  2. NLP经典模型系列(一):Word2Vec - 知乎 (zhihu.com)