返回介绍

6.2 词向量嵌入

发布于 2024-02-05 23:12:36 字数 5856 浏览 0 评论 0 收藏 0

本节将实现一个能够学习词向量的模型。对于各种NLP任务,这是一种表示词的强大方式。词向量嵌入这个话题近年来颇受关注,因为最近提出的方法已经可以足够高效地处理大规模文本语料库。对于该任务,我们不打算使用RNN,但对于后续其他任务,我们都将依赖本节所介绍的内容和方法。如果你对词向量的概念以及像word2vec这样的工具非常熟悉,但对于自己实现相关算法不感兴趣,可以放心地跳过这一节。

为何要将词表示为向量?最简单的方式是将词送入一个独热编码(one-hot encoding)的学习系统,即表示为一个长度为词汇表长度的向量,除该词语对应位置的元素为1外,其余元素均为0。这种方法有两个问题:首先,对于实际应用,这种表示方法会导致向量的维数很高,因为在自然语言中有许多不同的词语;其次,独热编码表示无法刻画不同词语之间的语义关联(而这种关联是显然存在的)。

词语的独热编码表示

作为语义关联问题的一个解决方案,依据共生关系(co-occurrence)表示单词的思路由来已久。这种方法的基本思路是,遍历一个大规模文本语料库,针对每个单词,统计其在一定距离范围(例如5)内的周围词汇。然后,用附近词汇的规范化数量表示每个词语。这种方法背后的思想是在类似语境中使用的词语在语义上也是相似的。这样,便可运用PCA或类似的方法对出现向量(occurrence vector)降维,从而得到更稠密的表示。虽然这种方法具有很好的性能,但它要求我们追踪所有词汇的共生矩阵,即一个宽度和高度均为词汇表长度的方阵。

词语的分布式表示

在2013年,Mikolov等提出了一种依据上下文计算词表示的实用有效的方法,相应的文章是Mikolov、Tomas等的《Efficient estimation of word representations in vector space》(arXiv preprint arXiv:1301.3781(2013))。他们的skip-gram模型从随机表示开始,并拥有一个试图依据当前词语预测一个上下文词语的简单分类器。误差同时通过分类器权值和词的表示进行传播,我们需要对这两者进行调整以减少预测误差。研究发现,在大规模语料库上训练该模型可表示向量逼近压缩后的共生向量。下面利用TensorFlow实现skip-gram模型。

6.2.1 准备维基百科语料库

在探讨skip-gram模型的细节之前,需要准备数据集。在本例中,我们将使用英文维基百科转储文件。默认的转储文件包含所有页面的完整修订历史,但从当前页面版本中,我们已经能够获取到约100GB的充足数据。本练习对其他语言同样适用,可从维基百科下载站点https://dumps.wikimedia.org/backup-index.html获取所有可用转储文件的概况。

为了以正确的格式表示数据,还需执行若干步骤。正如本书前文所讲的,数据收集和清洗是非常迫切和重要的任务。最终,我们决定遍历表示为独热编码词语的维基页面。为此,需要完成下列步骤:

1)下载转储文件,提取页面及其中的词语。

2)统计词语的出现次数,构建一个由最常见词语构成的词汇表。

3)利用该词汇表对提取的页面进行编码。

要将整个语料库一次性放入主存是非常困难的,因此通过逐行读取文件,并立即将结果写入磁盘的方式对数据流执行这些操作。按照这种方式,在不同步骤之间保存了检查点,以避免程序崩溃时不得不重新开始。现在利用下面的类来实现维基百科数据的处理。在__init__()中,可利用文件存在性检查理解检查点逻辑。

你可能已经注意到,我们仍然必须为该类实现两个重要的函数。第一个是_read_pages(),它的功能是下载维基百科转储文件——一个经过压缩的XML文件,并遍历各页面,从中提取移除格式后的纯文本。为了读取压缩的转储文件,需要使用bz2模块,它提供了一个open()函数,这个函数的工作方式与其标准版本类似,但更关注压缩和解压缩,即使以流式传输文件也是如此。为节省磁盘空间,对于中间结果也应做压缩处理。用于提取词语的正则表达式仅捕捉任意的连续字母序列以及一些单独出现的特殊字母。

