transformer模型原理

目录

一、Transformer的输入

1)词向量

 2)位置编码向量

二、Encoder block(编码块)

1)Multi-Head Attention

a.self-Attention机制

b.Add&Normalize:

c.Feed-Forward Networks

三、Decoder block(解码块)

1) 第一个 Multi-Head Attention

2) 第二个 Multi-Head Attention

四、Tramsformer的输出


上图为transformer的整体结构图。我们可以观察到,他本质是一个seq2seq的架构,也就是encoder-decoder模型。

我们将transformer分为四个部分来学习,分别是transformer的输入、Encoder block(编码块)、Decoder block(解码块)、4.transformer的输出。

一、Transformer的输入

(句子中每一个词的的词向量和位置编码向量的和)

1)词向量

Transformer输入是一个序列数据,以"Tom chase Jerry" 翻译成中文"汤姆追逐杰瑞"为例:
Encoder inputs的一部分就是"Tom chase Jerry" 分词后的词向量。可以是任意形式的词向量,如word2vec,GloVe,one-hot编码。

 2)位置编码向量

为什么要用位置编码向量?首先咱们知道,一句话中同一个词,如果词语出现位置不同,意思可能发生翻天覆地的变化,就比如:我欠他100W 和 他欠我100W。这两句话的意思一个地狱一个天堂。可见获取词语出现在句子中的位置信息是一件很重要的事情。但是咱们的Transformer 的是完全基于self-Attention,而self-attention是不能获取词语位置信息的,就算打乱一句话中词语的位置,每个词还是能与其他词之间计算attention值,就相当于是一个功能强大的词袋模型,对结果没有任何影响。所以在我们输入的时候需要给每一个词向量添加位置编码。


学过RNN的小伙伴都知道,Rnn的输入是不包含这个位置编码向量的。是因为Rnn的输入包含了上一个时刻的隐状态,而注意力层是不含有这种信息的。

怎么生成位置编码向量呢?

《Attention Is All You Need》论文中Transformer使用的是正余弦位置编码。
 

 解释一下上面的公式:
pos表示单词在句子中的绝对位置,pos=0,1,2…,例如:Jerry在"Tom chase Jerry"中的pos=2;dmodel表示词向量的维度,在这里dmodel=512;2i和2i+1表示奇偶性,i表示词向量中的第几维,例如这里dmodel=512,故i=0,1,2…255。

这种结构可以让模型容易地计算出相对位置,对于固定长度的间距 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)。

将单词的词 Embedding 和位置 Embedding 相加,就可以得到单词的表示向量 x就是 Transformer 的输入。

Transformer 的 Decoder的输入与Encoder的输出处理方法步骤是一样地,一个接受source数据,一个接受target数据,

在训练时:对应到上面例子里面就是:Encoder接受英文"Tom chase Jerry",Decoder接受中文"汤姆追逐杰瑞"。

而在预测时:Encoder端的有效输入只有<sos>符号,表示开始预测。此处看不明白的可以先跳过,后面代码会有详细解释。

padding

因为每个样本的原始句子的长度是不一样的,那么怎么能统一输入到encoder呢。此时padding操作登场了,假设样本中句子的最大长度是10,那么对于长度不足10的句子,需要补足到10个长度,shape就变为[10, 512], 补全的位置上的embedding数值自然就是0了

Padding Mask

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

二、Encoder block(编码块)

看上图第2部分 Encoder block。Encoder block是由N个encoder堆叠而成。上图2中的灰框部分就是一个encoder的内部结构,从图中我们可以看出一个encoder由Multi-Head Attention 和 全连接神经网络Feed Forward Network构成,中间还有两个Add&Norm模块,我们一个一个讲。

我们看看encoder都做了什么:                         

                         

和原论文图中稍有不同, 我们是将最后一个Norm放在第一个位置上,经实验验证,这样网络的效果更好。接下来分几个小部分具体来讲:

1)Multi-Head Attention

a.self-Attention机制

                             

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

Q, K, V 的计算

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

           

                                         

 公式中计算矩阵QK每一行向量的内积,为了防止内积过大,因此除以 d_{k} 的平方根。Q乘以K

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

                   

 得到QK^{T} 之后,使用 Softmax 计算每一个单词对于其他单词的 attention 系数,公式中的 Softmax 是对矩阵的每一行进行 Softmax,即每一行的和都变为 1.

                   

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

                   

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

                

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

              

