transformer

1. Transformer

1.1. transformer模型背景

Transformer 模型是自然语言处理领域中的一项重要创新。
其背景主要源于对传统神经网络模型在处理序列数据(如文本)时的局限性的突破需求。在 Transformer 出现之前,循环神经网络(RNN)及其变体(如长短期记忆网络 LSTM)是处理序列数据的常用模型,但它们存在一些问题,如难以并行处理、对长序列的记忆能力有限以及训练速度较慢等。
为了解决这些问题,Transformer 模型引入了注意力机制(Attention Mechanism),使得模型能够对输入序列的不同部分进行灵活且高效的关注和权重分配,从而能够更好地捕捉长距离的依赖关系。并且,由于其基于自注意力机制的架构,能够并行处理输入序列,大大提高了训练效率。
Transformer 模型的提出为自然语言处理带来了巨大的变革,被广泛应用于机器翻译、文本生成、问答系统等众多任务,并成为后续许多先进模型的基础架构。

1.1.1. RNN (Recurrent Neural Network) 是一种经典的神经网络结构,具有以下优缺点:

优点:
处理序列数据:RNN 是一种适用于处理序列数据的神经网络,能够有效地捕捉序列中的时序信息。
共享权重:RNN 在每个时间步都使用相同的参数,可以有效地共享权重,减少模型的复杂度和训练的参数数量。
上下文依赖建模:RNN 能够记忆之前的信息,并在后续时间步中利用该信息进行预测或决策,对于处理依赖于上下文的任务非常有用。
缺点:
梯度消失/爆炸:RNN 在反向传播时,由于参数共享和多次连乘的特性,容易出现梯度消失或梯度爆炸的问题,导致模型难以训练或无法收敛。
长期依赖问题:由于梯度消失的原因,RNN 在处理长序列时难以捕捉到长期依赖关系,只能有效利用较短的上下文信息。
计算效率较低:RNN 的计算过程是基于时间步展开的,每个时间步都需要依次计算,造成计算效率较低,尤其是处理较长序列时。
尽管 RNN 存在一些缺点,但它仍然是处理序列数据的重要工具。为了解决梯度消失和长期依赖问题,出现了一些改进的 RNN 变体,例如 LSTM (Long Short-Term Memory) 和 GRU (Gated Recurrent Unit)。这些变体通过引入门控机制来控制信息的流动和遗忘,能够更好地处理长序列和长期依赖关系。

1.1.2. LSTM神经网络的优缺点

LSTM (Long Short-Term Memory) 神经网络是一种特殊的循神经网络 (RNN),它在处理序列数据和长期依赖关系时具有一些优点和缺点:
优点:
1.长期依赖建模:LSTM通过引入门控机制,能够有效地捕捉和建模长期依赖关系。这使得它在处理序列数据时比传统的RNN模型更具优势,如自然语言处理中的语言建模、机器翻译等任务。
2.防止梯度消失和梯度爆炸:传统的RNN在训练过程中容易遇到梯度消失或梯度爆炸的问题,导致难以有效地学习长期依赖。而LSTM通过门控机制,可以在一定程度上解决这个问题,使得网络能够更稳定地进行训练。
3.灵活的门控结构:LSTM的核心是门控单元,包括输入门、遗忘门和输出门。这些门控结构能够控制信息的流动和保留,使得网络能够有选择地记忆和遗忘输入序列中的信息,从而更好地处理序列数据。
缺点:
1.计算复杂度较高:由于引入了门控机制,LSTM相对于传统的RNN模型而言计算复杂度更高。这可能导致在大规模数据集或复杂模型中的训练和推理过程较慢。
2参教量较多:LSTM网终中的门控单元和记忆单元增加了网终的参数量,特别是当网络层数较多时,参数量会进一步增加。这可能导致需要更多的训练数据和计算资源来进行训练和调优。
3.难以解释性:LSTM网络中的门控结构和复杂的记忆单元使得网络的决策过程相对难以解释和理解。相比于一些简单的模型,LSTM的结果和内部运作可能更难以解释给非专业人士。
总体而言,LSTM神经网络在处理序列数据和长期依赖关系方面具有很大优势,但也有一些缺点需要注意。在实际应用中,我们需要根据具体问题和数据集的特点来选择合适的神经网络模型和优化方法。

1.2. transformer简介

