真实世界的自然语言处理(二)(3)

本文涉及的产品
NLP自然语言处理_高级版,每接口累计50万次
文档翻译,文档翻译 1千页
NLP自然语言处理_基础版,每接口每天50万次
简介: 真实世界的自然语言处理(二)

真实世界的自然语言处理(二)(2)https://developer.aliyun.com/article/1519769

6.4.2 解码器

同样,Seq2Seq 模型的解码器与我们在第五章中介绍的语言模型类似。实际上,它们除了一个关键的区别外完全相同——解码器从编码器那里获取输入。我们在第五章中介绍的语言模型称为无条件语言模型,因为它们在没有任何输入或前提条件的情况下生成语言。另一方面,根据某些输入(条件)生成语言的语言模型称为条件语言模型。Seq2Seq 解码器是一种条件语言模型,其中条件是编码器生成的句子表示。请参见图 6.12,了解 Seq2Seq 解码器的工作原理的示例。


图 6.12 Seq2Seq 模型的解码器

就像语言模型一样,Seq2Seq 解码器从左到右生成文本。与编码器一样,您可以使用 RNN 来实现这一点。解码器也可以是多层 RNN。然而,解码器不能是双向的——你不能从两边生成一个句子。正如第五章中提到的那样,对过去生成的序列进行操作的模型被称为自回归模型

非自回归模型

如果你认为简单地从左到右生成文本太过受限制,那么你有道理。人类也不总是线性地写语言——我们经常在之后修订、添加和删除单词和短语。此外,线性地生成文本并不是很高效。句子的后半部分需要等待直到它的前半部分完成,这使得并行化生成过程非常困难。截至本文撰写时,研究人员正在大力开发非自回归的机器翻译模型,这些模型不会以线性方式生成目标句子(例如,请参阅 Salesforce Research 的这篇论文:arxiv.org/abs/1711.02281)。然而,它们在翻译质量上还没有超过自回归模型,大多数研究和生产的机器翻译系统仍然采用自回归模型。

解码器在训练阶段和预测阶段的行为略有不同。让我们先看看它是如何训练的。在训练阶段,我们确切地知道源句应该被翻译成目标句。换句话说,我们确切地知道解码器应该逐词生成什么。因此,解码器的训练方式与顺序标记模型的训练方式相似(参见第五章)。

首先,解码器被喂入由编码器产生的句子表示和一个特殊标记,该标记表示句子的开始。 第一个 RNN 单元处理这两个输入并产生第一个隐藏状态。 隐藏状态向量被馈送到一个线性层,该层收缩或扩展此向量以匹配词汇表的大小。 然后得到的向量通过 softmax,将其转换为概率分布。 此分布规定了词汇表中每个单词在接下来出现的可能性。

然后,这就是训练发生的地方。 如果输入是“Maria no daba una bofetada a la bruja verde”,那么我们希望解码器生成其英文等效句子:“Mary did not slap the green witch.” 这意味着我们希望最大化第一个 RNN 单元生成“Mary”的概率,给定输入句子。 这是本书中我们在很多地方见过的一个多类别分类问题——词嵌入(第三章),句子分类(第四章)和序列标记(第五章)。 您使用交叉熵损失来衡量期望结果与网络实际输出之间的差距有多远。 如果“Mary”的概率很大,那么好——网络会产生较小的损失。 另一方面,如果“Mary”的概率很小,则网络会产生较大的损失,这会鼓励优化算法大幅更改参数(魔法常量)。

然后,我们移动到下一个单元。 下一个单元接收由第一个单元计算的隐藏状态和单词“Mary”,不管第一个单元生成了什么。 与使用语言模型生成文本时喂入先前单元生成的标记不同,我们约束解码器的输入,以防止其“偏离”。 第二个单元基于这两个输入产生隐藏状态,然后用于计算第二个单词的概率分布。 我们通过将分布与期望输出“did”进行比较来计算交叉熵损失,并继续移动到下一个单元。 我们一直这样做,直到达到最终标记,即。 句子的总损失是句子中所有单词产生的所有损失的平均值,如图 6.13 所示。


图 6.13 训练 Seq2Seq 解码器

最后,以这种方式计算的损失用于调整解码器的模型参数,以便下一次它能生成期望的输出。 请注意,在此过程中也会调整编码器的参数,因为损失通过句子表示一直传播回编码器。 如果编码器产生的句子表示不好,那么解码器无论如何努力,都无法生成高质量的目标句子。

6.4.3 贪婪解码

现在让我们看看解码器在预测阶段的行为,其中给定了一个源句子给网络,但我们不知道正确的翻译应该是什么。在这个阶段,解码器的行为很像我们在第五章讨论过的语言模型。它被提供了由编码器产生的句子表示,以及一个特殊的标记,表示句子的开头。第一个循环神经网络单元处理这两个输入并产生第一个隐藏状态,然后将其馈送到线性层和 softmax 层,以产生目标词汇的概率分布。关键部分来了——与训练阶段不同,你不知道接下来应该出现的正确单词,所以你有多个选项。你可以选择任何一个具有相当高概率的随机单词(比如“dog”),但最好的选择可能是选择概率最高的单词(如果是“Mary”那就太幸运了)。机器翻译系统生成刚刚选择的单词,然后将其馈送到下一个循环神经网络单元。这个过程重复进行,直到遇到特殊标记。图 6.14 说明了这个过程。


图 6.14 使用 Seq2Seq 解码器进行预测

好的,我们都准备好了吗?我们可以继续评估我们的机器翻译系统了吗,因为它正在尽其所能产生最佳的翻译?不要那么快——在这种方式解码目标句子时可能会出现许多问题。

首先,机器翻译解码的目标是最大化整个目标句子的概率,而不仅仅是单个单词。这正是你训练网络要做的事情——为正确的句子产生最大的概率。然而,前面描述的每一步选择单词的方式是为了最大化该单词的概率。换句话说,这种解码过程只保证了局部最大概率。这种短视、局部最优的算法在计算机科学中被称为贪婪,我刚刚解释的解码算法被称为贪婪解码。然而,仅仅因为你在每一步都在最大化单词的概率并不意味着你在最大化整个句子的概率。一般来说,贪婪算法不能保证产生全局最优解,而使用贪婪解码可能会让你陷入次优翻译的困境。这并不是很直观,所以让我用一个简单的例子来说明这一点。

当你在每个时间步选择单词时,你有多个单词可以选择。你选择其中一个然后移动到下一个循环神经网络单元,它会产生另一组可能选择的单词,这取决于你之前选择的单词。这可以用一个树状结构来表示,就像图 6.15 所示的那样。该图显示了你在一个时间步选择的单词(例如“did”)如何分支到下一个时间步可以选择的一组可能单词(“you”和“not”)。


图 6.15 解码决策树