Multi-Head Attention 很简单,就是在self-attention的基础上,对于输入的embedding矩阵,self-attention只使用了一组W^{Q},W^{V}W^{K},来进行变换得Query,Keys,Values。而Multi-Head Attention使用多组 W^{Q},W^{V}W^{K},得到多组Query,Keys,Values,然后每组分别计算得到一个Z矩阵,最后将得到的多个Z矩阵进行拼接。

           

  从上图中可以看到,在经过Multi-Head Attention得到矩阵Z之后,并没有直接传入全连接神经网络FNN,而是经过了一步:Add&Normalize。

b.Add&Normalize:

Add

Add,就是在Z的基础上加了一个残差块X,加入残差块X的目的是为了防止在深度神经网络训练中发生退化问题,退化的意思就是深度神经网络通过增加网络的层数,Loss逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss反而增大。为什么深度神经网络会发生退化,为什么添加残差块能够防止退化问题,残差块又是什么?这就牵扯到ResNet残差神经网络的知识了

ResNet 残差神经网络:

首先解答第一个问题,为什么深度神经网络会发生退化?
举个例子:假如某个神经网络的最优网络层数是18层,但是我们在设计的时候并不知道到底多少层是最优解,本着层数越深越好的理念,我们设计了32层,那么32层神经网络中有14层其实是多余地,我们要想达到18层神经网络的最优效果,必须保证这多出来的14层网络必须进行恒等映射,恒等映射的意思就是说,输入什么,输出就是什么,可以理解成F(x)=x这样的函数,因为只有进行了这样的恒等映射咱们才能保证这多出来的14层神经网络不会影响我们最优的效果。
但现实是神经网络的参数都是训练出来地,要想保证训练出来地参数能够很精确的完成F(x)=x的恒等映射其实是很困难地。多余的层数较少还好,对效果不会有很大影响,但多余的层数一多,可能结果就不是很理想了。这个时候大神们就提出了ResNet 残差神经网络来解决神经网络退化的问题。

残差块是什么?

上图就是构造的一个残差块,可以看到X是这一层残差块的输入,也称作F(X)为残差,X为输入值,F(X)是经过第一层线性变化并激活后的输出,该图表示在残差网络中,第二层进行线性变化之后激活之前,F(X)加入了这一层输入值X,然后再进行激活后输出。在第二层输出值激活前加入X,这条路径称作shortcut连接。

为什么添加了残差块能防止神经网络退化问题呢?
咱们再来看看添加了残差块后,咱们之前说的要完成恒等映射的函数变成什么样子了。是不是就变成h(X)=F(X)+X,我们要让h(X)=X,那么是不是相当于只需要让F(X)=0就可以了,这里就巧妙了!神经网络通过训练变成0是比变成X容易很多地,因为大家都知道咱们一般初始化神经网络的参数的时候就是设置的[0,1]之间的随机数嘛。所以经过网络变换后很容易接近于0。举个例子:

假设该网络只经过线性变换,没有bias也没有激活函数。我们发现因为随机初始化权重一般偏向于0,那么经过该网络的输出值为[0.6 0.6],很明显会更接近与[0 0],而不是[2 1],相比与学习h(x)=x,模型要更快到学习F(x)=0。
并且ReLU能够将负数激活为0,过滤了负数的线性变化,也能够更快的使得F(x)=0。这样当网络自己决定哪些网络层为冗余层时,使用ResNet的网络很大程度上解决了学习恒等映射的问题,用学习残差F(x)=0更新该冗余层的参数来代替学习h(x)=x更新冗余层的参数。
这样当网络自行决定了哪些层为冗余层后,通过学习残差F(x)=0来让该层网络恒等映射上一层的输入,使得有了这些冗余层的网络效果与没有这些冗余层的网络效果相同,这样很大程度上解决了网络的退化问题。

到这里,关于Add中为什么需要加上一个X,要进行残差网络中的shortcut你清楚了吗?Transformer中加上的X也就是Multi-Head Attention的输入,X矩阵。

Normalize

为什么要进行Normalize呢?
在神经网络进行训练之前,都需要对于输入数据进行Normalize归一化,目的有二:1,能够加快训练的速度。2.提高训练的稳定性。

为什么使用Layer Normalization(LN)而不使用Batch Normalization(BN)呢?     

 

 先看图,LN是在同一个样本中不同神经元之间进行归一化,而BN是在同一个batch中不同样本之间的同一位置的神经元之间进行归一化。
BN是对于相同的维度进行归一化,但是咱们NLP中输入的都是词向量,一个300维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是不适合地,因此这里选用的是LN。

 

c.Feed-Forward Networks

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

                                     