在进行独热编码时,需要一个词汇表。之后,便可按照每个词在词汇表中的索引对其进行编码。为了将一些拼写错误或极不常见的词语移除,词汇表仅包含vocabulary_size–1个最常见的词语及一个用于标识所有不在词汇表中的词语的<unk>标记。这个标记也为我们提供了一个可用于未出现的单词的词向量。

由于提取了纯文本,并为各单词定义了编码,因此可动态地形成训练样本。这是非常有利的,因为要保存这些样本需要大量存储空间。由于大部分时间都用于训练,所以这不会对性能造成太大影响。我们也希望将生成的样本组织到一些批数据中,以使训练更加高效。借助这个模型,能够使用大的批数据,因为分类器并不需要占用大量内存。

那么如何形成训练样本?前文曾介绍过,skip-gram模型会依据当前词语预测上下文词语。在遍历文本时,我们用当前词语作为数据,其周围的词语作为目标创建训练样本。当上下文尺寸为R=5时,则可从每个单词生成2R=10个训练样本,其中R个词来自当前单词的左边,而将右边的R个单词作为目标值。然而,可能有人认为对于语义上下文,距离较近的近邻比较远的近邻更为重要。因此,可以为每个单词从范围[1,D=10]中随机选择一个上下文尺寸,尽量少地创建具有较远上下文词语的训练样本。

6.2.2 模型结构

至此,维基百科语料库已准备完毕,下面定义计算词向量的模型。

初始时,每个单词都被表示为一个随机向量。依据这种单词的中层表示,一个分类器会试图预测它的上下文单词之一的当前表示,然后我们对误差进行传播,以对权值和输入单词的表示进行微调。因此,这里将tf.Variable用于词的表示。

这里使用了MomentumOptimizer进行模型的优化,虽然不够智能,但效率却非常高。该优化器能够很好地处理大规模维基百科语料库,从而实现skip-gram背后的思想,能够比那些智能的算法利用更多的数据。

现在,我们的模型唯一缺少的部分是分类器。这是成功的skip-gram模型的核心,下面来研究它的工作原理。

6.2.3 噪声对比分类器

对于skip-gram模型,有多种代价函数可选,其中有一种噪声对比估计损失(noise-contrastive estimation loss)已被证明具有优异的性能。理想情况下,我们希望预测结果与目标尽可能地接近,而且与那些不是当前单词的目标词汇具有较远的距离。可以用softmax分类器对此进行建模,但并不希望每次都计算和训练词汇表中所有单词的输出。可考虑的解决方案是总使用一些新的随机向量作为负样本,也称为对比样本。经过足够的训练迭代,这种方法可以近似softmax分类器,而且仅需要十几个类别。TensorFlow为此提供了一个便捷的函数tf.nn.nce_loss。

6.2.4 训练模型

现在,语料库已准备好,模型的定义也完成了。下面给出整合所有功能的代码。训练结束后,可将最终的词向量写入另一个文件。下面的例子仅使用了维基百科语料库的一个子集,它在普通CPU上训练时长约5个小时。要使用完整的语料库,可从下列链接获取:https://dumps.wikimedia.org/enwiki/20160501/enwiki-20160501-pages-meta-current.xml.bz2。

如你所见,我们利用了AttrDict类,它等价于Python的dict,两者的区别在于,前者可将键作为属性访问,如params.batch_size。更多细节请参考第8章。

经过约5个小时的训练,我们将得到由学习获得的、作为NumPy数组被保存的嵌入表示。虽然在后续章节中将使用该嵌入表示,实际上,如果不希望亲自完成这些计算,完全可以。笔者为你提供了可在线获取的预训练词向量嵌入模型,并会在后文中需要使用该模型的地方给出下载链接。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文