每个单词到单词的转换都加上了一个分数,该分数对应于选择该转换的概率有多大。你的目标是在从时间步 1 到 4 遍历一条路径时最大化得分的总和。在数学上,概率是 0 到 1 之间的实数,并且你应该将(而不是相加)每个概率相乘以获得总数,但我在这里简化了问题。例如,如果你从“Mary”到“did”,然后到“you”和“do”,你刚生成了一个句子“Mary did you do”,总分是 1 + 5 + 1 = 7。

在之前看到的贪婪解码器生成时间步 2 的“did”后,它将面临两个选择:用 5 分数生成“you”或用 3 分数生成“not”。因为它只是选择得分最高的那个,它会选择“you”并继续前进。然后在时间步 3 之后,它将面临另一个分支——用 1 分数生成“do”或用 2 分数生成“know”。同样,它将选择最大的分数,这样你就会得到“Mary did you know”的翻译,其分数为 1+ 5 + 1 = 8。

这并不是一个坏结果。至少,它不像第一条路径一样糟糕,它的总得分为 7。通过在每个分支上选择最大分数,你确保你的最终结果至少是像样的。然而,如果你在时间步 3 选择了“not”呢?乍一看,这似乎不是个好主意,因为你得到的分数只有 3,比你走另一条路径的 5 小。但在下一个时间步,通过生成“slap”,你得到了 5 分的分数。回顾起来,这是正确的决定——总体而言,你得到了 1 + 3 + 5 = 9 分,这比沿着另一个“you”路径得到的分数要高。通过牺牲短期回报,你能够在长期获得更大的回报。但是由于贪婪解码器的近视性质,它永远不会选择这条路径——它无法回溯并改变其心意,一旦选择了一条而不是另一条路径。

如果看一下图 6.15 中的玩具示例,选择哪个方向以最大化总分数似乎很容易,但在现实中,你不能“预见”未来——如果你处于时间步 t,你无法预测在时间步 t + 1 及以后会发生什么,直到你实际选择一个单词并将其馈送到 RNN 中为止。但是,最大化单个概率的路径不一定是最优解。你无法尝试每个可能的路径并查看你得到的分数,因为词汇表通常包含成千上万个独特的单词,这意味着可能的路径数呈指数增长。

令人沮丧的事实是,您无法合理地期望在短时间内找到最大化整个句子概率的最优解路径。但是你可以避免陷入困境(或至少减少陷入困境的可能性),这就是梁搜索解码器的作用。

6.4.4 梁搜索解码

让我们想象如果你处于同样的情境下应该怎么做。假设你是一名大学大二的学生,在本学年结束前,你需要决定选择哪个专业。你的目标是在你一生中最大化收入(或幸福或其他你关心的东西),但你不知道哪个专业对于这一目标来说是最好的。你不能尝试每个可能的专业并观察几年后的结果——专业太多,你也无法回到过去。并且仅仅因为有些专业在短期内看起来很有吸引力(例如选择经济学专业可能会带来在大型投资银行的好实习机会),并不意味着这条道路在长期来看是最好的(请看 2008 年发生了什么)。

在这种情况下,你可以做的一件事是通过同时选择多个专业(双专业或辅修)而不是 100%致力于特定的专业,来进行投机。几年后,如果情况与你想象的不同,仍然可以更改主意并追求另一种选择,如果你贪婪地选择专业(即只考虑短期前景),则这是不可能的。

梁搜索解码的主要思想类似于这个——不是只选择一条路径,而是同时追求多条路径(称为假设)。这样,你就为“黑马”留下了一些空间,也就是那些在最初得分不高但可能后来表现出色的假设。让我们使用图 6.16 中的示例,这是图 6.15 的略微修改版本。

梁搜索解码的关键思想是使用(图 6.16 底部),可以将其看作是一种缓冲区,可以同时保留多个假设。梁的大小,即它可以保持的假设数,称为梁宽度。让我们使用大小为 2 的梁并看看会发生什么。最初,你的第一个假设只包含一个词“Mary”,得分为 0。当你转向下一个单词时,你选择的单词被附加到假设中,并且得分增加了你刚刚走过的路径的得分。例如,当你转到“did”时,它会生成一个新的假设,包含“Mary did”和得分 1。


图 6.16 梁搜索解码

如果在任何特定时间步有多个词可供选择,假设可能会产生多个子假设。在时间步 2,你有三个不同的选择——“你”,“不”和“n’t”——这会生成三个新的子假设:[Mary did you] (6),[Mary did not] (4) 和 [Mary did n’t] (3)。这就是束搜索解码的关键部分:因为束中只有有限的空间,任何不够好的假设在按分数排序后都会从束中掉下来。因为在这个例子中束只能容纳两个假设,除了前两个以外的任何东西都会被挤出束外,这就留下了[Mary did you] (6)和[Mary did not] (4)。

在时间步 3,每个剩余的假设可以产生多达两个子假设。第一个([Mary did you] (6))将生成[Mary did you know] (8)和[Mary did you do] (7),而第二个([Mary did not] (4))会变成[Mary did not slap] (9)。这三个假设按分数排序,最好的两个将作为束搜索解码的结果返回。

恭喜——现在你的算法能够找到最大化总分数的路径。通过同时考虑多个假设,束搜索解码可以增加你找到更好解决方案的机会。然而,它永远不是完美的——注意,一个同样好的路径[Mary did n’t do],得分为 9,在时间步 3 就从束中掉出来了。要“拯救”它,你需要增加束宽度到 3 或更大。一般来说,束宽度越大,翻译结果的期望质量就越高。然而,这是有一个折衷的:因为计算机需要考虑多个假设,随着束宽度的增加,它会线性变慢。

在 Fairseq 中,你可以使用 —beam 选项来改变束大小。在第 6.3.3 节的示例中,我使用了 —beam 5 来使用束宽度为 5。你已经在不知不觉中使用了束搜索。如果你使用相同的命令调用 —beam 1,这意味着你使用的是贪婪解码而不是束搜索,你可能会得到略有不同的结果。当我尝试这样做时,我得到的结果几乎相同,除了最后一个:“counts, please”,这不是“La cuenta, por favor.” 的一个很好的翻译。这意味着使用束搜索确实有助于提高翻译质量!

6.5 评估翻译系统

在这一节中,我想简要谈谈评估机器翻译系统的话题。准确评估机器翻译系统是一个重要的话题,无论在理论上还是在实践中。

6.5.1 人工评估

评估机器翻译系统输出的最简单、最准确的方法是使用人工评估。毕竟,语言是为人类翻译的。被人类认为好的翻译应该是好的。

