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

本文涉及的产品
图片翻译,图片翻译 100张
NLP 自学习平台,3个模型定制额度 1个月
NLP自然语言处理_高级版,每接口累计50万次
简介: 真实世界的自然语言处理(二)

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

7.4.3 训练和运行分类器

当您运行脚本时,您会在训练结束时看到类似以下日志输出:

{'best_epoch': 1,
 'best_validation_accuracy': 0.40236148955495005,
 'best_validation_f1_measure': 0.37362638115882874,
 'best_validation_loss': 1.346440097263881,
 'best_validation_precision': 0.4722222089767456,
 'best_validation_recall': 0.30909091234207153,
 'epoch': 10,
 'peak_cpu_memory_MB': 601.656,
 'training_accuracy': 0.993562734082397,
 'training_cpu_memory_MB': 601.656,
 'training_duration': '0:01:10.138277',
 'training_epochs': 10,
 'training_f1_measure': 0.994552493095398,
 'training_loss': 0.03471498479299275,
 'training_precision': 0.9968798756599426,
 'training_recall': 0.9922360181808472,
 'training_start_epoch': 0,
 'validation_accuracy': 0.35149863760217986,
 'validation_f1_measure': 0.376996785402298,
 'validation_loss': 3.045241366113935,
 'validation_precision': 0.3986486494541168,
 'validation_recall': 0.35757574439048767}

这意味着训练精度达到了约 99%,而验证精度则达到了约 40%。同样,这是过拟合的典型症状,即您的模型非常强大,可以很好地拟合训练数据,但不能很好地泛化到验证和测试数据集。我们的 CNN 具有许多能够记住训练数据中显著模式的过滤器,但这些模式未必有助于预测验证实例的标签。在本章中,我们不太担心过拟合。有关避免过拟合的常见技术,请参见第十章。

如果您想对新实例进行预测,可以使用与第二章相同的预测器。AllenNLP 中的预测器是您训练好的模型的一个轻量级包装器,负责将输入和输出格式化为 JSON 格式并将实例提供给模型。您可以使用以下代码段使用您训练好的 CNN 模型进行预测:

predictor = SentenceClassifierPredictor(model, dataset_reader=reader)
logits = predictor.predict('This is the best movie ever!')['logits']
label_id = np.argmax(logits)
print(model.vocab.get_token_from_index(label_id, 'labels'))

摘要

  • CNN 使用称为内核的过滤器和称为卷积的操作来检测输入中的局部语言模式。
  • 卷积层使用的激活函数称为 ReLU,它将负值截断为零。
  • CNN 然后使用池化层来聚合卷积层的结果。
  • CNN 预测是转换不变的,意味着即使对输入进行线性修改后也保持不变。
  • 您可以通过修改文本分类器的几行代码将基于 CNN 的编码器用作 AllenNLP 中的 Seq2VecEncoder。

第八章:注意力与 Transformer

本章内容包括

  • 使用注意力机制生成输入摘要,提高 Seq2Seq 模型的质量
  • 用自注意力替换 RNN 风格的循环,一种使输入摘要自身的机制
  • 用 Transformer 模型改进机器翻译系统
  • 使用 Transformer 模型和公开可用数据集构建高质量的拼写检查器

到目前为止,本书的重点一直是循环神经网络(RNNs),它是一种强大的模型,可应用于各种 NLP 任务,如情感分析、命名实体识别和机器翻译。在本章中,我们将介绍一个更加强大的模型——Transformer¹——一种基于自注意力概念的全新编码器-解码器神经网络架构。自 2017 年问世以来,它毫无疑问是最重要的 NLP 模型。它不仅是一个强大的模型(例如,用于机器翻译和各种 Seq2Seq 任务),而且还被用作许多现代 NLP 预训练模型的底层架构,包括 GPT-2(第 8.4.3 节)和 BERT(第 9.2 节)。自 2017 年以来的现代 NLP 发展最好的总结可以概括为“Transformer 时代”。

