Transformer (Attention Is All You Need)

在这里插入图片描述

Transformer: the first sequence transduction model (序列转录模型) based entirely on attention, replacing the recurrent layers most commonly used in encoder-decoder architectures with multi-headed self-attention.

Sequence-to-sequence (Seq2seq)

sequence transduction model

  • Seq2seq: Input a sequence, output a sequence (The output length is determined by model)
    在这里插入图片描述

Seq2seq model 的一般架构: Encoder + Decoder

在这里插入图片描述

Transformer: Encoder

  • Encoder: 输入一组向量,输出一组同样长度的向量 (Transformer 用的是 self-attention 而非 RNN,因此在时间方向上是可以并行计算的!这可以极大地提高网络训练速度。同时由于是并行计算,Transformer 在长时序任务中也不会像 RNN 一样产生严重的信息遗忘)
    在这里插入图片描述

为了方便残差连接,Transformer 中的 embedding 层、multi-head attention 层以及全连接层的输出维度相同,均为 d m o d e l d_{model} dmodel,这也使得网络的参数量不会过大。这样 Encoder 一共只有两个超参 N N N d m o d e l d_{model} dmodel (论文中选定 N = 6 , d m o d e l = 512 N=6,d_{model}=512 N=6,dmodel=512)


  • 简单来说,Transformer 的 Encoder 由若干个 block 构成,如下图所示,每个 block 中,input vector 先经过 self-attention 后,再经过全连接层得到 output vector
    在这里插入图片描述

把这个 self attention 和下面的 RNN 比较一下,可以看到,Self-attention 可以并行地抽取出输入序列中所有位置元素的信息,而 RNN 则需要串行地抽取
在这里插入图片描述

相比起 RNN,self-attention 对模型的假设更少,这使得我们需要用更多的数据和更大的模型才能训练出与 RNN 或 CNN 同样的效果

  • 在 Transformer 的原始论文中,Encoder 的结构还要更复杂一些。如下图所示,Transformer 在 self-attention 和全连接层之后引入了 skip connectionlayer normalization (还有一些细节在之后介绍)
    在这里插入图片描述

为什么使用 Layer Normalization 而不是 Batch Normalization 呢?

  • 假设输入数据的形状是 N × T × d m o d e l N\times T\times d_{model} N×T×dmodel,其中 N N N 为批大小, T T T 为序列长度。如果是 BN,那么就需要将输入数据 reshape 为 N × ( T × d m o d e l ) N\times (T\times d_{model}) N×(T×dmodel) 的二维数据,然后以列为单位进行归一化。但这里需要注意的是,由于我们在处理 seq2seq 任务,不同样本的序列长度其实是不同的,在进行批处理时,我们会将序列长度较短的样本用特殊的填充字符填充到相同的序列长度 T T T。因此如果使用 BN,那么长序列样本在归一化时必定会收到填充字符的影响,这是不合理的。而 LN 就没有这种问题

Scaled Dot-Product Attention

  • 假设 query 和 key 向量的维度均为 d k d_k dk,value 向量的维度为 d v d_v dv,那么所有的 query 向量和 key 向量就分别组成了两个 T × d k T\times d_k T×dk 的矩阵 Q Q Q K K K,所有的 value 向量就组成了一个 T × d v T\times d_v T×dv 的矩阵 V V V。此时使用 Q K T QK^T QKT 便可得到 T × T T\times T T×T 的 Attention matrix。相比普通的 Dot-Product Attention,Transformer 在这里增加了 Scale 的步骤,也就是将 Attention matrix 除以 d k \sqrt{d_k} dk 。再将经过 Scale 之后的 Attention matrix 与 V V V 相乘即可得到最终的 T × d v T\times d_v T×dv 的输出。总结成数学式子即为
    在这里插入图片描述
    在这里插入图片描述
  • 那么为什么要加上 Scale 呢?这是因为当 d k d_k dk 比较大时 (论文中选用的 d k d_k dk 为 512),计算相似度的点积结果的绝对值也会比较大 (这里不妨假设 q q q k k k 的元素均为独立的随机变量,均值为 0,方差为 1。由于当两个变量独立时, V a r ( X Y ) = E ( X 2 Y 2 ) − ( E ( X Y ) ) 2 = V a r ( X ) V a r ( Y ) + V a r ( X ) ( E ( Y ) ) 2 + V a r ( Y ) ( E ( X ) ) 2 = Var(XY)=E(X^2Y^2)−(E(XY))^2=Var(X)Var(Y)+Var(X)(E(Y))^2+Var(Y)(E(X))^2= Var(XY)=E(X2Y2)(E(XY))2=Var(X)Var(Y)+Var(X)(E(Y))2+Var(Y)(E(X))2=,因此 q q q k k k 点积的均值为 0,方差则变成了 d k d_k dk),这使得在训练时 softmax 函数处于梯度非常小的区域,训练速度很慢。为此可以除以 d k \sqrt{d_k} dk 来减小该影响