正如之前提到的,我们对好的翻译有一些考虑因素。对于这两个方面最重要且常用的概念是充分性(也称为忠实度)和流畅性(也与可理解性密切相关)。充分性是源句子中的信息在翻译中反映的程度。如果通过阅读它的翻译,你可以重构源句子表达的大量信息,那么该翻译具有很高的充分性。流畅性则是翻译在目标语言中的自然程度。例如,如果你正在翻译为英语,“Mary did not slap the green witch”是一种流畅的翻译,而“Mary no had a hit with witch, green”则不是,尽管这两种翻译几乎是同样充分的。请注意,这两个方面在某种程度上是独立的-你可以想象一种流畅但不充分的翻译(例如,“Mary saw a witch in the forest”是一种完全流畅但不充分的翻译),反之亦然,就像之前的例子一样。能够产生既充分又流畅输出的 MT 系统是有价值的。

MT 系统通常通过将其翻译呈现给人类注释器并要求他们对其每个方面进行 5 或 7 点的评价来进行评估。流畅性更容易判断,因为它只需要目标句子的单语种听众,而充分性需要源语言和目标语言的双语种人员。

6.5.2 自动评估

尽管人工评估给出了 MT 系统质量的最准确评估,但并不总是可行的。在大多数情况下,你可能无法负担雇用人类评估员以在你需要时评估 MT 系统的输出。如果你处理的是不常见的语言对,你可能根本找不到双语的说话者来评估其充分性。

但更重要的是,在开发 MT 系统时需要不断地评估和监测其质量。例如,如果你使用 Seq2Seq 模型去训练一个 NMT 系统,你需要每次调整一个超参数就重新评估其性能。否则,你就不知道你的更改对它的最终性能是否有好或坏的影响。更糟糕的是,如果你要做像“early stopping”(见第 6.3.2 节)这样的事情来决定何时停止训练过程,你需要在每个周期之后评估它的性能。你不可能雇用人来在每个周期评估你的中间模型-这将是开发 MT 系统的一个可怕的缓慢的方法。这也是浪费时间,因为最初模型的输出在很大程度上是垃圾,不值得人类评估。中间模型的输出之间存在大量的相关性,人类评估员将花费大量的时间评估非常相似甚至相同的句子。

因此,如果我们能够使用某种自动方式来评估翻译质量,将是可取的。这种工作方式类似于我们之前看到的其他 NLP 任务的一些自动度量,例如分类的准确度、精确度、召回率和 F1 度量。其思想是提前为每个输入实例创建期望的输出,并将系统的输出与之进行比较。通常,这是通过为每个源句准备一组人为创建的翻译(称为参考)并计算参考和系统输出之间的某种相似度来完成的。一旦你创建了参考并定义了指标,你就可以根据需要自动评估翻译质量多少次。

计算参考和系统输出之间相似度的最简单方法之一是使用单词错误率(WER)。WER 反映系统相对于参考的错误数量,通过插入、删除和替换的相对数量来衡量。该概念类似于编辑距离,不同之处在于 WER 是针对单词而不是字符计数的。例如,当参考句子是“玛丽没有打绿色的女巫”,系统翻译为“玛丽打了绿色的邪恶女巫”时,你需要进行三次“编辑”才能将后者与前者匹配——插入“没有”,用“打”替换“击中”,删除“邪恶”。如果你将三除以参考长度(= 7),就是你的 WER(= 3/7,或 0.43)。WER 越低,翻译质量越好。

尽管 WER 简单易用,但在评估机器翻译系统时如今并不常用。一个原因与多个参考有关。对于单个源句可能有多个同样有效的翻译,但是当存在多个参考时如何应用 WER 并不清楚。对于机器翻译中自动评估最常用的稍微复杂一点的指标是 BLEU(双语评估学习)。BLEU 通过使用修改后的精确度来解决多个参考的问题。接下来我将用一个简单的例子来说明这一点。

在以下表格中,我们评估一个候选人(系统的输出)“the the the the the the the”(顺便说一句,这是一个糟糕的翻译)与两个参考:“猫在地毯上”和“地毯上有只猫”。BLEU 的基本思想是计算候选中所有唯一单词的精度。因为候选中只有一个唯一单词“the”,如果计算其精度,它将自动成为候选的分数,即为 1,或 100%。但这似乎有些不对。

候选 the the the the the the the
参考 1 地毯
参考 2 那里 一只 地毯

因为参考译文中只有两个“the”,系统生成的虚假“the”不应该计入精度。换句话说,我们应该将它们视为误报。我们可以通过将精度的分母限制为参考译文中该词的最大出现次数来做到这一点。因为在这种情况下(在参考译文 1 中)是 2,其修改后的精度将为 2/7,约为 29%。在实践中,BLEU 不仅使用唯一的词(即一元词),还使用候选译文和参考译文中长度不超过 4 的所有唯一单词序列(n 元词)。

然而,我们可以通过另一种方式操纵这个指标——因为它基于精度而不是召回率,一个机器翻译系统可以通过产生很少的系统确信的词语来轻松获得高分。在前面的例子中,你只需简单地产生“cat”(甚至更简单地,“the”),BLEU 分数将达到 100%,这显然不是一个好的翻译。BLEU 通过引入简洁惩罚来解决这个问题,如果候选翻译比参考译文短,就会打折扣。

精确自动评估指标的开发是一个活跃的研究领域。许多新的指标被提出并用于解决 BLEU 的缺点。我们在这一部分只是浅尝辄止。虽然新的指标显示与人类评估的相关性更高,并声称更好,但 BLEU 仍然是目前最广泛使用的指标,主要是因为其简单性和悠久的传统。

6.6 案例研究:构建聊天机器人

在本节中,我将讨论 Seq2Seq 模型的另一个应用——聊天机器人,这是一种 NLP 应用,你可以与之进行对话。我们将使用 Seq2Seq 模型构建一个非常简单但功能齐全的聊天机器人,并讨论构建智能代理的技术和挑战。

6.6.1 引入对话系统

我在第 1.2.1 节简要介绍了对话系统。简而言之,主要有两种类型的对话系统:面向任务和聊天机器人。尽管面向任务的对话系统用于实现一些特定目标,例如在餐厅预订和获取一些信息,但聊天机器人用于与人类进行对话。由于商业对话人工智能系统如亚马逊 Alexa、苹果 Siri 和谷歌助手的成功和大量普及,对话技术目前是自然语言处理从业者的热门话题。

您可能不知道如何开始构建可以进行会话交流的自然语言处理应用程序。我们该如何构建一个“智能”的东西来“思考”,以便它能为人类输入生成有意义的响应?这似乎有些遥远和困难。但是,如果您退后一步,看看我们与其他人的典型对话,有多少实际上是“智能”的呢?如果您像我们大多数人一样,那么您正在进行的会话中有很大一部分都是自动驾驶的:“你好吗?”“我没事,谢谢”“祝你有个愉快的一天”“你也是!”等等。您可能还有一组“模板”回复,对于许多日常问题,例如“你在干什么?”和“你来自哪里?”这些问题可以通过查看输入来回答。甚至更复杂的问题,如“X 中你最喜欢的餐厅是什么?”(其中 X 是您城市的一个地区的名称)和“你最近看过任何 Y 类型的电影吗?”(其中 Y 是一种类型),都可以通过“模式匹配”并从记忆中检索相关信息来回答。

