文章目录
理解 n-gram
介绍神经语言模型
神经概率语言模型
Word2Vec
CBOW
Skip-gram
fastText
词表示模型的全局向量
实现语言模型
训练嵌入模型
可视化嵌入向量
概括
在本节中,我们将讨论循环网络、自然语言和序列处理。我们将讨论自然语言处理中最先进的技术,例如序列和注意力模型,以及谷歌的 BERT。
本章是几章中的第一章,我们将在自然语言处理( NLP ) 的背景下讨论不同的神经网络算法。NLP 教计算机处理和分析自然语言数据,以执行机器翻译、情感分析、自然语言生成等任务。但要成功解决如此复杂的问题,我们必须以计算机可以理解的方式来表示自然语言,而这并非易事。
要理解为什么,让我们回到图像识别。神经网络输入相当直观——一个具有预处理像素强度的 2D 张量,它保留了图像的空间特征。让我们拍摄一张 28 x 28 的 MNIST 图像,其中包含 784 个像素。图像中有关数字的所有信息仅包含在这些像素中,我们不需要任何外部信息来对图像进行分类。我们还可以安全地假设每个像素(可能不包括靠近图像边界的像素)都带有相同的信息权重。因此,我们将它们全部提供给网络以发挥其魔力,并让结果说明一切。
现在,让我们关注文本数据。与图像不同,我们有 1D(相对于 2D)数据——一个长的单词序列。一般的经验法则是单行距 A4 页面包含 500 个单词。为了向网络(或任何 ML 算法)提供与单个 MNIST 图像等效的信息,我们需要 1.5 页的文本。文本结构有几个层次;从字符开始,然后是单词、句子和段落,所有这些都可以放在 1.5 页的文本中。图像的所有像素都与一个数字相关;但是,我们不知道所有单词是否都与同一主题相关。为了避免这种复杂性,NLP 算法通常使用较短的序列。即使一些算法使用循环神经网络(RNNs),它考虑了所有先前的输入,在实践中,它们仍然被限制在紧接在前面的单词的一个相对较短的窗口中。因此,NLP 算法必须用更少(更少量的输入信息)做更多的事情(表现良好)。
为了帮助我们解决这个问题,我们将使用一种特殊类型的向量词表示(语言模型)。我们将讨论的语言模型使用单词的上下文(其周围的单词)来创建与该单词关联的唯一嵌入向量。与 one-hot 编码相比,这些向量携带了更多关于单词的信息。它们是各种 NLP 任务的基础。
在本章中,我们将介绍以下主题:
- 理解n- gram
- 介绍神经语言模型:
- 神经概率语言模型
- Word2Vec 和 fastText
- 词表示的全局向量
- 实现语言模型
理解 n-gram
基于词的语言模型定义了词序列的 概率分布。 给定一个长度为m的单词序列(例如,一个句子),它将概率P ( w1, ... , w m )分配给完整的单词序列。我们可以按如下方式使用这些概率:
- 估计 NLP 应用程序中不同短语的可能性。
- 作为创建新文本的生成模型。基于单词的语言模型可以计算给定单词跟随单词序列的可能性。
长 序列的概率推断,比如w 1 , ..., w m,通常是不可行的。我们可以使用联合概率的链式法则(第 1 章,神经网络的基本要素)计算P ( w 1 , ... , w m )的联合概率:
给定较早单词的较晚单词的概率将特别难以从数据中估计。这就是为什么这种联合概率通常通过独立假设来近似,即第i个单词仅依赖于n-1 个先前的单词。我们将只对n 个连续单词组合的联合概率进行建模,称为n- gram。例如,在短语the quick brown fox中,我们有以下n -gram:
- 1-gram:The、quick、brown和fox(也称为 unigram)。
- 2-gram:快速、快速棕色和棕色狐狸(也称为二元组)。
- 3-grams:快速棕色和快速棕色狐狸(也称为 trigram)。
- 4-grams:快速棕色狐狸。
联合分布的推断是在n- gram 模型的帮助下进行近似的,该模型将联合分布分成多个独立的部分。
术语n -grams可以指其他类型的长度为 n 的序列,例如n 个字符。
如果我们有大量的文本语料库,我们可以找到直到某个n(通常为 2 到 4)的所有 n-gram 并计算该语料库中每个n -gram的出现次数。从这些计数中,我们可以估计每个n -gram 的最后一个单词的概率,给定前面的n-1 个单词:
- 1-gram:
- 2-gram:
- N-gram:
现在可以使用第i个单词仅依赖于之前的n-1个单词的独立假设来近似联合分布。
例如,对于 unigram,我们可以使用以下公式来近似联合分布:
对于三元组,我们可以使用以下公式来近似联合分布:
我们可以看到,基于词汇量大小,n-gram 的数量随着n呈指数增长。例如,如果一个小词汇表包含 100 个单词,那么可能的 5-gram 的数量将是100 5 = 10,000,000,000个不同的 5-gram。相比之下,莎士比亚的整部作品包含大约 30,000 个不同的单词,说明使用具有大n的n- gram 是不可行的。不仅存在存储所有概率的问题,而且我们还需要一个非常大的文本语料库来为较大的 n 值创建合适的n - gram 概率估计。
这个问题被称为维度灾难。当可能的输入变量(词)的数量增加时,这些输入值的不同组合的数量呈指数增长。当学习算法需要每个相关的值组合至少一个示例时,就会出现维度灾难,这就是n- gram 建模中的情况。我们的n越大,我们可以更好地逼近原始分布,并且我们需要更多的数据来对n- gram 概率进行良好的估计。
既然我们已经熟悉了n- gram 模型和维度灾难,那么让我们来讨论一下如何借助神经语言模型来解决它。
介绍神经语言模型
克服维度诅咒的一种方法是学习单词 的低维分布式 表示(神经概率语言模型,http://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf)。这种分布式表示是通过学习一个嵌入函数来创建的,该函数将单词空间转换为单词嵌入的低维空间,如下所示:
-> one-hot encoding -> 词嵌入向量
来自大小为V的词汇表中的单词被转换为大小为V的 one-hot 编码向量(每个单词都被唯一地编码)。然后,嵌入函数将这个V维空间转换为大小为D(这里,D = 4)的分布式表示。
这个想法是嵌入函数学习有关单词的语义信息。它将词汇表中的每个单词与一个连续值的向量表示相关联,即单词嵌入。每个词对应这个嵌入空间中的一个点,不同的维度对应这些词的语法或语义属性。
目标是确保嵌入空间中彼此接近的单词具有相似的含义。这样,语言模型就可以利用一些词在语义上相似的信息。例如,它可能会了解到fox和cat在语义上是相关的,并且 quick brown fox和quick brown cat都是有效的短语。然后可以用一系列嵌入向量替换一系列单词,这些嵌入向量捕获这些单词的特征。我们可以使用这个序列作为各种 NLP 任务的基础。例如,尝试对文章的情绪进行分类的分类器可能会使用先前学习的词嵌入而不是 one-hot 编码向量进行训练。通过这种方式,词的语义信息变得容易用于情感分类器。
词嵌入是解决 NLP 任务时的核心范式之一。我们可以使用它们来提高可能没有大量可用标记数据的其他任务的性能。接下来,我们将讨论 2001 年引入的第一个神经语言模型(作为一个例子,深度学习中的许多概念并不新鲜)。
我们通常用粗体非斜体小写字母表示向量,例如w。但是神经语言模型中的约定是使用斜体小写,例如w。在本章中,我们将使用这个约定。
在下一节中,我们将看看神经概率语言模型( NPLM )。
神经概率语言模型
可以通过前馈全连接网络学习语言模型和隐式嵌入函数。给定一个n-1 个单词的序列( w t-n+1 , ..., w t-1),它尝试输出下一个单词w t的概率分布(下图基于http:/ /www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf):
给定单词w t-n+1 ... w t-1 ,输出单词 w t的概率分布的神经网络语言模型。C是嵌入矩阵
网络层扮演不同的角色,例如:
- 嵌入层采用单词w i的 one-hot 表示,并通过将其与嵌入矩阵C相乘,将其转换为单词的嵌入向量。这种计算可以通过查表有效地实现。嵌入矩阵C在单词之间共享,因此所有单词都使用相同的嵌入函数。C是一个V * D矩阵,其中V是词汇表的大小,D是嵌入的大小。换句话说,矩阵C表示隐藏tanh层的网络权重。
- 生成的嵌入被连接起来并用作隐藏层的输入,隐藏层使用tanh激活。因此,隐藏层的输出由函数表示,其中H表示嵌入到隐藏层权重,d表示隐藏偏差。
- 最后,我们的输出层具有权重U、偏差、b和 softmax 激活,它们将隐藏层映射到词空间概率分布:
该模型同时学习词汇表中所有单词的嵌入(嵌入层)和单词序列的概率函数模型(网络输出)。它能够将此概率函数推广到训练期间未见过的单词序列。在训练集中可能看不到测试集中的特定单词组合,但在训练期间更有可能看到具有相似嵌入特征的序列。自从 我们可以根据单词的位置(已经存在于文本中)构建训练数据和标签,训练这个模型是一个无监督的学习任务。接下来,我们将讨论 word2vec 语言模型,该模型于 2013 年推出,引发了人们对神经网络背景下的 NLP 领域的兴趣。
Word2Vec
很多研究都致力于创建更好的词嵌入模型,特别是通过省略对词序列的概率函数的学习。最流行的方法之一是使用word2 vec ( http://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf和https: //arxiv.org/abs/1301.3781,https://arxiv.org/abs/1310.4546)。_ 与 NPLM 类似,word2vec 根据焦点词的上下文(周围词)创建嵌入向量。它有两种风格:连续词袋(CBOW)和Skip-gram。我们将从 CBOW 开始,然后我们将讨论 Skip-gram。
CBOW
CBOW 根据其上下文(周围的词)预测最可能的词。例如,给定序列The quick _____ fox jumps,模型将预测brown。上下文是焦点单词的前n个单词和后n 个单词(与 NPLM 不同,只有前面的单词参与)。以下屏幕截图显示了在文本中滑动的上下文窗口:
n = 2的 word2vec 滑动上下文窗口。相同类型的上下文窗口适用于 CBOW 和 Skip-gram
CBOW 以相同的权重获取上下文中的所有单词,并且不考虑它们的顺序(因此名称中的包)。它有点类似于 NPLM,但因为它只学习嵌入向量,我们将在以下简单神经网络的帮助下训练模型:
一个 CBOW 模型网络
以下是它的工作原理:
- 该网络具有输入层、隐藏层和输出层。
- 输入是 one-hot 编码的词表示。每个单词的 one-hot 编码向量大小等于词汇表的大小V。
- 嵌入向量由网络的输入到隐藏权重W V × D表示。它们是 V × D形矩阵,其中D是嵌入向量的长度(与隐藏层中的单元数相同)。与 NPLM 一样,我们可以将权重视为一个查找表,其中每一行代表一个词嵌入向量。因为每个输入词都是单热编码的,所以它总是会激活单行权重。也就是说,对于每个输入样本(单词),只有单词自己的嵌入向量会参与。
- 所有上下文词的嵌入向量被平均以产生隐藏网络层的输出(没有激活函数)。
- 隐藏激活用作大小为V的输出 softmax 层的输入(权重向量W ' D × V),它预测在输入词的上下文(接近度)中最有可能找到的词。具有最高激活的索引代表 one-hot 编码的相关词。
我们将使用梯度下降和反向传播来训练网络。训练集由(上下文和标签)单热编码的单词对组成,在文本中彼此非常接近。例如,如果部分文本是序列[the, quick, brown, fox, jumps]并且n = 2,则训练元组将包括de ([quick, brown], the), ([the, brown, fox], quick), ([the, quick, fox jumps], brown),等等。由于我们只对嵌入W V×D感兴趣,因此我们将在训练完成后丢弃其余的网络权重W ' V×D 。
CBOW 会告诉我们哪个词最有可能出现在给定的上下文中。这可能是罕见词的问题。例如,给定上下文今天的天气真的是____,模型将预测单词beautiful而不是fabulous(嘿,这只是一个例子)。CBOW 的训练速度比 Skip-gram 快几倍,并且对频繁出现的单词的准确度略高。
Skip-gram
给定一个输入词,Skip-gram 模型可以预测其上下文(与 CBOW 相反)。例如,单词brown将预测单词The quick fox jumps。与 CBOW 不同,输入是一个单热词。但是我们如何在输出中表示上下文词呢?Skip-gram 不是试图同时预测整个上下文(所有周围的单词),而是将上下文转换为多个训练对,例如(fox, the)、(fox, quick)、(fox, brown)和(fox, jumps)。再一次,我们可以用一个简单的单层网络训练模型:
Skip-gram 模型网络
与 CBOW 一样,输出是一个 softmax,它表示 one-hot 编码的最可能的上下文词。输入到隐藏的权重W V × D表示词嵌入查找表,隐藏到输出的权重W ' D × V仅在训练期间相关。隐藏层没有激活函数(即,它使用线性激活)。
我们将使用反向传播训练模型(这里没有惊喜)。给定一个单词序列w 1 , ..., w M,Skip-gram 模型的目标是最大化平均对数概率 w这里n是窗口大小:
该模型将概率 定义如下
:
在这个例子中,w I和w O是输入和输出的词,v w和v ' w分别是输入和输出权重W V × D和W ' D × V中对应的词向量(我们保留原来的论文的符号)。由于网络没有隐藏的激活函数,它的一个输入/输出词对的输出值只是输入词向量 和输出词向量的乘积
(因此是转置操作)
。
word2vec 论文的作者指出,单词表示不能表示不是单个单词组合的惯用短语。例如,New York Times是一份报纸,而不仅仅是New、 York和Times的含义的自然组合。为了克服这个问题,该模型可以扩展到包括整个短语。然而,这显着增加了词汇量。而且,从前面的公式可以看出,softmax 分母需要计算词汇表中所有单词的输出向量。此外,W ' D × V矩阵的每个权重都会在每个训练步骤中更新,这会减慢训练速度。
为了解决这个问题,我们可以用所谓的负采样( NEG ) 代替 softmax。对于每个训练样本,我们将采用正训练对(例如(fox, brown))以及k个额外的负训练对(例如(fox, puzzle)),其中k通常在 [5,20] 的范围内。我们不会预测与输入词(softmax)最匹配的词,而是简单地预测当前的词对是否为真。实际上,我们将多项分类问题(分类为多个类别之一)转换为二元逻辑回归(或二元分类)问题。通过学习正负对之间的区别,分类器最终将以与多项分类相同的方式学习词向量。在 word2vec 中,负对的词是从一个特殊的分布中提取的,与更频繁的词相比,它更频繁地绘制频率较低的词。
与稀有词相比,一些最频繁出现的词具有较少的信息价值。此类词的示例是定冠词和不定冠词a、an和the。与和城市相比,该模型将从伦敦和城市对中受益更多,因为几乎所有单词都经常与. 反之亦然——在对大量示例进行训练后,常用词的向量表示不会发生显着变化。为了解决稀有词和频繁词之间的不平衡,论文作者提出了一种下采样方法,其中每个词w i, 的训练集以一定的概率被丢弃,由启发式公式计算,其中f( wi )是单词w i的频率,t是阈值(通常在 10 -5左右):
它积极地对频率大于t的单词进行二次采样,但也保留了频率的排名。
总而言之,我们可以说,总的来说,与 CBOW 相比,Skip-gram 在稀有词上的表现更好,但训练时间更长。
fastText
fastText ( fastText ) 是Facebook AI Research ( FAIR ) 小组创建的用于学习词嵌入和文本分类的库。Word2Vec 将语料库中的每个单词视为一个原子实体,并为每个单词生成一个向量,但这种方法忽略了单词的内部结构。相比之下,fastText 将每个单词w分解为一个包含n 个字符的包。例如,如果n = 3 ,我们可以将那里的单词分解为字符 3-grams 和整个单词的特殊序列<there> :
<th , the , her , ere , re>
请注意使用特殊字符<和>来指示单词的开头和结尾。这对于避免来自不同单词的n- gram 之间的不匹配是必要的。例如,单词her将被表示为<her>并且它不会被误认为来自单词there的n- gram her。fastText 的作者建议3 ≤ n ≤ 6。
回想一下我们在Skip-gram部分介绍的 softmax 公式。让我们通过用通用评分函数s替换 word2vec 网络的向量乘法运算来概括它,其中w t是输入词,w c是上下文词:
在 fastText 的情况下,我们将用其n- gram 的向量表示的总和来表示一个单词。让我们用G w = {1 ... G}表示出现在单词w中的n -gram 集合,用v g表示n- gram g的向量表示,以及上下文词的潜在向量c ,与v'c 。 _ 那么,fastText定义的评分函数就变成了:
实际上,我们用 Skip-gram 类型的词对训练了 fastText 模型,但输入词被表示为一个n -gram 包。
与传统的 word2vec 模型相比,使用字符n -grams 有几个优点:
- 如果未知或拼写错误的单词与模型熟悉的其他单词共享n- gram,它可以对它们进行分类。
- 它可以为稀有词生成更好的词嵌入。即使一个词很少见,它的字符n- gram 仍然与其他词共享,因此嵌入仍然可以很好。
现在我们熟悉了 word2vec,我们将介绍 Global Vectors for Word Representation 语言模型,它可以改进一些 word2vec 的不足。
词表示模型的全局向量
word2vec 的一个缺点是它只使用单词的本地上下文,而不考虑它们的全局共现。这样,模型就失去了一个现成的、有价值的信息来源。顾名思义,单词表示的全局向量( GloVe ) 模型试图解决这个问题 ( https://nlp.stanford.edu/pubs/glove.pdf )。
该算法从全局词-词共现矩阵 X开始。单元格 X ij表示单词 j在单词 i的上下文中出现的频率 。下表显示了我喜欢 DL序列的大小为n = 2 的窗口的共现矩阵。我喜欢自然语言处理。我喜欢骑自行车:
一个我喜欢DL的序列的共现矩阵。我喜欢自然语言处理。我喜欢骑自行车
让我们表示任何单词在单词i with的上下文中出现的次数,以及单词j在单词i with的上下文中出现的概率。为了更好地理解这对我们有何帮助,我们将使用一个示例来显示目标词ice和steam与从 60 亿个标记语料库中选择的上下文词的共现概率:
目标词 ice 和 steam 与来自 60 亿令牌语料库的选定上下文词的共现概率:来源:https://nlp.stanford.edu/pubs/glove.pdf
底行显示概率的比率。solid一词(第一列)与ice相关,但与steam相关性较小,因此它们的概率之间的比率很大。相反,气体与蒸汽的关系比与冰的关系更大,它们的概率之间的比率非常小。水和时尚这两个词与两个目标词的相关性相同,因此概率之比接近于一。该比率更好地区分相关词(固体和气体)和不相关词(水和气体)。时尚),与原始概率相比。此外,它更擅长区分两个相关词。
根据前面的论点,GloVe 的作者建议从共现概率的比率开始词向量学习,而不是概率本身。有了这个起点,并记住比率
取决于三个词——i、j和k——我们可以定义 GloVe 模型的最一般形式如下,其中
是词向量,并且
是特殊的上下文向量,我们稍后将讨论(是实数的D维向量空间):
换句话说,F是这样一个函数,当用这三个特定向量计算时(我们假设我们已经知道它们),将输出概率比。此外,F应该对概率比的信息进行编码,因为我们已经确定了它的重要性。由于向量空间本质上是线性的,因此编码此信息的一种方法是使用两个目标词的向量差。因此,函数变为如下:
接下来,让我们注意函数参数是向量,但概率的比率是标量。为了解决这个问题,我们可以取参数的点积:
然后,让我们观察一个词和它的上下文词之间的区别是任意的,我们可以自由地交换这两个角色。因此,我们应该有
,但前面的等式不满足这个条件。长话短说(论文中有更详细的解释),为了满足这个条件,我们需要以下面等式的形式引入另一个限制,其中 和是偏置标量值:
这个公式的一个问题是log(0)是未定义的,但是X ik条目中的大多数将是0。此外,它采用相同权重的所有共现,但罕见的共现是嘈杂的,并且比更频繁的共现携带更少的信息。为了解决所有这些问题,作者提出了一个最小二乘回归模型,每个共现都有一个加权函数f(X ij ) 。该模型具有以下成本函数:
最后,加权函数f应该满足几个属性。首先,f(0) = 0。然后,f(x)应该是非递减的,这样罕见的共现就不会被过度加权。最后,对于较大的x值, f(x)应该相对较小,这样频繁的共现就不会被过度加权。基于这些特性和他们的实验,作者提出了以下功能:
下图显示了f(x):
权重函数f(X ij )的截止值为x max = 100 和α = 3/4。作者的实验表明,这些参数效果最好;资料来源:https://nlp.stanford.edu/pubs/glove.pdf
该模型生成两组词向量:W和。当X是对称的时,W和是等价的,并且仅由于它们的随机初始化而不同。但作者指出,训练一组网络并平均它们的结果通常有助于防止过度拟合。为了模仿这种行为,他们选择使用总和作为最终的词向量,观察到性能的小幅提升。
这结束了我们关于神经语言模型的讨论。在下一节中,我们将看到如何训练和可视化 word2vec 模型。
实现语言模型
在本节中,我们将实现一个简短的管道,用于预处理文本序列并使用处理后的数据训练 word2vec 模型。我们还将实现另一个示例来可视化嵌入向量并检查它们的一些有趣属性。
本节中的代码需要以下 Python 包:
- Gensim(3.80 版,https: //radimrehurek.com/gensim/ )是一个用于无监督主题建模和 NLP 的开源 Python 库。它支持我们目前讨论的所有三种模型(word2vec、GloVe 和 fastText)。
- 自然语言工具包(NLTK,https : //www.nltk.org/ ,版本 3.4.4)是用于符号和统计 NLP 的库和程序的 Python 套件。
- Scikit-learn (版本 0.19.1,https: //scikit-learn.org/ )是一个开源 Python ML 库,具有各种分类、回归和聚类算法。更具体地说,我们将使用 t-Distributed Stochastic Neighbor Embedding ( t-SNE , t-SNE – Laurens van der Maaten ) 来可视化高维嵌入向量(稍后会详细介绍)。
有了这个介绍,让我们继续训练语言模型。
训练嵌入模型
在第一个示例中,我们将根据列夫·托尔斯泰的经典小说《战争与和平》训练 word2vec 模型。小说作为常规文本文件存储在代码存储库中。开始吧:
1.按照传统,我们将进行导入:
import logging
import pprint # beautify prints
import gensim
import nltk
2.然后,我们将日志记录级别设置为,INFO以便我们可以跟踪训练进度:
logging.basicConfig(level=logging.INFO)
3.接下来,我们将实现文本标记化管道。标记化是指将文本序列分解成片段(或标记),例如单词、关键字、短语、符号和其他元素。标记可以是单个单词、短语,甚至是整个句子。我们将实现两级标记化;首先,我们将文本拆分为句子,然后将每个句子拆分为单个单词:
class TokenizedSentences:
"""Split text to sentences and tokenize them"""
def __init__(self, filename: str):
self.filename = filename
def __iter__(self):
with open(self.filename) as f:
corpus = f.read()
raw_sentences = nltk.tokenize.sent_tokenize(corpus)
for sentence in raw_sentences:
if len(sentence) > 0:
yield gensim.utils.simple_preprocess(sentence, min_len=2, max_len=15)
迭代器将TokenizedSentences小说所在的文本文件名作为参数。以下是其余部分的工作原理:
- 迭代从读取corpus变量中文件的全部内容开始。
- 在 NLTK函数raw_sentences的帮助下,原始文本被分成句子列表(变量)。nltk.tokenize.sent_tokenize(corpus)例如,它将返回一个['I like DL.', 'I like NLP.','I enjoy cycling.']用于输入 list 'I like DL. I like NLP. I enjoy cycling.'。
- 接下来,每个都用函数sentence进行预处理。gensim.utils.simple_preprocess(sentence, min_len=2, max_len=15)它将文档转换为小写标记列表,忽略太短或太长的标记。例如,'I like DL'句子将被标记到['like', 'dl']列表中。标点符号也被删除。生成的标记化句子作为最终结果。
4.然后,我们将实例化TokenizedSentences:
sentences = TokenizedSentences('war_and_peace.txt')
5.接下来,我们将实例化 Gensim 的 word2vec 训练模型:
model = gensim.models.word2vec. \
Word2Vec(sentences=sentences,
sg=1, # 0 for CBOW and 1 for Skip-gram
window=5, # the size of the context window
negative=5, # negative sampling word count
min_count=5, # minimal word occurrences to include
iter=5, # number of epochs
)
该模型sentences作为训练数据集。Word2Vec支持我们在本章中讨论过的模型的所有参数和变体。例如,您可以使用sg参数在 CBOW 或 Skip-gram 之间切换。您还可以设置上下文窗口大小、负采样计数、时期数和其他内容。您可以探索代码本身中的所有参数。
gensim.models.word2vec.Word2Vec或者,您可以通过替换来使用fastText 模型gensim.models.fasttext.FastText(它使用相同的输入参数)。
6.构造Word2Vec函数也启动训练。片刻之后(您不需要 GPU,因为训练数据集很小),生成的嵌入向量存储在model.wv对象中。一方面,它就像一个字典,您可以访问每个单词的向量,model.wv['WORD_GOES_HERE'],但是,它还支持一些其他有趣的功能。您可以使用该方法根据词向量的差异来衡量不同词之间的相似度model.wv.most_similar。首先,它将每个词向量转换为单位向量(长度为 1 的向量)。然后,它计算目标词的单位向量和所有其他词的单位向量之间的点积。两个向量之间的点积越高,它们就越相似。例如,pprint.pprint(model.wv.most_similar(positive='mother', topn=5))将输出与该单词最相似的五个单词'mother'及其点积:
[('sister', 0.9024157524108887),
('daughter', 0.8976515531539917),
('brother', 0.8965438008308411),
('father', 0.8935455679893494),
('husband', 0.8779271245002747)]
结果可以证明词向量正确地编码了词的含义。这个词'mother'在意义上确实与'sister'、'daughter'等相关。
我们还可以找到与目标词组合最相似的词。例如,model.wv.most_similar(positive=['woman', 'king'], topn=5)将取词向量的平均值,'woman'然后'king'它会找到与新平均值最相似的词向量:
[('heiress', 0.9176832437515259), ('admirable', 0.9104862213134766), ('honorable', 0.9047746658325195), ('creature', 0.9040032625198364), ('depraved', 0.9013445973396301)]
我们可以看到有些词是相关的 ( 'heiress'),但大多数不是 ( 'creature', 'admirable')。也许我们的训练数据集太小而无法捕获像这样的更复杂的关系。
可视化嵌入向量
为了获得更好的词向量,与训练嵌入模型部分中的词向量相比,我们将训练另一个 word2vec 模型。然而,这一次,我们将使用一个更大的语料库——text8数据集,它由来自维基百科的前 100,000,000 个字节的纯文本组成。该数据集包含在 Gensim 中,它被标记为一个长长的单词列表。有了这个,让我们开始吧:
1.像往常一样,进口是第一位的。我们还将日志记录设置INFO为更好的措施:
import logging
import pprint # beautify prints
import gensim.downloader as gensim_downloader
import matplotlib.pyplot as plt
import numpy as np
from gensim.models.word2vec import Word2Vec
from sklearn.manifold import TSNE
logging.basicConfig(level=logging.INFO)
2.接下来,我们将训练Word2vec模型。这一次,我们将使用 CBOW 进行更快的训练。我们将加载数据集gensim_downloader.load('text8'):
model = Word2Vec(
sentences=gensim_downloader.load('text8'), # download and load the text8 dataset
sg=0, size=100, window=5, negative=5, min_count=5, iter=5)
3.要查看这个模型是否更好,我们可以尝试找到与'woman'and最相似'king'但与 最不相似的单词'man'。理想情况下,其中一个词是'queen'. 我们可以用表达式来做到这一点pprint.pprint(model.wv.most_similar(positive=['woman', 'king'], negative=['man']))。输出如下:
[('queen', 0.6532326936721802), ('prince', 0.6139929294586182), ('empress', 0.6126195192337036), ('princess', 0.6075714230537415), ('elizabeth', 0.588543176651001), ('throne', 0.5846244692802429), ('daughter', 0.5667101144790649), ('son', 0.5659586191177368), ('isabella', 0.5611927509307861), ('scots', 0.5606790781021118)]
确实,最相似的词是'queen',但其余词也是相关的。
4.接下来,我们将借助收集到的词向量上的 t-SNE 可视化模型在 2D 图中显示这些词。t-SNE对二维或三维点上的每个高维嵌入向量进行建模,相似的对象在附近的点上建模,不同的对象在远处的点上建模,概率很高。我们将从几个开始target_words,然后我们将收集与每个目标词最相似的n 个词(及其向量)的集群。以下是执行此操作的代码:
target_words = ['mother', 'car', 'tree', 'science', 'building', 'elephant', 'green']
word_groups, embedding_groups = list(), list()
for word in target_words:
words = [w for w, _ in model.most_similar(word, topn=5)]
word_groups.append(words)
embedding_groups.append([model.wv[w] for w in words])
5.然后,我们将使用以下参数在收集的集群上训练一个 t-SNE 可视化模型:
- perplexity与匹配每个点的原始向量和缩减向量时考虑的最近邻居的数量松散相关。换句话说,它决定了算法是关注数据的局部属性还是全局属性。
- n_components=2指定输出向量维数。
- n_iter=5000是训练迭代的次数。
- init='pca'使用基于主成分分析( PCA ) 的初始化。
该模型将embedding_groups簇作为输入,并输出带有 2D 嵌入向量embeddings_2d的数组。以下是实现:
# Train the t-SNE algorithm
embedding_groups = np.array(embedding_groups)
m, n, vector_size = embedding_groups.shape
tsne_model = TSNE(perplexity=8, n_components=2, init='pca', n_iter=5000)
# generate 2d embeddings from the original 100d ones
embeddings_2d = tsne_model.fit_transform(embedding_groups.reshape(m * n, vector_size))
embeddings_2d = np.array(embeddings_2d).reshape(m, n, 2)
6.接下来,我们将展示新的 2D 嵌入。为此,我们将初始化绘图及其一些属性以获得更好的可见性:
# Plot the results
plt.figure(figsize=(16, 9))
# Different color and marker for each group of similar words
color_map = plt.get_cmap('Dark2')(np.linspace(0, 1, len(target_words)))
markers = ['o', 'v', 's', 'x', 'D', '*', '+']
7.然后,我们将遍历每个similar_words集群,并将其单词作为点显示在散点图上。我们将为每个集群使用一个唯一的标记。这些点将用相应的单词进行注释:
# Iterate over all groups
for label, similar_words, emb, color, marker in \
zip(target_words, word_groups, embeddings_2d, color_map, markers):
x, y = emb[:, 0], emb[:, 1]
# Plot the points of each word group
plt.scatter(x=x, y=y, c=color, label=label, marker=marker)
# Annotate each point with its corresponding caption
for word, w_x, w_y in zip(similar_words, x, y):
plt.annotate(word, xy=(w_x, w_y), xytext=(0, 15),
textcoords='offset points', ha='center', va='top', size=10)
8.最后,我们将显示绘图:
plt.legend()
plt.grid(True)
plt.show()
我们可以看到每个相关词簇是如何在 2D 图的一个封闭区域中分组的:
目标词及其最相似词簇的 t-SNE 可视化
该图再次证明获得的词向量包含词的相关信息。随着这个例子的结束,我们也结束了这一章。
概括
这是专门介绍 NLP 的第一章。恰当地,我们从当今大多数 NLP 算法的基本构建块开始——单词及其基于上下文的向量表示。我们从n -grams 和将单词表示为向量的需要开始。然后,我们讨论了 word2vec、fastText 和 GloVe 模型。最后,我们实现了一个简单的管道来训练嵌入模型,并使用 t-SNE 可视化词向量。