在本章中,我们首先介绍了注意力机制,这是机器翻译中取得突破的一种机制,然后介绍了自注意力,这是 Transformer 模型的基础概念。我们将构建两个 NLP 应用程序——西班牙语到英语的机器翻译器和一个高质量的拼写检查器,并学习如何将 Transformer 模型应用于您的日常应用程序。正如我们将在后面看到的那样,Transformer 模型可以在某些任务(如翻译和生成)中以接近人类水平的性能显著提高 NLP 系统的质量,而且几乎可以超越 RNNs。

8.1 什么是注意力?

在第六章中,我们介绍了 Seq2Seq 模型——一种使用编码器和解码器将一个序列转换为另一个的 NLP 模型。Seq2Seq 是一种多功能且强大的范式,尽管“简单” Seq2Seq 模型也不是没有局限性。在本节中,我们讨论了 Seq2Seq 模型的瓶颈,并激发了使用注意力机制的动机。

8.1.1 简单 Seq2Seq 模型的局限性

让我们先回顾一下 Seq2Seq 模型的工作原理。Seq2Seq 模型由编码器和解码器组成。解码器接收源语言中的一系列标记,并将其通过 RNN 运行,最终产生一个固定长度的向量。这个固定长度的向量是输入句子的表示。解码器,另一个 RNN,接收这个向量,并逐标记地产生目标语言中的一个序列。图 8.1 说明了如何使用简单的 Seq2Seq 模型将西班牙语句子翻译成英语。


图 8.1 简单 Seq2Seq 模型中的瓶颈

这个 Seq2Seq 架构非常简单而强大,但众所周知,它的基本版本(如图 8.1 所示)在翻译句子方面不如其他传统的机器翻译算法(如基于短语的统计机器翻译模型)。如果你仔细观察它的结构,可能就能猜出其中的原因——它的编码器试图将源句子中的所有信息“压缩”到句子表示中,这是一个固定长度的向量(例如,256 个浮点数),而解码器则试图仅从该向量中恢复整个目标句子。无论源句子有多长(或多短),向量的大小都是固定的。中间向量是一个巨大的瓶颈。如果你考虑一下人类实际上如何在语言之间进行翻译,这听起来相当困难且有些不寻常。专业的翻译人员不会一口气读完源句子然后把它的翻译写下来。他们会根据需要多次参考源句子,以翻译目标句子中的相关部分。

将所有信息压缩成一个向量可能(并且确实)对短句子有效,正如我们稍后在 8.2.2 节中将看到的那样,但随着句子变得越来越长,这种方法变得越来越困难。研究表明,基本 Seq2Seq 模型的翻译质量随着句子变得越来越长而变差。²

8.1.2 注意力机制

解码器如果能够在生成目标标记时参考编码器的某个特定部分,将会容易得多。这类似于人类翻译员(解码器)根据需要参考源句子(编码器)。

这可以通过使用 注意力 来实现,注意力是神经网络中的一种机制,它专注于输入的特定部分并计算其上下文相关的摘要。这就像拥有某种包含输入所有信息的键值存储,然后用查询(当前上下文)查找它一样。存储的值不仅仅是单个向量,而通常是一个向量列表,每个标记关联一个相应的键。这有效地增加了解码器在进行预测时可以参考的“内存”大小。

在讨论注意力机制如何在 Seq2Seq 模型中工作之前,让我们先看一下它以一般形式的运行情况。图 8.2 描绘了一个具有以下特征的通用注意力机制:

  1. 注意力机制的输入是值及其相关的键。输入值可以采用许多不同的形式,但在自然语言处理中,它们几乎总是向量列表。对于 Seq2Seq 模型,这里的键和值是编码器的隐藏状态,它们代表了输入句子的标记编码。
  2. 每个与值关联的键都使用注意力函数 f 与查询进行比较。通过将 f 应用于查询和每个键之一,您会得到一组分数,每个键值对一个,然后将其归一化以获得一组注意力权重。特定的函数 f 取决于体系结构(稍后会详细介绍)。对于 Seq2Seq 模型,这会给出一个输入令牌的分布。输入令牌越相关,其权重越大。
  3. 输入值由第 2 步中获得的相应权重加权,并相加以计算最终摘要向量。对于 Seq2Seq 模型,此摘要向量附加到解码器隐藏状态以辅助翻译过程。