如果您将对话视为一组“回合”,其中响应是通过模式匹配来生成的,那么这看起来与典型的自然语言处理问题非常相似。特别是,如果您认为对话是一个问题,其中 NLP 系统只是将您的问题“翻译”为其响应,那么这正是我们可以使用本章迄今涵盖的 Seq2Seq 模型的地方。我们可以将之前(人类的)话语视为外国语句子,并将聊天机器人“翻译”成另一种语言。尽管在这种情况下,这两种语言都是英语,但是在 NLP 中,通常将输入和输出视为两种不同的语言,并将 Seq2Seq 模型应用于它们,包括摘要(将更长的文本缩短)和语法纠错(将有错误的文本纠正为无错误的文本)。

6.6.2 准备数据集

在本案例中,我们将使用自我对话语料库(github.com/jfainberg/self_dialogue_corpus),其中包含 24,165 个对话。这个数据集的特殊之处在于,这些对话并不是两个人之间的实际对话,而是由一人扮演双方所写的虚构对话。虽然还有其他几个基于文本的聊天机器人的对话数据集(例如 OpenSubtitles 数据集,opus.nlpl.eu/OpenSubtitles-v2018.php),但这些数据集通常存在噪声并且常常包含粗言秽语。相比之下,通过收集编造的对话,自我对话语料库在仅有一人的情况下提高了一半的质量(因为你只需要一个人而不是两个人!)。

与之前相同,我对语料进行了分词和转换,使其可被 Fairseq 解读。您可以按以下方式获取转换后的数据集:

$ mkdir -p data/chatbot
$ wget https://realworldnlpbook.s3.amazonaws.com/data/chatbot/selfdialog.zip
$ unzip selfdialog.zip -d data/chatbot

您可以使用以下 paste 命令的组合(以水平方式拼接文件)和 head 命令来查看训练部分的开头。请注意,我们使用 fr(表示“外语”,而不是“法语”)来表示我们正在从中翻译的“语言”:

$ paste data/chatbot/selfdialog.train.tok.fr data/chatbot/selfdialog.train.tok.en | head 
...
Have you played in a band ?    What type of band ?
What type of band ?    A rock and roll band .
A rock and roll band .    Sure , I played in one for years .
Sure , I played in one for years .    No kidding ?
No kidding ?    I played in rock love love .
I played in rock love love .    You played local ?
You played local ?    Yes
Yes    Would you play again ?
Would you play again ?    Why ?
...

如您所见,每一行都包含一个话语(在左侧)和对其的回应(在右侧)。请注意,此数据集与我们在第 6.3.1 节中使用的西班牙语-英语平行语料库具有相同的结构。下一步是运行 fairseq-preprocess 命令将其转换为二进制格式,如下所示:

$ fairseq-preprocess \
    --source-lang fr \
    --target-lang en \
    --trainpref data/chatbot/selfdialog.train.tok \
    --validpref data/chatbot/selfdialog.valid.tok \
    --destdir data/chatbot-bin \
    --thresholdsrc 3 \
    --thresholdtgt 3

再次,这与我们为西班牙语翻译器示例运行的内容类似。只需注意您指定的源语言—我们在这里使用的是 fr 而不是 es。

6.6.3 训练和运行聊天机器人

现在,聊天机器人的训练数据准备好了,让我们从这些数据中训练一个 Seq2Seq 模型。您可以像下面这样调用 fairseq-train 命令,几乎使用与上次相同的参数:

$ fairseq-train \
    data/chatbot-bin \
    --arch lstm \
    --share-decoder-input-output-embed \
    --optimizer adam \
    --lr 1.0e-3 \
    --max-tokens 4096 \
    --save-dir data/chatbot-ckpt

与先前一样,请注意验证损失随每个时代的变化。当我尝试时,验证损失在约五个时代内减小,然后开始缓慢上升。在观察到验证损失趋于稳定后,随时按 Ctrl + C 停止训练命令。Fairseq 将自动将最佳模型(按验证损失测量)保存为 checkpoint_best.pt。

最后,您可以通过调用 fairseq-interactive 命令来运行聊天机器人模型,如下所示:

$ fairseq-interactive \
    data/chatbot-bin \
    --path data/chatbot-ckpt/checkpoint_best.pt \
    --beam 5 \
    --source-lang fr \
    --target-lang en

与前面的示例一样,您可以输入源句并与聊天机器人对话,让它将它们“翻译”为另一种语言!以下是我与我训练的模型进行的一部分对话(为了清晰起见,我添加了粗体)。再次强调,您的结果可能不同:

Hi !
S-0     Hi !
H-0     -1.011711835861206      Hey .
P-0     -0.6860 -2.1352 -0.2140
How are you ?
S-1     How are you ?
H-1     -0.6552960276603699     Doing good , you ?
P-1     -0.6472 -0.8651 -1.0960 -0.9257 -0.3337 -0.0640
Good .
S-2     Good .
H-2     -0.8540937900543213     Do you have a favorite team ?
P-2     -2.3027 -0.0550 -1.7088 -0.4403 -0.1763 -2.0262 -0.1150 -0.0085
Yeah I like the Seahawks .
S-3     Yeah I like the Seahawks .
H-3     -0.6036218404769897     Do you have a favorite team ?
P-3     -2.7614 -0.0525 -1.0905 -0.3178 -0.3213 -0.1049 -0.1701 -0.0106
How about you ?
S-4     How about you ?
H-4     -1.2329542636871338     I 'm a big fan of them .
P-4     -0.7648 -1.8435 -1.6808 -1.2868 -0.2102 -0.1362 -3.5273 -1.0031 -0.6438
What 's your favorite movie ?
S-5     What 's your favorite movie ?
H-5     -0.8238655924797058     Oh , that 's a tough one . I 'd have to say Ghost Busters .
P-5     -3.2622 -0.3213 -1.4815 -0.1012 -0.3417 -0.3069 -0.2432 -0.6954 -1.0858 -2.3246 -0.4296 -0.0039 -0.0905 -2.6429 -0.1676 -0.3853 -0.1221
Oh yeah , I like that movie too .
S-6     Oh yeah , I like that movie too .
H-6     -1.0659444332122803     Yeah , that 's a good one .
P-6     -2.0782 -0.1724 -2.5089 -0.9560 -1.5034 -0.9040 -0.4471 -0.5435 -0.4801

在这个例子中,对话看起来很自然。因为自我对话语料库是通过限制可能的对话主题集来构建的,所以如果您保持在这些主题(电影、体育、音乐等)上,对话更有可能顺利进行。

