小白看Word2Vec的正确打开姿势|全部理解和应用

有个用心的读者最近做了一篇论文,想知道Word2Vec的相关理论和应用方法,作为一个有强迫症的我,去翻查了大量的文献资料,决定从Word2Vec的上下文来温习一下这个NLP的基础以及做相关的知识体系的基本构建,为做Word2Vec的朋友提供一个参考。

内容目录:

  1. Word2Vec可以做什么用?
  2. Word2Vec定义?为什么需要他?
  3. Word2Vec是如何工作的?
  4. Word2Vec的知识大纲
  5. CBOW模型
  6. Pytorch implementation
  7. Skip Gram 模型
  8. Gensim Python: Skip-gram algorithm
  9. TensorFlow implementation: Skip-gram algorithm

一、 Word2Vec的用途调查

对接触到一个新的领域,个人的学习方法是自上往下去逐步深挖,毕竟每个人的时间都比较有限,从应用层,一直挖到算法层,可能已经能让我们很好的去复现一篇论文,那么从算法层挖到优化层,就可以很好的去发表一篇论文。由于篇幅限制,这里我们只从应用层挖到算法层,对于想继续深入研究的大牛可以继续往下研究。废话不多说:我们先看看这个东西可以做什么用。作为平常喜欢检索的我,看看一些标题党就明白了:

使用Word2vec进行音乐推荐​towardsdatascience.com

 

文本数据深度学习方法的动手直观方法-Word2Vec,GloVe和FastText​towardsdatascience.com

 

使用Word2Vec和Xgboost查找类似的Quora问题​towardsdatascience.com

 

使用Word2Vec更好地嵌入分类功能​towardsdatascience.com

 

使用word2vec分析新闻头条并预测文章成功​towardsdatascience.com

 

使用Python进行的另一种Twitter情绪分析-第11部分(CNN + Word2Vec)​towardsdatascience.com

 

使用Word2vec构建推荐系统​medium.com

 

以上这些都是medium中的数据科学软文,写的都很好,这里只是看看大家都在用Word2Vec研究啥。大概可以看到大部分也都是做推荐和分类的系统的模型。Word2Vec只不过是我们深度学习中做推荐系统的一种。将词向量用于

  • 语言建模
  • 聊天机器人
  • 机器翻译
  • 问题回答
  • … 还有很多

再细分来说他是针对NLP的一个范畴。接下来我们就对Word2Vec做一个定义:

2. Word2Vec是什么?为什么需要他?

NLP前沿实际上都严重依赖于单词向量。现在让我们讨论单词向量必须具有什么样的含义才能使模型更好。使用单词向量时,语义上接近的单词在模型中似乎是相似的计算。

单词嵌入是文档词汇表最流行的表示形式之一。它能够捕获文档中单词的上下文,语义和句法相似性,与其他单词的关系等。

词嵌入到底是什么?广义的说,它们是特定单词的向量表示。话虽如此,接下来是如何生成它们?更重要的是,它们如何捕获上下文?

单词向量是单词的数字表示形式,保留了单词之间的语义关系。例如,一词的向量与一词的向量非常类似。但是,铅笔的向量将与cat的词向量大不相同。这种相似性是由在相同上下文中使用所讨论的两个单词(即[cat,dog]或[cat,pencil])的频率定义的。例如,考虑以下句子,

我认为我不需要在上述句子中拼写出奇数,而显然需要用铅笔来拼写为遗漏词。你为什么觉得这是一个奇怪的句子?拼写很好,语法正确,那为什么呢?这是因为上下文,使用的铅笔一词不正确。这应该使您相信单词上下文对单词本身的影响。词向量算法使用词的上下文来学习词的数字表示,以便在相同上下文中使用的词具有相似的词向量。

Word2Vec是使用浅层神经网络学习单词嵌入的最流行技术之一。它是由Tomas Mikolov于2013年在Google上开发的

考虑以下类似的句子:Have a good day 和 Have a great day它们几乎没有不同的含义。如果我们构建一个详尽的词汇表(我们称其为V),则其V = {Have, a, good, great, day}

现在,让我们为V中的每个单词创建一个单编码的矢量。我们的单编码的矢量的长度等于V的大小(= 5)。除了索引中代表词汇表中相应单词的元素之外,我们将有一个零向量。该特定元素将是一个。下面的编码可以更好地说明这一点。

