模型结构

1、结构图如下。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_学习


上图是论文中 Transformer 的内部结构图,左侧为 Encoder block,右侧为 Decoder block。可以看到 Encoder block 包含一个 Multi-Head Attention,而 Decoder block 包含两个 Multi-Head Attention (其中有一个用到 Masked)。Multi-Head Attention 上方还包括一个 Add & Norm 层,Add 表示残差连接 (Residual Connection) 用于防止网络退化,Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化。

2、名词解释

Input Embedding: 可以理解为将输入的数据转化为特定的向量。本文中是将输入数据转化为512纬的向量。

Positional Encoding: 因为Attention机制关注全局信息,所以需要用一个位置向量来记住单词的位置。这个位置向量可以通过公式或者算法获得,文中采用的是公式。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_02


Self-Attention: 顾名思义就是自己和自己计算的注意力机制。输入的数据通过这个模块可以得到三个不同的矩阵Q,K,V。然后对这三个矩阵进行运算输出一个包含输入单词信息的矩阵Z。

Multi-Head Attention: 多头自注意力就是多个自注意力机制,文章中是8个自注意力。
Add & Norm: Add 表示残差连接 (Residual Connection) 用于防止网络退化,Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化。
①Add指X+MultiHeadAttention(X),是一种残差连接,即每一个前馈神经网络的输入不光包含上述self-attention的输出Z,还包含最原始的输入。通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_学习_03


Norm指 Layer Normalization,通常用于 RNN 结构,Layer Normalization 会将每一层神经元的输入都转成均值方差都一样的,这样可以加快收敛。

Feed Forward: Feed Forward 层比较简单,是一个两层的全连接层,第一层的激活函数为 Relu,第二层不使用激活函数,对应的公式如下。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_04


X是输入,Feed Forward 最终得到的输出矩阵的维度与 X 一致。

Masked Multi-Head Attention: 即多个自注意力,论文中采用的是8个。

多头注意力机制:

他扩展了模型关注不同位置的能力,这对翻译一下句子特别有用,因为我们想知道“it”是指代的哪个单词。

第二个方面,他给了自注意力层多个“表示子空间”。对于多头自注意力机制,我们不止有一组Q/K/V权重矩阵,而是有多组(论文中使用8组),所以每个编码器/解码器使用8个“头”(可以理解为8个互不干扰自的注意力机制运算),每一组的Q/K/V都不相同。然后,得到8个不同的权重矩阵Z,每个权重矩阵被用来将输入向量投射到不同的表示子空间。

Linear layer:

- 目的:将由解码器堆栈产生的向量投影到一个更大的向量中,称为对数向量。这个向量对应着模型的输出词汇表;向量中的每个值,对应着词汇表中每个单词的得分;

softmax层:

- 操作:这些分数转换为概率(所有正数,都加起来为1.0)。选择具有最高概率的单元,并且将与其相关联的单词作为该时间步的输出

Attention 机制 是对 source 中各个元素 的 value 进行加权求和,而 query 和 key 用于计算 对应 value 的权值系数

  • attention 的核心 就是从 大量信息中 筛选出少量的 重要信息;
  • 具体操作:每个 value 的 权值系数,代表 其 重要度;
    缺点:忽略了 源端或目标端 词与词间 的依赖关系

self-attention的结构在计算每个token时,总是会考虑整个序列其他token的表达;学习句子内部的词依赖关系,捕获句子的内部结构。

  • 优点
  • 捕获源端和目标端词与词间的依赖关系
  • 捕获源端或目标端自身词与词间的依赖关系
  • self-attention 是如何解决长距离依赖问题的呢?
  • 解决方式:
  • 利用注意力机制来“动态”地生成不同连接的权重,从而处理变长的信息序列

工作流程

数据从输入到encoder到decoder输出这个过程中的流程(以机器翻译为例子):

1、获取Transformer的输入

对于机器翻译来说,一个样本是由原始句子和翻译后的句子组成的。比如原始句子是: “我爱机器学习”,那么翻译后是 ’i love machine learning‘。 则该一个样本就是由“我爱机器学习”和 “i love machine learning” 组成。

