【DL】第 6 章:文本生成转换器

在本章中,您将:

  • 从概念上了解注意力机制如何模仿我们对句子中某些词的重视程度高于其他词的方式

  • 从第一原则深入研究注意力机制的工作原理,包括如何创建和操作查询、键和值

  • 了解因果掩蔽对于文本生成任务的重要性

  • 了解如何将注意力头分组为多头注意力层

  • 查看多头注意力层如何构成 Transformer Block 的一部分,其中还包括层归一化和跳过连接

  • 创建位置编码,捕获每个标记的位置以及词标记嵌入。

  • 在 Keras 中构建一个 Transformer,以生成葡萄酒评论

  • 分析 Transformer 模型的输出,包括询问注意力分数以检查模型正在寻找的位置。

  • 了解 Transformer 的三个系列,包括每个系列可以处理的任务类型的示例以及最著名的最先进实现的描述。


我们在第 5 章中看到了如何使用递归神经网络 (RNN)(例如 LSTM 和 GRU)在文本数据上构建生成模型。这些自回归模型一次处理一个标记的顺序数据,不断更新捕获输入的当前潜在表示的隐藏向量。RNN 可以设计为通过在隐藏向量上应用密集层和 softmax 激活来预测序列中的下一个单词。在 2017 年之前,这被认为是生成文本的最复杂方法,当时一篇论文永远改变了文本生成的格局。

Google Brain 的论文自信地题为“Attention Is All You Need” ,以引入“注意力”的概念而闻名——一种现在为大多数最先进的文本生成模型提供支持的机制。

作者展示了如何创建称为 Transformers 的强大神经网络用于顺序建模,不需要复杂的循环或卷积架构,而是仅依赖注意力机制。这种方法克服了 RNN 方法的一个关键缺点,即并行化具有挑战性,因为它必须一次处理一个标记的序列。变形金刚是高度可并行化的,允许它们在海量数据集上进行训练。

Transformer 架构现在支持一些最令人印象深刻的生成建模实例,例如用于语言任务的 Google BERT 和 OpenAI 的 GPT-3 以及用于音乐生成的 MuseNet。

在本章中,我们将深入研究现代文本生成模型如何利用 Transformer 架构在文本生成挑战中达到最先进的性能。

数据准备

我们将使用可通过 Kaggle 获得的Wine Reviews 数据集。这是一组超过 130,000 条葡萄酒评论,以及描述和价格等元数据。

您可以通过运行图书存储库中的 Kaggle 数据集下载器脚本来下载数据集,如示例 6-1所示。这会将葡萄酒评论和附带的元数据本地保存到/data文件夹中。

示例 6-1 :下载 Wine Review 数据集
bash scripts/download_kaggle_data.sh zynicide wine-reviews

数据准备步骤与第 5 章中准备输入 LSTM 的数据时遇到的步骤相同,因此我们不会在这里详细重复。步骤如图6.1 所示:

  1. 加载数据并创建每种酒的文本字符串描述列表。

  1. 用空格填充标点符号,使每个标点符号都被视为一个单独的

  1. 将字符串传递通过一个TextVectorization层,该层将数据和垫/剪辑标记为固定长度。

  1. 创建一个训练集,其中输入是标记化的文本字符串,要预测的输出是相同的字符串移位一个标记。

图6.1 Transformer 的数据处理。

The Transformer

了解 Transformer 如何工作的第一步是了解注意力机制的工作原理。注意力机制使 Transformer 与众不同,并且有别于语言建模的循环方法。当我们对注意力有了深入的了解后,我们就会看到它是如何在 Transformer 架构中使用的。

注意力

当你写作时,你对句子中下一个词的选择会受到你已经写过的其他词的影响。例如,假设你开始一个句子如下:

“The pink elephant tried to get into the car but it was too”