图 8.2 使用注意力机制对输入进行总结

由于第 3 步,注意力机制的输出始终是输入向量的加权和,但它们如何加权是由注意力权重确定的,而注意力权重又是从键和查询计算得出的。换句话说,注意力机制计算的是 上下文(查询)相关的输入摘要。神经网络的下游组件(例如基于 RNN 的 Seq2Seq 模型的解码器,或者 Transformer 模型的上层)使用此摘要进一步处理输入。

在接下来的几节中,我们将学习 NLP 中两种最常用的注意力机制类型 —— 编码器-解码器注意力(也称为 交叉注意力;在基于 RNN 的 Seq2Seq 模型和 Transformer 中都使用)和自注意力(在 Transformer 中使用)。

8.2 带有注意力的序列到序列

在本节中,我们将学习如何将注意力机制应用于 RNN 基础的 Seq2Seq 模型,注意力机制是首次发明的。我们将研究它如何与具体示例一起工作,然后我们将使用 fairseq 实验带有和不带有注意力机制的 Seq2Seq 模型,以观察它对翻译质量的影响。

8.2.1 编码器-解码器注意力

正如我们之前所看到的,注意力是在特定上下文下创建输入摘要的机制。我们使用了一个键值存储和一个查询作为它如何工作的类比。让我们看看注意力机制如何在基于 RNN 的 Seq2Seq 模型中使用,使用随后的具体示例。


图 8.3 在基于 RNN 的 Seq2Seq 模型中添加注意力机制(浅色阴影框)

图 8.3 展示了一个带有注意力机制的 Seq2Seq 模型。一开始看起来很复杂,但实际上它只是一个基于 RNN 的 Seq2Seq 模型,在编码器顶部左上角的浅色阴影框中添加了一些额外的“东西”。如果你忽略里面的内容,将其视为黑匣子,它所做的就是简单地接受一个查询并从输入中返回某种摘要。它计算这个摘要的方式只是我们在 8.1.2 节中介绍的通用注意力形式的一个变体。它的执行步骤如下:

  1. 注意力机制的输入是编码器计算的隐藏状态列表。这些隐藏状态既用作键也用作值(即,键和值是相同的)。某个令牌(例如,“no”令牌)的编码器隐藏状态反映了关于该令牌及其之前所有令牌的信息(如果 RNN 是单向的),或整个句子(如果 RNN 是双向的)。
  2. 假设你已经解码到“Mary did.”。此时解码器的隐藏状态被用作查询,与每个键使用函数 f 进行比较。这会产生一个注意力分数列表,每个键值对应一个分数。这些分数确定了解码器在尝试生成跟在“Mary did.”后面的单词时应该关注输入的哪个部分。
  3. 这些分数被转换为概率分布(一组正值,总和为 1),用于确定哪些向量应该得到最多的关注。这个注意力机制的返回值是所有值的加权和,加权值为注意力分数经过 softmax 归一化后的值。

你可能想知道注意力函数 f 是什么样的。f 的几个变体是可能的,这取决于它如何计算键和查询之间的注意力分数,但这些细节在这里并不重要。值得注意的一点是,在提出注意力机制的原始论文中,作者使用了一个“迷你”神经网络来计算键和查询之间的注意力分数。

这个基于“迷你”网络的注意力函数不是你只需事后将其插入到 RNN 模型中就能让其正常工作的东西。它是作为整个网络的一部分进行优化的—也就是说,当整个网络通过最小化损失函数进行优化时,注意力机制也会变得更好,因为这样做也有助于解码器生成更好的翻译并降低损失函数。换句话说,整个网络,包括注意力机制,都是端到端训练的。这通常意味着,随着网络的优化,注意力机制开始学习只关注输入的相关部分,这通常是目标标记与源标记对齐的地方。换句话说,注意力计算了源标记和目标标记之间某种“软”单词对齐。

8.2.2 使用注意力构建 Seq2Seq 机器翻译

在第 6.3 节中,我们使用由 Facebook 开发的 NMT 工具包 fairseq 构建了我们的第一个机器翻译(MT)系统。 使用来自 Tatoeba 的平行数据集,我们构建了一个基于 LSTM 的 Seq2Seq 模型,将西班牙语句子翻译成英语。