Have= [1,0,0,0,0]`; a = [0,1,0,0,0]`; good = [0,0,1,0,0]`; great = [0,0,0,1,0]`; day = [0,0,0,0,1]`(`代表转置)

如果我们尝试可视化这些编码,我们可以想到一个5维空间,其中每个单词占据一个维,而与其余单词无关(沿着其他维没有投影)。这意味着‘good’ and ‘great’ 和 ‘day’ and ‘have’不一样,这是不正确的。

它将「字词」转换成「向量」形式,可以把对文本内容的处理简化为向量空间中的向量运算,计算出向量空间上的相似度,来表示文本语义上的相似度。

  • word2vec计算的是余弦值 (cosine),距离范围为0–1之间,值越大代表两个词关联度越高。
  • 词向量:用Distributed Representation表示词,通常也被称为「Word Representation」或「Word Embedding」。

我们的目标是使上下文相似的单词占据紧密的空间位置。在数学上,此类向量之间的角度的余弦值应接近1,即角度接近0。如下图所示:

google image : 语义相近的放在同一个空间

这就是生成分布式表示的想法。直观地,我们引入了一个单词对其他单词的某种依赖性。在该词的上下文中的词将在这种依赖性中获得更大的比重one hot encoding representations中,所有的单词是独立彼此的如前面提到的。

3. Word2Vec如何工作?

Word2Vec是一种构造此类嵌入的方法。可以使用两种方法(都涉及神经网络)来获得它:

  • Skip Gram
  • Common Bag Of Words (CBOW)

CBOW模型:此方法将每个单词的上下文作为输入,并尝试预测与上下文相对应的单词。考虑我们的例子:Have a great day.

