摘要
优势序列转导模型基于复杂的循环或包括一个编码器和一个解码器的卷积神经网络。最好的表现良好的模型还通过attention 连接编码器和解码器机制。我们提出了一种新的简单的网络架构,Transformer, 完全基于注意力机制,省去了递归和卷积完全。在两个机器翻译任务上的实验表明,这些模型可以质量优越,同时具有更强的并行性和显著的要求训练时间更少。我们的模型在WMT 2014 英语-上达到 28.4 BLEU在现有的最佳结果上有所提升,包括超过 2 个 BLEU。在 W MT 2014 英法翻译任务中, 我们的模型建立了一个新的单模型最先进的 BLEU 得分为 41.8 在 8 个 gpu 上进行 3.5 天的培训,只是培训成本的一小部分文献中的最佳模型。我们表明,Transformer 很好地泛化到应用于英语句法分析的其他任务中。
介绍
循环神经网络,长短期记忆[13]和门控循环[7]神经网络特别是,已经被坚定地确立为序列建模和语言建模和机器翻译等转导问题[35,2,5 ]。此后, 大量的努力继续推动循环语言模型和编码器-解码器架构的边界[38,2 4,15 ]。循环模型通常沿着输入和输出序列的符号位置进行因子计算。将计算时间中的位置与步骤对齐, 它们生成一个隐藏状态 ht 序列,作为位置 t 的输入的前一个隐藏状态手 t− 1 的函数。这种固有的顺序性排除了训练示例中的并行化, 在较长的序列长度时, 这变得至关重要, 因为内存约束限制了示例之间的批处理。最近的工作通过因子分解技巧[21]和条件计算[32]在计算效率方面取得了显著 的改进,同时在后者的情况下也提高了模型性能。然而,顺序计算的基本约束仍然存在。注意力机制已经成为各种任务中引人注目的序列建模和转导模型的组成部分,允许对依赖关系进行 建模, 而无需考虑它们在输入或输出序列中的距离[2,19 ]。然而,除了少数[27]之外, 在所有情况下,这种注意力机制都与循环网络一起使用。本文提出 Tr ansfo rm er, 一种避免递归的模型架构, 完全依靠注意力机制来获取输入和输出之间的全局依赖关系。Transf orm e r 允许显著更多的并行化, 并可以在 8 个 P 100 gp u 上训练仅 12 个小时后达到翻译质量的新水平。
背景
减少顺序计算的目标也构成了 Exten ded N eur al GP U[16]、Byte N et[18]和 ConvS 2S [ 9]的基础, 它们都使用卷积神经网络作为基本构建块, 对所有输入和输出位置并行计算隐藏表示。在这些模型中, 将来自两个任意输入或输出位置的信号关联起来所需的操作数量随着位置之间的距离而增长,对 于 ConvS 2S 是线性的, 对于 Byte Net 是对数的。这使得学习距离较远位置之间的依赖关系[12]变得更加困难。在 Transfor me r 中, 这减少到恒定数量的操作, 尽管以降低有效分辨率为代价,因为平均注意力加权位置,我们用多头注意力抵消了这种效应, 如 3.2 节所述。自注意力, 有时称为内部注意力,是一种将单个序列的不同位置联系起来的注意力机制, 以便计算序列的表示。自注意力已成功应用于各种任务, 包括阅读理解、抽象摘要、文本蕴涵和学习任务独立的句子表示[4, 27,2 8,22]。端到端记忆网络基于循环注意力机制而不是序列对齐的递归, 并已被证明在简单语言问答和语言建模任务[34]上表现良好。然而,据我们所知, Tr ansfor m er 是第一个完全依赖自注意力来计算其输入和输出表示而不使用序列对齐 rnn 或卷积的转导模型。在接下来的章节中,我们将描述 Tr ansfo rm er, 激发自注意力, 并讨论其相对于[17 ,18]和[9 ]等模型的优势。
模型架构
大多数具有竞争力的神经序列转导模型具有编码器-解码器结构[5,2, 35]。在这里,编码器映射一个符号表示的输入序列(x1 ,… , xn )映射到连续表示序列 z = (z1 , … , zn )。给定 z, 解码器然后生成一个输出序列(y1 , … , ym )的符号, 每次一个元素。在每一步模型都是自回归的[10],在生成下一个时将之前生成的符号作为额外的输入进行消费。Tr ansfo rm e r 遵循这种使用编码器和解码器的堆叠自注意力和逐点全连接层的整体架构,分别在图 1 的左半部分和右半部分中显示。
模型架构图
编码器和解码器
编码器:编码器 由 N = 6 个相同层的堆栈组成。每一层有两个子层。第一个是多头自注意力机制, 第二个是简单的、按位置全连接的前馈网络。我们在两个子层的每个周围都采用了残差连接[11 ], 其次是层归一化[1]。也就是说, 每个子层的输出是 L ay er N or m( x + S ubl ay er( x)),其中 S ubl ay er( x) 是子层本身实现的函数。为了方便这些残差连接,模型中的所有子层,以及嵌入层,都会产生 维数 dmodel = 512 的输出。
解码器 :解码器也由 N = 6 个相同层的堆栈组成。除了每个编码器层的两个子层外, 解码器还插入了第三个子层,该子层对编码器堆栈的输出执行多头注意力。与编码器类似, 我们在每个子层周围采用残差连接, 然后进行层归一化。我们还修改了解码器堆栈中的 self-attention 子层, 以防止位置对后续位置的关注。这种掩蔽,结合输出嵌入被一个位置偏移的事实,确保对位置 i 的预测只能依赖于位置小于 i 的已知输出。
注意力机制
注意力函数可以描述为将一个查询和一组键值对映射到一个输出,其中查询、键、值和输出都是向量。 输出被计算为值的加权和,其中分配给每个值的权重是通过查询与相应键的兼容函数计算出来的。
缩放点积注意力
多头注意
图 2:(上)缩放点产品注意力。(下)多头注意由平行运行的几个注意层组成。
缩放点产品注意力
我们将特别的注意力称为“缩放的点积注意力” (图 2)。输入由 dk 维的查询和键,以及 d 维的值√ 组成。我们计算 v 查询与所有键的点积,每个键除以 d, 并应用 kso ftm ax 函数来获得值的权重。在实践中,我们同时计算一组查询上的注意力函数,打包成一个矩阵 q,键和值也打包成矩阵 K和 V。我们将输出矩阵计算为:
最常用的两种注意力功能是加性注意力[2],以及点积(multiple - plicative)注意力。点积注意力和我们的算法是一样的,除了缩放因子√ 1d 。加性注意力使用前馈网络计算兼容性函数,与单个隐藏层。虽然两者在理论复杂度上相似, 但点积注意力在实践中要快得多,空间效率更高, 因为它可以使用高度优化的矩阵乘法代码来实现。虽然对于较小的 d 值,k 两种机制的表现类似, 但对于较大的 dk [3]值,加法注意力的表现优于点积注意力。我们怀疑,对于较大的 dk 值,点积在量级上增长很大,将 softmax 函数推入其梯度极小的区域 4。为了抵消这种影响,我们将点积缩放√ 1d 。
多头注意力机制
我们发现,与其用 dmod el 维的键、值和查询执行单一的注意力函数, 用不同的、学习到的线性投影分别将查询、键和值线性投影 h 次, 对 d、k d 和 k d 维 v 是有益的。然后, 在这些投影版本的查询、键和值上,我们并行执行注意力函数, 产生 d 维 v 输出值。这些被连接并再次投影,得到最终的值,如图 2 所示。
多头注意力允许模型在不同位置共同注意来自不同表示子空间的信息。在单一注意力头的情况下, 平均抑制了这一点。
其中投影是参数矩阵 W i Q ∈ R dmo del × d k , WiK ∈ Rd m o d el × dk, WiV ∈ Rd mo d e l × dv
在这项工作中, 我们采用 h = 8 个平行注意力层(pa rallel attention lay ers ,简称 h ea ds)。对于每一个我们使用 dk = dv = dmod el / h = 64。由于每个头的维度降低了,总的计算成本与全维度的单头注意力类似。
注 意力在模型中的应用
Transformer 以三种不同的方式使用多头注意力:
•在“ 编码器-解码器注意力”层中, 查询来自前一个解码器层, 记忆键和值来自编码器的输出。这使得解码器中的每个位置都能参与到输入序列中的所有位置。这模仿了序列到 序列模型中典型的编码器-解码器注意力机制, 如[3 8,2, 9]。
•编码器包含自注意力层。在 self-attention 层中,所有的键、值和查询都来自同一个地方, 在这种情况下, 是编码器中前一层的输出。编码器中的每个位置都可以关注编码器前一层的所有位置。
类似地,解码器中的自注意力层允许解码器中的每个位置关注解码器中的所有位置,直到并 包括该位置。我们需要防止解码器中向左的信息流, 以保持自回归属性。我们通过屏蔽 (设置为− ∞ )softmax 输入中对应于非法连接的所有值, 在缩放的点积注意力中实现这一点。参见图 2。
位置前馈网络
除了 attention 子层之外, 我们编码器和解码器中的每一层都包含一个完全连接的前馈网络,该网络分别相同地应用于每个位置。这由两个线性变换组成, 中间有一个 R e LU 激活。
虽然线性变换在不同位置上是相同的,但它们在层与层之间使用不同的参数。另一种描述方法 是将其描述为两个核大小为 1 的卷积。输入和输出的维数是 dmod el = 512, 内层的维数 d ff = 2048。
嵌入和 Softmax
与其他序列转导模型类似, 我们使用学习嵌入将输入标记和输出标记转换为 dmod el 维向量。我们还使用通常的学习线性变换和 softma x 函数将解码器输出转换为预测的下一个标记概率。在我们的模型中,我们在两个嵌入层和 pre-softmax√ 线性变换之间共享相同的权重矩阵,类似于[30]。在嵌入层中,我们将这些权重乘以 d。model
位置编码
由于我们的模型不包含递归和卷积,为了使模型利用序列的顺序,我们必须注入一些关于相对或绝 对位置的信息表 1:不同层类型的最大路径长度、每层复杂度和最小顺序操作数。N 是序列长度, d 是表示维度,k 是卷积的核大小, r 是受限自注意力中的邻域大小。
序列中的 token。为此,我们在编码器和解码器堆栈底部的输入嵌入中添加“位置编码”。位置编码与嵌入具有相同 model 的维度,以便两者可以相加。位置编码有很多选择,学习和固定 [9]。在这项工作中,我们使用了不同频率的正弦和余弦函数:
其中 pos 是位置, I 是维度。也就是说, 位置编码的每个维度对应一个正弦信号。波长形成从 2π 到10000· 2π 的几何级数。我们选择了这个函数,因为我们假设它可以让模型很容易地学会通过相对位置来参加,因为对于任何固定的偏移量 k, PEcpos+k an 都可以表示为的线性函数浆果。我们还尝试使用学习到的位置嵌入[9]代替,发现两个版本产生了几乎相同的结果(参见表 3 行(E))。我们选择了正弦版本, 因为它可能允许模型推断出比训练过程中遇到的序列长度更长。
为什么需要自注意力机制
在本节中,我们将自注意力层的各个方面与递归层和卷积层进行比较, 这些层通常用于映射一个变长符号表示序列(x1, … , xn )到另一个长度相等的序列(z1, … , zn ),用 xi , zi ∈ Rd , 比如典型的序列转导编码器或解码器中的一个隐藏层。激励我们使用 self- atte ntio n 我们考虑三个需求。一个是每层的总计算复杂度。另一个是可以并行化的计算量, 以所需的最小顺序操作数来衡量。
第三是网络中长程依赖关系之间的路径长度。学习长程依赖关系是许多序列转导任务中的关键挑 战。影响学习此类依赖关系能力的一个关键因素是信号在网络中必须遍历的前向和后向路径的长 度。输入和输出序列中任何位置组合之间的这些路径越短,就越容易学习长程依赖关系 [12]。因此, 我们还比较了由不同层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。
如表 1 所述, self-attention 层将所有位置与固定数量的顺序执行操作连接起来, 而循环层需要 O (n) 顺序操作。在计算复杂度方面, 当序列长度 n 小于表示维度 d 时, 自注意力层比循环层更快, 这是机器翻译中最先进的模型使用的句子表示最常见的情况, 如词块[38]和字节对[31]表示。为了提高涉及非常长的序列的任务的计算性能, 可以将自注意力限制为只考虑大小为 rin 的邻域输入序列以各自的输出位置为中心。这将使最大路径长度增加到 O(n/ r)。我们计划在未来的工作中进一步研究这种方法。具有核宽度 k < n 的单个卷积层不会连接所有输入和输出位置对。这样做需要一个 O(n/ k)卷积层的堆栈, 在连续的核的情况下, 或在扩张的卷积[18]的情况下, O(logk (n)), 增加网络中任何两个位置之间最长路径的长度。卷积层通常比递归层更昂贵, 是递归层的 k 倍。然而,可分离卷积[6]大大降低了复杂度, 达到 O( k· n· d + n· d2 ).然而, 即使 k = n, 可分离卷积的复杂度也等于自注意力层和点向前馈层的组合, 这是我们在我们的模型中采用的方法。作为附带好处, self-attention 可以产生更可解释的模型。我们检查了来自我们模型的注意力分布,并在附录中介绍和讨论示例。单个注意力头不仅清楚地学会了执行不同的任务,而且许多注意力头似 乎表现出与句子的句法和语义结构相关的行为。
模型训练
训练数据和批处理
我们在标准的 W MT 2014 英语-德语数据集上进行训练,该数据集包含大约 450 万个句子对。句子使用字节对编码[3]进行编码,该编码具有大约 37000 个 tok en 的共享源-目标词汇表。对于英语-法语, 我们使用了更大的 W M T 2 014 英语-法语数据集,该数据集由 36M 个句子组成, 并将 token 拆分为 32000 个词片词汇[38]。句子对按近似序列长度批处理在一起。每个训练批次包含一组句子对, 其中约包含 25000 个源词词和 25 000 个目标词词。
硬件与调度
我们使用了 Adam 优化器[20],β1 = 0.9, β2 = 0.98 和? = 10− 9。我们在训练过程中改变了学习率,根据公式:
这对应于第一个 w ar mu p_ste ps 训练步骤的学习率线性增加, 然后与步骤数的逆平方根成比例地减少它。我们使用了 w a rm up _step s = 4000。
正则化
我们在训练过程中使用三种类型的正则化:
残差 Dropout 我们将 Dro pout[33]应用于每个子层的输出,然后将其添加到子层输入并归一化。此外,我们将 dropout 应用于编码器和解码器堆栈中的嵌入和位置编码的总和。对于基础模型,我们使用 Pdrop= 0.1 的速率。
表 2:Transformer 在英语-德语和英语-法语 newstest2014 测试中获得了比以前的最先进型号更好的BLEU 分数,而培训费用只是其中的一小部分。
标签平滑在训练过程中,我们采用了值?ls = 0.1[3 6]的标签平滑。这损害了困惑, 因为模型学会了更不确定,但提高了准确性和 B LE U 分数。
结果
机器翻译
在 W M T 2014 英德翻译任务中, 大型变压器模型(表 2 中的 transform er (big))比之前报道的最佳模型(包括集成)高出 2.0 B LE U 以上, 建立了新的最先进的 BL E U 分数 28.4。这个模型的配置列在表3 的底线中。在 8 块 P 100 gp u 上训练了 3.5 天。即使我们的基础模型也超过了所有之前发布的模型和集成,而训练成本只是任何竞争模型的一小部分。
在 W M T 20 14 英语到法语翻译任务上,我们的大模型达到了 41.0 的 BLE U 分数, 优于之前发布的所有单一模型,而训练成本不到之前最先进模型的 1/ 4。英语到法语训练的 Tra nsfor me r(大)模型使用的辍学率 Pdro p = 0.1,而不是 0.3。
对于基础模型,我们使用一个通过平均最后 5 个检查点得到的单一模型,每 10 分钟编写一次。对于大型模型, 我们对最后 20 个检查点取平均。我们使用波束搜索,波束大小为 4,长度惩罚 α = 0.6[38 ]。这些超参数是在开发集上进行实验后选择的。我们在推理期间将最大输出长度设置为输入长度+ 50,但在可能的情况下提前终止[38 ]。
表 2 总结了我们的结果,并将我们的翻译质量和训练成本与文献中的其他模型架构进行了比较。我们通过将训练时间、使用的 GP U 数量和每个 GP U5 持续单精度浮点能力的估计相乘来估计用于训练模型的浮点操作数量。
模型版本
为了评估 Tr ansfo rm er 不同组件的重要性, 我们以不同的方式改变了基本模型, 测量了开发集上英语到德语翻译的性能变化, ne wstest201 3。我们使用了上一节所述的波束搜索,但没有检查点平均。我们在表 3 中展示了这些结果。
在表 3 行(A )中, 我们改变注意力头的数量以及注意力键和值维度, 保持计算量不变, 如 3.2. 2 节所述。虽然单头注意力比最好的设置差 0.9 个 BL EU ,但过多的头质量也会下降。
在表 3 行(B)中,我们观察到减少注意力键大小 k 会损害模型质量。这表明确定兼容性并不容易, 一个比点积更复杂的兼容性函数可能是有益的。我们在©行和(D)行中进一步观察到,正如预期的那样, 更大的模型更好, dropo ut 在避免过拟合方面非常有帮助。在行(E)中, 我们用学习到的位置嵌入[9]替换了我们的正弦位置编码, 并观察到与基本模型几乎相同的结果。
英语短语结构分析
为了评估 Tran sform er 是否可以泛化到其他任务,我们在英语短语句法分析上进行了实验。这项任务提出了特定的挑战:输出受强结构的影响约束条件和明显长于输入。此外,RNN 序列到序列模型还未能在小数据体制[37]中取得最先进的结果。
我们在 P enn Tr eeb an k 的华尔街日报(WS J)部分[25]上训练了一个 dmodel = 1024 的 4 层转换器,大约训练了 40K 个句子。我们还在半监督环境下训练它, 使用来自大约 1700 万个句子[37]的更大的高置信度和 BerkleyP arser 语料库。我们在仅 WS J 设置中使用了 16K 代币的词汇表,在半监督设置中使用了 32K 代币的词汇表。
我们只进行了少量实验来选择 section 22 开发集上的 dropout 、注意力和残差(第 5.4 节)、学习率和光束大小, 所有其他参数从英语到德语基础翻译模型保持不变。在推理过程中,我们将最大输出长度增加到输入长度+ 300。对于仅 W S J 和半监督设置, 我们使用了 21 和 α = 0.3 的光束大小。
我们在表 4 中的结果表明, 尽管缺乏特定任务的调优, 我们的模型表现得惊人地好, 产生了比所有之前报道的模型更好的结果,除了循环神经网络语法[8]。
与 RN N 序列到序列模型[37]相比, 即使仅在 40K 句的 WS J 训练集上训练, Tra nsfor m er 也优于
Berkele y- Parser[29 ] 。
结论
在这项工作中, 我们提出了 Tra nsfor m er, 这是第一个完全基于注意力的序列转导模型,用多头自注意力替换了编码器-解码器架构中最常用的循环层。对于翻译任务, Tr a nsfor m er 的训 练速度可以 明显快于基 于循环层或 卷积层的架 构。在 W M T 2014 英语到德语和 W M T 201 4 英语到法语的翻译任务上, 我们都达到了一个新的水平。在前一个任务中,我们最好的模型甚至超过了所有之前报告的集成。我们对基于注意力的模型的未来感到兴奋,并 计划将它们应用于其他任务。我 们计划将Tr ans fo r mer 扩展到涉及文本以外的输入和输出模态的问题,并研究局部的、受限的注意力机制, 以有效处理图像、音频和视频等大型输入和输出。使生成更少的顺序性是我们的另一个研究。
代码复现(参考)
# 定义模型架构
class TransformerModel:
def __init__(self, num_layers, d_model, num_heads, d_ff, input_vocab_size, target_vocab_size):
# 初始化模型参数
self.num_layers = num_layers
self.d_model = d_model
self.num_heads = num_heads
self.d_ff = d_ff
# 定义编码器和解码器层
self.encoder = Encoder(num_layers, d_model, num_heads, d_ff, input_vocab_size)
self.decoder = Decoder(num_layers, d_model, num_heads, d_ff, target_vocab_size)
# 定义最后的线性层
self.output_layer = nn.Linear(d_model, target_vocab_size)
def forward(self, input_seq, target_seq):
# 编码器的前向传播
encoder_output = self.encoder(input_seq)
# 解码器的前向传播
decoder_output = self.decoder(target_seq, encoder_output)
# 经过线性层的变换
output = self.output_layer(decoder_output)
return output
# 定义编码器
class Encoder:
def __init__(self, num_layers, d_model, num_heads, d_ff, vocab_size):
self.num_layers = num_layers
# 定义多层的自注意力机制
self.attention_layers = nn.ModuleList([
AttentionLayer(d_model, num_heads, d_ff) for _ in range(num_layers)
])
# 定义编码器的嵌入层
self.embedding = nn.Embedding(vocab_size, d_model)
# 定义位置编码
self.position_encoding = PositionalEncoding(d_model)
def forward(self, input_seq):
# 嵌入层的前向传播
embedded_input = self.embedding(input_seq)
# 加上位置编码
encoded_input = self.position_encoding(embedded_input)
# 自注意力机制的前向传播
output = encoded_input
for attention_layer in self.attention_layers:
output = attention_layer(output)
return output
# 定义解码器
class Decoder:
def __init__(self, num_layers, d_model, num_heads, d_ff, vocab_size):
self.num_layers = num_layers
# 定义多层的自注意力机制
self.attention_layers = nn.ModuleList([
AttentionLayer(d_model, num_heads, d_ff) for _ in range(num_layers)
])
# 定义解码器的嵌入层
self.embedding = nn.Embedding(vocab_size, d_model)
# 定义位置编码
self.position_encoding = PositionalEncoding(d_model)
def forward(self, target_seq, encoder_output):
# 嵌入层的前向传播
embedded_target = self.embedding(target_seq)
# 加上位置编码
encoded_target = self.position_encoding(embedded_target)
# 自注意力机制的前向传播
output = encoded_target
for attention_layer in self.attention_layers:
output = attention_layer(output)
return output
# 定义注意力层
class AttentionLayer:
def __init__(self, d_model, num_heads, d_ff):