在本节中,我们将尝试使用 Seq2Seq 机器翻译系统,并看看注意力如何影响翻译质量。 我们假设您已经按照我们构建 MT 系统的步骤操作,通过下载数据集并运行 fairseq-preprocess 和 fairseq-train 命令(第 6.3 节)。 之后,您运行了 fairseq-interactive 命令以将西班牙语句子交互式地翻译成英语。 您可能已经注意到,从这个仅花了您 30 分钟构建的 MT 系统得到的翻译实际上相当不错。 实际上,我们使用的模型架构(—arch lstm)默认内置了注意力机制。 请注意,当您运行以下 fairseq-train 命令时

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

您应该已经在终端中看到了您的模型的输出,如下所示:

...
LSTMModel(
  (encoder): LSTMEncoder(
    (embed_tokens): Embedding(16832, 512, padding_idx=1)
    (lstm): LSTM(512, 512)
  )
  (decoder): LSTMDecoder(
    (embed_tokens): Embedding(11416, 512, padding_idx=1)
    (layers): ModuleList(
      (0): LSTMCell(1024, 512)
    )
 (attention): AttentionLayer(
 (input_proj): Linear(in_features=512, out_features=512, bias=False)
 (output_proj): Linear(in_features=1024, out_features=512, bias=False)
 )
  )
)
...

这告诉您,您的模型有一个编码器和一个解码器,但解码器还有一个称为注意力的组件(类型为 AttentionLayer),如代码片段中的粗体所示。 这正是我们在第 8.2.1 节中讨论过的“小型网络”。

现在让我们训练相同的模型,但是不使用注意力机制。您可以在 fairseq-train 中添加—decoder-attention 0 来禁用注意力机制,同时保持其他所有内容不变,如下所示:

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

当您运行此命令时,您将看到类似的输出,接下来显示了模型的架构,但没有注意力机制:

LSTMModel(
  (encoder): LSTMEncoder(
    (embed_tokens): Embedding(16832, 512, padding_idx=1)
    (lstm): LSTM(512, 512)
  )
  (decoder): LSTMDecoder(
    (embed_tokens): Embedding(11416, 512, padding_idx=1)
    (layers): ModuleList(
      (0): LSTMCell(1024, 512)
    )
  )
)

正如我们在第 6.3.2 节中看到的,训练过程在训练和验证之间交替进行。 在训练阶段,神经网络的参数通过优化器进行优化。 在验证阶段,这些参数被固定,并且模型在称为验证集的数据集的一个保留部分上运行。 除了确保训练损失下降外,您还应该在训练过程中查看验证损失,因为它更好地表示了模型在训练数据之外的泛化能力。

在这个实验中,您应该观察到由注意力模型实现的最低验证损失约为 1.727,而无注意力模型的最低验证损失约为 2.243。 较低的损失值意味着模型更好地适应了数据集,因此这表明注意力有助于改善翻译。 让我们看看这是否属实。 正如我们在第 6.3.2 节中所做的,您可以通过运行以下 fairseq-interactive 命令来交互地生成翻译:

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

在表 8.1 中,我们比较了带有和不带有注意力的模型生成的翻译。基于注意力的模型得到的翻译与我们在第 6.3.3 节中看到的一样。请注意,基于没有注意力的模型得到的翻译比具有注意力的模型要糟糕得多。如果您看一下“¿Hay habitaciones libres?”和“Maria no daba una bofetada a la bruja verde”的翻译,您会看到其中的陌生令牌“”(表示“未知”)。这里发生了什么?

表 8.1 模型带有和不带有注意力的翻译比较

西班牙语(输入) 带有注意力 没有注意力
¡Buenos días! 早上好! Good morning!
¡Hola! 你好! Hi!
¿Dónde está el baño? 厕所在哪里? Where’s the toilet?
¿Hay habitaciones libres? 有空房间吗? Are there rooms?
¿Acepta tarjeta de crédito? 你们接受信用卡吗? Do you accept credit card?
La cuenta, por favor. 请结账。 Check, please.
Maria no daba una bofetada a la bruja verde. Maria 没有打绿色女巫。 Mary wasn’t a of the pants.