这个样本的原始句子的单词长度是length=4,即‘我’ ‘爱’ ‘机器’ ‘学习’。经过embedding后每个词的embedding向量是512。那么“我爱机器学习”这个句子的embedding后的维度是[4,512 ] (若是批量输入,则embedding后的维度是[batch, 4, 512])。

Transformer的输入是句子中每一个单词的表示向量X,X由单词的 Embedding 和单词位置的 Embedding 相加得到。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_05

单词Embedding

单词的 Embedding 有很多种方式可以获取,例如可以采用 Word2Vec、Glove 等算法预训练得到,也可以在 Transformer 中训练得到。

位置Embedding

因为 Transformer 不采用 RNN 的结构,而是使用全局信息,不能利用单词的顺序信息,所以 Transformer 中使用位置 Embedding 保存单词在序列中的相对或绝对位置。

位置 Embedding 用 PE表示,PE 的维度与单词 Embedding 是一样的。PE 可以通过训练得到,也可以使用某种公式计算得到。在 Transformer 中采用了后者,计算公式如下:

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_06


其中,pos 表示单词在句子中的位置,d 表示 PE的维度 (与词 Embedding 一样),2i 表示偶数的维度,2i+1 表示奇数维度 (即 2i≤d, 2i+1≤d)。使用这种公式计算 PE 有以下的好处:

  • 使 PE 能够适应比训练集里面所有句子更长的句子,假设训练集里面最长的句子是有 20 个单词,突然来了一个长度为 21 的句子,则使用公式计算的方法可以计算出第 21 位的 Embedding。
  • 可以让模型容易地计算出相对位置,对于固定长度的间距 k,PE(pos+k) 可以用 PE(pos) 计算得到。因为 Sin(A+B) = Sin(A)Cos(B) + Cos(A)Sin(B), Cos(A+B) = Cos(A)Cos(B) - Sin(A)Sin(B)。

padding

假设样本中句子的最大长度是10,那么对于长度不足10的句子,需要补足到10个长度,shape就变为[10, 512], 补全的位置上的embedding数值自然就是0了

Padding Mask

对于输入序列一般要进行padding补齐,也就是说设定一个统一长度N,在较短的序列后面填充0到长度为N。对于那些补零的数据来说,attention机制不应该把注意力放在这些位置上,所以需要进行一些处理。具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样经过softmax后,这些位置的权重就会接近0。Transformer的padding mask实际上是一个张量,每个值都是一个Boolean,值为false的地方就是要进行处理的地方。

2、Encoder过程

Self-Attention的作用

解决长距离依赖问题

  • 长距离依赖:对于序列问题,第 t 时刻的输出 把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_07 依赖于 t 之前的输入,也就是说依赖于 把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_08 ,当间隔 k 逐渐增大时,把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_学习_09 的信息将难以被 把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_07
  • 对于当前query,你需要与句子中 所有 key 进行点乘后再 Softmax ,以获得句子中所有 key对于当前query的 score(可以理解为贡献度),然后与所有词的value向量进行加权融合之后,就能使当前 把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_07
  • 因为有Position Encoding(PE)和Q,K,V。所以Self-Attention可以实现长距离依赖,关注上下文信息。Q,K,V可以计算其他单词与本单词之间的联系,PE可以记住单词的位置。

Self-Attention结构

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_12


上图是 Self-Attention 的结构,在计算的时候需要用到矩阵 Q(查询), K(键值), V(值)。在实际中,Self-Attention 接收的是单词的表示向量X(第一个Encoder) 或者上一个 Encoder block 的输出的矩阵Z。而 Q, K, V 正是通过 Self-Attention 的输入X进行线性变换得到的。计算如下图:

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_学习_13