类别内容
论文Attention Is All You Need
论文地址https://arxiv.org/pdf/1706.03762.pdf
项目地址https://pytorch.org/docs/stable/_modules/torch/nn/modules/transformer.html#Transformer
作者Ashish Vaswani∗ Noam Shazeer 等
单位2017 年,Google 机器翻译团队
摘要主要的序列转导模型是基于复杂的循环或卷积神经网络,包括一个编码器和一个解码器。表现最好的模型还通过注意机制连接编码器和解码器。我们提出了一个新的简单的网络架构,transformer,完全基于注意力机制,完全摒弃递归和卷积。在两个机器翻译任务上的实验表明,这些模型在质量上更优越,同时更具并行性,并且需要更少的训练时间。我们的模型在WMT 2014英语-德语翻译任务上实现了28.4 BLEU,比现有的最佳结果(包括集合)提高了2个BLEU以上。在WMT 2014英法翻译任务中,我们的模型在8个gpu上训练3.5天后,建立了一个新的单模型最先进的BLEU分数41.8,这是文献中最佳模型训练成本的一小部分。我们通过将Transformer成功地应用于具有大量和有限训练数据的英语选区解析,证明了它可以很好地推广到其他任务。
贡献这篇论文的主要贡献是提出了一种新的神经网络架构,即Transformer,它完全基于注意力机制,不涉及循环和卷积操作。Transformer在机器翻译任务上表现出了更好的性能,同时具有更高的并行性和训练效率。

1.3. transformer整体结构

Transformer是一种基于自注意力机制的深度学习模型,广泛应用于自然语言处理任务。它的核心特点包括自注意力机制、多头注意力、编码器-解码器架构、位置编码、残差连接和层归一化,以及并行化计算能力。

在这里插入图片描述
Transformer模型的核心组件
Transformer模型的核心组件主要包括以下几个部分:

  • 自注意力机制(Self-Attention Mechanism):这是Transformer的核心,它允许模型在处理序列数据时,考虑序列中各个元素之间的相互关系,从而捕捉长距离依赖。
  • 多头注意力机制(Multi-Head Attention):通过将注意力机制分解为多个“头”,模型能够在不同的表示子空间中学习信息,增强了模型对不同位置的敏感性。
  • 前馈神经网络(Feed-Forward Neural Network):位于自注意力机制之后,用于进一步处理和转换注意力机制的输出,每个位置的处理是独立的。
  • 编码器和解码器(Encoder-Decoder):Transformer模型由编码器和解码器组成,编码器负责处理输入序列,解码器负责生成输出序列。解码器在处理目标序列时,不仅关注序列本身,还通过编码器-解码器注意力机制关注编码器的输出123。
  • 位置编码(Positional Encoding):由于Transformer模型不使用循环结构,位置编码被添加到输入序列中,以提供单词在序列中的位置信息。
  • 残差连接(Residual Connection) 和 层归一化(Layer Normalization):这些技术用于改善梯度传播,增加模型的训练稳定性,并加速收敛速度。
    这些组件共同工作,使得Transformer模型能够有效地处理序列数据,特别是在自然语言处理任务中表现出色。

1.3.1. 编码器和解码器

编码器:
编码器由N = 6个相同层的堆栈组成。每一层有两个子层。第一种是多头自注意机制,第二种是简单的、位置完全连接的前馈网络。我们在每一个子层周围使用残差连接[11],然后进行层归一化[1]。也就是说,每个子层的输出是 L a y e r N o r m ( x + S u b l a y e r ( x ) ) LayerNorm(x + Sublayer(x)) LayerNorm(x+Sublayer(x)),其中 S u b l a y e r ( x ) Sublayer(x) Sublayer(x)是子层本身实现的函数。为了方便这些残差连接,模型中的所有子层以及嵌入层产生的输出维度为dmodel = 512。
解码器:
解码器也由N = 6相同层的堆栈组成。除了每个编码器层中的两个子层之外,解码器插入第三个子层,该子层对编码器堆栈的输出执行多头注意。与编码器类似,我们在每个子层周围使用残差连接,然后进行层归一化。我们还修改了解码器堆栈中的自注意子层,以防止位置关注后续位置。这种掩模,再加上输出嵌入被偏移一个位置的事实,确保了位置 i i i的预测只能依赖于位置小于 i i i的已知输出。

1.3.2. 注意力

注意力函数可以描述为将一个query和一组key-value对映射为一个输出,其中query、key、value和output都是向量。output是通过所有value的加权和计算得到,其中,分配给每个value的权重是由query和对应的key的兼容函数计算得到。

在这里插入图片描述

1.3.2.1. 缩放点积注意力 (Scaled Dot-Product Attention)

缩放点积注意力如Figure2所示。输入由维度 d k d_k dk的query和key,以及维度 d v d_v dv的value组成。计算query和所有keys的点积,将每个点积除以 d k \sqrt{d_k} dk ,并应用一个softmax函数来获得这些值的权重。
在实际中,在一组queries上同时计算注意力函数,打包成一个矩阵 Q Q Q。keys和values也被打包成矩阵 K K K V V V。计算矩阵表达形式:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V (1) Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V \tag{1} Attention(Q,K,V)=softmax(dk QKT)V(1)

