关于人工智能:编码器-基于-Transformers-的编码器解码器模型

2次阅读

共计 6668 个字符,预计需要花费 17 分钟才能阅读完成。

基于 transformer 的编码器 - 解码器模型是 表征学习 模型架构 这两个畛域多年研究成果的结晶。本文简要介绍了神经编码器 - 解码器模型的历史,更多背景常识,倡议读者浏览由 Sebastion Ruder 撰写的这篇精彩 博文。此外,倡议读者对 自注意力 (self-attention) 架构 有一个根本理解,能够浏览 Jay Alammar 的 这篇博文 温习一下原始 transformer 模型。

本文分 4 个局部:

  • 背景 – 简要回顾了神经编码器 - 解码器模型的历史,重点关注基于 RNN 的模型。
  • 编码器 - 解码器 – 论述基于 transformer 的编码器 - 解码器模型,并论述如何应用该模型进行推理。
  • 编码器 – 论述模型的编码器局部。
  • 解码器 – 论述模型的解码器局部。

每个局部都建设在前一部分的根底上,但也能够独自浏览。这篇分享是第三局部 编码器

编码器

如前一节所述,基于 transformer 的编码器将输出序列映射到上下文相干的编码序列:

$$ f_{\theta_{\text{enc}}}: \mathbf{X}_{1:n} \to \mathbf{\overline{X}}_{1:n} $$

仔细观察架构,基于 transformer 的编码器由许多 残差注意力模块 重叠而成。每个编码器模块都蕴含一个 双向 自注意力层,其后跟着两个前馈层。这里,为简略起见,咱们疏忽归一化层 (normalization layer)。此外,咱们不会深刻探讨两个前馈层的作用,仅将其视为每个编码器模块 \({}^1 \) 的输入映射层。双向自留神层将每个输出向量 \(\mathbf{x’}_j, \forall j \in {1, \ldots, n} \) 与全副输出向量 \(\mathbf{x’}_1, \ldots, \mathbf{x’}_n \) 相关联并通过该机制将每个输出向量 \(\mathbf{x’}_j \) 提炼为与其本身上下文相干的表征: \(\mathbf{x”}_j \)。因而,第一个编码器块将输出序列 \(\mathbf{X}_{1:n} \) (如下图浅绿色所示) 中的每个输出向量从 上下文无关 的向量表征转换为 上下文相干 的向量表征,前面每一个编码器模块都会进一步细化这个上下文表征,直到最初一个编码器模块输入最终的上下文相干编码 \(\mathbf{\overline{X}}_{1:n} \) (如下图深绿色所示)。

咱们对 编码器如何将输出序列 "I want to buy a car EOS" 变换为上下文编码序列 这一过程进行一下可视化。与基于 RNN 的编码器相似,基于 transformer 的编码器也在输出序列最初增加了一个 EOS,以提醒模型输出向量序列已完结 \({}^2 \)。

上图中的 基于 transformer 的编码器由三个编码器模块组成。咱们在右侧的红框中具体列出了第二个编码器模块的前三个输出向量: \(\mathbf{x}_1 \),\(\mathbf {x}_2 \) 及 \(\mathbf{x}_3 \)。红框下部的全连贯图形容了双向自注意力机制,下面是两个前馈层。如前所述,咱们次要关注双向自注意力机制。

能够看出,自注意力层的每个输入向量 \(\mathbf{x”}_i, \forall i \in {1, \ldots, 7} \) 都 间接 依赖于 所有 输出向量 \(\mathbf{x’}_1, \ldots, \mathbf{x’}_7 \)。这意味着,单词“want”的输出向量示意 \(\mathbf{x’}_2 \) 与单词“buy”(即 \( \mathbf{x’}_4 \) ) 和单词“I”(即 \( \mathbf{x’}_1 \) ) 间接相干。因而,“want”的输入向量表征, \(\mathbf{x”}_2 \),是一个交融了其上下文信息的更精密的表征。

咱们更深刻理解一下双向自注意力的工作原理。编码器模块的输出序列 \(\mathbf{X’}_{1:n} \) 中的每个输出向量 \(\mathbf{x’}_i \) 通过三个可训练的权重矩阵 \(\mathbf{W}_q \) ,\(\mathbf{W}_v \) ,\(\mathbf{W}_k \) 别离投影至 key 向量 \(\mathbf{k}_i \)、value 向量 \(\mathbf{v}_i \) 和 query 向量 \(\mathbf{q}_i \) (下图别离以橙色、蓝色和紫色示意):

$$ \mathbf{q}_i = \mathbf{W}_q \mathbf{x’}_i,$$
$$ \mathbf{v}_i = \mathbf{W}_v \mathbf{x’}_i,$$
$$ \mathbf{k}_i = \mathbf{W}_k \mathbf{x’}_i, $$
$$ \forall i \in {1, \ldots n}$$