让输入到神经网络这个词为great。注意,这里我们试图使用单个上下文输入单词great预测目标单词(d ay 更具体地说,我们使用输入字的一种热编码,并与目标字的一种热编码(d ay)相比,测量输出误差。 在预测目标词的过程中,我们学习目标词的向量表示。

让我们更深入地研究实际架构。

CBOW模型

看不懂没关系,后面详细叙述:

Skip-Gram model:

Skip-Gram model试图预测给定单词的直接邻居。它看起来前面一个单词,后面一个单词,或者两个前面,或者其他一些变化。我们将要建模的单词用作输入X,并将周围的单词用作目标输出Y。

一旦我们可以相当准确地预测周围的单词,就删除输出层,并使用隐藏层获取我们的单词向量。这是一个很酷的黑客工具,可以产生非常有趣的结果。

Skip-Gram model

这看起来像多上下文CBOW模型刚刚被翻转。在某种程度上是对的。

我们将目标词输入网络。该模型输出C个概率分布。这是什么意思?

对于每个上下文位置,我们获得V个概率的C个概率分布,每个单词一个。

在这两种情况下,网络都使用反向传播进行学习。详细的数学可以在这里找到,接下来到了正式的东西了!

4. Word2Vec的知识大纲

Word2Vec的知识大纲,清晰的大图在文末获取

4.1 One-Hot编码

什么是One-Hot 编码,这个简单的编码方法处理可枚举的特征时还是很有用的。

One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都有它独立的寄存器位,并且在任意时候,其中只有一位有效。举个例子,假设我们有四个样本(行),每个样本有三个特征(列),如图:

我们的feature_1有两种可能的取值,比如是男/女,这里男用1表示,女用2表示。feature_2 和feature_3各有4种取值(状态)。

one-hot编码就是保证每个样本中的单个特征只有1位处于状态1,其他的都是0。上述状态用one-hot编码如下图所示:

说白了就是,一个特征有多少种可能的取值,那么就用多少位二进制表示,所以可以看到,这种编码方法只适用于可枚举完的特征,对于连续特征没法完全枚举的,这个时候需要灵活处理下,比如对某个范围内的数当成一个值。

再考虑几个例子,比如有三个特征:

["male", "female"]
["from Europe", "from US", "from Asia"]
["uses Firefox", "uses Chrome", "uses Safari", "uses Internet Explorer"]

将它换成独热编码后,应该是:

feature1=[01,10]
feature2=[001,010,100]
feature3=[0001,0010,0100,1000]

优缺点分析

  • 优点:一是解决了分类器不好处理离散数据的问题,二是在一定程度上也起到了扩充特征的作用。
  • 缺点:在文本特征表示上有些缺点就非常突出了。首先,它是一个词袋模型,不考虑词与词之间的顺序(文本中词的顺序信息也是很重要的);其次,它假设词与词相互独立(在大多数情况下,词与词是相互影响的);最后,它得到的特征是离散稀疏的。

为什么得到的特征是离散稀疏的?

上面举例比较简单,但现实情况可能不太一样。比如如果将世界所有城市名称作为语料库的话,那这个向量会过于稀疏,并且会造成维度灾难。

  • 杭州 [0,0,0,0,0,0,0,1,0,……,0,0,0,0,0,0,0]
  • 上海 [0,0,0,0,1,0,0,0,0,……,0,0,0,0,0,0,0]
  • 宁波 [0,0,0,1,0,0,0,0,0,……,0,0,0,0,0,0,0]
  • 北京 [0,0,0,0,0,0,0,0,0,……,1,0,0,0,0,0,0]

在语料库中,杭州、上海、宁波、北京各对应一个向量,向量中只有一个值为1,其余都为0。

可以看到,当城市数比较大的时候,每一个城市表示的向量是非常长的,这就导致了很大的浪费,因为里面一大堆0几乎是没有用的。

一个最简单粗暴的方法就是降维了,比如PCA操作,降维后的特征肯定就不是0-1的特征了,而是小数表示的特征,这样才能用很低的维度覆盖大的特征区间。

当然降维的方法不止PCA,还有很多,我们要说的word2vec就是一种。word2vec

说起word2vec,首先需要简单理解下基于神经网络的自编码模型,自编码,其实就是一种降维方法。基础自编码的网络结构如下:

网络的输入就是一组特征,对应到本文就是一组0-1串的特征,输出维度和输入维度一样,中间有一层隐含层映射,我们的目的就是训练网络使得输出X 尽可能等于输入X。训练好以后,任意一个x进入模型都可以得到中间层的输出结果,此时中间层的输出结果就可以认为是降维后的结果。像本图,就是一个4维降二维

因为word2vec是NLP里面应用的,是对词的一个编码,NLP里面一个很大的特点是什么呢?就是一段话中,一个词的表示是和上下文相关的。也就是说这是一个带有时间先后与相对顺序的表示。那么既要实现上面的降维,又要兼顾词的先后顺序关系,word2vec就是要解决这样的问题。

怎么解决的?首先还是有一个基础的神经网络自编码模型:

那么怎么考虑上下文的信息呢?很简单,输入的时候不光是一个词,而是上下文多个词一起当成输入:

5. CBOW模型

这是一种多对一的模型(CBOW),还有一种一对多(Skip-Gram)模型,我们先说这种多对一模型。CBOW的训练模型如图所示:

这个网络结构就是最开始的自编码网络,只不过它的输入不是一次性输入的,而是好几批输入的,而隐含层的结果是好几批输入的加权平均值。

详细的过程为:

1 输入层:上下文单词的onehot.

2 这些单词的onehot分别乘以共享的输入权重矩阵W.

3 所得的向量相加求平均作为隐层向量.

4 乘以输出权重矩阵W {NV}

5 得到输出向量。

6 输出向量与真实单词的onehot做比较,误差越小越好。

这样就可以训练网络了。训练完毕后,输入层的每个单词与矩阵W相乘得到的向量的就是我们想要的词向量(word embedding),这个矩阵(所有单词的word embedding)也叫做look up table(其实这个look up table就是矩阵W自身),也就是说,任何一个单词的onehot乘以这个矩阵都将得到自己的词向量。有了look up table就可以免去训练过程直接查表得到单词的词向量了。

相比于原始的自编码,word2vec最大的不同点在于输入上,要考虑先后关系的自编码,这一点值得好好理解下。

这是多对一的方式,还有一种一对多的方式:同理,目的为了保证上下文的顺序关系的同时实现降维。

word2vec训练最终我们需要的是训练出来的权重矩阵,有了此权重矩阵就能实现输入单词的onehot降维,同时这个降维还包含了上下文的先后循序关系。这就是word2vec。

考虑由[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q, r,s,t,u,v,w,x,y,z],进一步令整数0至25代表各自的字母。实施CBOW时,将窗口大小保持为2,我们可以看到“ a”与“ b”和“ c”有关,“ b”与“ a”,“ c”和“ d”有关,依此类推。因此,代表输入词的一个热向量将具有尺寸[26,1]。借助该模型,我们将找到大小为[10,1]的密集和分布向量。嵌入向量的大小是任意选择的。

下图说明了上下文和目标词。

CBOW和Skip-gram模型的排列。字符和相应的整数分为输入和目标列表。对于每一列,第一列代表输入字符,每个子列表中的其余项目都是输入的目标。

在窗口大小为2的情况下,黑色区域表示没有共现,而白色区域表示字符一起出现。#还要注意对角线对称性-意味着从a到b的共现意味着从b到a的共现,但是描述了这些关系不同的轴。

Word2Vec是一个概率模型。该模型的关键组成部分是2个权重矩阵。第一矩阵(w1)的行和第二矩阵(w2)的列分别嵌入输入词和目标词。给定选定的输入单词,然后将这两个单词向量的乘积用于获得成为目标单词的概率。在训练之后,使用梯度下降来优化这些嵌入矢量,从而使针对真实目标的概率最大化。显然,矩阵w1和w2是分解概率矩阵,该概率矩阵与共现矩阵非常相似。

下图说明并说明了模型。

Word2Vec模型的示意图。输入和输出向量的大小为V,而隐藏向量(嵌入)的大小为N

在此插图中,模型正在学习将6个字符(“ a”至“ f”)嵌入3维嵌入向量中。 A.窗口大小为2的这些字符的同时出现矩阵,同时出现表示为存在或不存在。(大小6、6) B.权重矩阵w1-换位。矩阵w1的每一行都嵌入一个单词的向量,因为一个热向量唯一选择w1的对应行(或W1转置的col) C。一个热向量的矩阵,每一列代表一个词/项 D。w1和输入矩阵的乘积得到矩阵h(隐藏层)。在这里,整个输入矩阵都是一个单位矩阵,只是将权重矩阵w1作为隐藏矩阵h传递。然而,不必是一个单位矩阵,输入矩阵的顺序和大小(以及隐藏层)可以是不同的 E。权重矩阵w2-已转置(大小6,3)。矩阵w2的每一列都紧密代表目标词 F。隐藏层-h,与之前 G中描述的w1相同。w2的每一行-转置了带有隐藏列(嵌入输入词)的乘积,输出的分数为vocab H的大小 如所提到的与隐藏1列w2_transposed相互作用的所有行-在记分矩阵,总产物中的一列的结果w2_transposed和h是矩阵S(尺寸6,δ) I.使用SoftMax被施加到记分矩阵。列中的每个条目都转换为概率 J。概率矩阵-该矩阵的列中的每个项目表示在输入单词 L的情况下成为目标单词的概率 错误-真实索引位置的概率应最大化,错误比较分配给与真实目标 M对应的索引的概率来计算 反向支撑—开始时,权重是随机初始化的,因此所有目标的概率都很低且相似。经过训练,梯度的反向传播和权重的优化,目标周围的概率很密集,其他地方的概率接近于0。

6. Implemetation in Pytorch

https://towardsdatascience.com/word2vec-made-easy-139a31a4b8ae​towardsdatascience.com

 

7. 从原始文本到词向量:高级方法

现在,凭着扎实的直觉,我们将首先讨论Word2vec算法的高级机制。我们将在后面的部分中完善细节,直到可以确定我们知道如何实现Word2vec算法为止。为了以无监督的方式学习单词向量(即没有人工标记数据),我们必须定义并完成某些任务。这是这些任务的高级列表。

  1. 从原始文本创建格式为[输入词,输出词]的数据元组,其中每个词都表示为one-hot vectors, from raw text
  2. 定义一个模型,该模型可以将one-hot vectors作为输入和输出,进行训练
  3. 定义一个损失函数来预测正确的单词(实际上是在输入单词的上下文中)以优化模型
  4. 通过确保相似的词具有相似的词向量来评估模型

这个看似简单的过程将导致学习非常强大的单词向量。让我们进入上述管道的每个步骤的精妙之处。

1. 从原始文本创建结构化数据

这不是一个非常困难的任务。这是对原始文本的简单操作,以将其放入特定的结构。想想下面的句子。

The cat pushed the glass off the table

从这句话创建的数据如下所示。句子后的每一行代表一个数据点。蓝色框代表one-hot input word(中间单词,称为目标单词),红色框代表the one-hot output word(上下文窗口中除中间单词之外的任何单词,称为上下文单词)。从单个上下文窗口创建两个数据点(正负1的滑窗)。上下文窗口的大小由用户定义。上下文窗口大小越大,模型的性能越好。但是,当上下文窗口大小较大时,随着数据量的增加,您将付出计算时间。不要将目标词与神经网络的目标(正确的输出)混淆,这是两件事。

为Word2Vec滑窗结构化数据的方法

2. 定义嵌入层和神经网络

神经网络用于从上面定义的结构化数据中学习。但是,它带有一个转折!为了清楚起见,您具有以下组件。

  • 一批输入表示为one-hot vectors
  • 一批输出表示为one-hot vectors(仅在训练阶段)
  • 嵌入层
  • 神经网络

如果您不了解最后两个组件的性能以及工作方式,则不必害怕。我们将探究这些组件中的每一个,以了解它们的作用。

3. Embedding layer: stores all the word vectors

在我们的议程中,首先是嵌入层。嵌入层存储在词汇表中找到的所有单词的单词向量。如您所见,这是一个巨大的矩阵(of size[vocabulary size x embedding size])。嵌入大小是用户可调的参数。数值越高,模型的性能越好。但是,超过某个点(例如,嵌入大小为500),您将不会获得令人瞠目结舌的性能/大小增益。这个巨大的矩阵是随机初始化的(就像一个神经网络一样),并在优化过程中一点一点地进行调整,以揭示强大的单词向量。这就是它的样子。

What the embedding layer looks like

4. Neural network: maps word vectors to outputs

接下来的是我们模型的最后一个乐高积木;神经网络。在训练过程中,神经网络将输入一个单词并尝试预测输出单词。然后,使用损失函数,我们对模型的错误分类进行惩罚,并对模型的正确分类进行奖励。我们将要求限制为一次处理单个输入和单个输出。但是实际上,您要分批处理数据(例如64个数据点)。让我们描述一下训练过程中使用的确切过程:

  1. 对于给定的输入单词(目标单词),从嵌入层中找到相应的单词向量
  2. 将单词向量馈送到神经网络,然后尝试预测正确的输出单词(上下文单词)
  3. 通过比较预测词和真实上下文词,计算损失
  4. 将损失与随机优化器一起使用以优化神经网络和嵌入层

要注意的一件事是,在计算预测时,我们使用softmax激活将预测归一化为有效概率分布。

5. Fitting all together: inputs to model to outputs

了解了Word2vec算法的所有基本要素后,我们可以将所有部分放在一起。这样,一旦训练好该模型,我们要做的就是将嵌入层保存到磁盘上。然后,我们可以在一天中的任何时候享受语义保留的单词向量。在下面,我们看到了整个图片。

模型如何看待其最终形式

数据的这种局部排列和模型布局被称为skip-gram algorithm;Word2vec算法。这就是我们要重点关注的。另一种算法称为连续词袋(CBOW)模型。

6. Defining a loss function: to optimize the model

到目前为止,我们尚未讨论的关键信息之一就是损失函数。通常,标准softmax交叉熵损失是分类任务的良好损失函数。对于Word2vec模型,使用这种损失不是很实际,对于诸如情感分析(您有2种可能的输出:肯定或否定的结果)这样的简单任务而言,这不是很实用。在这里事情会变得时髦。在消耗数十亿个单词的真实单词任务中,词汇量可以轻松增长到100,000甚至更多。这使得softmax归一化的计算繁重。这是因为softmax的完整计算需要针对所有输出节点计算交叉熵损失。

因此,我们正在寻找一种更智能的替代方法,称为“ 采样softmax损失”(sampled softmax loss)。在采样的softmax损失中,执行以下操作。请注意,与标准softmax交叉熵损失相比,有很多变化。首先,计算给定目标单词的真实上下文单词ID和对应于真实上下文单词ID的预测值之间的交叉熵损失。然后,我们加上K根据一些噪声分布采样的负样本的交叉熵损失。概括地说,我们将损失定义如下:

SigmoidCrossEntropy是我们可以在单个输出节点上定义的损耗,而与其余节点无关。由于我们的词汇量可能会很大,因此非常适合我们的问题。我不会详细介绍这种损失的细节。您无需了解如何实现此功能,因为这些功能可以作为TensorFlow中的内置功能使用。但是了解损耗所涉及的参数(例如K)很重要。采样的softmax损失通过考虑两种类型的实体来计算损失:

  • 预测向量中真实上下文单词ID给出的索引(上下文窗口中的单词)
  • K 指示单词ID并被视为噪音的索引(上下文窗口之外的单词)

通过举例说明,我将其进一步可视化。

获取采样的softmax层的正样本和负样本

 

8. Gensim Python: Skip-gram algorithm

Gensim是用于自然语言处理的开源python库,首先,我们需要安装genism软件包。Gensim可在Linux,Windows和Mac OS X上运行,并且应在支持Python 2.7+和NumPy的任何其他平台上运行。Gensim取决于以下软件:

有两种安装方式。我们可以在终端中运行以下代码来安装genism软件包。

pip install --upgrade gensim

或者,对于Conda环境:

conda install -c conda-forge gensim

Word2vec不是深度神经网络,它将文本转换为数字形式,深度神经网络可以将其处理为输入。

如何训练word2vec模型

  • 在带有滑动窗口的训练语料库中移动:每个单词都是一个预测问题。
  • 目的是使用相邻单词来预测当前单词(反之亦然)。
  • 预测结果决定我们是否调整当前单词向量。向量逐渐收敛到(希望)最优值。

数学

以下是word2vec嵌入背后的数学运算。输入层是一键编码的矢量,因此它在该单词索引中得到“ 1”,在其他任何地方都得到“ 0”。当我们将此输入向量乘以权重矩阵时,实际上是在抽出与该单词索引对应的一行。此处的目的是取出重要的行,然后将其余的行扔掉。

这是word2vec工作原理的主要机制。

当我们使用Tensorflow / KerasPytorch进行此操作时,它们在此过程中有一个特殊的层称为“嵌入层”。因此,我们不会自己做数学运算,只需要传递一键编码的矢量,“嵌入层”就可以完成所有脏活。

预处理文本

现在,我们将为BBC新闻数据集实现word2vec嵌入。

  • 我们使用Gensim训练word2vec嵌入。
  • 我们使用NLTK和spaCy预处理文本。
  • 我们使用t-SNE可视化高维数据。
import re, string 
import pandas as pd   
from collections import defaultdict
import spacy
from sklearn.manifold import TSNE
from nltk.corpus import stopwords
STOPWORDS = set(stopwords.words('english'))
from gensim.models import Word2Vec
%matplotlib inline

df = pd.read_csv('bbc-text.csv')

def clean_text(text):
    '''Make text lowercase, remove text in square brackets, remove punctuation and remove words containing numbers.'''
    text = text.lower()
    text = re.sub(r'\[.*?\]', '', text)
    text = re.sub(r'[%s]' % re.escape(string.punctuation), '', text)
    text = re.sub(r'\w*\d\w*', '', text)
    # Remove a sentence if it is only one word long
    if len(text) > 2:
        return ' '.join(word for word in text.split() if word not in STOPWORDS)

df_clean = pd.DataFrame(df.text.apply(lambda x: clean_text(x)))
  • We use spaCy for lemmatization.
  • Disabling Named Entity Recognition for speed.
  • Remove pronouns.
nlp = spacy.load('en', disable=['ner', 'parser']) # disabling Named Entity Recognition for speed

def lemmatizer(text):        
    sent = []
    doc = nlp(text)
    for word in doc:
        sent.append(word.lemma_)
    return " ".join(sent)

df_clean["text_lemmatize"] =  df_clean.apply(lambda x: lemmatizer(x['text']), axis=1)
df_clean['text_lemmatize_clean'] = df_clean['text_lemmatize'].str.replace('-PRON-', '')

现在,我们可以看看前10个最常用的单词。

sentences = [row.split() for row in df_clean['text_lemmatize_clean']]
word_freq = defaultdict(int)
for sent in sentences:
    for i in sent:
        word_freq[i] += 1
        
sorted(word_freq, key=word_freq.get, reverse=True)[:10]

在Gensim中实现Word2vec嵌入

  • min_count:模型中要包含的单词在语料库中出现的最小次数。数字越大,语料库中的单词越少。
  • window:句子中当前词和预测词之间的最大距离。
  • size:特征向量的维数。
  • workers:我知道我的系统有4个核心。
  • model.build_vocab:准备模型词汇。
  • model.train:训练单词向量。
  • model.init_sims():当我们不打算进一步训练模型时,我们将使用以下代码行使模型更具存储效率。
w2v_model = Word2Vec(min_count=200,
                     window=5,
                     size=100,
                     workers=4)
                     
w2v_model.build_vocab(sentences)
w2v_model.train(sentences, total_examples=w2v_model.corpus_count, epochs=w2v_model.iter)
w2v_model.init_sims(replace=True)

探索模型

  • 查找与“经济”最相似的词
w2v_model.wv.most_like(positive = ['economy'])

 

  • 这两个词彼此有多相似?

w2v_model.wv.similarity('company','business')

代码地址:

https://github.com/zjgulai/PyCon-Canada-2019-NLP-Tutorial​github.com

 

9. TensorFlow implementation: Skip-gram algorithm

在这里,我们将重新讨论刚刚讨论的实现。在本节中,我们将实现以下内容。

  • 数据生成器
  • Skip-gram algorithm模型(使用TensorFlow)
  • 运行skip-gram算法

1. 数据产生器

首先让我们了解如何生成数据。因为我们已经讨论了数据生成的内部机制,所以我们将不讨论该代码的细节。这只是将逻辑转换为实现。

def generate_batch(batch_size, window_size):
 global data_index 
 
 # two numpy arras to hold target words (batch)
 # and context words (labels)
  batch = np.ndarray(shape=(batch_size), dtype=np.int32)
  labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
 
 # span defines the total window size
  span = 2 * window_size + 1 
 
 # The buffer holds the data contained within the span
  queue = collections.deque(maxlen=span)
 
 # Fill the buffer and update the data_index
 for _ in range(span):
    queue.append(data[data_index])
    data_index = (data_index + 1) % len(data)
 
for i in range(batch_size // (2*window_size)):
    k=0
# Avoid the target word itself as a prediction
for j in list(range(window_size))+list(range(window_size+1,2*window_size+1)):
      batch[i * (2*window_size) + k] = queue[window_size]
      labels[i * (2*window_size) + k, 0] = queue[j]
      k += 1 

# Everytime we read num_samples data points, update the queue
    queue.append(data[data_index])

# If end is reached, circle back to the beginning
    data_index = (data_index + np.random.randint(window_size)) % len(data)

return batch, labels

2. Defining the skip-gram model

batch_size = 128 
embedding_size = 64 
window_size = 4 
num_sampled = 32 # Number of negative examples to sample.

batch_size在我们在给定的时间过程定义的数据点的数量。然后,embedding_size是单词向量的大小。下一个超参数window_size定义了我们上面可视化的上下文窗口的大小。最后,num_sampled定义损失函数(K)中的负样本数。然后,我们为输入和输出定义TensorFlow占位符。

tf.reset_default_graph()
#训练输入数据(目标单词ID)。
train_dataset = tf.placeholder(tf.int32,shape = [batch_size])#训练输入标签数据(上下文单词ID)
train_labels = tf.placeholder(tf.int32,shape = [batch_size,1])

在这里,train_dataset获取batch_size代表所选目标单词集合的单词ID列表。最后,train_labels代表batch_size所选目标词的对应上下文词的列表。接下来,我们定义定义模型所需的模型参数:嵌入层以及神经网络的权重和偏差。

############################################# 
model变量
############################################### 
#嵌入层
 embeddings = tf.Variable(tf.random_uniform([vocabulary_size,embedding_size],-1.0,1.0))
#神经网络权重和偏差
 softmax_weights = tf.Variable(
    tf.truncated_normal([vocabulary_size,embedding_size],
                        stddev / math.sqrt(embedding_size))
)
softmax_biases = tf.Variable(tf.random_uniform([vocabulary_size],-0.01,0.01))

我们将嵌入层定义为TensorFlow变量:embeddings。然后定义神经网络的权重(softmax_weights)和偏差(softmax_biases)。此后,我们定义了将嵌入层连接到神经网络以共同优化嵌入层和神经网络所需的关键操作。

# Look up embeddings for a batch of inputs. 
embed = tf.nn.embedding_lookup(embeddings, train_dataset)

tf.nn.embedding_lookup函数以我们的嵌入层作为输入和一组单词ID(train_dataset),并将相应的单词向量输出到变量embed。定义了嵌入查找函数后,我们可以定义上面讨论的采样softmax损失函数。

################################################ 
#              Computes loss                   # 
################################################ 
loss = tf.reduce_mean(tf.nn.sampled_softmax_loss( weights=softmax_weights, biases=softmax_biases, inputs=embed, labels=train_labels, num_sampled=num_sampled, num_classes=vocabulary_size) )

在这里,该tf.nn.sampled_softmax_loss函数采用一组权(softmax_weights),偏差(softmax_biases),与在中找到的单词ID相对应的一组单词向量train_dataset,正确的上下文单词的ID(train_labels),噪声样本的数量(num_sampled)和词汇量(vocabulary_size)。通过输出计算操作和定义的损失,我们可以定义优化器,以针对嵌入层和神经网络的参数优化损失。

################################################ 
#                 Optimization                 # 
################################################ 
optimizer = tf.train.AdamOptimizer(0.001).minimize(loss)

Then we get the normalized embedding layer by making the vector magnitude equal to 1.

################################################ 
#                For evaluation                # ################################################ 
norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keepdims=True)) 
normalized_embeddings = embeddings / norm