两个最常用的注意力函数是加法注意力点积注意力。点积注意力与这里的算法相同,除了这里包含一个缩放因子 1 d k \frac{1}{\sqrt{d_k}} dk 1。加法注意力函数使用包含单隐层的前馈网络计算兼容性函数。虽然两种方法在理论上复杂性相似,但是在实际中,点积注意力更快且空间上更高效。因为它可以通过高度优化的矩阵乘法代码实现。
对于较小的 d k d_k dk值,两种机制性能相似;对于较大的 d k d_k dk值,由于加法注意力没有缩放,性能优于点积注意力。由于怀疑,对于dk的大值,点积的幅度越来越大,将softmax函数推到梯度极小的区域。为了抵消这种效应,才使用 1 d k \frac{1}{\sqrt{d_k}} dk 1来缩放点积

1.3.2.2. 多头注意力机制

相比较与单一注意力函数,可以发现将queries、keys和values使用不同的学习到的线性投影参数 h h h倍线性投影到 d k d_k dk d k d_k dk d v d_v dv维是有益的。对每一个queries、keys、values的投影版本中,并行执行注意力函数,产生 d v d_v dv维输出值。它们被拼接后再次投影,产生如Figure2中最终的值。
多头注意允许模型共同关注来自不同位置的不同表示子空间的信息。如果只有一个attention head,它的平均值会削弱这个信息。

M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , ⋯   , h e a d h ) W O w h e r e h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) MultiHead(Q,K,V)=Concat(head_1, \cdots, head_h)W^O \\ where \quad head_i = Attention(QW_i^Q,KW_i^K,VW_i^V) MultiHead(Q,K,V)=Concat(head1,,headh)WOwhereheadi=Attention(QWiQ,KWiK,VWiV)
其中投影是参数矩阵 W i Q ∈ R d m o d e l × d k , W i K ∈ R d m o d e l × d k , W i V ∈ R d m o d e l × d v W_i^Q \in \mathbb{R}^{d_{model}\times d_k}, W_i^K \in \mathbb{R}^{d_{model}\times d_k}, W_i^V \in \mathbb{R}^{d_{model}\times d_v} WiQRdmodel×dk,WiKRdmodel×dk,WiVRdmodel×dv, W O ∈ R h d v × d m o d e l W^O \in \mathbb{R}^{h d_v \times d_{model}} WORhdv×dmodel
在论文中使用了 h = 8 h=8 h=8并行注意力层或头。对于每一个头,使用 d k = d v = d m o d e l / h = 64 d_k = d_v = d_{model}/h = 64 dk=dv=dmodel/h=64。由于每个头部的维数降低,其总计算代价与全维的单头注意相似。

1.3.2.3. 模型中注意力的应用

在Transformer中多头注意力机制有三种不同的使用方式:
在编码器-解码器注意力层中,queries来自解码器的前一层,记忆keys和values来自编码器的输出。这允许解码器中的每个位置都参与输入序列中的所有位置。这模拟了序列到序列模型中典型的编解码器注意机制。
在编码器中包含的自我注意层。在自注意力层中,所有的keys、values和queries都来自同一个位置,在这种情况下,它们是编码器中上一层的输出。编码器中的每个位置都可以处理编码器上一层中的所有位置。
在解码器中的自我注意力层。解码器中的自注意层允许解码器中的每个位置关注解码器中的所有位置,直到并包括该位置。我们需要防止解码器中向左的信息流,以保持自回归特性。我们通过屏蔽(设置为−∞)softmax输入中对应于非法连接的所有值来在缩放点积注意内部实现这一点。见图2。

1.3.3. 基于位置的前馈神经网络

除了注意力子层外,编码器和解码器中的每个层都包含一个完全连接的前馈网络,它分别和相同地应用于每个位置。这由两个线性变换组成,中间有一个ReLU激活。
F F N ( x ) = m a x ( 0 , x W 1 + b 1 ) W 2 + b 2 (2) FFN(x) = max(0, xW_1+b_1)W_2+b_2 \tag{2} FFN(x)=max(0,xW1+b1)W2+b2(2)
虽然线性转换在不同的位置上是相同的,但它们每层都使用不同的参数。另一种描述它的方法是用核大小为1的两个卷积。输入和输出的维度都是 d m d e l = 512 d_{mdel}=512 dmdel=512,内部层维度 d f f = 2048 d_{ff}=2048 dff=2048

1.3.4. 词嵌入和Softmax

与其他序列转换模型类似,我们使用学习到的嵌入词向量 将输入字符和输出字符转换为维度为 d m o d e l d_{model} dmodel
的向量。我们还使用普通的线性变换和softmax函数将decoder输出转换为预测的下一个词符的概率。在模型中,两个嵌入层之间和pre-softmax线性变换共享相同的权重矩阵,类似于[30]。 在嵌入层中,将这些权重乘以 d m o d e l \sqrt{d_{model}} dmodel

1.3.5. 位置编码