这些是分配给词汇表之外(OOV)词汇的特殊令牌。我们在第 3.6.1 节中提及了 OOV 词汇(当我们介绍用于 FastText 的子词概念时)。大多数自然语言处理应用都在一个固定的词汇表中操作,每当它们遇到或尝试生成超出预定义集合的词汇时,这些词汇都会被替换为一个特殊的令牌 。这类似于当方法不知道如何处理输入时返回的特殊值(例如 Python 中的 None)。因为这些句子包含某些词汇(我怀疑它们是“libres”和“bofetada”),没有注意力的 Seq2Seq 模型,其内存是有限的,不知道该如何处理它们,简单地回归到最安全的操作,即生成一个通用的、捕获所有的符号 。另一方面,您可以看到注意力防止系统生成这些符号,并有助于提高生成的翻译的整体质量。

8.3 Transformer 和自注意力

在这一节中,我们将学习 Transformer 模型的工作原理,具体来说,是它如何利用一种称为自注意力的新机制生成高质量的翻译。自注意力机制使用每个令牌作为上下文,为每个令牌创建了整个输入的摘要。

8.3.1 自注意力

正如我们之前看到的,注意力是一种创建输入的上下文相关摘要的机制。对于基于 RNN 的 Seq2Seq 模型,输入是编码器隐藏状态,而上下文是解码器隐藏状态。Transformer 的核心思想,自注意力,也创建了输入的摘要,除了一个关键的区别——创建摘要的上下文也是输入本身。请参见图 8.4,了解自注意力机制的简化示例。


图 8.4 自注意力将输入转化为摘要。

为什么这很好?为什么它有效?正如我们在第四章中讨论的那样,RNN 也可以通过循环遍历输入标记并更新内部变量(隐藏状态)来创建输入的摘要。这是有效的-我们之前看到当 RNN 与注意力结合时可以生成良好的翻译,但是它们有一个关键问题:因为 RNN 按顺序处理输入,随着句子变得越来越长,处理标记之间的远程依赖关系变得越来越困难。

让我们看一个具体的例子。如果输入句子是“The Law will never be perfect, but its application should be just”,了解代词“its”指的是什么(“The Law”)对于理解句子的含义以及任何后续任务(如准确翻译句子)都很重要。然而,如果您使用 RNN 来编码这个句子,要学习这个代词的共指关系,RNN 需要先学习在隐藏状态中记住名词“The Law”,然后等待循环遇到目标代词(“its”),同时学会忽略之间的所有无关内容。对于神经网络来说,这听起来像是一种复杂的技巧。

但事情不应该那么复杂。像“its”这样的单数所有格代词通常指的是它们前面最近的单数名词,而与它们之间的词无关,因此简单的规则“用最近出现的名词替换它”就足够了。换句话说,在这种情况下,“随机访问”比“顺序访问”更适合。自注意力更擅长学习这种远程依赖关系,稍后我们将会看到。

让我们通过一个例子来了解自注意力是如何工作的。假设我们要将西班牙语翻译成英语,并且想要编码输入句子中的前几个单词“Maria no daba”。我们还将关注一个特定的标记“no”,以及如何从整个输入计算其嵌入。第一步是将目标标记与输入中的所有标记进行比较。自注意力通过使用投影 W[Q]将目标转换为查询,使用投影 W[K]将所有标记转换为键,并使用函数 f 计算注意力权重来完成这一步骤。由 f 计算得到的注意力权重通过 softmax 函数进行归一化和转换为概率分布。图 8.5 说明了这些步骤,注意力权重如何计算。与我们在 8.2.1 节中涵盖的编码器-解码器注意力机制一样,注意力权重决定了我们从输入标记中获得的值如何“混合”。对于像“its”这样的词,我们希望相关词的权重会更高,比如之前的例子中的“Law”。


图 8.5 从键和查询计算注意力权重

在下一步中,将每个输入令牌对应的向量通过投影 W[V] 转换为值向量。每个投影值都由相应的注意权重加权,并加总以生成摘要向量。请参见图 8.6 进行说明。