这里的全连接层是一个两层的神经网络,先线性变换,然后ReLU非线性,再线性变换。这里的x就是我们Multi-Head Attention的输出Z,假设Z是(2,64)维的矩阵,假设W1是(64,1024),其中W2与W1维度相反(1024,64),那么按照上面的公式:
FFN(Z)=(2,64)x(64,1024)x(1024,64)=(2,64),我们发现维度没有发生变化,这两层网络就是为了将输入的Z映射到更加高维的空间中(2,64)x(64,1024)=(2,1024),然后通过非线性函数ReLU进行筛选,筛选完后再变回原来的维度。

 注意encoder的输出并没直接作为decoder的直接输入。

三、Decoder block(解码块)

  • 包含两个 Multi-Head Attention 层。
  • 第一个 Multi-Head Attention 层采用了 Masked 操作。
  • 第二个 Multi-Head Attention 层的K, V矩阵使用 Encoder 的编码信息矩阵进行计算,而Q使用上一个 Decoder block 的输出计算。
  • 最后有一个 Softmax 层计算下一个翻译单词的概率。

1) 第一个 Multi-Head Attention

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

下面的描述中使用了类似 Teacher Forcing 的概念。在 Decoder 的时候,是需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 "<Begin>" 预测出第一个单词为 "I",然后根据输入 "<Begin> I" 预测下一个单词 "have"。

Decoder 预测

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

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

输入矩阵与 Mask 矩阵

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

Q乘以K的转置

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

Softmax 之前 Mask

得到 Mask  之后在 Mask 上进行 Softmax,每一行的和都为 1。但是单词 0 在单词 1, 2, 3, 4 上的 attention score 都为 0。

第四步:使用 Mask 与矩阵 V相乘,得到输出 Z,则单词 1 的输出向量  是只包含单词 1 信息的。

Mask 之后的输出

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

2) 第二个 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)。

四、Tramsformer的输出

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

Decoder Softmax 之前的 Z

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

Decoder Softmax 预测

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

训练的时候,1.初始decoder的time step为1时(也就是第一次接收输入),其输入为一个特殊的token,可能是目标序列开始的token(如<BOS>),也可能是源序列结尾的token(如<EOS>),也可能是其它视任务而定的输入等等,不同源码中可能有微小的差异,其目标则是预测翻译后的第1个单词(token)是什么;2.然后<BOS>和预测出来的第1个单词一起,再次作为decoder的输入,得到第2个预测单词;3后续依此类推;

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

训练:
1. 把“我/爱/机器/学习”embedding后输入到encoder里去,最后一层的encoder最终输出的outputs [10, 512](假设我们采用的embedding长度为512,而且batch size = 10),此outputs 乘以新的参数矩阵,可以作为decoder里每一层用到的K和V;

2. 将<sos>作为decoder的初始输入,将decoder的最大概率输出词 A1和‘i’做cross entropy计算error。

3. 将<sos>,"i" 作为decoder的输入,将decoder的最大概率输出词 A2 和‘love’做cross entropy计算error。

4. 将<sos>,"i","love" 作为decoder的输入,将decoder的最大概率输出词A3和'machine' 做cross entropy计算error。

5. 将<sos>,"i","love ","machine" 作为decoder的输入,将decoder最大概率输出词A4和‘learning’做cross entropy计算error。

6. 将<sos>,"i","love ","machine","learning" 作为decoder的输入,将decoder最大概率输出词A5和终止符</s>做cross entropy计算error。

Sequence Mask

上述训练过程是挨个单词串行进行的,那么能不能并行进行呢,当然可以。可以看到上述单个句子训练时候,输入到 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" 】

这样的矩阵了 。这就是我们需要输入矩阵。这个mask矩阵就是 sequence mask,其实它和encoder中的padding mask 异曲同工。

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

这样我们就可以进行并行计算进行训练了。

测试

训练好模型, 测试的时候,比如用 '机器学习很有趣'当作测试样本,得到其英语翻译。

这一句经过encoder后得到输出tensor,送入到decoder(并不是当作decoder的直接输入):

1.然后用起始符<bos>当作decoder的 输入,得到输出 machine

2. 用<bos> + machine 当作输入得到输出 learning

3.用 <bos> + machine + learning 当作输入得到is

4.用<bos> + machine + learning + is 当作输入得到interesting

5.用<bos> + machine + learning + is + interesting 当作输入得到 结束符号<eos>

我们就得到了完整的翻译 'machine learning is interesting'

可以看到,在测试过程中,只能一个单词一个单词的进行输出,是串行进行的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值