由于我们的模型不包含循环或卷积,为了让模型利用序列的顺序信息,必须加入序列中关于字符相对或者绝对位置的一些信息。 为此,在encoder和decoder堆栈底部的输入嵌入中添加“位置编码”。 位置编码和嵌入的维度 d m o d e l d_{model} dmodel 相同,所以它们两个可以相加。有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码[9].
在这项工作中,我们使用不同频率的正弦和余弦函数:
P E ( p o s , 2 i ) = s i n ( p o s / 1000 0 2 i / d m o d e l ) P E p o s , 2 i + 1 = c o s ( p o s / 1000 0 2 i / d m o d e l ) PE_{(pos, 2i)}=sin(pos/10000^{2i/d_{model}}) \\ PE_{pos, 2i+1}=cos(pos/10000^{2i/d_{model}}) PE(pos,2i)=sin(pos/100002i/dmodel)PEpos,2i+1=cos(pos/100002i/dmodel)
其中 p o s pos pos 是位置, i i i是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。波长形成了从2π到10000·2π的几何数列。我们之所以选择这个函数,是因为我们假设它可以让模型很容易地通过相对位置来学习,因为对任意确定的偏移 k k k, P E p o s + k PE_{pos + k} PEpos+k 可以表示为 P E p o s PE_{pos} PEpos的线性函数。
们还尝试使用预先学习的positional embeddings[9]来代替正弦波,发现这两个版本产生了几乎相同的结果 (see Table 3 row (E))。我们之所以选择正弦曲线,是因为它允许模型扩展到比训练中遇到的序列长度更长的序列。

1.4. 为什么选择Self-Attention

在这一节中,我们将self-attention layers与常用的recurrent layers和convolutional layers进行各方面的比较,比较的方式是 将一个可变长度的符号表示序列 ( x 1 , . . . , x n ) ( x_1 , . . . , x _n ) (x1,...,xn) 映射到另一个等长序列 ( z 1 , . . . , z n ) (z_1, ..., z_n) (z1,...,zn),用 x i , z i ∈ R d x_i,z_i \in\mathbb{R}^d xi,ziRd,比如在典型的序列转换的encoder或decoder中的隐藏层。我们考虑三个方面,最后促使我们使用self-attention。
一是每层的总计算复杂度。一个是可以并行化的计算量,以所需的最小序列操作数衡量。
第三个是网络中长期依赖关系之间的路径长度。学习长期依赖关系是许多序列转导任务中的一个关键挑战。影响学习这种依赖关系能力的一个关键因素是信号在网络中向前和向后穿过的路径的长度。在输入和输出序列中的任何位置组合之间的这些路径越短,就越容易学习长期依赖关系[12]。因此,我们也比较了由不同层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。

如表1所示,self-attention layer用常数次 O ( 1 ) O(1) O(1)的操作连接所有位置,而recurrent layer需要 O ( n ) O(n) O(n)顺序操作。在计算复杂度方面,当序列长度N小于表示维度D时,self-attention layers比recurrent layers更快,这是使用最先进的机器翻译模型表示句子时的常见情况,例如word-piece [38] 和byte-pair [31] 表示。为了提高包含很长序列的任务的计算性能,可以仅在以输出位置为中心,半径为 r r r的的领域内使用self-attention。这将使最大路径长度增长到 O ( n / r ) O(n/r) O(n/r)。我们计划在今后的工作中进一步研究这种方法。

核宽度为 k < n k<n k<n的单层卷积不会连接每一对输入和输出的位置。要这么做,在相邻的内核情况下,需要一个 O ( n / k ) O(n/k) O(n/k)个卷积层的堆栈, 在空洞卷积的情况下需要 O ( l o g k ( n ) ) O(log_k(n)) O(logk(n)) 层[18],它们增加了网络中任意两个位置之间的最长路径的长度。 卷积层通常比循环层代价更昂贵,这与因子 k k k有关。然而,可分离卷积[6]大幅减少复杂度到 O ( k ⋅ n ⋅ d + n ⋅ d 2 ) O(k⋅n⋅d+n⋅d^2) O(knd+nd2)。然而,即使 k = n k=n k=n,可分离卷积的复杂度等于self-attention layer和point-wise feed-forward layer的组合,这是我们在模型中采用的方法。

一个随之而来的好处是,self-attention可以产生更多可解释的模型。我们从我们的模型中研究attention的分布,并在附录中展示和讨论示例。每个attention head不仅清楚地学习到执行不同的任务,还表现出了许多和句子的句法和语义结构相关的行为。

1.5. 训练