然而,一旦您开始谈论不熟悉的话题,聊天机器人就会对其答案失去信心,如下所示:

What 's your name ?
S-0     What 's your name ?
H-0     -0.9807574152946472 I do n't know , but I do n't think I 've                           heard of them .
P-0     -1.4680 -2.2938 -0.0635 -1.0242 -1.2039 -0.5201 -0.3381 -2.2612 -0.1665 -1.6033 -0.6112 -1.5515 -0.8739 -0.8453 -1.0143 -0.4702 -0.3637
What do you do ?
S-1     What do you do ?
H-1     -1.170507788658142 I do n't know .
P-1     -0.9807 -2.1846 -0.3276 -0.9733 -1.3650 -1.1919
Are you a student ?
S-2     Are you a student ?
H-2     -0.9505285024642944 I 'm not sure .
P-2     -1.5676 -1.5270 -0.6944 -0.2493 -0.8445 -0.8204

这是一个众所周知的现象—一个简单的基于 Seq2Seq 的聊天机器人很快就会退化到生成“我不知道”和“我不确定”等模板回答,每当被问及它不熟悉的事物时。这与我们训练这个聊天机器人的方式有关。因为我们训练模型是为了使其在训练数据中最小化损失,它能采取的最佳策略是生成适用于尽可能多的输入句子的东西。非常通用的短语,比如“我不知道”,可以成为许多问题的答案,所以这是一个安全的策略,可以减少损失!

6.6.4 后续步骤

虽然我们的聊天机器人可以为许多输入产生逼真的响应,但它离完美还有很远的路要走。它处理不好的一个问题是专有名词。当你询问需要具体答案的问题时,就会看到这一点,比如:

What 's your favorite show ?
S-0     What 's your favorite show ?
H-0     -0.9829921722412109     I would have to say <unk> .
P-0     -0.8807 -2.2181 -0.4752 -0.0093 -0.0673 -2.9091 -0.9338 -0.3705

这里,是未知词的通用特殊符号。聊天机器人正在尝试回答某些问题,但该问题在训练数据中出现的频率太低,以至于不能被视为独立的词。这是一般简单 NMT 系统中出现的问题。因为模型需要将有关单词的所有信息压缩成 200 多维的数字向量,所以许多细节和类似单词之间的区别都被牺牲了。想像一下将关于你所在城市所有餐厅的信息压缩成一个 200 维的向量!

此外,我们训练的聊天机器人没有任何 “记忆” 或任何上下文概念。你可以通过问一系列相关问题来测试这一点,如下所示:

Do you like Mexican food ?
S-0     Do you like Mexican food ?
H-0     -0.805641770362854      Yes I do .
P-0     -1.0476 -1.1101 -0.6642 -0.6651 -0.5411
Why do you like it ?
S-1     Why do you like it ?
H-1     -1.2453081607818604     I think it 's a great movie .
P-1     -0.7999 -2.1023 -0.7766 -0.7130 -1.4816 -2.2745 -1.5750 -1.0524 -0.4324

在第二个问题中,聊天机器人在理解上下文方面有困难,并产生了完全无关的响应。要正确回答这样的问题,模型需要理解代词 “it” 指的是前面的名词,即本例中的 “Mexican food”。NLP 系统在现实世界中解决哪些提及指向哪些实体的任务被称为共指消解。系统还需要维护某种类型的记忆,以跟踪到目前为止在对话中讨论了哪些内容。

最后,在本章中讨论的简单 Seq2Seq 模型在处理长句子方面并不擅长。如果您回顾一下图 6.2,就会理解这一点 - 模型使用 RNN 读取输入语句,并使用固定长度的句子表示向量表示有关句子的所有内容,然后从该向量生成目标语句。无论输入是“Hi!”还是“The quick brown fox jumped over the lazy dog.”,句子表示都会成为瓶颈,特别是对于更长的输入。因此,在 2015 年左右,直到发明了一种称为 注意力 的机制来解决这个问题之前,神经 MT 模型无法击败传统的基于短语的统计 MT 模型。我们将在第八章详细讨论注意力。

概括

  • Seq2Seq 模型使用编码器和解码器将一个序列转换为另一个序列。
  • 你可以使用 fairseq 框架在一小时内构建工作中的翻译系统。
  • Seq2Seq 模型使用解码算法生成目标序列。贪心解码每一步都最大化概率,而束搜索则尝试同时考虑多个假设来寻找更好的解决方案。
  • 用于自动评估翻译系统的一个指标叫做 BLEU。
  • 通过使用 Seq2Seq 模型和对话数据集,可以构建一个简单的聊天机器人。

^(1.) 详细信息请参阅 Oriol Vinyals 等人的“Grammar as a Foreign Language”(2015 年;arxiv.org/abs/1412.7449)。

^(2.) 请注意每行开头的 $ 是由 shell 渲染的,您无需输入它。

第七章:卷积神经网络

本章内容包括

  • 通过检测模式解决文本分类问题。
  • 使用卷积层来检测模式并生成分数。
  • 使用池化层来聚合由卷积产生的分数。
  • 通过组合卷积和池化来构建卷积神经网络(CNN)。
  • 使用 AllenNLP 构建基于 CNN 的文本分类器。

在之前的章节中,我们介绍了线性层和 RNN,这是 NLP 中常用的两种主要神经网络体系结构。在本章中,我们介绍了另一种重要的神经网络类别,称为卷积神经网络(CNN)。CNN 具有与 RNN 不同的特征,使它们适用于检测语言模式至关重要的 NLP 任务,例如文本分类。

7.1 介绍卷积神经网络(CNN)

本节介绍了另一种类型的神经网络体系结构,称为卷积神经网络(CNN),它以与 RNN 不同的方式运行。CNN 特别擅长于模式匹配任务,在 NLP 社区中越来越受欢迎。

7.1.1 循环神经网络及其缺点。

在第四章中,我们讨论了句子分类,这是一个自然语言处理任务,接收一些文本作为输入并为其生成标签。我们还讨论了如何使用循环神经网络(RNN)来完成该任务。作为复习,RNN 是一种具有“循环”的神经网络,它从开头开始逐个元素地处理输入序列直到结束。在每一步更新的内部循环变量称为隐藏状态。当 RNN 完成处理整个序列时,最终时间步长处的隐藏状态表示输入序列的压缩内容,可用于包括句子分类在内的 NLP 任务。或者,您可以在每一步之后取出隐藏状态并将其用于为单词分配标签(例如 PoS 和命名实体标签)。在循环中反复应用的结构称为单元。具有简单乘法和非线性的 RNN 称为香草埃尔曼 RNN。另一方面,基于 LSTM 和 GRU 的 RNN 使用更复杂的单元,这些单元使用存储器和门。

