赞
踩
举例:中文输入为“我爱你”,通过 Transformer 翻译为 “I Love You”。
我们不直接给 Transformer 输入简单的one-hot vector,原因包括这种表达方式的结果非常稀疏,非常大,且不能表达 word 与 word 之间的特征。所以这里对词进行 embedding,用较短的向量表达这个 word 的属性。一般在 Pytorch 中,我们都是用 nn.Embedding 来做,或者直接用 one-hot vector 与权重矩阵 W 相乘得到。
nn.Embedding 包含一个权重矩阵 W,对应的 shape 为 ( num_embeddings,embedding_dim )。num_embeddings 指的是词汇量,即想要翻译的 vocabulary 的长度。embedding_dim 指的是想用多长的 vector 来表达一个词,可以任意选择,比如64,128,256,512等。在 Transformer 论文中选择的是512(即 d_model =512)。
其实可以形象地将 nn.Embedding 理解成一个 lookup table,里面对每一个 word 都存了向量 vector 。给任意一个 word,都可以从表中查出对应的结果。
处理 nn.Embedding 权重矩阵有两种选择:
d_model = 512,具体实现时,embedding的输出除了经过Pytorch本身的nn.Embedding层,同时还与根号d_model 相乘,最后输出embedding。
我们对每一个 word 进行 embedding 作为 input 表达。但是还有问题,embedding 本身不包含在句子中的相对位置信息。
那 RNN 为什么在任何地方都可以对同一个 word 使用同样的向量呢?因为 RNN 是按顺序对句子进行处理的,一次一个 word。但是在 Transformer 中,输入句子的所有 word 是同时处理的,没有考虑词的排序和位置信息。
对此,Transformer 的作者提出了加入 ”positional encoding“ 的方法来解决这个问题。”positional encoding“ 使得 Transformer 可以衡量 word 位置有关的信息。
positional encoding 与 word embedding 相加就得到 embedding with position。
那么具体 ”positional encoding“ 怎么做?为什么能表达位置信息呢?作者探索了两种创建 positional encoding 的方法:
试验后发现两种选择的结果是相似的,所以采用了第2种方法,优点是不需要训练参数,而且即使在训练集中没有出现过的 句子长度 上 也能用。
计算 positional encoding 的公式为:
那么如何理解Transformer论文中的positional encoding,和三角函数有什么关系?
再回头来看这个公式:
先创建全为0的
p
e
pe
pe,然后表示出公式里的
p
o
s
pos
pos(position)和公式中的分母(div_term),再使用
s
i
n
/
c
o
s
sin/cos
sin/cos即可,最后将原来的x和positional encoding相加经过dropout输出。
我们带着问题来进行分析
Transformer模型中,decoder的第一个输入是什么?
这里由于还没有讲解后面的Encoder和Decoder部分,所以新手可能不太懂,但对于已经对Transformer有一定了解的同学来说,这个部分的理解是非常关键的。新手可以先跳过此部分,最后再回头来看这个部分。
对于Outputs(shifted right)部分,也存在word embedding和positional encoding相加的过程,刚刚上述对此的分析已经很详细了,一定要知道预测/训练过程中,Outputs(shifted right)是如何参与进来的,文章的最后会给出更多预测/训练过程的细节解释。
Encoder 相对 Decoder 会稍微麻烦一些。 Encoder 由 6 个相乘的 Layer 堆叠而成(6并不是固定的,可以基于实际情况修改),看起来像这样:
每个Encoder Layer 包含 2 个 sub-layer:
第一个是 multi-head self-attention mechanism(多头自注意力)
第二个是 simple,position-wise fully connected feed-forward network
理解 Multi-Head Attention 机制对于理解 Transformer 特别重要,并且在 Encoder 和 Decoder 中都有用到。
概述:
我们把该机制的输入定义为 x,x 在 Encoder 的不同位置,含义有所不同。在最开始的输入处(图中的in),x 的含义是final representation。在 EncoderLayer 的各层中间,x 代表前一层 EncoderLayer 的输出。
详细解释: Annotated Transformer 中 Multi-Headed attention 的实现为
下面分3步详细介绍一下 MultiHeadedAttention 的 forward() 函数:
forward 的 input 包括:query,key,values和mask。这里先暂时忽略 mask。 query,key和value 是哪来的? 实际上他们是 “x” 重复了三次得来的。x 是初始的final representation或前一个 EncoderLayer 的输出,见 EncoderLayer 的代码红色框部分,self.self_atttn 是 MultiHeadedAttention 的一个实例化:
这里对MultiHeadedAttention实例化后的self_attn()输入三个同样x。
这里的x的shape为[nbatches, L, 512],那么query、key、value同样也是这样的shape。nbatches 对应 batch size,L 对应 sequence length ,512 对应 d_model。
Step 1)(对着MultiHeadedAttention实现代码来看步骤)
Step 2)
attention函数具体为:
Step 3)
看完了Attention源码实现,我们回到MultiHeadedAttention实现的源码中:
此时x的shape为 [ nbatches, 8, L, 64 ],x.transpose(1,2) 得到 [ nbatches, L, 8, 64 ]。然后使用 view 进行 reshape 得到 [ nbatches, L, 512 ]。可以理解为8个heads结果的 concatenate 。 最后使用 linear layer 进行转换。shape仍为 [ nbatches, L, 512 ]。也就是说,Encoder Sub-layer 1: Multi-Head Attention Mechanism的输入和输出的维度是一致的,输入的shape为[ nbatches, L, 512 ],各种变换后的输出的shape也为[ nbatches, L, 512 ]。
左图是Attention的计算过程(名为Scaled Dot-Product Attention),右图为整体多头自注意力机制的运算过程,看看就行,知道有这个图即可,这个图只是方便新手理解,从源码的角度来看,才更清晰。
从公式可以看出,x经过relu和线性层输出为FFN(x)。
而具体实现中,x经过线性层w_1,再经过relu,再经过dropout,最后再经过线性层w_2,输入输出的维度均没有发生变化。
理论和具体代码实现还是有差别的。
Encoder 总共包含6个 EncoderLayers 。每一个 EncoderLayer 包含2个 SubLayer:
SubLayer-1 做 Multi-Headed Attention
SubLayer-2 做 feedforward neural network
Encoder 与 Decoder 的交互方式可以理解为:
Decoder 也是N层堆叠的结构。被分为3个 SubLayer,可以看出 Encoder 与 Decoder 三大主要的不同:
mask 的目标在于防止 decoder “seeing the future”,就像防止考生偷看考试答案一样。mask包含1和0:
self_attn(“masked” Multi-Headed Attention), src_attn(encoder-decoder multi-head attention) 均是MultiHeadedAttention的实例,只不过第一个有mask,第二个没有mask。
src_attn中的输入为x,m,m,其中x为上一个self_attn的输出,m来自Encoder的输出,即query是自己的(上一个self_attn的输出),key和value是Encoder输出给的。
最后的 linear layer 将 decoder 的输出扩展到与 vocabulary size 一样的维度上。经过 softmax 后,选择概率最高的一个 word 作为预测结果。
假设我们有一个已经训练好的网络,在做预测时,步骤如下:
但是在训练过程中,decoder 没那么好时,预测产生的词很可能不是我们想要的。这个时候如果再把错误的数据再输给 decoder,就会越跑越偏:
(注意上面是预测过程,这里是训练过程)
这里在训练过程中要使用到 “teacher forcing”。利用我们知道他实际应该预测的 word 是什么,在这个时候喂给他一个正确的结果作为输入。
相对于选择最高的词 (greedy search)(正常的预测方式),还有其他选择,比如 “beam search”,其可以保留多个预测的 word。 Beam Search 方法不再是只得到一个输出放到下一步去训练了,我们可以设定一个值,拿多个值放到下一步去训练,这条路径的概率等于每一步输出的概率的乘积:
或者也可以使用“Scheduled Sampling”:一开始我们只用真实的句子序列进行训练,而随着训练过程的进行,我们开始慢慢加入模型的输出作为训练的输入这一过程。
其实不止在Transformer中使用到了Teacher Forcing,早在RNN模型中已有这种东西了。
残差连接就是上一个输出的结果与该块的输入相加,如下图:
为什么要用残差连接呢?
首先大家已经形成了一个通识,在一定程度上,网络越深表达能力越强,性能越好。不过,好是好了,随着网络深度的增加,带来了许多问题,梯度消散,梯度爆炸;更好的优化方法,更好的初始化策略,BN层,Relu等各种激活函数,都被用过了,但是仍然不够,改善问题的能力有限,直到残差连接被广泛使用。
研究直接表明训练深度神经网络失败的原因并不是梯度消失,而是权重矩阵的退化,也就是每个层中只有少量的隐藏单元对不同的输入改变它们的激活值,而大部分隐藏单元对不同的输入都是相同的反应,此时整个权重矩阵的秩不高。并且随着网络层数的增加,连乘后使得整个秩变的更低。
这也是我们常说的网络退化问题,虽然是一个很高维的矩阵,但是大部分维度却没有信息,表达能力没有看起来那么强大。
注意一下:梯度消失/爆炸 和 网络退化是两个层面的东西,网络退化和迭代有关;而梯度消失是层数增多而变的愈发严重,和迭代没什么关系
总的来说一句话,残差连接打破了网络的对称性,提升了网络的表征能力。
会单独写一篇文章来对相关的batch normalization等进行总结,这里暂时不多说,知道其大概过程即可。
Mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask。
其中,padding mask 在所有的 scaled dot-product attention 里面都需要用到,而 sequence mask 只有在 decoder 的 self-attention 里面用到。
什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。因为这些填充的位置,其实是没什么意义的,所以我们的 attention 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。
具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax,这些位置的概率就会接近0!(可以看下图的源码,会发现mask是在softmax之前进行的,所以在这些位置上的值负无穷,经过softmax概率便接近为0)
而我们的 padding mask 实际上是一个张量(下面有个红色紫色Mask表示图),每个值都是一个 Boolean,值为 false(0) 的地方就是我们要进行处理的地方。
sequence mask 是为了使得 decoder 不能看见未来的信息。也就是对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。
那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为 1,下三角的值权威0,对角线也是 0。把这个矩阵作用在每一个序列上,就可以达到我们的目的啦。如下图所示:
x和y轴均为对应序列的index,矩阵下三角为0,是我们要进行mask的地方,预测序列的index为0时,什么都看不见;index为1时,可以看见0;以此类推进行处理。
Transformer KO~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。