图 8.6 计算所有值的加权和

如果这是“常规”的编码器-解码器注意机制,那就是这样了。在解码期间,每个令牌只需要一个摘要向量。然而,编码器-解码器注意力和自注意力之间的一个关键区别是后者会针对输入中的每个令牌重复此过程。如图 8.7 所示,这会为输入产生一组新的嵌入,每个令牌一个。


图 8.7 为整个输入序列生成摘要(细节被省略)

自注意力产生的每个摘要都考虑了输入序列中的所有令牌,但权重不同。因此,对于诸如“its”之类的词,它可以融入一些来自相关词语的信息,例如“法律”,无论这两个词有多远。使用类比,自注意力通过对输入进行随机访问产生摘要。这与 RNN 形成对比,后者只允许对输入进行顺序访问,并且这也是 Transformer 之所以是编码和解码自然语言文本的强大模型之一的关键原因之一。

我们需要解释自注意力的最后一个细节才能完全理解它。现在,前面介绍的自注意机制只能使用输入序列的一个方面来生成摘要。例如,如果您希望自注意力学习每个代词指代哪个单词,它可以做到这一点——但您也可能希望根据其他一些语言学方面“混合”其他单词的信息。例如,您可能希望参考代词修改的其他单词(在这种情况下是“应用”)。解决方案是为每个令牌计算多组密钥、值和查询,并计算多组注意权重以“混合”关注不同输入方面的值。最终的嵌入是以这种方式生成的摘要的组合。这种机制被称为多头自注意力(图 8.8)。


图 8.8 多头自注意力生成具有多个密钥、值和查询的摘要。

如果你想要完全理解 Transformer 层的工作原理,你需要学习一些额外的细节,但本节已经涵盖了最重要的概念。如果你对更多细节感兴趣,请查看*《图解 Transformer》jalammar.github.io/illustrated-transformer/),这是一个写得很好的指南,用易于理解的插图解释了 Transformer 模型。此外,如果你有兴趣用 Python 从零开始实现 Transformer 模型,请查看《注释版 Transformer》*(nlp.seas.harvard.edu/2018/04/03/attention.html)。

8.3.2 Transformer

Transformer 模型不仅仅使用单步自注意力来编码或解码自然语言文本。它重复应用自注意力到输入中,逐渐转换它们。与多层 RNN 一样,Transformer 还将一系列转换操作分组到一个层中,并重复应用它。图 8.9 显示了 Transformer 编码器的一个层。

每个层内都有很多操作,我们的目标不是解释每一个细节——你只需要理解多头自注意力是其核心,后跟通过前馈神经网络的转换(图 8.9 中的“FF”)。引入了残差连接和归一化层,以使模型更容易训练,尽管这些操作的细节超出了本书的范围。Transformer 模型反复应用这个层,将输入从文字的形式(原始词嵌入)转换为更抽象的东西(句子的“含义”)。在原始的 Transformer 论文中,Vaswani 等人用了六层进行机器翻译,尽管如今更大的模型通常使用 10-20 层。


图 8.9 一个具有自注意力和前馈层的 Transformer 编码器层

到这一步,你可能已经注意到自注意力操作完全独立于位置。换句话说,即使我们颠倒“Maria”和“daba”之间的单词顺序,自注意力的嵌入结果也完全相同,因为该操作只关注单词本身和来自其他单词的聚合嵌入,而不考虑它们的位置。这显然非常限制——自然语言句子的意义很大程度上取决于单词的顺序。那么,Transformer 如何编码单词顺序呢?

Transformer 模型通过生成一些人工嵌入来解决这个问题,这些嵌入在位置之间不同,并在将它们馈送到层之前添加到词嵌入中。这些嵌入被称为位置编码如图 8.10 所示,可以由某些数学函数(如正弦曲线)生成,或者在训练过程中根据位置学习。这样,Transformer 可以区分第一个位置的“Maria”和第三个位置的“Maria”,因为它们具有不同的位置编码。


图 8.10 将位置编码添加到输入中以表示词序