显然下一个词应该是big的同义词。我们怎么知道呢?句子中的某些其他词对于帮助我们做出决定很重要。例如,它是一头大象,而不是树懒,这意味着我们选择而不是。如果它是一个游泳池,而不是汽车,我们可能会选择scared作为big的可能替代品。最后,进入汽车的动作暗示尺寸是问题所在——如果大象试图压扁汽车,我们可能会选择快速作为最后的词。

句子中的其他词根本不重要。例如,大象是粉红色的这一事实对我们选择最终词没有影响。同样,句子中的次要词(the、but、it 等)给出了句子的语法形式,但在这里对于确定所需的形容词并不重要。

换句话说,我们正在注意句子中的某些词,而在很大程度上忽略了其他词。如果我们的模型可以做同样的事情,那岂不是很棒!

注意力机制(也称为注意力头) 中的 Transformer 就是为了做到这一点而设计的。它能够决定它想要从中提取信息的输入位置,以便有效地提取有用的信息,而不会被不相关的细节所混淆。这使得它在各种情况下都非常灵活,因为它可以决定在推理时要在哪里寻找信息。相比之下,循环层试图建立一个通用的隐藏状态,在每个时间步捕获输入的整体表示。这种方法的一个弱点是许多已经包含在隐藏向量中的单词将不会与手头的直接任务直接相关(例如预测下一个单词),正如我们刚刚看到的那样。注意头不会遇到这个问题,

查询、键和值

那么注意力头如何决定它想在哪里寻找信息呢?在我们进入细节之前,让我们通过暂时拟人化我们的粉红色大象示例中的单词来描述它是如何在高层次上工作的……

想象一下,最后一个词想预测句子中下一个词是什么。为了帮助完成这项任务,前面的其他词与他们的观点相呼应,但他们的贡献取决于他们对自己预测后续词的专业知识的信心程度。例如,“大象”这个词可能会自信地表明它更可能是一个与大小或响度相关的词,而“是”这个词并不能提供太多信息来缩小可能性。

换句话说,我们可以将 attention head 视为一种检索系统,其中对键值存储(句子中的其他词及其意见)进行查询(“后面还有什么词?”)和结果输出是值的总和,由查询和每个键之间的共振加权。

我们现在将详细介绍该过程(图6.2 ),再次参考我们的粉红色大象句子。

图6.2 注意头的机制。

查询可以被认为是手头当前任务的表示(例如“后面还有什么词?”)在这个例子中,它是从单词的嵌入中派生出来的,通过一个权重矩阵将它传递到W_Q将向量的维数从 更改embed_dim为key_dim。

关键向量是句子中每个单词的表示——您可以将它们视为对每个单词可以帮助完成的预测任务类型的描述它们以与查询类似的方式派生,通过将每个嵌入传递给权重矩阵W_K以将每个向量的维数从 更改embed_dim为key_dim。请注意,键和查询的长度相同 ( key_dim)。

值向量也是句子中单词的表示——您可以将它们视为每个单词的实际贡献它们是通过将每个嵌入传递给权重矩阵W_V以将每个向量的维数从 更改为embed_dim来导出的val_dim。请注意,值向量不一定必须与键和查询具有相同的长度(但为简单起见,通常如此)。

在 attention head 中,每个键都与查询进行比较,通过使用缩放的点积QK^T / sqrt(key_dim)——这就是键和查询必须具有相同长度的原因。对于特定的键-查询对,这个数字越高,键与查询的共鸣就越大,因此它可以对注意力头的输出做出更大的贡献。将 softmax 应用于缩放后的点积,以确保贡献总和为 1。

然后将值向量乘以这些权重并相加得到注意力头的最终输出 - 一个长度向量val_dim。该向量从句子中的单词中捕获混合意见,以预测后面跟着什么单词

多头注意力

没有理由只停留在一个关注点上!在 Keras 中,我们可以构建一个MultiHeadAttention层,连接多个注意力头的输出,让每个注意力头学习不同的注意力机制,这样整个层就可以学习更复杂的关系。