RNN 在现代 NLP 中是一种强大的工具,具有广泛的应用范围;但是,它们并非没有缺点。首先,RNN 速度较慢-无论如何都需要逐个元素地扫描输入序列。它们的计算复杂度与输入序列的长度成正比。其次,由于它们的顺序性质,RNN 难以并行化。想象一个多层 RNN,其中多个 RNN 层堆叠在一起(如图 7.1 所示)。在朴素实现中,每一层都需要等到所有下面的层完成对输入的处理。


图 7.1 多层 RNN

其次,对于某些任务,RNN 结构过于复杂和低效。例如,在第四章中我们讲解了检测符合语法的句子的任务。在最简单的形式下,任务是在一个两个词组成的句子中识别主谓一致性的正确与否。如果句子包含诸如“I am”和“you are”之类的短语,那么它是符合语法的。如果包含“I are”或“you am”,那么就不符合语法。在第四章,我们构建了一个简单的带非线性的 LSTM-RNN 来识别有四个单词词汇量的两个词组成句子的语法正确性。但是,如果你需要对一个词汇量非常大的任意长度的句子进行分类,这个过程就开始变得非常复杂了。你的 LSTM 需要从大量的噪声(与一致性无关的其他单词和短语)中识别出信号(主谓一致性),同时学习使用更新操作来处理输入的每一个元素。

但是如果你仔细考虑,无论句子有多长或者词汇量有多大,你的网络的任务应该还是相当简单——如果句子包含有效的短语(如“I am”和“you are”),那么它符合语法。否则,不符合语法。实际上,这个任务与我们在第一章中看到的“如果-那么”情感分析器非常相似。很明显,LSTM RNN 的结构对于这个任务来说过于复杂,简单的文字和短语模式匹配就足够了。

7.1.2 句子分类的模式匹配

如果你看一下文本分类的一般情况,很多任务可以通过“模式匹配”来有效解决。以垃圾邮件过滤为例:如果你想要检测垃圾邮件,只需要查找像“v1agra”和“商机”这样的词语和短语,甚至不需要读完整封邮件;这些模式出现在什么地方并不重要。如果你想要从电影评论中检测情感,检测到像“amazing”和“awful”这样的积极和消极词语就足够了。换句话说,学习和检测这种本地语言模式,而不考虑它们的位置,对于文本分类任务是一种有效而高效的策略,也可能对其他自然语言处理任务有效。

在第三章,我们学习了 n 元语法的概念,即一个或多个词的连续序列。它们经常被用作自然语言处理中更正式定义的语言单位(如短语和从句)的代理。如果有一种工具能够遍历大量的文本噪声并检测作为信号的 n 元语法,那将非常适合文本分类。

7.1.3 卷积神经网络(CNNs)

卷积神经网络(CNN)正是做到这一点的。CNN 是一种神经网络类型,它涉及一种称为卷积的数学运算,简单来说,它检测有用于当前任务的局部模式。CNN 通常由一个或多个卷积层和一个或多个池化层组成,卷积层进行卷积操作,池化层负责聚合卷积结果。请参见图 7.2。分别在第 7.2 节和第 7.3 节中详细介绍卷积层和池化层。


图 7.2 卷积神经网络

CNN 受到人脑视觉系统的启发,在计算机视觉任务(如图像分类和目标检测)中被广泛使用。近年来,CNN 的使用在自然语言处理领域越来越流行,特别是在文本分类、序列标注和机器翻译等任务中。

7.2 卷积层

在本节中,我们将讨论卷积层,这是 CNN 架构的核心部分。术语卷积听起来可能有点可怕,但本质上它只是一种模式匹配。我们将使用图示和直观的例子来说明它的工作原理。

7.2.1 使用滤波器进行模式匹配

卷积层是 CNN 中最重要的组件。如前所述,卷积层将一种称为卷积的数学运算应用于输入向量,并产生输出。但是什么是卷积?理解卷积的严格定义需要了解线性代数,因此我们将使用类比和具体示例来理解它。想象一下,你手里拿着一个带有复杂图案的矩形玻璃块(就像你在教堂里看到的彩色玻璃),在观察它的同时将其滑动到输入序列上。如果输入模式与玻璃块的模式匹配,更多的光线透过玻璃进去,你会得到更大的输出值。如果输入模式看起来不像玻璃块的模式或者相反,你会得到更小的输出值。换句话说,你正在使用带有彩色玻璃块的道具在输入序列中寻找特定的模式。

这个类比比较模糊,所以让我们回顾一下我们在第四章中使用的语法检测的例子,并看看如何将卷积层应用到这个任务上。回顾一下,我们的神经网络接收一个包含两个词的句子作为输入,并需要区分出语法正确的序列和语法错误的序列。词汇表中只有四个词–“I”,“you”,“am”和“are”,它们由单词嵌入表示。类似地,输入句子只有四种可能性–“I am”,“I are”,“you am”和“you are”。你希望网络对前两种情况产生 1,对其他情况产生 0。请参见图 7.3 进行说明。


图 7.3 识别英文语法正确的句子

现在,让我们将词嵌入表示为模式。我们用黑色圆表示值-1,白色圆表示 1。然后,您可以将每个单词向量表示为两个圆的一对(请参见图 7.3 左侧的表)。同样,您可以将每个两个词的句子表示为两个向量的小“片段”,或者四个圆(请参见图 7.3 右侧的表)。我们的任务开始看起来更像是一个模式识别任务,网络需要学习对应于语法句子的黑白模式。

然后,让我们想象一个相同大小的“滤波器”(两个圆×两个圆),它充当我们之前讨论过的彩色玻璃。该滤波器的每个圆也是黑色或白色,对应值-1 和 1。您将通过这个滤波器查看一个模式,并确定是否这是您要找的模式。您可以通过将滤波器放在模式上并计算两者之间的颜色匹配数量来执行此操作。对于四个位置中的每一个,如果颜色匹配(黑色-黑色或白色-白色),则得分+1,如果不匹配(黑色-白色或白色-黑色),则得分-1。您的最终得分是四个分数的总和,从-4(无匹配)到+4(四次匹配)。请参见图 7.4 中的一些示例。


图 7.4 卷积滤波器示例

您得到的分数取决于模式和滤波器,但如图所示,当滤波器与模式相似时,分数变大,当两者不相似时,分数变小。当两者完全匹配时,您获得最大分数(4),当两者完全相反时,您获得最小分数(-4)。该滤波器充当输入的模式检测器。虽然这是一个非常简化的例子,但基本上显示了卷积层在做什么。在卷积神经网络中,这种滤波器称为

在更一般的设置中,您有一个任意长度的输入句子,并且从左到右将一个核滑过句子。请参见图 7.5 以了解此过程的示意图。该核反复应用于连续的两个词,以生成一系列分数。因为我们在这里使用的核覆盖了两个词,所以它被称为具有大小为 2 的核。而且,因为输入嵌入中有两个维度(称为通道),所以核的输入通道数量为 2。