分类信息
训练数据和batch标准的WMT 2014英语-德语数据集其中包含约450万个句子对。 这些句子使用byte-pair编码[3]进行编码,源语句和目标语句共享大约37000个词符的词汇表。
对于英语-法语翻译,我们使用大得多的WMT 2014英法数据集,它包含3600万个句子,并将词符分成32000个word-piece词汇表[38]。 序列长度相近的句子一起进行批处理。 每个训练batch的句子对包含大约25000个源词符和25000个目标词符。
硬件与策略在一台具有8个 NVIDIA P100 gpu的机器上训练我们的模型。对于paper中描述的使用超参数的基础模型,每个训练步骤大约需要0.4秒。我们对基础模型进行了总共100000步或12小时的训练。对于我们的大型模型(见表3的底线),步进时间为1.0秒。大模型 使用了30万步(3.5天)的训练。
优化器我们使用Adam优化器[20],其中β1 = 0.9, β2 = 0.98及ϵ= 10-9。 我们根据以下公式在训练过程中改变学习率:
l r a t e = d m o d e l − 0.5 ⋅ m i n ( s t e p _ n u m − 0.5 , s t e p _ n u m ⋅ w a r m u p s t e p s − 1.5 ) lrate=d_{model}^{-0.5}\cdot min(step\_num^{-0.5},step\_num \cdot warmup_steps^{-1.5}) lrate=dmodel0.5min(step_num0.5,step_numwarmupsteps1.5)
这对应于在第一次 w a r m u p _ s t e p s warmup\_steps warmup_steps 步骤中线性地增加学习速率,并且随后将其与步骤数的平方根成比例地减小。 我们使用 w a r m u p _ s t e p s = 4000 warmup\_steps=4000 warmup_steps=4000
正则化训练中我们采用三种正则化:
Layer Normalization: 对每个子层的输出进行归一化处理,使得每个神经元输入的分布更加稳定,这有利于模型的训练和泛化性能。
Residual Dropout: 在输出的每个子层上应用dropout,这个操作在additive操作(子层的输出加上子层的输入)和 normalized操作之前。此外,在编码器和解码器堆栈中,我们将dropout应用到嵌入和位置编码的和。对于基础模型,我们使用 P d r o p = 0.1 P_{drop}=0.1 Pdrop=0.1的丢弃率。
Label Smoothing: 在训练过程中,我们采用了值 ϵ l s = 0.1 \epsilon_{ls}=0.1 ϵls=0.1[36]的标签平滑。这会影响ppl,因为模型学习到了更多的不确定性,但提高了准确率和BLEU评分。

在这里插入图片描述

1.6. 结果

1.6.1. 机器翻译

在WMT 2014英语-德语翻译任务中,大型Transformer模型(表2中的Transformer (big))比以前报道的最佳模型(包括整合模型)高出2个以上的BLEU评分,以28.4分建立了一个全新的SOTA BLEU分数。 该模型的配置列在表3的底部。 在8 个P100 GPU上花费3.5 天进行训练。 即使我们的基础模型也超过了以前发布的所有模型和整合模型,且训练成本只是这些模型的一小部分。

我们的模型在 WMT2014 英语-德语的翻译任务上取得了28.4的BLEU评分。在现有的表现最好模型的基础上,包括整合模型,提高了2个BLEU评分。
  
  在WMT 2014英语-法语翻译任务中,我们的大型模型的BLEU得分为41.0,超过了之前发布的所有单一模型,训练成本低于先前最先进模型的1 ∕ 4 。 英语-法语的Transformer (big) 模型使用 P d r o p = 0.1 P_{drop}=0.1 Pdrop=0.1,而不是0.3。

对于基础模型,我们使用的单个模型来自最后5个checkpoints的平均值,这些checkpoints每10分钟保存一次。 对于大型模型,我们对最后20个checkpoints进行了平均。 我们使用beam search,beam大小为4 ,长度惩罚α = 0.6 [38]。 这些超参数是在开发集上进行实验后选定的。 在推断时,我们设置最大输出长度为输入长度+50,但在条件允许时会尽早终止[38]。

表2总结了我们的结果,并将我们的翻译质量和训练成本与文献中的其他模型体系结构进行了比较。 我们通过将训练时间、所使用的GPU的数量以及每个GPU的持续单精度浮点能力的估计相乘来估计用于训练模型的浮点运算的数量。

1.6.2. 模型变体

为了评估Transformer不同组件的重要性,我们以不同的方式改变我们的基础模型,观测在开发集newstest2013上英文-德文翻译的性能变化。 我们使用前一节所述的beam search,但没有平均checkpoint。 我们在表3中列出这些结果.

在表3的行(A)中,我们改变attention head的数量和attention key和value的维度,保持计算量不变,如3.2.2节所述。 虽然只有一个head attention比最佳设置差0.9 BLEU,但质量也随着head太多而下降。

在表3行(B)中,我们观察到减小key的大小 d k d_k dk会有损模型质量。 这表明确定兼容性并不容易,并且比点积更复杂的兼容性函数可能更有用。 我们在行(C)和(D)中进一步观察到,如预期的那样,更大的模型更好,并且dropout对避免过度拟合非常有帮助。 在行(E)中,我们用学习到的positional encoding[9]来替换我们的正弦位置编码,并观察到与基本模型几乎相同的结果。