Attention与Self-Attention的区别

  • 具体计算过程是一样的,都是先计算Q与K的点积,获取关系系数,然后再与V相乘。
  • 计算对象不同,attention是source对target的attention,而self attention 是source 对source的attention。Attention机制发生在Target和Source中的所有元素之间。
  • attention告诉的是每个部分的重要程度,self-attention告诉的是各个部分的关联关系!
  • 举例:比如entity1,entity2,entity3….,attn会输出[0.1,0.2,0.5,….]这种,Attention告诉你entity3重要些。self attention会给你一个矩阵,告诉你 entity1 和entity2、entity3 ….的关联程度、entity2和entity1、entity3…的关联程度。
  • Attention是两个系统的,Self-Attention是一个系统内部的。

Q、K、V是干嘛的?

Q即Query,K即Key,V即Value。

  • query和key用于计算对应value 的权值系数。查询向量query点乘key得到score: 计算其他词对这个词的重要性,也就是权值;
  • 权值score和各个上下文中的V向量的加权求和
  • 把上下文各个字的V融入目标字的原始 V 中
  • 文章中的Q,K,V,这三个向量都可以表示"我"这个词,但每个向量的作用并不一样,Q 代表 query,当计算"我"这个词时,它就能代表"我"去和其他词的 K 进行点乘计算其他词对这个词的重要性,当计算完点乘后,我们只是得到了每个词对“我”这个词的权重,需要再乘以一个其他词的向量,也就是V(value),才完成"我"这个词的计算,同时也是完成了用其他词来表征"我"的一个过程。

Q、K、V的计算

Self-Attention 的输入用矩阵 X进行表示,则可以使用线性变阵矩阵 WQ, WK, WV 计算得到 Q, K, V。计算如下图所示,注意 X, Q, K, V 的每一行都表示一个单词

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_14

Self-Attention的输出

得到矩阵 Q, K, V之后就可以计算出 Self-Attention 的输出了,计算的公式如下。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_15


公式中计算矩阵 Q和 K 每一行向量的内积,为了防止内积过大,因此除以 dk 的平方根。还起到调节作用,使得内积不至于太大。实际上是Q,K,V的最后一个维度,当 把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_深度学习_16 越大,Q与K的转置 就越大,可能会将 Softmax 函数推入梯度极小的区域;

Q 乘以 K 的转置后,得到的矩阵行列数都为 n,n 为句子单词数,这个矩阵可以表示单词之间的 attention 强度。下图为 Q 乘以 K 的转置,1234 表示的是句子中的单词。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_17


得到 QKT 之后,使用 Softmax 计算每一个单词对于其他单词的 attention 系数(这里可以根据矩阵乘法理解,对应行乘对应列,即每个单词相乘)。 公式中的 Softmax 是对矩阵的每一行进行 Softmax,即每一行的和都变为 1。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_18


得到 Softmax 矩阵之后可以和 V相乘,得到最终的输出 Z。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_19


上图中 Softmax 矩阵的第 1 行表示单词 1 与其他所有单词的 attention 系数,最终单词 1 的输出 Z1 等于所有单词 i 的值 Vi 根据 attention 系数的比例加在一起得到,如下图所示:

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_深度学习_20


注意上图中不再是矩阵的乘法,这一步是经过softmax归一化之后,将V向量乘上softmax的结果,这个思想主要是为了保持我们想要关注的单词的值不变,而掩盖掉那些不相关的单词(例如将他们乘上很小的数字)

Multi-Head Attention

在上一步,我们已经知道怎么通过 Self-Attention 计算得到输出矩阵 Z,而 Multi-Head Attention 是由多个 Self-Attention 组合形成的,下图是论文中 Multi-Head Attention 的结构图。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Soft_21


从上图可以看到 Multi-Head Attention 包含多个 Self-Attention 层,首先将输入 X分别传递到 h 个不同的 Self-Attention 中,计算得到 h 个输出矩阵 Z。下图是 h=8 时候的情况,此时会得到 8 个输出矩阵 Z。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_22


得到 8 个输出矩阵 Z1 到 Z8 之后,Multi-Head Attention 将它们拼接在一起 (Concat),然后传入一个 Linear层,得到 Multi-Head Attention 最终的输出 Z。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_23