连接的输出通过一个最终的权重矩阵W_O传递,以将向量投影到所需的输出维度,在我们的例子中与查询的输入维度相同 ( embed_dim),以便层可以按顺序堆叠在彼此

图6.3 显示了层的输出MultiHeadAttention是如何构造的。在 Keras 中,我们可以简单地编写示例 6-2中所示的行来创建这样的层。

示例 6-2 :在 Keras 中创建多头注意力层
keras.layers.MultiHeadAttention(
    num_heads = 4, 
    key_dim = 128, 
    value_dim = 64, 
    output_shape = 256 
    )

1.这个多头注意力层有 4 个头

2.键(和查询)是长度为 128 的向量

3.这些值(因此也是每个头的输出)是长度为 64 的向量

4.输出向量的长度为 256

图6.3 具有四个头的多注意层

因果掩蔽

到目前为止,我们假设输入到注意力头的查询是单个向量。然而,为了提高训练效率,理想情况下我们希望注意力层能够同时对输入中的每个单词进行操作,预测每个单词的后续单词。换句话说,我们希望 Transformer 能够并行处理一组查询向量(即矩阵)。

您可能认为我们可以将向量一起批处理成一个矩阵,然后让线性代数处理其余部分。这是真的,但我们需要一个额外的步骤——我们需要对查询键点积应用掩码,以避免未来单词的信息泄漏。如图6.4 所示。

图6.4 对一批输入查询的注意力分数进行矩阵计算,使用因果注意力掩码来隐藏查询不可用的键(因为它们出现在句子的后面)。

如果没有这个掩码,Transformer 将能够完美地猜出句子中的下一个单词,因为它将使用单词本身的键作为特征!用于创建因果掩码的代码如示例 6-3所示,生成的 numpy 数组(转置以匹配图表)如图6.5 所示。

示例 6-3 :因果掩码函数
def causal_attention_mask(batch_size, n_dest, n_src, dtype):
    i = tf.range(n_dest)[:, None]
    j = tf.range(n_src)
    m = i >= j - n_src + n_dest
    mask = tf.cast(m, dtype)
    mask = tf.reshape(mask, [1, n_dest, n_src])
    mult = tf.concat(
        [tf.expand_dims(batch_size, -1), tf.constant([1, 1], dtype=tf.int32)], 0
    )
    return tf.tile(mask, mult)

np.transpose(causal_attention_mask(1, 10, 10, dtype = tf.int32)[0])

图6.5 作为 numpy 数组的因果掩码 - 1 表示注意,0 表示不注意。

我们对所有变形金刚中存在的多头注意力机制的解释到此结束。值得注意的是,这样一个有影响力的层的可学习参数仅包括每个注意力头的 3 个密集连接的权重矩阵 ( W_Q, W_K, W_V) 和一个用于重塑输出的权重矩阵 ( W_O)。在多注意力层中根本没有卷积或循环机制!

接下来,我们将退后一步,看看多头注意力层是如何形成一个更大的组件的一部分,这个组件被称为 Transformer Block。

The Transformer Block

Transformer Block 是 Transformer 中的单个组件,它在多头注意力层周围应用一些跳跃连接、前馈(密集)层和归一化。图6.6 显示了变压器块的示意图。

图6.6 Transformer Block

首先,请注意查询是如何绕过多头注意力层传递以添加到输出的——这是一个跳跃连接,在现代深度学习架构中很常见。这意味着我们可以构建非常深的神经网络,而不会受到梯度消失问题的影响,因为跳跃连接提供了一条无梯度的高速公路,允许网络不间断地向前传输信息。

其次,在 Transformer Block 中使用层归一化来为训练过程提供稳定性。我们已经在整本书中看到了批量归一化层的实际应用,其中每个通道的输出都被归一化为零均值和 1 的标准差。归一化统计数据是在批次和空间维度上计算的。

相反,Transformer 中的层归一化通过计算跨通道的归一化统计数据来归一化批处理中每个序列的每个位置。就规范化统计数据的计算方式而言,它与批量规范化完全相反。图6.7 显示了批归一化和层归一化之间的区别。