请留神,对每个输出向量 \(\mathbf{x}_i (\forall i \in {i, \ldots, n} \) ) 而言,其所应用的权重矩阵都是 雷同 的。将每个输出向量 \(\mathbf{x}_i \) 投影到 querykeyvalue 向量后,将每个 query 向量 \(\mathbf{q}_j (\forall j \in {1, \ldots, n} \) ) 与所有 key 向量 \(\mathbf{k}_1, \ldots, \mathbf{k}_n \) 进行比拟。哪个 key 向量与 query 向量 \(\mathbf{q}_j \) 越类似,其对应的 value 向量 \(\mathbf{v}_j \) 对输入向量 \(\mathbf{x”}_j \) 的影响就越重要。更具体地说,输入向量 \(\mathbf{x”}_j \) 被定义为所有 value 向量的加权和 \(\mathbf{v}_1, \ldots, \mathbf{v}_n \) 加上输出向量 \(\mathbf{x’}_j \)。而各 value 向量的权重与 \(\mathbf{q}_j \) 和各个 key 向量 \(\mathbf{k}_1, \ldots, \mathbf{k}_n \) 之间的余弦类似度成正比,其数学公式为 \(\textbf{Softmax}(\mathbf{K}_{1:n}^\intercal \mathbf{q}_j) \),如下文的公式所示。对于自注意力层的残缺形容,倡议读者浏览 这篇 博文或 原始论文。

好吧,又简单起来了。咱们以上例中的一个 query 向量为例图解一下双向自留神层。为简略起见,本例中假如咱们的 基于 transformer 的解码器只有一个注意力头 config.num_heads = 1 并且没有归一化层。

图左显示了上个例子中的第二个编码器模块,左边具体可视化了第二个输出向量 \(\mathbf{x’}_2 \) 的双向自留神机制,其对应输出词为“want”。首先将所有输出向量 \(\mathbf{x’}_1, \ldots, \mathbf{x’}_7 \) 投影到它们各自的 query 向量 \(\mathbf{q}_1, \ldots, \mathbf{q}_7 \) (上图中仅以紫色显示前三个 query 向量),value 向量 \(\mathbf{v}_1, \ldots, \mathbf{v}_7 \) (蓝色) 和 key 向量 \(\mathbf{k}_1, \ldots, \mathbf{k}_7 \) (橙色)。而后,将 query 向量 \(\mathbf{q}_2 \) 与所有 key 向量的转置 ( \(\mathbf{K}_{1:7}^{\intercal} \) ) 相乘,随后进行 softmax 操作以产生 自注意力权重 。自注意力权重最终与各自的 value 向量相乘,并加上输出向量 \(\mathbf{x’}_2 \),最终输入单词“want”的上下文相干表征, \(\mathbf{x”}_2 \) (图右深绿色示意)。整个等式显示在图右框的上部。\(\mathbf{K}_{1:7}^{\intercal} \) 和 \(\mathbf{q}_2 \) 的相乘使得将“want”的向量表征与所有其余输出 (“I”,“to”,“buy”,“a”,“car”,“EOS”) 的向量表征相比拟成为可能,因而自注意力权重反映出每个输出向量 \(\mathbf{x’}_j \) 对“want”一词的最终表征 \(\mathbf{x”}_2 \) 的重要水平。

为了进一步了解双向自注意力层的含意,咱们假如以下句子:“房子很漂亮且位于市中心,因而那儿公共交通很不便 ”。“那儿”这个词指的是“房子”,这两个词相隔 12 个字。在基于 transformer 的编码器中,双向自注意力层运算一次,即可将“房子”的输出向量与“那儿”的输出向量相关联。相比之下,在基于 RNN 的编码器中,相距 12 个字的词将须要至多 12 个工夫步的运算,这意味着在基于 RNN 的编码器中所需数学运算与间隔呈线性关系。这使得基于 RNN 的编码器更难对长程上下文表征进行建模。此外,很显著,基于 transformer 的编码器比基于 RNN 的编码器 - 解码器模型更不容易失落重要信息,因为编码的序列长度绝对输出序列长度放弃不变, \(\textbf{len}(\mathbf{X}_{1:n}) = \textbf{len}(\mathbf{\overline{X}}_{1:n}) = n \),而 RNN 则会将 \(\textbf{len}((\mathbf{X}_{1:n}) = n \) 压缩到 \(\textbf{len}(\mathbf{c}) = 1 \),这使得 RNN 很难无效地对输出词之间的长程依赖关系进行编码。

除了更容易学到长程依赖外,咱们还能够看到 transformer 架构可能并行处理文本。从数学上讲,这是通过将自注意力机制示意为 querykeyvalue 的矩阵乘来实现的:

$$\mathbf{X”}_{1:n} = \mathbf{V}_{1:n} \text{Softmax}(\mathbf{Q}_{1:n}^\intercal \mathbf{K}_{1:n}) + \mathbf{X’}_{1:n} $$

输入 \(\mathbf{X”}_{1:n} = \mathbf{x”}_1, \ldots, \mathbf{x”}_n \) 是由一系列矩阵乘计算和 softmax 操作算得,因而能够无效地并行化。请留神,在基于 RNN 的编码器模型中,隐含状态 \(\mathbf{c} \) 的计算必须按程序进行: 先计算第一个输出向量的隐含状态 \(\mathbf{x}_1 \); 而后计算第二个输出向量的隐含状态,其取决于第一个隐含向量的状态,依此类推。RNN 的程序性妨碍了无效的并行化,并使其在古代 GPU 硬件上比基于 transformer 的编码器模型的效率低得多。

太好了,当初咱们应该对:
a) 基于 transformer 的编码器模型如何无效地建模长程上下文表征,以及
b) 它们如何无效地解决长序列向量输出这两个方面有了比拟好的了解了。