注意看下图,在 Encoder 的 self-attention 中, q , k , v q,k,v q,k,v 并没有经过三个不同的线性层映射,而是直接使用的 input vector。也就是说, q , k , v q,k,v q,k,v 其实是同一个向量
在这里插入图片描述

  • 下面是 Scaled Dot-Product Attention 的快速矩阵实现:
def attention(query, key, value, mask=None, dropout=None):
    "Compute 'Scaled Dot Product Attention'"
    # query / key: shape -> [N, T, d_k] or [N, H, T, d_k]
    # value: shape -> [N, T, d_v] or [N, H, T, d_v]
    # 	H stands for head number, which is used in Multi-head attention
    d_k = query.size(-1)
    # matmul only computes the last two dims as matrix multiplication
    scores = torch.matmul(query, key.transpose(-2, -1)) \
             / math.sqrt(d_k)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim = -1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn

Multi-Head Attention

  • Multi-Head Attention 先使用 h h h 个不同的矩阵将 Q , K , V Q,K,V Q,K,V 线性映射到 h h h 个不同的低维空间,然后各自进行 Scaled Dot-Product Attention,最后将 h h h 个不同的结果 (i.e. h h h d v d_v dv 维的向量) 进行连接后再通过一个线性映射将结果映射回 d m o d e l d_{model} dmodel 维的空间
    在这里插入图片描述
    在这里插入图片描述
  • Multi-Head Attention 使得模型可以同时关注来自不同特征子空间的注意力信息,进而挖掘出不同种类的相关性,增强模型的表现能力

论文中选择 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。由于 nulti-head Attention 中每个 head 都是映射到低维子空间中进行计算的,因此总的计算量与 single-head attention 类似

  • 事实上,Multi-Head Attention 也可以实现基于矩阵的快速计算
    • 我们可以令 W Q = [ W 1 Q . . . W h Q ] , W K = [ W 1 K . . . W h K ] , W V = [ W 1 V . . . W h V ] W^Q=\begin{bmatrix}W^Q_1&...&W^Q_h\end{bmatrix},W^K=\begin{bmatrix}W^K_1&...&W^K_h\end{bmatrix},W^V=\begin{bmatrix}W^V_1&...&W^V_h\end{bmatrix} WQ=[W1Q...WhQ],WK=[W1K...WhK],WV=[W1V...WhV],其中 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV 的大小均为 d m o d e l × ( h d k ) d_{model}\times (hd_k) dmodel×(hdk),也就是 d m o d e l × d m o d e l d_{model}\times d_{model} dmodel×dmodel
    • Q Q Q 的大小为 N × T × d m o d e l N\times T\times d_{model} N×T×dmodel。由于 matmul 是在矩阵的最后两个维度上进行矩阵乘法,因此, matmul ( Q , W Q ) \text{matmul}(Q,W^Q) matmul(Q,WQ) 即为一个 N × T × h d k N\times T\times hd_k N×T×hdk 的矩阵 (如果不考虑批处理,则 Q W Q = [ Q W 1 Q . . . Q W h Q ] QW^Q=\begin{bmatrix}QW^Q_1&...&QW^Q_h\end{bmatrix} QWQ=[QW1Q...QWhQ] 为一个 T × h d k T\times hd_k T×hdk 的矩阵,每 h h h 列即为一个 head 的 Q Q Q 矩阵)。可以对上述矩阵进行形状变换,将其变换为 N × T × h × d k N\times T\times h\times d_k N×T×h×dk 的矩阵。再进行坐标轴变换,得到 N × h × T × d k N\times h\times T\times d_k N×h×T×dk 的矩阵,这样就可以正常地代入之前的 attention 函数进行计算,得到 N × h × T × d v N\times h\times T\times d_v N×h×T×dv 的矩阵,将其重新经过形状变换和坐标轴变换之后就得到了 N × T × h d v N\times T\times hd_v N×T×hdv 的矩阵,也就相当于将 multi-head 得到的结果连接到了一起
    • 最后再经过一个 d m o d e l × d m o d e l d_{model}\times d_{model} dmodel×dmodel 的线性变换即可得到最终的结果