图 7.5 在输入句子上滑动核

注意 嵌入维度被称为通道的原因是因为 CNN 最常应用于计算机视觉任务,其中输入通常是不同通道的 2-D 图像,这些通道对应于不同颜色的强度(如红色、绿色和蓝色)。在计算机视觉中,核是二维的,并在输入的 2-D 图像上移动,这也被称为 2-D 卷积。然而,在自然语言处理中,核通常是一维的(1-D 卷积),并且只有一个尺寸。

7.2.2 整流线性单元(ReLU)

作为下一步,让我们考虑如何使用核来获得期望的输出(图 7.3 中的 Desired 列)。如果我们使用图 7.4 中第二列所示的滤波器会怎样?从现在开始,我们将这个核称为核 1。这个核完全匹配第一个模式并给它一个高分,同时给其他模式给出零或负分数。图 7.6 显示了将核 1 应用于每个模式时的分数(称为分数 1)。


图 7.6 对模式应用核 1

现在让我们忘记分数的大小,专注于它们的符号(正数和负数)。前三个模式的符号在分数 1 和所需之间匹配,但对于最后一个模式则不是。要正确评分,即给出正分数,您需要使用另一个与最后一个模式完全匹配的滤波器。我们称这个核为核 2。图 7.7 显示了应用核 2 到每个模式时的分数(称为分数 2)。

核 2 可以为最后三个模式给出与所需符号匹配的正确分数,但不能为第一个模式。但是,如果仔细观察图 7.6 和 7.7,看起来如果有一种方法可以在核给出负分数时忽略输出,然后组合来自多个核的分数,那么就可以更接近所需的分数。


图 7.7 对模式应用核 2

让我们考虑一个函数,它将任何负输入夹紧为零,同时保持任何正值不变。在 Python 中,这个函数可以写成如下:

def f(x):
    if x >= 0:
        return x
    else:
        return 0

或者更简单

def f(x):
    return max(0, x)

您可以通过将此函数应用于分数 1 和分数 2 来忽略负值,如图 7.8 和 7.9 所示。


图 7.8 对分数 1 应用 ReLU


图 7.9 对分数 2 应用 ReLU

这个函数,被称为修正线性单元,或称为 ReLU(发音为“rel-you”),是深度学习中最简单但最常用的激活函数之一。 它通常与卷积层一起使用,虽然它非常简单(它只是将负值夹紧为零),但它仍然是一个激活函数,它使神经网络能够学习复杂的非线性函数(参见第四章,了解为什么非线性激活函数很重要)。 它还具有有利的数学属性,使得优化网络变得更容易,尽管理论细节超出了本书的范围。

7.2.3 合并分数

如果您查看图 7.8 和图 7.9,所谓的“固定”分数—显示在 f(Score 1) 和 f(Score 2) 列中—至少部分地捕捉到了期望的分数。 您所需做的就是将它们结合在一起(通过求和)并调整范围(通过除以 4)。 图 7.10 展示了这个结果。


图 7.10 结合两个内核的结果

在合并之后,分数与期望的结果完全匹配。 到目前为止,我们所做的一切都是设计与我们想要检测的模式相匹配的内核,然后简单地组合分数。 比较一下我们在第 4.1.3 节中处理的 RNN 示例,那里我们需要使用一些复杂的数值计算来推导参数。 希望这个例子足以向您展示 CNN 对于文本分类可以有多简单而强大!

我们在本节中处理的示例仅用于介绍 CNN 的基本概念,因此我们偷了很多懒。 首先,在实践中,模式和内核不仅仅是黑白的,而是包含实值数字。 应用内核到模式后的分数不是通过计算颜色匹配次数得到的,而是通过一种称为内积的数学运算得到的,它捕捉了两者之间的相似性。 第二,内核产生的分数不是通过某种任意的操作(就像我们在本节中所做的那样)组合在一起的,而通常是通过线性层(见 3.4.3 节)组合在一起的,该线性层可以学习针对输入的线性变换以产生输出。 最后,内核和最终线性层中的权重(魔法常数 w 和 b)都是 CNN 的可训练参数,这意味着它们的值会被调整,以使 CNN 能够产生期望的分数。

7.3 池化层

在前一节中,我们假设输入只是两个词——主语和动词的组合,尽管在实践中,CNN 的输入可以是任意长度的。您的 CNN 不仅需要检测模式,还需要在输入中可能存在的大量噪声中找到它们。正如我们在第 7.2 节中看到的,您将一个核从左到右滑过句子,并且核会重复应用于两个连续的单词以产生一系列分数。剩下的问题是如何处理这些产生的分数。具体来说,我们应该在图 7.11 中的“?”位置使用什么操作来获得所需的分数?这个操作需要具有一些属性——它必须是可以应用于任意数量的分数的东西,因为句子可能非常长。它还需要以一种对输入句子中目标模式(“我是”的单词嵌入)的位置不可知的方式聚合分数。您能想出答案吗?


图 7.11 聚合分数以获得所需分数

汇总分数的最简单方法是取它们的最大值。因为图 7.11 中的最大分数为 4,它将成为该层的输出。这种汇总操作称为池化,而执行汇总的神经网络子结构称为池化层。您还可以执行其他类型的数学运算来进行聚合,例如取平均值,尽管最常用的是取最大值(称为最大池化)。

汇总分数将被馈送到一个线性层,可选地与其他核的分数结合,并用作预测分数。整个过程如图 7.12 所示。现在我们有一个完全功能的 CNN!

与我们迄今看到的其他神经网络一样,线性层的输出被馈送到 softmax 以产生标签上的概率分布。然后将这些预测值与真实标签进行比较,以产生损失并用于优化网络。

在我们结束之前,对 CNN 还有几句话:请注意,图 7.12 中的 CNN 无论搜索模式(“我是”)在输入句子中的位置如何,都会产生相同的预测值。这是由于卷积核的局部性以及我们刚刚添加的最大池化层的属性。通常情况下,即使输入句子通过移动几个单词而被修改,CNN 也会产生相同的预测。从技术上讲,CNN 被称为变换不变,这是 CNN 的一个重要属性。如果您使用图像识别示例,则该属性可能更直观。猫的图像仍然是猫的图像,无论猫在图像中的位置如何。同样,一个语法正确的英文句子(例如,“我是学生”)仍然是语法正确的,即使句子通过在开头添加几个单词(例如,“那是对的”)而被转换为“那是对的,我是学生”。


图 7.12 带有多个卷积核的完整 CNN

因为 CNN 中的卷积核不相互依赖(与 RNN 不同,后续单元需要等待所有前面的单元完成输入处理),所以 CNN 计算效率高。GPU 可以并行处理这些卷积核,不用等待其他卷积核的输出。由于这个特性,CNN 通常比大小相似的 RNN 更快。

7.4 案例研究:文本分类