在这里插入图片描述

1.6.3. 英文选区解析

在这里插入图片描述
为了评估Transformer是否可以推广到其他任务,我们在英语选区解析上进行了实验。这项任务提出了具体的挑战:输出受到强大的结构约束,并且明显长于输入的时间。此外,RNN序列到序列的模型还不能在小数据体制[37]中获得最先进的结果。
我们用 d m o d e l = 1024 d_{model}=1024 dmodel=1024 在Penn Treebank[25]的Wall Street Journal(WSJ)部分训练了一个4层的transformer,约40K个训练句子。 我们还使用更大的高置信度和BerkleyParser语料库,在半监督环境中对其进行了训练,大约17M个句子[37]。 我们使用了一个16K词符的词汇表作为WSJ唯一设置,和一个32K词符的词汇表用于半监督设置。
我们只在开发集的Section 22 上进行了少量的实验来选择dropout、attention 和residual(第5.4节)、learning rates和beam size,所有其他参数从英语到德语的基础翻译模型保持不变。在推断过程中,我们将最大输出长度增加到输入长度+300。 对于WSJ和半监督设置,我们都使用beam size = 21 和α = 0.3 。

表4中我们的结果表明,尽管缺少特定任务的调优,我们的模型表现得非常好,得到的结果比之前报告的Recurrent Neural Network Grammar [8]之外的所有模型都好。
与RNN序列到序列模型[37]相比,即使仅在40K个句子的WSJ训练集上进行训练,变压器的性能也优于伯克利解析器[29]。

1.7. 结论

在这项工作中,我们提出了Transformer,第一个完全基于attention的序列转换模型,用multi-headed self-attention取代了encoder-decoder架构中最常用的recurrent layers。

对于翻译任务,Transformer比基于循环或卷积层的体系结构训练更快。 在WMT 2014英语-德语和WMT 2014英语-法语翻译任务中,我们取得了最好的结果。 在前面的任务中,我们最好的模型甚至胜过以前报道过的所有整合模型。

我们对基于attention的模型的未来感到兴奋,并计划将它们应用于其他任务。 我们计划将Transformer扩展到除文本之外的涉及输入和输出模式的问题,并研究局部的、受限的attention机制,以有效地处理图像、音频和视频等大型输入和输出。 让生成具有更少的顺序性是我们的另一个研究目标。

我们用于训练和评估模型的代码可以在https://github.com/tensorflow/tensor2tensor找到。

我们十分感谢Nal Kalchbrenner和Stephan Gouws卓有成效的评论、修正和灵感。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. transformer代码实现

2.1. pytorch官方实现nn.transformer

2.1.1. 将transformer看成黑盒

在这里插入图片描述
现在将其变成黑盒,将其盖住:
在这里插入图片描述
现在再来看下Transformer的输入和输出:
在这里插入图片描述
这里是一个翻译任务中transformer的输入和输出。transformer的输入包含两部分:

  • inputs: 原句子对应的tokens,且是完整句子。一般0表示句子开始(<bos>),1表示句子结束(<eos>),2为填充(<pad>)。填充的目的是为了让不同长度的句子变为同一个长度,这样才可以组成一个batch。在代码中,该变量一般取名src。
  • outputs(shifted right):上一个阶段的输出。虽然名字叫outputs,但是它是输入。最开始为0(<bos>),然后本次预测出“我”后,下次调用Transformer的该输入就变成<bos> 我。在代码中,该变量一般取名tgt。

Transformer的输出是一个概率分布。

2.1.2. transformer的推理过程

这里先讲Transformer的推理过程,因为这个简单。其实通过上面的讲解,你可能已经清楚了。上面是Transformer推理的第一步,紧接着第二步如图:
在这里插入图片描述
Transformer的推理过程就是这样一遍一遍调用Transformer,直到输出<eos>或达到句子最大长度为止。

通常真正在实战时,Transformer的Encoder部分只需要执行一遍就行了,这里为了简单起见,就整体重新执行。

2.1.3. transformer的训练过程