图 8.11 显示了 Transformer 解码器。虽然很多事情正在进行,但一定要注意两个重要的事情。首先,你会注意到一个额外的机制,称为交叉注意力,插入在自注意力和前馈网络之间。这个交叉注意力机制类似于我们在第 8.2 节介绍的编码器-解码器注意力机制。它的工作方式与自注意力完全相同,唯一的区别是注意力的值来自编码器,而不是解码器,总结了从编码器提取的信息。


图 8.11 Transformer 解码器层,具有自注意力和交叉注意力

最后,Transformer 模型以与我们之前在第 6.4 节学习的基于 RNN 的 Seq2Seq 模型完全相同的方式生成目标句子。解码器由特殊标记初始化,并生成可能的下一个标记的概率分布。从这里,你可以选择具有最大概率的标记(贪婪解码,如第 6.4.3 节所示),或者在寻找最大化总分数的路径时保留一些具有最高概率的标记(波束搜索,如第 6.4.4 节所示)。事实上,如果你把 Transformer 解码器看作一个黑匣子,它生成目标序列的方式与 RNN 完全相同,你可以使用相同的一组解码算法。换句话说,第 6.4 节介绍的解码算法是一种通用的算法,不受底层解码器架构的影响。

8.3.3 实验

现在我们知道了 Transformer 模型的工作原理,让我们用它构建一个机器翻译系统。好消息是,序列到序列工具包 Fairseq 已经支持基于 Transformer 的模型(以及其他强大的模型),可以在训练模型时通过--arch transformer选项指定。假设你已经预处理了我们用于构建西班牙语到英语机器翻译的数据集,你只需要调整给予fairseq-train的参数,如下所示:

fairseq-train \
  data/mt-bin \
  --arch transformer \
  --share-decoder-input-output-embed \
  --optimizer adam --adam-betas '(0.9, 0.98)' --clip-norm 0.0 \
  --lr 5e-4 --lr-scheduler inverse_sqrt --warmup-updates 4000 \
  --dropout 0.3 --weight-decay 0.0 \
  --criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
  --max-tokens 4096 \
  --save-dir data/mt-ckpt-transformer

注意,这甚至可能在你的笔记本电脑上都无法运行。你真的需要 GPU 来训练 Transformer 模型。还要注意,即使有 GPU,训练也可能需要几个小时。更多关于使用 GPU 的信息请参见第 11.5 节。

这里出现了一些神秘的参数,但您不需要担心。当您运行此命令时,您可以看到模型结构。整个模型转储相当长,因此我们在清单 8.1 中省略了一些中间层。仔细观察,您会发现层次结构与我们之前显示的图形相对应。

清单 8.1 Fairseq 的 Transformer 模型转储