现在,我们已经了解了 CNN 的基础知识,在本节中,我们将使用 CNN 构建一个 NLP 应用并看看它在实践中的工作原理。正如之前提到的,CNN 在 NLP 中最受欢迎和直接的应用之一就是文本分类。CNN 擅长检测文本中的模式(如突出的单词和短语),这也是准确文本分类的关键。

7.4.1 复习:文本分类

我们已经在第二章和第四章中介绍了文本分类,但是为了回顾一下,文本分类是指一个 NLP 系统给定一段文本分配一个标签的任务。如果文本是一个电子邮件,标签是邮件是否为垃圾邮件,那就是垃圾邮件过滤。如果文本是一个文档(比如新闻文章),标签是它的主题(如政治、商业、技术或体育),那就叫做文档分类。根据输入和输出的不同,还存在许多其他变种的文本分类。但是在本节中,我们将再次处理情感分析,它的输入是一些表达作者主观意见的文本(如电影和产品评论),输出是意见的标签(如正面或负面,甚至星级评价),也被称为极性

在第二章和第四章中,我们构建了一个 NLP 系统,使用 Stanford Sentiment Treebank 检测给定电影评论的情感极性,这是一个包含电影评论及其极性(非常正面,正面,中立,负面,非常负面)的数据集。在本节中,我们将构建同样的文本分类器,但是使用 CNN 而不是 RNN。好消息是,我们可以重用第二章编写的大部分代码,在这一部分只需要修改几行代码将 RNN 替换为 CNN。这在很大程度上归功于 AllenNLP 强大而设计良好的抽象,它可以让您通过公共接口与许多具有不同架构的模块一起工作。让我们下面看看它的运行。

7.4.2 使用 CnnEncoder

记住,在第 4.4 节中,我们定义了文本分类的 LstmClassifier 如下:

class LstmClassifier(Model):
    def __init__(self,
                 embedder: TextFieldEmbedder,
                 encoder: Seq2VecEncoder,
                 vocab: Vocabulary,
                 positive_label: str = '4') -> None:
    ...

我们没有对这个定义进行深入的思考,但是从这个构造函数中,我们可以看到这个模型是建立在两个子组件之上的:一个名为embedderTextFieldEmbedder和一个名为encoderSeq2VecEncoder,除此之外还有词汇表和正标签的字符串,这些对我们的讨论不相关。我们在第三章详细讨论了词嵌入,尽管我们只是简要涉及了编码器。这个Seq2VecEncoder到底是什么意思呢?

在 AllenNLP 中,Seq2VecEncoder是一类神经网络架构,它接受一系列向量(或一般张量)并返回一个单个向量。RNN 是其中的一个例子,它接受由多个向量组成的可变长度输入,并在最后一个单元格中将其转换为单个向量。我们使用以下代码基于 LSTM-RNN 创建了一个Seq2VecEncoder的实例:

encoder = PytorchSeq2VecWrapper(
    torch.nn.LSTM(EMBEDDING_DIM, HIDDEN_DIM, batch_first=True))

但只要你的组件具有相同的输入和输出规范,你就可以使用任何神经网络架构作为Seq2VecEncoder。在编程语言中,Seq2VecEncoder类似于 Java(以及许多其他语言中的)中的接口——接口定义了你的类是什么样子的,它做什么,但它们不关心你的类是如何做到的。实际上,你的模型可以简单地对所有输入向量求和以产生输出,而不需要任何复杂的变换,比如非线性变换。事实上,这就是BagOfEmbeddingsEncoder—AllenNLP 中实现的Seq2VecEncoder之一的做法。

接下来,我们使用 CNN 将一系列向量“压缩”为单个向量。在 AllenNLP 中,基于 CNN 的Seq2VecEncoder实现为CnnEncoder,可以如下实例化:

encoder = CnnEncoder(
    embedding_dim=EMBEDDING_DIM,
    num_filters=8,
    ngram_filter_sizes=(2, 3, 4, 5))

在这个例子中,embedding_dim指定了输入嵌入的维度。第二个参数num_filters告诉我们将使用多少个过滤器(或内核,如第 7.2.1 节所解释的)。最后一个参数ngram_filter_sizes指定了 n-gram 大小的列表,即这些内核的大小。在这里,我们使用 2、3、4 和 5 的 n-gram 大小,这意味着有 8 个用于 bigram 的内核,8 个用于 trigram,以此类推,直到 5-gram。总而言之,这个 CNN 可以学习 32 个不同的内核来检测模式。CnnEncoder通过一个最大池化层运行这些内核的结果,并得出一个总结输入的单个向量。

训练流水线的其余部分几乎与我们在第二章中看到的 LSTM 版本相同。整个代码都可以在 Google Colab 上找到。www.realworldnlpbook.com/ch7.html#cnn-nb。但有一个注意事项:由于一些 n-gram 过滤器具有宽形状(例如,4-gram 和 5-gram),您需要确保每个文本字段至少具有该长度,即使原始文本很短(例如,只有一个或两个单词)。您需要了解 AllenNLP 中的批处理和填充工作原理(我们将在第十章中介绍)才能充分理解如何处理这一问题,但简而言之,您需要在初始化标记索引器时指定 token_min_padding_length 参数,如下所示:

token_indexer = SingleIdTokenIndexer(token_min_padding_length=5)
reader = StanfordSentimentTreeBankDatasetReader(
    token_indexers={'tokens': token_indexer})

真实世界的自然语言处理(二)(4)https://developer.aliyun.com/article/1519772

相关文章
|
4月前
|
机器学习/深度学习 自然语言处理 PyTorch
真实世界的自然语言处理(二)(1)
真实世界的自然语言处理(二)
52 1
|
4月前
|
机器学习/深度学习 自然语言处理 异构计算
真实世界的自然语言处理(三)(2)
真实世界的自然语言处理(三)
28 3
|
4月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(三)(1)
真实世界的自然语言处理(三)
34 3
|
4月前
|
机器学习/深度学习 自然语言处理 数据可视化
真实世界的自然语言处理(一)(4)
真实世界的自然语言处理(一)
23 2
|
4月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(4)
真实世界的自然语言处理(二)
37 1
|
4月前
|
机器学习/深度学习 JSON 自然语言处理
真实世界的自然语言处理(一)(5)
真实世界的自然语言处理(一)
40 1
|
4月前
|
机器学习/深度学习 人工智能 自然语言处理
真实世界的自然语言处理(一)(3)
真实世界的自然语言处理(一)
23 1
|
4月前
|
机器学习/深度学习 自然语言处理 监控
真实世界的自然语言处理(一)(2)
真实世界的自然语言处理(一)
38 1
|
4月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(5)
真实世界的自然语言处理(二)
33 0
|
4月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(2)
真实世界的自然语言处理(二)
62 0

热门文章

最新文章

下一篇
DDNS