图6.7 Layer Normalization,与 Batch Normalization 相对。标准化统计数据是在蓝色单元格中计算的(来源:https ://arxiv.org/pdf/2003.07845.pdf

层归一化在最初的 Transformer 论文中使用,通常用于基于文本的任务,以避免在批处理中跨序列创建归一化依赖关系。然而,最近的工作,如https://arxiv.org/pdf/2003.07845.pdf挑战了这一假设,表明通过一些调整,一种批归一化形式仍然可以在 Transformers 中使用,优于更传统的层归一化。

最后,Transformer Block 中包含一组前馈(即密集连接)层,以允许组件在我们深入网络时提取更高级别的特征。

[Link to Come] 中显示了 Transformer Block 的 Keras 实现。

示例 6-4 :因果掩码函数
class TransformerBlock(keras.layers.Layer):
    def __init__(self, num_heads, key_dim, embed_dim, ff_dim, dropout_rate=0.1): 
        super(TransformerBlock, self).__init__()
        self.num_heads = num_heads
        self.key_dim = key_dim
        self.embed_dim = embed_dim
        self.ff_dim = ff_dim
        self.dropout_rate = dropout_rate
        self.attn = keras.layers.MultiHeadAttention(num_heads, key_dim, output_shape = embed_dim)
        self.dropout_1 = keras.layers.Dropout(self.dropout_rate)
        self.ln_1 = keras.layers.LayerNormalization(epsilon=1e-6)
        self.ffn_1 = keras.layers.Dense(self.ff_dim, activation="relu")
        self.ffn_2 = keras.layers.Dense(self.embed_dim)
        self.dropout_2 = keras.layers.Dropout(self.dropout_rate)
        self.ln_2 = keras.layers.LayerNormalization(epsilon=1e-6)

    def call(self, inputs):
        input_shape = tf.shape(inputs)
        batch_size = input_shape[0]
        seq_len = input_shape[1]
        causal_mask = causal_attention_mask(batch_size, seq_len, seq_len, tf.bool) 
        attention_output, attention_scores = self.attn(inputs, inputs, attention_mask=causal_mask, return_attention_scores=True) 
        attention_output = self.dropout_1(attention_output)
        out1 = self.ln_1(inputs + attention_output) 
        ffn_1 = self.ffn_1(out1) 
        ffn_2 = self.ffn_2(ffn_1)
        ffn_output = self.dropout_2(ffn_2)
        return (self.ln_2(out1 + ffn_output), attention_scores) 

1.组成TransformerBlock层的子层在初始化函数中定义

2.创建因果掩码以隐藏查询中的未来键

3.创建多头注意力层,指定注意力掩码

4.第一个添加和归一化

5.前馈层

6.第二个添加和归一化

位置编码

在我们将所有东西放在一起训练我们的 Transformer 之前还有最后一步!您可能已经注意到,在多头注意力层中,没有任何东西关心查询之前的键的顺序。每个键和查询之间的点积是并行处理的,而不是顺序处理的,就像在递归神经网络中一样。这是一个优势(因为并行化效率的提高)但也是一个问题,因为我们显然需要注意力层能够为以下两个句子预测不同的输出:

  • The dog looked at the boy and … (barked?)

  • The boy looked at the dog and … (smiled?)

为了解决这个问题,我们在创建初始 Transformer Block 的输入时使用了一种称为位置编码的技术。我们不仅使用标记嵌入对每个标记进行编码,还使用位置嵌入对标记的位置进行编码。

令牌嵌入是使用标准Embedding层创建的,可将每个令牌转换为学习向量。我们还可以以相同的方式创建位置嵌入,使用标准Embedding层将每个整数位置转换为学习向量。最初的 Transformer 论文使用三角函数来计算每个位置的嵌入——我们将在下一章探索音乐生成时介绍这一点。

为了构造联合令牌/位置编码,将令牌嵌入添加到位置嵌入中,如图6.8 所示。这样,序列中每个单词的含义和位置都被捕获在一个向量中。

图6.8 标记嵌入被添加到位置嵌入以给出标记位置编码

定义层的代码如示例 6-5TokenAndPositionEmbedding所示。

示例 6-5 :图层TokenAndPositionEmbedding_
class TokenAndPositionEmbedding(keras.layers.Layer):
    def __init__(self, maxlen, vocab_size, embed_dim):
        super(TokenAndPositionEmbedding, self).__init__()
        self.maxlen = maxlen
        self.vocab_size =vocab_size
        self.embed_dim = embed_dim
        self.token_emb = keras.layers.Embedding(input_dim=vocab_size, output_dim=embed_dim) 
        self.pos_emb = keras.layers.Embedding(input_dim=maxlen, output_dim=embed_dim) 

    def call(self, x):
        maxlen = tf.shape(x)[-1]
        positions = tf.range(start=0, limit=maxlen, delta=1)
        positions = self.pos_emb(positions)
        x = self.token_emb(x)
        return x + positions 

1.Embedding使用层嵌入令牌

2.令牌的位置也使用Embedding层嵌入

3.该层的输出是标记和位置嵌入的总和

训练 Transformer

现在我们准备好构建我们的 Transformer 了!

最初,我们将专注于可以生成文本的 Transformer,如OpenAI 的原始 GPT 论文 ( https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf ) 中所述总体架构如图6-9所示。

图6.9 生成文本的Transformer模型架构(来源:https ://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf

网络的最终输出是一个简单的Dense层,对词汇表中的单词数量进行 softmax。我们可以在 [Link to Come] 中看到模型是如何构建的——为简单起见,我们将只使用一个 Transformer Block,而不是论文中的 12 个。

示例 6-6 :在 Keras 中生成文本的 Transformer 模型
MAX_LEN = 80
VOCAB_SIZE = 10000
EMBEDDING_DIM = 256
N_HEADS = 2
KEY_DIM = 256
FEED_FORWARD_DIM = 256

inputs = keras.layers.Input(shape=(None,), dtype=tf.int32) 
x = TokenAndPositionEmbedding(MAX_LEN, VOCAB_SIZE, EMBEDDING_DIM)(inputs) 
x, attention_scores = TransformerBlock(N_HEADS, KEY_DIM, EMBEDDING_DIM, FEED_FORWARD_DIM)(x) 
outputs = keras.layers.Dense(VOCAB_SIZE, activation = 'softmax')(x) 
model = keras.Model(inputs=inputs, outputs=[outputs, attention]) 
model.compile("adam", loss=[keras.losses.SparseCategoricalCrossentropy(), None]) 
model.fit(train_ds, epochs=5)

1.被Input填充(用零)

2.文本使用TokenAndPositionEmbedding层进行编码

3.编码通过一个TransformerBlock

4.转换后的输出通过具有 softmax 激活的层传递Dense,以预测后续单词的分布。

5.Transformer 模型将一系列单词标记作为输入,并输出预测的后续单词分布。Transformer Block 的输出也被返回,这样我们就可以检查模型是如何引导它的注意力的。

6.该模型是SparseCategoricalCrossentropy在预测的单词分布上有损失的情况下编译的。

分析Transformer

现在我们已经编译和训练了 Transformer,我们可以开始使用它来生成长文本字符串了。我们还可以查询从中输出的注意力分数TransformerBlock,以了解 Transformer 在生成过程中的不同点在哪里寻找信息。

生成文本

我们可以通过应用以下过程来生成新文本:

  1. 向网络提供现有的单词序列,并要求它预测下一个单词。

  1. 将这个词附加到现有序列并重复。

网络将为我们可以从中采样的每个单词输出一组概率。因此,我们可以使文本生成是随机的,而不是确定性的。此外,我们可以temperature在采样过程中引入一个参数来指示我们希望该过程的确定性。

这使用与第 5 章中用于 LSTM 文本生成的相同TextGenerator,因此为了简洁起见,我们不会在这里重复代码块。

让我们看一下在两个不同temperature值下的实际情况(图6.10 )。

图6.10 在temperature= 1.0 和temperature= 0.5 时生成输出。

关于这两段经文,有几点需要注意。首先,两者在风格上都类似于来自原始训练集的葡萄酒评论。它们都以葡萄酒的产区和类型开头,并且葡萄酒类型在整个段落中保持一致(例如,它不会在中途切换颜色)。正如我们之前在第 5 章中看到的那样,生成的温度为 1.0 的文本比温度为 0.2 的示例更具冒险性,因此更不准确。因此,生成多个温度为 1.0 的样本将导致更多的变化,因为模型是从具有更大方差的概率分布中采样的。

查看注意力分数

我们还可以要求模型告诉我们在决定句子中的下一个词时对每个词的关注程度。输出TransformerBlock每个头的注意力分数,这是句子中前面单词的 softmax 分布(见图6.4 )。

为了证明这一点,图6.11 显示了三个不同输入提示的概率最高的前 5 个标记,以及两个头对每个前面单词的平均注意力。

图 6.11 遵循各种序列的单词概率分布。前面的词根据它们的平均注意力得分着色,即两个注意力头的平均值。深蓝色表示更多的注意力被放在这个词上。

在第一个示例中,模型密切关注国家(德国),以便确定与该地区相关的词。这是有道理的!为了确定葡萄酒来自哪个地区,它需要从与国家相关的词语中获取大量信息,以确保它们匹配。它不需要过多关注前两个标记(葡萄酒评论),因为它们不包含有关该地区的任何有用信息。

在第二个例子中,它需要回溯到葡萄(雷司令),所以要注意第一次提到它的时间。它可以通过直接关注单词来提取此信息,无论它在句子中有多远(在 80 个单词的上限内)。请注意,这与循环神经网络有很大不同,循环神经网络依赖于隐藏状态来维护序列长度上的所有有趣信息,以便在需要时可以利用它,这是非常低效的。

最后的序列显示了一个示例,说明 Transformer 如何根据信息组合选择合适的形容词。这里的注意力再次集中在葡萄(雷司令)上,但也集中在它含有残糖的事实上。由于雷司令通常是一种甜酒,并且已经提到了糖分,因此将其描述为略带甜味而不是略带泥土味是有道理的。

能够以这种方式询问网络,准确了解它从哪里提取信息,以便对每个后续单词做出准确的决定,这是令人难以置信的信息量。我强烈建议您尝试输入提示,看看您是否可以让模型关注句子中真正后面的单词,让您自己相信基于注意力的模型比更传统的循环模型更强大!

Transformer种类

到目前为止,我们已经构建了一种称为解码器的 Transformer - 它一次生成一个标记的单个文本字符串,并且是完全自引用的,仅从输入字符串中的先前单词中提取信息。

对于语言翻译等其他任务,我们需要使用一种略有不同的模型架构,称为编码器-解码器Transformer,因为我们要将一个文本字符串翻译成另一个文本字符串。正如您可能猜到的,还有第三种类型的 Transformer,称为编码,它专注于提取文本输入的有意义的上下文表示,而不是文本生成。

因此存在三种不同的高级 Transformer 架构,encoder-decoderencoderdecoder。在表6-1中,我们展示了三种架构的汇总表,其中包含每种架构中最著名的示例以及每种模型类型的典型用例。

表 6-1 :三种高层Transformer架构

类型

例子

用例

编码器-解码器

T5 (Google)

总结、翻译、答疑

编码器

BERT (Google)

句子分类,命名实体识别,抽取式问答

解码器

GPT-3(OpenAI)

文本生成

在以下部分中,我们将详细介绍每种模型类型。

编码器-解码器架构

最初的 Transformer 论文侧重于语言翻译——即将一种语言的文本输入转换为另一种语言的输出。请注意,这与我们在本章中解决的挑战不同,因为新语言的文本生成需要以原始语言的整个输入字符串为条件。原始论文通过引入编码器-解码器Transformer 架构解决了这个问题,如图6.12 所示。

图6.12 编码器-解码器 Transformer 模型(来源: https: //arxiv.org/abs/1706.03762

这个图的大部分内容我们已经很熟悉了——我们可以看到 Transformer 块被重复,位置嵌入被用来捕获输入序列的顺序。该模型与我们在本章前面构建的解码器模型之间的两个主要区别如下:

  • 在左侧,一组 N = 6 个编码器Transformer 块对要翻译的序列进行编码。请注意,注意力层上没有因果掩蔽。这是因为我们没有生成更多的文本来扩展要翻译的序列,而是我们只想学习整个序列的良好表示,可以将其提供给解码器。因此,编码器中的注意力层可以完全暴露,以捕获单词之间的所有交叉依赖关系,无论顺序如何。

  • 在右侧,一组 N = 6 个解码器Transformer 块生成翻译后的文本。初始注意力层是自引用的(即键、值和查询来自相同的输入),并且使用因果掩蔽来确保来自未来标记的信息不会泄漏到要预测的当前词。然而,我们可以看到随后的注意力层从编码器中提取键和值,只留下从解码器本身传递过来的查询。这称为交叉引用注意力,意味着解码器可以关注要翻译的输入序列的编码器表示。这就是解码器如何知道翻译需要传达什么意思!

图6.13 显示了交叉引用注意的示例。当在街道上下文中使用时,解码器层的两个注意力头能够协同工作,为单词the提供正确的德语翻译。在德语中,根据名词的性别,有三个定冠词(der、die、das),但 Transformer 知道选择die,因为一个注意力头能够注意单词street(德语中的阴性词),而另一个负责翻译单词 ( the )。此示例来自 TensorFlow GitHub 存储库,其中包含一个Colab 笔记本您可以在其中试用训练有素的 Transformer 模型,了解编码器和解码器的注意力机制如何影响将给定句子翻译成德语。

图6.13 一个注意力头如何注意“the”这个词,另一个注意力头如何注意“street”这个词,以便将“the”这个词正确地翻译成德语单词“die”,作为女性定冠词翻译成“Straße”

使用编码器-解码器结构的现代 Transformer 的一个示例是 Google 的 T5 模型 ( https://arxiv.org/abs/1910.10683 )。这将一系列任务重新构建为文本到文本的框架,包括翻译、语言可接受性、句子相似性和文档摘要,如图6.14 所示。

图6.14 T5 如何将一系列任务重构为文本到文本框架的示例,包括翻译、语言可接受性、句子相似性和文档摘要(来源:https: //ai.googleblog.com/2020/02/exploring-transfer -learning-with-t5.html )

解码器码Transformer

我们已经在本章中构建了解码器 Transformer 的示例。解码器 Transformer 包含因果掩码,用于生成文本。

解码器模型的一个著名示例是GPT-3 - 开发的模型由 OpenAI 开发,经过训练可以预测一段文本中的下一个单词。GPT-3 的输出示例,给出系统提示语句,如图6.15 所示。

图6.15 GPT-3 如何扩展给定系统提示的示例(来源:https: //beta.openai.com/playground

虽然 GPT-3 的模型权重不是开源的,但该模型可作为商业工具和 API 使用。

MuseNet是一个模型同样由 OpenAI 发布,将 Transformer 架构应用于音乐生成,我们将在下一章进一步探讨。与 GPT-3 一样,它是一种解码器架构,经过训练可以在给定一系列先前音符的情况下预测下一个音符。

在音乐生成任务中,序列N的长度随着音乐的进行而变大,这意味着每个头的N × N注意力矩阵的存储和计算成本变得很高。我们不能只剪辑输入序列,因为我们希望模型围绕长期结构构建乐曲,并重述几分钟前的主题和短语,就像人类作曲家的情况一样。

为了解决这个问题,MuseNet 使用了一种称为稀疏变换器的变换器形式。注意矩阵中的每个输出位置仅计算输入位置子集的权重,从而降低了训练模型所需的计算复杂性和内存。因此,MuseNet 可以全神贯注地操作 4,096 个标记,并且可以学习各种风格的长期结构和旋律结构。(例如,参见OpenAI’s Chopin and Mozart recordings on SoundCloud.)

编码器Transformer

编码器 Transformer 只是一个没有因果掩码的解码器 Transformer。它们不用于文本生成,因此允许令牌关注任何其他令牌以捕获所有交叉依赖性,无论顺序如何。相反,它们用于需要从整体上理解输入的任务,例如句子分类、命名实体识别和提取式问题回答(例如,找到给定文本块的答案)。

它们通常以自回归方式进行训练,通过破坏给定的输入(例如通过屏蔽其中的随机单词)然后要求模型尝试重建原始句子。

编码器 Transformer 的一个著名示例是 BERT(Bidirectional Encoder Representations from Transformers) - Google 开发的一种模型,可预测句子中缺失的单词,给定所有层中缺失单词前后的上下文。

通过屏蔽语言模型实现这一点:在训练期间,15% 的单词被随机屏蔽掉,并且模型必须尝试在给定屏蔽输入的情况下重新创建原始句子。至关重要的是,标记为屏蔽的标记中有 10% 实际上与另一个词交换,而不是标记<MASK>,因此模型不仅必须学习如何<MASK>用实际单词替换标记,而且还应该寻找输入中的单词似乎不合适的句子,因为它们可能是已切换的单词。

这BERT 学习到的单词表示优于 GloVe 等对应物,因为它们会根据单词的上下文而变化。例如,这个词既可以用作动词(我需要给植物浇水),也可以用作名词(海洋里满是水)。无论上下文如何,GloVe 向量都为单词water分配完全相同的表示,而 BERT 结合周围信息为上下文中的单词创建定制表示。

可以通过附加特定于给定下游任务的输出层来构建 BERT。例如,情感分析等分类任务可以通过在 Transformer 输出之上添加一个分类层来构建,而问答任务可以通过使用指针网络作为 BERT 的输出层在输入序列中标记答案来解决。通过从预训练的 BERT 模型开始并微调附加的输出层,可以为各种建模任务快速训练极其复杂的语言模型。

概括

在本章中,我们介绍了 Transformer——一种最先进的文本生成技术。

Transformer 使用一种称为注意力的机制,它消除了对循环层(例如 LSTM)的需求。它的工作方式类似于信息检索系统,利用查询、键和值来决定要从每个输入令牌中提取多少信息。

注意力头可以组合在一起形成所谓的多头注意力层。然后将它们包裹在一个 Transformer Block 中,其中包括层规范化和跳过关注层周围的连接。Transformer 块可以堆叠以创建非常深的神经网络。

Causal masking 用于确保 Transformer 不会将下游令牌中的信息泄漏到当前预测中。此外,一种称为位置编码的技术用于确保输入序列的顺序不会丢失,而是与传统的词嵌入一起被烘焙到输入中。

在分析 Transformer 的输出时,我们发现它不仅可以生成新的文本段落,还可以询问网络的注意力层,以了解它在句子中的哪个位置寻找信息以改进其预测。Transformer 可以远距离访问信息而不会丢失信号,因为注意力分数是并行计算的,而不依赖于按顺序通过网络传递的隐藏状态,就像递归神经网络的情况一样。

我们看到了 Transformer 的三个家族——编码器-解码器、解码器和编码器,以及每个家族可以完成的不同任务。最后,我们还探讨了这三种模型的三个著名示例——T5、GPT-3 和 BERT。

猜你喜欢

转载自blog.csdn.net/sikh_0529/article/details/129369405
今日推荐