微信公众号中发送「词向量」,来拿我自己学习word2vec的必备资料吧!
若你是做NLP的,一定对词向量很亲切,若你是做推荐的,对词向量也一定不会陌生,以词向量为代表的序列向量化方法已经成为机器学习中必不可少的实战利器。
词向量技术可以认为是迁移学习思想在NLP领域的一次成功的实践,通过在开放领域海量、无标记语料集上进行训练,将海量的语义信息嵌入到词向量中去,进而将词向量作为各类NLP下游任务的输入,一般能够获得比one-hot等随机方式更好的效果!
当然,这是一篇旧闻新写,现在Bert等一系列预训练的语言模型又开始开启了NLP的新纪元,如今不懂Bert,就跟前几年不懂word2vec一样了,是找不到工作的吧!
后续也打算继续写一篇:从ELMo到Bert…
不过word2vec的思想永不过时,至今仍旧活跃在工业界的一线工人手中,不仅仅是在NLP领域,在推荐里面领域也存在各类基于其思想的**2vec方案。
好,进入正题~本篇为什么选择从word2vec写到ELMo为第一个阶段呢?
我认为word2vec开启了词向量的征途,后续也出现了doc2vec、FastText等优秀的词向量方法,但我工作中最常用的还是word2vec(个人爱好...),它是静态词向量时代的代表。
此外,ELMo首次引入预训练语言模型的概念,将词向量技术从静态词向量带入到了动态词向量的新时代,后续出现的GPT、Bert等优秀的方案后续会单独拿出来细说。
现实生活中,各国语言,我们可以认为是各个地区的人类相互约定俗成的一种信息的编码方式,因为人与人之间有一套固定的编码-解码规则,从而实现信息的流动。
同理,要让计算机能够“理解”人类的语言,也需要一定的编解码方式,就像通信领域的有自己领域的一套语法规则,NLP领域也需要一种对文本序列的编码方式,可以称之为文本的向量化技术。
文本的向量化一般是处理各类NLP任务的必备步骤之一,从最初的one-hot,到基于统计的共现矩阵、SVD、LDA等,以及基于语言模型的word2vec等方案。
基于one-hot的方式这里就不细说,下面简单介绍一下基于统计与语言模型的两类方案。
通过统计一个事先指定大小的窗口内的word共现次数,以word周边的共现词的次数做为当前word的vector。具体来说,我们通过从大量的语料文本中构建一个共现矩阵来定义word representation。
例如,有语料如下:
I like deep learning. I like NLP. I enjoy flying.
则其共现矩阵X如下:
矩阵定义的词向量在一定程度上缓解了one-hot向量相似度为0的问题,但没有解决数据稀疏性和维度灾难的问题。
既然基于共现矩阵方法得到的词向量存在着高维、稀疏性的问题,一个自然而然的解决思路是对原始词向量进行降维,从而得到一个稠密的连续词向量。
SVD作为一种降维方式,在推荐系统中基于User-Item的score矩阵推荐中也有比较广泛的应用,也可以理解为一种向量化表示方案。
首先,统计一个词语的共现矩阵X。X是一个|V|×|V| 大小的矩阵,Xij表示在所有语料中,词汇表V
中第i个词和第j个词同时出现的词数,|V|为词汇表的大小。
对X做矩阵分解,这里使用奇异值分解,得到矩阵正交矩阵U,对U进行归一化得到矩阵,即视为所有词的词向量:
SVD分解中的通过指定参数K=100,用于选取奇异值S中Top 100的特征值,进而确定了词汇-词向量的矩阵U,具体算法实现过程可以参考SVD的计算流程。
参数K,我们可以理解为指定的词汇主题的种类,而我们获得的词向量的K维数值,代表了该词汇与K个主题下相似的程度:
例如:
通过SVD处理共现矩阵之后得的词向量,是一个较稠密(dense)矩阵,该矩阵具有很多良好的性质:语义相近的词在向量空间中相近,甚至可以一定程度反映word间的线性关系。
但基于统计方法的词向量化方案仍旧有一些问题:
word2vec是基于语言模型的文本的向量化表示方案,也是静态词向量的代表之作。
在N-gram语言模型中,一般假设句子中某个词出现的概率与其前N个词有关。
这种关系可以通过条件概率来刻画:
2013年,Google团队提出了word2vec工具,word2vec工具主要包含两个模型:跳字模型(skip-gram)和连续词袋模型(continuous bag of words,简称CBOW),以及两种近似训练法:负采样(negative sampling)和层序softmax(hierarchical softmax)。值得一提的是,word2vec的词向量可以较好地表达不同词之间的相似和类比关系。
跳字(skip-gram)模型
在跳字(skip-gram)模型中,模型用一个中心词来预测它在文本序列中周围临近词。
假设文本序列是:
“I love you very much”
跳字模型所关心的是,给定“you”生成邻近词“I”、“love”、“very”和“much”的条件概率。
在这个例子中,“you”叫中心词,“I”、“love”、“very”和“much”叫背景词。
由于“you”只生成与它距离不超过2的背景词,该时间窗口的大小为2[与N-gram类似]。
我们来描述一下跳字模型[用最大似然估计的思想]:
假设词典索引集V的大小为|V|,且{0,1,…,|V|−1}。给定一个长度为T的文本序列中,文本序列中第t的词为w(t)。当时间窗口大小为m时,跳字模型需要最大化给定任一中心词生成所有背景词的概率(与N-gram语言模型类似):
上式的最大似然估计与最小化以下损失函数等价:
假设用v和u分别表示词汇作为 中心词 和 背景词 的向量。
每一个词,在模型中有两个词向量,一个是作为中心词时的词向量,一个是作为背景词时的词向量
即,对于词典中索引为i的词,它在作为中心词和背景词时的向量表示分别是vi和ui。
而词典中所有词的这两种向量正是跳字模型所要学习的模型参数。为了将模型参数植入损失函数,我们需要使用模型参数表达损失函数中的给定中心词生成背景词的条件概率。
给定中心词,假设生成各个背景词是相互独立的。
设中心词wc在词典中索引为c,背景词wo在词典中索引为o,损失函数中的给定中心词生成背景词的条件概率可以通过softmax函数定义为:
上式的含义:给定任何一个中心词Wc,产生背景词Wo的概率
此时,skip-gram模型的目标函数的形式就确定了,接下如何求解模型的参数呢?
利用随机梯度下降求解:
当序列长度T较大时,我们通常在每次迭代时随机采样一个较短的子序列来计算有关该子序列的损失。然后,根据该损失计算词向量的梯度并迭代词向量。
下面我们看看如何计算随机采样的子序列的损失有关中心词向量的梯度。
随机采样的子序列的损失实际上是对子序列中给定中心词生成背景词的条件概率的对数求平均。
通过求偏导,我们可以得到上式中条件概率的对数有关中心词向量vc的梯度:
也可以记做:
使用随机梯度的迭代方法更新参数的计算开销太大!!每次都需要遍历整个字典,对应的解决方案在后面(这也是word2vec为啥这么牛逼的原因...厉害的不是这个工具本身,而是一种思想的应用)
随机采样的子序列有关其他词向量的梯度同理可得。训练模型时,每一次迭代实际上是用这些梯度来迭代子序列中出现过的中心词和背景词的向量。训练结束后,对于词典中的任一索引为i的词,我们均得到该词作为中心词和背景词的两组词向量vi和ui。在自然语言处理应用中,我们会使用跳字模型的中心词向量。
连续词袋模型(CBOW模型)
连续词袋模型与跳字模型类似,与跳字模型最大的不同是:
连续词袋模型用一个中心词在文本序列周围的词来预测该中心词。
举个例子,假设文本序列为:
“I love you very much”
连续词袋模型所关心的是,邻近词“I”、“love”、“very”和“much”一起生成中心词“you”的概率。
假设词典索引集的大小为V,且V={0,1,…,|V|−1}</nobr>。给定一个长度为T的文本序列中,文本序列中第t个词为wu(t)。当时间窗口大小为m时,连续词袋模型需要最大化由背景词生成任一中心词的概率:
上式的最大似然估计与最小化以下损失函数等价:
我们可以用v和u分别表示背景词和中心词的向量(注意符号和跳字模型中的不同)。换言之,对于词典中索引为i的词,它在作为背景词和中心词时的向量表示分别是vi和ui。而词典中所有词的这两种向量正是连续词袋模型所要学习的模型参数。为了将模型参数植入损失函数,我们需要使用模型参数表达损失函数中的给定背景词生成中心词的概率。设中心词wc在词典中索引为c,背景词wo1、wo2、...wo2m在词典中索引为o1、o2、....o2m-1、o2m,损失函数中的给定背景词生成中心词的概率可以通过softmax函数定义为:
也可以采用随机梯度的方式来迭代更新获取模型从参数,同样存在需要迭代字典大小的次数,为了降低计算的复杂度,提出了基于负采样思想、层次softmax思想的两种近似计算的方案。
1.负采样思想
我们以跳字模型为例讨论负采样:
实际上,词典V的大小之所以会在损失中出现,是因为给定中心词wc生成背景词wo的条件概率P(w0∣wc)
随机梯度下降中使用了softmax运算,而softmax运算正是考虑了背景词可能是词典中的任一词(使用了全部词),并体现在分母上。
负采样中,分母上可能的背景词只考虑K个噪声词,至于K个噪声词的选择(采样)的概率与词频相关。
这K个噪声词与中心词的关系:
可以构造一个概率函数:
σ(x)=1/(1+exp(−x))
表示中心词wc和背景词wo同时出现在该训练数据窗口的概率,其中σ(x)属于[0,1]。
那么背景词与中心词同时出现在一个窗口的概率为:
那么,给定中心词wc生成背景词wo的条件概率的对数可以近似为:
上式的含义:中心词wc与背景词wo同时出(D=1)现概率,且中心词wc与噪音词wk不同时出现(D=0)的概率。
此时,我们已经抛弃了惹人烦的softmax函数…
假设噪声词wk在词典中的索引为ik,上式可改写为:
因此,有关给定中心词wc生成背景词wo的损失是:
假设词典V很大,每次迭代的计算开销由O(|V|)变为O(|K|)。当我们把K取较小值时,负采样每次迭代的计算开销将较小。
同理,我们也可以在CBOW模型中使用负采样的方法。
2.层次化softmax:
层序softmax是另一种常用的近似训练法。它利用了二叉树这一数据结构。树的每个叶子节点代表着词典V中的每个词。
假设L(w)为从二叉树的根节点到词w的叶子节点的路径(包括根和叶子节点)上的节点数。设n(w,j)为该路径上第j个节点,并设该节点的向量为un(w,j)。
以上图为例:L(w3)=4
设词典中的词wi的词向量为vi。那么,跳字模型和连续词袋模型所需要计算的给定词wi生成词w的条件概率为:
其中σ(x)=1/(1+exp(−x)),leftChild(n)是节点n的左孩子节点,如果判断x为真,[x]=1;反之[x]=−1。由于σ(x)+σ(−x)=1,给定词wi生成词典V中任一词的条件概率之和为1这一条件也将满足:
让我们计算给定词wi生成词w3的条件概率。我们需要将wi的词向量vi和根节点到w3路径上的非叶子节点向量一一求内积。由于在二叉树中由根节点到叶子节点w3的路径上需要向左、向右、再向左地遍历,我们得到:
整个遍历的路径已经通过Huffman编码唯一的确定了。
在使用softmax的跳字模型和连续词袋模型中,词向量和二叉树中非叶子节点向量是需要学习的模型参数。
假设词典V很大,每次迭代的计算开销由整个词表的大小O(|V|)下降至树的高度O(log2|V|)。
目前工作中我最常用的词向量方法还是Gensim中的word2vec,可能是因为怀旧吧….
补充:
Mikolov 三篇论文:http://www.sohu.com/a/114464910_465975
FastText官网:https://fasttext.cc/docs/en/unsupervised-tutorial.html
fastText 算法实际上是将目前用来算 word2vec 的网络架构做了个小修改,原先使用一个词的上下文的所有词向量之和来预测词本身(CBOW 模型),现在改为用一段短文本的词向量之和来对文本进行分类,通过一个有监督的任务来产生副产品-词向量。
fasttext也提供了两种模型,与word2vec的两种模型不太一样:
根据官网的介绍,一般skipgram模型能更好的利用上下文信息。
fasttext有别于word2vec的另一点是加了N-gram切分这个trick,将长词再通过ngram切分为几个短词,这样对于未登录词也可以通过切出来的ngram词向量合并为一个词。因为英文中单词存在各类单复数、词缀的现象,所以对于识别英文的未登录词的意义相比较中文更大一些。
Fasttext增加的N-gram的特征具体做法是:
把N-gram当成一个词,也用embedding向量来表示, 在计算隐层时,把N-gram的embedding向量也加进去求和取平均。
举个例子来说,假设某篇文章只有3个词,W1, W2, W3, N-gram的N取2, W1、w2、w3以及w12、W23分别表示词W1、W2、W3和bigram W1-W2, W2-W3的embedding向量,那么文章的隐层可表示为:
h=(W1+W2+W3+W12+W23)/5
通过back- propogation算法,就可以同时学到词的Embeding和n-gram的Embedding了 。
具体实现上,由于n-gram的量 远比word大的多,完全存下所有的n- gram也不现实。Fasttext采用了Hash桶的方式,把所有的n- gram都哈希到buckets个桶中,哈希到同一个桶的所有n-gram共享一个embedding vector。
ELMo官网:https://allennlp.org/elmo
艾伦研究所开发并并于6月初在NAACL 2018年发布ELMo(深度语境化的单词表示)。
ELMO(Embeddings from Language Models) ,被称为时下最好的通用词和句子嵌入方法,来自于语言模型的词向量表示,也是利用了深度上下文单词表征,该模型的优势: (1)能够处理单词用法中的复杂特性(比如句法和语义) (2)这些用法在不同的语言上下文中如何变化(比如为词的多义性建模)
ELMo与word2vec最大的不同: Contextual: The representation for each word depends on the entire context in which it is used. (即词向量不是一成不变的,而是根据上下文而随时变化,这与word2vec或者glove具有很大的区别)
ELMo是双向语言模型biLM的多层表示的组合,基于大量文本,ELMo模型是从深层的双向语言模型(deep bidirectional language model)中的内部状态(internal state)学习而来的,而这些词向量很容易加入到QA、文本对齐、文本分类等模型中,后面会展示一下ELMo词向量在各个任务上的表现。
它首先在大文本语料库上预训练了一个深度双向语言模型(biLM),然后把根据它的内部状态学到的函数作为词向量。实验表明,这些学到的词表征可以轻易地加入到现有的模型中,并在回答问题、文本蕴含、情感分析等 6 个不同的有难度的 NLP 问题中大幅提高最佳表现。实验表明显露出预训练模型的深度内部状态这一做法非常重要,这使得后续的模型可以混合不同种类的半监督信号。
ELMo方法已经集成到AllenNLP中,AllenNLP是一个相对成熟的基于深度学习的NLP工具包,它构建于 PyTorch之上。
对于python3.6以上的版本,可以借助pip一键安装:
pip install allennlp
也有大佬基于Keras实现了一个ELMo模型:
https://github.com/strongio/keras-elmo/blob/master/Elmo%20Keras.ipynb
此外,Tensorflow_Hub中也有预训练好的ELMo模型:
import tensorflow_hub as hub
import tensorflow as tf
elmo = hub.Module("https://tfhub.dev/google/elmo/2", trainable=True)
# just a random sentence
x = ["Roasted ants are a popular snack in Columbia"]
# Extract ELMo features
embeddings = elmo(x, signature="default", as_dict=True)["elmo"]
本篇介绍了词向量技术的第一个阶段的进化历程,即从静态词向量到动态词向量,后续计划继续写从ELMo到GPT、BERT的两阶段新模式(预训练+Finetuning)....
8.31