TransformerModel(
  (encoder): TransformerEncoder(
    (embed_tokens): Embedding(16832, 512, padding_idx=1)
    (embed_positions): SinusoidalPositionalEmbedding()
    (layers): ModuleList(
      (0): TransformerEncoderLayer(
        (self_attn): MultiheadAttention(                                   ❶
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (self_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_
         affine=True)
        (fc1): Linear(in_features=512, out_features=2048, bias=True)       ❷
        (fc2): Linear(in_features=2048, out_features=512, bias=True)
        (final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
      )
      ...
      (5): TransformerEncoderLayer(
        (self_attn): MultiheadAttention(
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (self_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
        (fc1): Linear(in_features=512, out_features=2048, bias=True)
        (fc2): Linear(in_features=2048, out_features=512, bias=True)
        (final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
      )
    )
  )
  (decoder): TransformerDecoder(
    (embed_tokens): Embedding(11416, 512, padding_idx=1)
    (embed_positions): SinusoidalPositionalEmbedding()
    (layers): ModuleList(
      (0): TransformerDecoderLayer(
        (self_attn): MultiheadAttention(                                   ❸
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (self_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
        (encoder_attn): MultiheadAttention(                                ❹
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (encoder_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_
         affine=True)
        (fc1): Linear(in_features=512, out_features=2048, bias=True)       ❺
        (fc2): Linear(in_features=2048, out_features=512, bias=True)
        (final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
      )
      ...
      (5): TransformerDecoderLayer(
        (self_attn): MultiheadAttention(
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (self_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
        (encoder_attn): MultiheadAttention(
          (out_proj): Linear(in_features=512, out_features=512, bias=True)
        )
        (encoder_attn_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_
         affine=True)
        (fc1): Linear(in_features=512, out_features=2048, bias=True)
        (fc2): Linear(in_features=2048, out_features=512, bias=True)
        (final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
      )
    )
  )
)

❶ 编码器的自注意力

❷ 编码器的前馈网络

❸ 解码器的自注意力

❹ 解码器的编码器-解码器

❺ 解码器的前馈网络

当我运行时,验证损失在大约第 30 个时期后收敛,此时您可以停止训练。我将同一组西班牙语句子翻译成英文的结果如下:

¡ Buenos días !
S-0     ¡ Buenos días !
H-0     -0.0753164291381836     Good morning !
P-0     -0.0532 -0.0063 -0.1782 -0.0635
¡ Hola !
S-1     ¡ Hola !
H-1     -0.17134985327720642    Hi !
P-1     -0.2101 -0.2405 -0.0635
¿ Dónde está el baño ?
S-2     ¿ Dónde está el baño ?
H-2     -0.2670585513114929     Where 's the toilet ?
P-2     -0.0163 -0.4116 -0.0853 -0.9763 -0.0530 -0.0598
¿ Hay habitaciones libres ?
S-3     ¿ Hay habitaciones libres ?
H-3     -0.26301929354667664    Are there any rooms available ?
P-3     -0.1617 -0.0503 -0.2078 -1.2516 -0.0567 -0.0532 -0.0598
¿ Acepta tarjeta de crédito ?
S-4     ¿ Acepta tarjeta de crédito ?
H-4     -0.06886537373065948    Do you accept credit card ?
P-4     -0.0140 -0.0560 -0.0107 -0.0224 -0.2592 -0.0606 -0.0594
La cuenta , por favor .
S-5     La cuenta , por favor .
H-5     -0.08584468066692352    The bill , please .
P-5     -0.2542 -0.0057 -0.1013 -0.0335 -0.0617 -0.0587
Maria no daba una bofetada a la bruja verde .
S-6     Maria no daba una bofetada a la bruja verde .
H-6     -0.3688890039920807     Mary didn 't slapped the green witch .
P-6     -0.2005 -0.5588 -0.0487 -2.0105 -0.2672 -0.0139 -0.0099 -0.1503 -0.0602

大多数英文翻译几乎完美。令人惊讶的是,模型几乎完美地翻译了最困难的句子(“Maria no daba . . .”)。这可能足以说服我们,Transformer 是一个强大的翻译模型。在它的出现之后,这个模型成为了研究和商业机器翻译的事实标准。

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

相关文章
|
6月前
|
机器学习/深度学习 自然语言处理 PyTorch
真实世界的自然语言处理(二)(1)
真实世界的自然语言处理(二)
73 1
|
6月前
|
机器学习/深度学习 自然语言处理 异构计算
真实世界的自然语言处理(三)(2)
真实世界的自然语言处理(三)
49 3
|
6月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(三)(1)
真实世界的自然语言处理(三)
45 3
|
6月前
|
机器学习/深度学习 自然语言处理 数据可视化
真实世界的自然语言处理(一)(4)
真实世界的自然语言处理(一)
33 2
|
6月前
|
机器学习/深度学习 JSON 自然语言处理
真实世界的自然语言处理(一)(5)
真实世界的自然语言处理(一)
60 1
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
真实世界的自然语言处理(一)(3)
真实世界的自然语言处理(一)
38 1
|
6月前
|
机器学习/深度学习 自然语言处理 监控
真实世界的自然语言处理(一)(2)
真实世界的自然语言处理(一)
58 1
|
6月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(5)
真实世界的自然语言处理(二)
42 0
|
6月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(3)
真实世界的自然语言处理(二)
47 0
|
6月前
|
机器学习/深度学习 自然语言处理 算法
真实世界的自然语言处理(二)(2)
真实世界的自然语言处理(二)
86 0