在Transformer推理时,我们是一个词一个词的输出,但在训练时这样做效率太低了,所以我们会将target一次性给到Transformer(当然,你也可以按照推理过程做),如图所示:
在这里插入图片描述
从图上可以看出,Transformer的训练过程和推理过程主要有以下几点异同:
1. 源输入src相同:对于Transformer的inputs部分(src参数)一样,都是要被翻译的句子。
2. 目标输入tgt不同:在Transformer推理时,tgt是从<bos>开始,然后每次加入上一次的输出(第二次输入为<bos> 我)。但在训练时是一次将“完整”的结果给到Transformer,这样其实和一个一个给结果上一致(可参考该篇的Mask Attention部分)。这里还有一个细节,就是tgt比src少了一位,src是7个token,而tgt是6个token。这是因为我们在最后一次推理时,只会传入前n-1个token。举个例子:假设我们要预测<bos> 我 爱 你 <eos>(这里忽略pad),我们最后一次的输入tgt是<bos> 我 爱 你(没有<eos>),因此我们的输入tgt一定不会出现目标的最后一个token,所以一般tgt处理时会将目标句子删掉最后一个token。
3. 输出数量变多:在训练时,transformer会一次输出多个概率分布。例如上图,就等价于是tgt为 <bos>时的输出,就等价于tgt为<bos> 我时的输出,依次类推。当然在训练时,得到输出概率分布后就可以计算loss了,并不需要将概率分布再转成对应的文字。注意这里也有个细节,我们的输出数量是6,对应到token就是我 爱 你 <eos> <pad> <pad>,这里少的是<bos>,因为<bos>不需要预测。计算loss时,我们也是要和的这几个token进行计算,所以我们的label不包含<bos>。代码中通常命名为tgt_y
当得到transformer的输出后,我们就可以计算loss了,计算过程如图:

在这里插入图片描述

2.1.4. pytorch中的nn.transformer

2.1.4.1. nn.Transformer简介

在Pytorch中已经为我们实现了Transformer,我们可以直接拿来用,但nn.Transformer和我们上图的还是有点区别,具体如图:
在这里插入图片描述
Transformer并没有实现EmbeddingPositional Encoding和最后的Linear+Softmax部分,这里我简单对这几部分进行说明:

Embedding: 负责将token映射成高维向量。例如将123映射成[0.34, 0.45, 0.123, ..., 0.33]。通常使用nn.Embedding来实现。但nn.Embedding参数并不是一成不变的,也是会参与梯度下降。关于nn.Embedding可参考文章Pytorch nn.Embedding的基本使用
Positional Encoding:位置编码。用于为token编码增加位置信息,例如I love you这三个token编码后的向量并不包含其位置信息(love左边是I,右边是you这个信息)。这个位置信息还挺重要的,有和没有真的是天差地别。
Linear+Softmax:一个线性层加一个Softmax,用于对nn.Transformer输出的结果进行token预测。如果把Transformer比作CNN,那么nn.Transformer实现的就是卷积层,而Linear+Softmax就是卷积层后面的线性层。
这里我先简单的演示一下nn.Transformer的使用:

# 定义编码器,词典大小为10,要把token编码成128维的向量
embedding = nn.Embedding(10, 128)
# 定义transformer,模型维度为128(也就是词向量的维度)
transformer = nn.Transformer(d_model=128, batch_first=True) # batch_first一定不要忘记
# 定义源句子,可以想想成是 <bos> 我 爱 吃 肉 和 菜 <eos> <pad> <pad>
src = torch.LongTensor([[0, 3, 4, 5, 6, 7, 8, 1, 2, 2]])
# 定义目标句子,可以想想是 <bos> I like eat meat and vegetables <eos> <pad>
tgt = torch.LongTensor([[0, 3, 4, 5, 6, 7, 8, 1, 2]])
# 将token编码后送给transformer(这里暂时不加Positional Encoding)
outputs = transformer(embedding(src), embedding(tgt))
outputs.size()

torch.Size([1, 9, 128])

Transformer输出的Shape和tgt编码后的Shape一致。在训练时,我们会把transformer的所有输出送给Linear,而在推理时,只需要将最后一个输出送给Linear即可,即outputs[:, -1]。

2.1.4.2. nn.Transformer的构造参数详解

Transformer构造参数众多,所以我们还需要将黑盒稍微打开一下:
在这里插入图片描述
nn.Transformer主要由两部分构成:nn.TransformerEncodernn.TransformerDecoder。而nn.TransformerEncoder又是由多个nn.TransformerEncoderLayer堆叠而成的,图中的Nx就是要堆叠多少层。nn.TransformerDecoder同理。

下面是nn.Transformer的构造参数:

  • d_model: Encoder和Decoder输入参数的特征维度。也就是词向量的维度。默认为512
  • nhead: 多头注意力机制中,head的数量。关于Attention机制,可以参考这篇文章。注意该值并不影响网络的深度和参数数量。默认值为8。
  • num_encoder_layers: TransformerEncoderLayer的数量。该值越大,网络越深,网络参数量越多,计算量越大。默认值为6
  • num_decoder_layers:TransformerDecoderLayer的数量。该值越大,网络越深,网络参数量越多,计算量越大。默认值为6
  • dim_feedforward:Feed Forward层(Attention后面的全连接网络)的隐藏层的神经元数量。该值越大,网络参数量越多,计算量越大。默认值为2048
  • dropout:dropout值。默认值为0.1
  • activation: Feed Forward层的激活函数。取值可以是string(“relu” or “gelu”)或者一个一元可调用的函数。默认值是relu
  • custom_encoder:自定义Encoder。若你不想用官方实现的TransformerEncoder,你可以自己实现一个。默认值为None
  • custom_decoder: 自定义Decoder。若你不想用官方实现的TransformerDecoder,你可以自己实现一个。
  • layer_norm_eps: Add&Norm层中,BatchNorm的eps参数值。默认为1e-5
  • batch_first:batch维度是否是第一个。如果为True,则输入的shape应为(batch_size, 词数,词向量维度),否则应为(词数, batch_size, 词向量维度)。默认为False。这个要特别注意,因为大部分人的习惯都是将batch_size放在最前面,而这个参数的默认值又是False,所以会报错
    norm_first – 是否要先执行norm。例如,在图中的执行顺序为 Attention -> Add -> Norm。若该值为True,则执行顺序变为:Norm -> Attention -> Add