class MultiHeadedAttention(nn.Module):
    def __init__(self, h, d_model, dropout=0.1):
        "Take in model size and number of heads."
        super(MultiHeadedAttention, self).__init__()
        assert d_model % h == 0
        # We assume d_v always equals d_k
        self.d_k = d_model // h
        self.h = h
        self.linears = clones(nn.Linear(d_model, d_model), 4)	# 4 个线性层
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
        
    def forward(self, query, key, value, mask=None):
        "Implements Figure 2"
	    # query / key / value: shape -> [N, T, d_k]
        if mask is not None:
            # Same mask applied to all h heads.
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)
        
        # 1) Do all the linear projections in batch from d_model => h x d_k 
        query, key, value = \
            [l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
             for l, x in zip(self.linears, (query, key, value))]
        
        # 2) Apply attention on all the projected vectors in batch. 
        x, self.attn = attention(query, key, value, mask=mask, 
                                 dropout=self.dropout)
        
        # 3) "Concat" using a view and apply a final linear. 
        x = x.transpose(1, 2).contiguous() \
             .view(nbatches, -1, self.h * self.d_k)
        return self.linears[-1](x)

Position-wise Feed-Forward Networks

  • 这其实就是 MLP, 输入 x x x 是一个 d m o d e l d_{model} dmodel 维的向量,通过一个线性层加 ReLU 将其映射到 d f f = 2048 d_{ff}=2048 dff=2048 维。由于还需要进行 skip connection,因此再通过一个线性层将其映射回 d m o d e l d_{model} dmodel 维。数学公式如下:
    在这里插入图片描述其中,所有位置对应的映射参数 W 1 , W 2 , b 1 , b 2 W_1,W_2,b_1,b_2 W1,W2,b1,b2 都是一样的
    在这里插入图片描述

To learn more ……

Transformer: Decoder

Decoder - Autoregressive (AT)

  • Auto-regressive (自回归): Decoder 过去时刻的输出也会作为当前时刻的输入 (consume the previously generated symbols as additional input when generating the next)

Speech Recognition as example

  • (1) Decoder 首先输入 Encoder 输出的一组向量,然后再输入一个特殊的标记 START,经过 Softmax 后得到第一个输出 “机” (输出 one-hot vector)
    在这里插入图片描述
  • (2) 有了第一个输出 “机” 之后,我们把上一个输出 “机” 也当作输入,得到下一个输出 “器”
    在这里插入图片描述
  • (3) 之后依次得到所有结果
    在这里插入图片描述

Error propogation (一步错,步步错)

  • Decoder 用自己之前的输出作为之后的输入,因此如果一步输出错误,后面的输出可能也会错误
    在这里插入图片描述

Decoder structure

在这里插入图片描述

在 Transformer 的 paper 中,与 Encoder 一样, N N N 同样取 6

  • Masked Self-attention: 相比于 self-attention,在输出 b i b^i bi 时,不考虑 a j a^j aj ( j > i j>i j>i) 的影响
    • Why masked? (Consider how does decoder work) - 因为采用自回归,Decoder 的输入是它之前的输出,是一个一个产生的,因此在输出 b i b^i bi 的时候, a j a^j aj 压根就还没有被生成。使用 Mask 之后,就能保证在训练时,任意时刻的输出不会受它后续时刻输出的影响,从而使得训练和推理时的行为一致
      在这里插入图片描述在这里插入图片描述它的实现也非常简单,还是看之前 Scaled Dot-product Attention 的那张图。可以看到,只需在 Scale 操作之后加上一个 mask,把 Attention Matrix 中无需 attention 的位置都替换为一个很大的负数 ( − 1 e 10 -1e10 1e10) 即可
      在这里插入图片描述