3. 运行代码

在这里,我们将讨论有关如何运行先前定义的TensorFlow模型的详细信息。首先,我们定义a session,然后随机初始化所有TensorFlow变量。

num_steps = 250001 
session = tf.InteractiveSession() 
# Initialize the variables in the graph
tf.global_variables_initializer().run() 
print('Initialized') 
average_loss = 0

现在,对于预定义的步骤数,我们生成了一批数据:目标词(batch_data)和上下文词(batch_labels)。

for step in range(num_steps): 
    # Generate a single batch of data
    batch_data, batch_labels = generate_batch( batch_size, window_size)

然后,对于每个生成的批次,我们通过运行优化嵌入层和神经网络session.run([optimize, loss],...)。我们还得出了损失,以确保其随着时间的推移而减少。

    # Optimize the embedding layer and neural network
# compute loss
    feed_dict = {train_dataset : batch_data, train_labels : batch_labels}
    _, l = session.run([optimizer, loss], feed_dict=feed_dict)

在这里,每隔5000步,我们将平均损失打印出来,作为视觉辅助。

  if (step+1) % 5000 == 0:
 if step > 0:
        average_loss = average_loss / 5000
      print('Average loss at step %d: %f' % (step+1, average_loss))
      average_loss = 0

最后,我们得到了最终的嵌入,随后将其用于某些单词的可视化。