2.1.4.3. Transformer的forward参数详解

Transformer的forward参数需要详细解释,这里我先将其列出来,进行粗略解释,然后再逐个进行详细解释:

  • src: Encoder的输入。也就是将token进行Embedding并Positional Encoding之后的tensor。必填参数。Shape为(batch_size, 词数, 词向量维度)
  • tgt: 与src同理,Decoder的输入。 必填参数。Shape为(词数, 词向量维度)
  • src_mask: 对src进行mask。不常用。Shape为(词数, 词数)
  • tgt_mask:对tgt进行mask。常用。Shape为(词数, 词数)
  • memory_mask – 对Encoder的输出memory进行mask。 不常用。Shape为(batch_size, 词数, 词数)
  • src_key_padding_mask:对src的token进行mask. 常用。Shape为(batch_size, 词数)
  • tgt_key_padding_mask:对tgt的token进行mask。常用。Shape为(batch_size, 词数)
  • memory_key_padding_mask:对tgt的token进行mask。不常用。Shape为(batch_size, 词数)

上面的所有mask都是0代表不遮掩,-inf代表遮掩。另外,src_mask、tgt_mask和memory_mask是不需要传batch的。
补充:上面的说法是pytorch1.11版本。我发现1.11版本key_padding_mask可以用True/False,但1.12版本key_padding_mask好像只能是True/False,其中True表示遮掩,而False表示不遮掩(这个可不要弄混了,这个和Transformer原论文实现正好相反,如果弄反了,会造成结果为nan)。

上面说了和没说其实差不多,重要的是每个参数的是否常用和其对应的Shape(这里我默认batch_first=True)。 接下来对各个参数进行详细解释。

src和tgt
src参数和tgt参数分别为Encoder和Decoder的输入参数。它们是对token进行编码后,再经过Positional Encoding之后的结果。

例如:我们一开始的输入为:[[0, 3, 4, 5, 6, 7, 8, 1, 2, 2]],Shape为(1, 10),表示batch_size为1, 每句10个词。

在经过Embedding后,Shape就变成了(1, 10, 128),表示batch_size为1, 每句10个词,每个词被编码为了128维的向量。

src就是这个(1, 10, 128)的向量。tgt同理

src_mask、tgt_mask和memory_mask
要真正理解mask,需要学习Attention机制,可参考该篇。这里只做一个简要的说明。

在经过Attention层时,会让每个词具有上下文关系,也就是每个词除了自己的信息外,还包含其他词的信息。例如:苹果 很 好吃苹果 手机 很 好玩,这两个苹果显然指的不是同一个意思。但让苹果这个词具备了后面好吃手机这两个词的信息后,那就可以区分这两个苹果的含义了。

在Attention中,我们有这么一个“方阵”,描述着词与词之间的关系,例如:

       苹果  很  好吃
苹果 [[0.5, 0.1, 0.4],[0.1, 0.8, 0.1],
好吃  [0.3, 0.1, 0.6],]

在上述矩阵中,苹果这个词与自身, 很和好吃三个词的关系权重就是[0.5, 0.1, 0.4],通过该矩阵,我们就可以得到包含上下文的苹果了,即

import torch.nn as nn
import torch


if __name__ == "__main__":
    transformer_model = nn.Transformer(nhead=16, num_encoder_layers=12)
    src = torch.rand((10, 32, 512))
    tgt = torch.rand((20, 32, 512))
    out = transformer_model(src, tgt)

官方使用说明https://pytorch.org/docs/stable/generated/torch.nn.Transformer.html

参考:RNN,LSTM,CNN,RCNN的优缺点
参考:Transformer(一)–论文翻译:Attention Is All You Need 中文版
参考:Transformer(二)–论文理解:transformer 结构详解
参考:Docs > torch.nn > Transformer
参考:Pytorch中 nn.Transformer的使用详解与Transformer的黑盒讲解
参考:Pytorch入门实战(5):基于nn.Transformer实现机器翻译(英译汉)
参考:Model Description
参考:word_language_model/model.py
参考:层层剖析,让你彻底搞懂Self-Attention、MultiHead-Attention和Masked-Attention的机制和原理
参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花逐流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值