可以看到 Multi-Head Attention 输出的矩阵 Z与其输入的矩阵 X 的维度是一样的。

组成 Encoder

通过上面描述的 Multi-Head Attention, Feed Forward, Add & Norm 就可以构造出一个 Encoder block,Encoder block 接收输入矩阵 X(n×d),并输出一个矩阵 C(n×d),其中n是单词个数,d是向量维度。通过多个 Encoder block 叠加就可以组成 Encoders。

第一个 Encoder block 的输入为句子单词的表示向量矩阵,后续 Encoder block 的输入是前一个 Encoder block 的输出,最后一个 Encoder block 输出的矩阵就是 编码信息矩阵 C,这一矩阵后续会用到 Decoder 中。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_24


encoder输入输出

从输入开始,再从头理一遍单个encoder这个过程:

  1. 输入x
  2. x 做一个层归一化: x1 = norm(x)
  3. 进入多头self-attention: x2 = self_attention(x1)
  4. 残差加成:x3 = x + x2
  5. 再做个层归一化:x4 = norm(x3)
  6. 经过前馈网络: x5 = feed_forward(x4)
  7. 残差加成: x6 = x3 + x5
  8. 输出x6

3、Decoder过程

Sequence Mask

具体的例子如下:

样本:“我/爱/机器/学习”和 “i/ love /machine/ learning”
训练:

  1. 把“我/爱/机器/学习”embedding后输入到encoder里去,最后一层的encoder最终输出的outputs [10, 512](假设我们采用的embedding长度为512,而且batch size = 1),此outputs 乘以新的参数矩阵,可以作为decoder里每一层用到的K和V;
  2. 将作为decoder的初始输入,将decoder的最大概率输出词 A1和‘i’做cross entropy计算error。
  3. 将,“i” 作为decoder的输入,将decoder的最大概率输出词 A2 和‘love’做cross entropy计算error。
  4. 将,“i”,“love” 作为decoder的输入,将decoder的最大概率输出词A3和’machine’ 做cross entropy计算error。
  5. 将,“i”,"love ",“machine” 作为decoder的输入,将decoder最大概率输出词A4和‘learning’做cross entropy计算error。
  6. 将,“i”,"love ",“machine”,“learning” 作为decoder的输入,将decoder最大概率输出词A5和终止符做cross entropy计算error。

上述训练过程是挨个单词串行进行的,那么能不能并行进行呢, 当然可以。可以看到上述单个句子训练时候,输入到 decoder的分别是

<bos>

<bos>,“i”

<bos>,“i”,“love”

<bos>,“i”,"love ",“machine”

<bos>,“i”,"love ",“machine”,“learning”

那么为何不将这些输入组成矩阵,进行输入呢?这些输入组成矩阵形式如下:

【<bos>

<bos>,“i”

<bos>,“i”,“love”

<bos>,“i”,"love ",“machine”

<bos>,“i”,"love ",“machine”,“learning” 】

怎么操作得到这个矩阵呢?

将decoder在上述2-6步次的输入补全为一个完整的句子

【<bos>,“i”,"love ",“machine”,“learning”
<bos>,“i”,"love ",“machine”,“learning”
<bos>,“i”,"love ",“machine”,“learning”
<bos>,“i”,"love ",“machine”,“learning”
<bos>,“i”,"love ",“machine”,“learning”】

然后将上述矩阵矩阵乘以一个 mask矩阵

【1 0 0 0 0

1 1 0 0 0

1 1 1 0 0

1 1 1 1 0

1 1 1 1 1 】

这样是不是就得到了

【<bos>

<bos>,“i”

<bos>,“i”,“love”

<bos>,“i”,"love ",“machine”

<bos>,“i”,"love ",“machine”,“learning” 】

这样将这个矩阵输入到decoder(其实你可以想一下,此时这个矩阵是不是类似于批处理,矩阵的每行是一个样本,只是每行的样本长度不一样,每行输入后最终得到一个输出概率分布,作为矩阵输入的话一下可以得到5个输出概率分布)。
这样就可以进行并行计算进行训练了。