We do not know the correct output length

  • Adding “Stop Token
    在这里插入图片描述在这里插入图片描述

Decoder – Non-autoregressive (NAT)

AT v.s. NAT

在这里插入图片描述

  • NAT 是一次输出所有向量,而不是像 AT 一样,一个一个地输出

NAT

  • How to decide the output length for NAT decoder?
    • (1) Another predictor for output length: 输入为 NAT 的输出,输出为一个代表 output length 的整数
    • (2) Output a very long sequence, ignore tokens after END
      在这里插入图片描述
  • Advantage: parallel, more stable generation (e.g., TTS)
  • NAT is usually worse than AT (why? Multi-modality)

To learn more ……

Encoder-Decoder

Transformer

在这里插入图片描述


Embeddings and Softmax

  • 为了减小参数量,Input embedding、Output embedding 以及 Softmax 前的 Linear 层的参数是共享的。下面简单分析一下 Linear 层为何可以与 embedding 层共享参数:Linear 层的权重 W W W 的大小为 # w o r d × d m o d e l \#word\times d_{model} #word×dmodel,若 x x x 为 Decoder 输出的 d m o d e l d_{model} dmodel 维的向量,那么当 Linear 层与 embedding 层共享参数时, W x Wx Wx 的每一行即为 x x x 与一个单词的词向量的点积,它们越相似,点积结果就应该越大,最后再通过 Softmax 转化为概率
  • 值得注意的是,Transformer 不止共享了参数,还使用了一个 trick:将 embedding 层的参数乘上 d m o d e l \sqrt{d_{model}} dmodel 。这是因为在学习 Embedding 层参数时,会把每一个向量的 L2 norm 学得相对比较小,这样当维度比较大时权重值会比较小,但 Embedding 之后还要加上 Positional Encoding,而 Positional Encoding 并不会随在维度比较大时保持一个较小的 L2 norm,因此乘上 d m o d e l \sqrt{d_{model}} dmodel 后可以使得 Embedding 和 Positional Encoding 保持在同一个 scale 上 (The reason we increase the embedding values before addition is to make the positional encoding relatively smaller. This means the original meaning in the embedding vector won’t be lost when we add them together.)

Positional Encoding

  • Transformer 通过使用 Positional Encoding 来直接给 Encoder 和 Decoder 的输入加上时序信息。Transformer 中 Positional Encoding 的是由不同频率的正余弦函数生成的:
    在这里插入图片描述这里 p o s pos pos 表示位置, i i i 表示维度 (Positional Encoding 中,每个位置都由一个 d m o d e l d_{model} dmodel 维的向量表示)
  • 首先要说明的是,Positional Encoding 的生成方式多种多样,利用正余弦函数生成只是其中的一种。下面解释一下为什么选用上面的方法生成 Positional Encoding。为此,我们可以思考一下自己如何构造 Positional Encoding。Positional Encoding 需要使得 (1) 不同位置对应的向量不同,因此最简单的方法就是第 i i i 个位置的 Positional Encoding 就由 i i i 表示,如下图所示:
    在这里插入图片描述但这种方法会使得序列靠后的 Positional Encoding 过大,从而掩盖掉 Embedding 的信息。也就是说,Positional Encoding 还需要使得 (2) 向量元素在一定范围内。那么如果第 i i i 个位置的 Positional Encoding 由 i N − 1 \frac{i}{N-1} N1i 表示呢?
    在这里插入图片描述这样虽然使得 Positional Encoding 的值限制在了一定范围内,但不同长度文本的位置编码步长是不同的,较短的文本中紧紧相邻的两个字的位置编码差异,会和长文本中相邻数个字的两个字的位置编码差异一致。这显然是不合适的,我们关注的位置信息,最核心的就是相对次序关系,尤其是上下文中的次序关系,如果使用这种方法,那么在长文本中相对次序关系会被「稀释」。也就是说,(3) Positional Encoding 的步长不能受文本长度影响。结合上面的要求 (2)(3),我们可以使用一个有界周期函数来生成位置编码,即
    P E ( p o s ) = sin ⁡ ( p o s α ) PE(pos)=\sin\left(\frac{pos}{\alpha}\right) PE(pos)=sin(αpos)其中 α \alpha α 用来调节位置编码函数的波长,当 α \alpha α 比较大时,波长比较长,相邻字的位置编码之间的差异比较小。但这样还不能满足要求 (1),不同位置的 Positional Encoding 仍可能是相同的
    在这里插入图片描述为此,可以利用不同维度生成多个位置编码。如果不同维度对应的 α \alpha α 不同,甚至在一些维度将正弦函数替换为余弦函数,那么就是论文中的构造方法了:
    在这里插入图片描述可以看到,每个维度的三角函数波长在 2 π 2\pi 2π 10000 ⋅ 2 π 10000\cdot2\pi 100002π 之间,这使得每一维度上都包含了一定的位置信息,而各个位置字符的位置编码又各不相同
    在这里插入图片描述