sg_embeddings = normalized_embeddings.eval() 
session.close()

最后,如果使用诸如t-SNE的流形学习算法可视化嵌入,您将获得以下内容。

如您所见,与猫有关的单词是在特定方向上找到的,而与狗有关的单词是在不同方向上找到的。介于两者之间的单词(例如动物或宠物)介于这两个方向之间,这几乎是我们所需要的。

代码地址:

https://github.com/zjgulai/exercises_thushv_dot_com​github.com

 

10.结论

这使我们结束了对话。单词向量是单词的非常强大的表示形式,可帮助机器学习模型更好地执行。我们经历了数据生成过程以及Word2vec模型中发现的不同组件。然后,我们讨论了Word2vec算法的一种特定变体;a skip-gram model。我们在TensorFlow中完成了算法的实现。最后,我们对嵌入进行了可视化,发现学习到的嵌入实际上描述了一些有用的语义。您可以在下方找到代码执行文件。

针对word2vec的主要训练算法有两种,一种是连续词袋(CBOW),另一种称为跳跃语法。这两种方法之间的主要区别是CBOW使用上下文预测目标词,而skip-gram使用单词预测目标词。通常,与CBOW方法相比,skip-gram方法具有更好的性能,因为它可以捕获单个单词的两种语义。例如,它将具有两种用于Apple的矢量表示,一种用于公司,另一种用于水果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI周红伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值