第一个 Multi-Head Attention

Decoder block 的第一个 Multi-Head Attention 采用了 Sequence Masked 操作,因为在翻译的过程中是顺序翻译的,即翻译完第 i 个单词,才可以翻译第 i+1 个单词。通过 Masked 操作可以防止第 i 个单词知道 i+1 个单词之后的信息。 下面以 “我有一只猫” 翻译成 “I have a cat” 为例,了解一下 Masked 操作。

在 Decoder 的时候,是需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 “” 预测出第一个单词为 “I”,然后根据输入 “ I” 预测下一个单词 “have”。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_深度学习_25


Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 ( I have a cat) 和对应输出 (I have a cat ) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面用 0 1 2 3 4 5 分别表示 “ I have a cat ”。

第一步: 是 Decoder 的输入矩阵和 Mask 矩阵,输入矩阵包含 “ I have a cat” (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。在 Mask 可以发现单词 0 只能使用单词 0 的信息,而单词 1 可以使用单词 0, 1 的信息,即只能使用之前的信息。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_26


第二步: 接下来的操作和之前的 Self-Attention 一样,通过输入矩阵 X计算得到 Q, K, V 矩阵。然后计算 Q 和 KT 的乘积 QKT。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_Self_27


第三步: 在得到 QKT 之后需要进行 Softmax,计算 attention score,我们在 Softmax 之前 需要使用 Mask矩阵遮挡住每一个单词之后的信息,遮挡操作如下:

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_transformer_28


得到 Mask QKT 之后在 Mask QKT 上进行 Softmax,每一行的和都为 1。第四步: 使用 Mask QKT 与矩阵 V相乘,得到输出 Z,则单词 1 的输出向量 Z1 是只包含单词 1 信息的。被mask掉的部分不包含信息。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_深度学习_29


第五步: 通过上述步骤就可以得到一个 Mask Self-Attention 的输出矩阵 Zi,然后和 Encoder 类似,通过 Multi-Head Attention 拼接多个输出 Zi 然后计算得到第一个 Multi-Head Attention 的输出 Z,Z与输入 X 维度一样。

第二个 Multi-Head Attention

Decoder block 第二个 Multi-Head Attention 变化不大, 主要的区别在于其中 Self-Attention 的 K, V矩阵不是使用 上一个 Decoder block 的输出计算的,而是使用 Encoder 的编码信息矩阵 C 计算的。

根据 Encoder 的输出 C计算得到 K, V,根据上一个 Decoder block 的输出 Z 计算 Q (如果是第一个 Decoder block 则使用输入矩阵 X 进行计算),后续的计算方法与之前描述的一致。

这样做的好处是在 Decoder 的时候,每一位单词都可以利用到 Encoder 所有单词的信息 (这些信息无需 Mask)。

Softmax 预测输出单词

Decoder block 最后的部分是利用 Softmax 预测下一个单词,在之前的网络层我们可以得到一个最终的输出 Z,因为 Mask 的存在,使得单词 0 的输出 Z0 只包含单词 0 的信息,如下。

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_学习_30


Softmax 根据输出矩阵的每一行预测下一个单词

把transformer移到pytorchlightning后损失降不下来 transformer模型pytorch_深度学习_31


这就是 Decoder block 的定义,与 Encoder 一样,Decoder 是由多个 Decoder block 组合而成。

4、优势

  1. self-attention + position embedding(PE)解决长依赖问题,PE提供单词的位置信息,self-attention提供单词之间的依赖关系信息。
  2. 可以并行运行。
  • encoder的并行:单词之间不存串行的依赖关系,Multi-Head Attention通过拼接可以同时计算多个单词。
  • decoder的并行:单词之间存在串行的依赖关系,利用sequence masked矩阵mask后面的单词实现并行计算。
  1. 可以获取全局信息\上下文信息。原因跟解决长依赖问题一样,基于self-attention + position embedding。