回忆一下,在计算机中我们是用若干个 bit 来表示数字的,这可以理解为我们用一个向量来表示一个数字,这个向量的所有元素只可能取 0 或 1,且不同维度的值都对应周期不同的周期函数 (在 0、1 之间变动的周期函数)。现在我们也同样用一个 d m o d e l d_{model} dmodel 维的向量来表示一个数字,并且每一维都对应一个不同周期的周期函数


Cross attention

  • 在 Cross attention 中,query 来自之前的 decoder 层,而 key 和 value 则来自 encoder 的输出。这里就不再是 self-attention 了,而是与在 RNN 中使用 Attention 机制的方法相同,目的是获取一个上下文向量
    在这里插入图片描述
  • 联想: 现在 Decoder 只使用了 Encoder 最后一层的输出,可以进一步探索 Encoder 和 Decoder 不同的连接方式

Training

  • (1) minimize cross entropy: 最小化 Decoder 输出的所有概率向量与 Ground truth 之间的交叉熵之和
  • (2) Teacher Forcing: using the ground truth as input. (在训练时,Decoder 的输入使用 Ground Truth 而非 Decoder 之前的输出)
    在这里插入图片描述

Scheduled Sampling


Varied learning rate

  • Transformer 的 paper 中提出使用如下的可变学习率,配合 Adam 优化器 ( β 1 = 0.9 , β 2 = 0.98 , ε = 1 0 − 9 \beta_1 = 0.9, β_2 = 0.98,\varepsilon= 10^{−9} β1=0.9,β2=0.98,ε=109) 进行模型训练:
    在这里插入图片描述
  • 可以看到, d m o d e l d_{model} dmodel 越大,学习率越小。并且学习率在 warmup_steps 步内线性增加,之后按指数衰减

Residual Dropout

  • 在 MLP 和 multi-head attention 层之后 (Add and norm 之前) 以及 Embedding 和 Positional encoding 相加之后都加上 dopout 层, P d r o p o u t = 0.1 P_{dropout}=0.1 Pdropout=0.1

Label Smoothing

  • 在利用 Softmax 进行训练时,正确的词对应的概率不再是 1,而是设为 ϵ l s = 0.1 \epsilon_{ls}=0.1 ϵls=0.1,剩下的错误词的概率为 0.9 除以词典大小
  • This hurts perplexity, as the model learns to be more unsure, but improves accuracy and BLEU score.

实验结果

在这里插入图片描述

Tips

Copy Mechanism


To learn more…

Guided Attention

To learn more…

  • Monotonic Attention
  • Location-aware attention

Beam Search

  • Assume there are only two tokens ( V = 2 V=2 V=2). The red path is Greedy Decoding. The green path is the best one. (Not possible to check all the paths …)
    在这里插入图片描述
  • Beam Search 可以找出一个最佳路径的近似解
    • 需要注意,对于某些任务而言,最佳路径不一定是最好的: Randomness is needed for decoder when generating sequence in some tasks. (e.g. sentence completion, TTS)

Optimizing Evaluation Metrics?

  • 我们在训练模型时,最小化的是 cross entropy,但我们在评估模型时计算的则是 blue score (non-differentiable),那么可不可能在训练时就直接最大化 blue score 呢?
    在这里插入图片描述
  • When you don’t know how to optimize, just use reinforcement learning (RL)! (把 blue score 当作 reward,model 看作 agent)

References

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值