当初,咱们写一个 MarianMT 编码器 - 解码器模型的编码器局部的小例子,以验证这些实践在实践中行不行得通。


\({}^1 \) 对于前馈层在基于 transformer 的模型中所表演的角色的具体解释超出了本文的领域。Yun 等人 (2017) 的工作认为前馈层对于将每个上下文向量 \(\mathbf{x’}_i \) 映射到指标输入空间至关重要,而单靠 自注意力 层无奈达成这一目标。这里请留神,每个输入词元 \(\mathbf{x’} \) 都经由雷同的前馈层解决。更多详细信息,倡议读者浏览论文。

\({}^2 \) 咱们毋庸将 EOS 附加到输出序列,尽管有工作表明,在很多状况下退出它能够进步性能。相同地,基于 transformer 的解码器必须把 \(\text{BOS} \) 作为第 0 个指标向量,并以之为条件预测第 1 个指标向量。

from transformers import MarianMTModel, MarianTokenizer
import torch

tokenizer = MarianTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-de")
model = MarianMTModel.from_pretrained("Helsinki-NLP/opus-mt-en-de")

embeddings = model.get_input_embeddings()

# create ids of encoded input vectors
input_ids = tokenizer("I want to buy a car", return_tensors="pt").input_ids

# pass input_ids to encoder
encoder_hidden_states = model.base_model.encoder(input_ids, return_dict=True).last_hidden_state

# change the input slightly and pass to encoder
input_ids_perturbed = tokenizer("I want to buy a house", return_tensors="pt").input_ids
encoder_hidden_states_perturbed = model.base_model.encoder(input_ids_perturbed, return_dict=True).last_hidden_state

# compare shape and encoding of first vector
print(f"Length of input embeddings {embeddings(input_ids).shape[1]}. Length of encoder_hidden_states {encoder_hidden_states.shape[1]}")

# compare values of word embedding of "I" for input_ids and perturbed input_ids
print("Is encoding for `I` equal to its perturbed version?:", torch.allclose(encoder_hidden_states[0, 0], encoder_hidden_states_perturbed[0, 0], atol=1e-3))

输入:

    Length of input embeddings 7. Length of encoder_hidden_states 7
    Is encoding for `I` equal to its perturbed version?: False

咱们比拟一下输出词嵌入的序列长度 ( embeddings(input_ids),对应于 \(\mathbf{X}_{1:n} \) ) 和 encoder_hidden_​​states 的长度 (对应于 \( \mathbf{\overline{X}}_{1:n} \) )。同时,咱们让编码器对单词序列“I want to buy a car”及其轻微改变版“I want to buy a house”别离执行前向操作,以查看第一个词“I”的输入编码在更改输出序列的最初一个单词后是否会有所不同。

不出意外,输出词嵌入和编码器输入编码的长度, \(\textbf{len}(\mathbf{X}_{1:n}) \) 和 \(\textbf{len}(\mathbf{\overline{X}}_{1:n}) \),是相等的。同时,能够留神到当最初一个单词从“car”改成“house”后,\(\mathbf{\overline{x}}_1 = \text{“I”} \) 的编码输入向量的值也扭转了。因为咱们当初曾经了解了双向自注意力机制,这就难能可贵了。

顺带一提,自编码 模型 (如 BERT) 的架构与 基于 transformer 的编码器模型是齐全一样的。自编码 模型利用这种架构对凋谢域文本数据进行大规模自监督预训练,以便它们能够将任何单词序列映射到深度双向表征。在 Devlin 等 (2018) 的工作中,作者展现了一个预训练 BERT 模型,其顶部有一个工作相干的分类层,能够在 11 个 NLP 工作上取得 SOTA 后果。你能够从 此处 找到 🤗 transformers 反对的所有 自编码 模型。

敬请关注其余部分的文章。


英文原文: https://hf.co/blog/encoder-decoder

原文作者: Patrick von Platen

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的利用及大规模模型的训练推理。

审校 / 排版: zhongdongy (阿东)

正文完
 0