赞
踩
Transformer没有你想的那么难,我们开篇。
左图为:Bi-RNN 右图:self-attention layer
到这里我们需要花点时间,去知道self-attention到底在做什么?
请参考:
OK,到这里,我们大致可以有一个总结:
oh,原来transformer里面是由self-attention组成的,self-attention有很多好处,所以我们可以用它替代原本RNN的工作,可就仅仅如此吗?
本文不想过度吹嘘 self-attention layer(前文已经吹过了…),这里,我希望你看到两个单词 seq2seq model,既然是一个model.那它肯定不只是self-attention。而且这里也需要重点注意,transformer的厉害不仅仅是self-attention的结果,而是整个model里所有组建的共同作用,所有的他们都很重要。
所以,从此刻开始,我们把重点集中在 seq2seq model上。
整个分析流程参考:[ 2 ]
seq2seq结构
从编码器输入的句子首先会经过一个自注意力(self-attention)层,这层帮助编码器在对每个单词编码时关注输入句子的其他单词。自注意力层的输出会传递到前馈(feed-forward)神经网络中。每个位置的单词对应的前馈神经网络都完全一样(译注:另一种解读就是一层窗口为一个单词的一维卷积神经网络)。当然,我们这里只是宏观介绍,毕竟我们还可以继续往下拆解。
说了编码器,对比着的说一下解码器:
解码器中也有编码器的自注意力(self-attention)层和前馈(feed-forward)层。除此之外,这两个层之间还有一个注意力层,用来关注输入句子的相关部分(和seq2seq模型的注意力作用相似)。
输入序列的每个单词都经过自编码过程。然后,他们各自通过前向传播神经网络——完全相同的网络,而每个向量都分别通过它。
所以,我们总结一下:Encode由n个编码单元组成,每个编码器的核心是n个特征提取器,而我们比较过CNN,RNN,self-attention做特征提取的优缺点,self-attention是那个最优秀的孩子,所以我们transformer里的特征提取器就选用了self-attention。
很明显,想要继续解剖,我们需要重点关注这个self-attention长什么样子?
我单独把它拿出去做了以篇文章,不清楚的可以参考:self-attention机制
但是上文,我们只是单纯理解了self-attention机制,我们还需要一点点扩充知识!
通过增加一种叫做“多头”注意力(“multi-headed” attention)的机制,论文进一步完善了自注意力层,并在两方面提高了注意力层的性能:
- 它扩展了模型专注于不同位置的能力。在上面的例子中,虽然每个编码都在z1中有或多或少的体现,但是它可能被实际的单词本身所支配。如果我们翻译一个句子,比如“The animal didn’t cross the street because it was too tired”,我们会想知道“it”指的是哪个词,这时模型的“多头”注意机制会起到作用。
- 它给出了注意力层的多个“表示子空间”(representation subspaces)。接下来我们将看到,对于“多头”注意机制,我们有多个查询/键/值权重矩阵集(Transformer使用八个注意力头,因此我们对于每个编码器/解码器有八个矩阵集合)。这些集合中的每一个都是随机初始化的,在训练之后,每个集合都被用来将输入词嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。
- 其实个人理解就是 :每一个head都是独立的,意味着每一个都会有自己的见解,那多个head提取出来的信息,一定比一个多,所以效果也会相对好一点,毕竟,encode的目的是特征提取,提取信息越多肯定相对越好。
多头引入的效果是什么?引入后我该怎么计算呢?这里我们不给出详细计算,
但给你思路,很简单:煎饼果子,各来一套
我原来只有一套 Q,K,V用来表示一个head。对于多个head,我多弄几个不就行了!所以参考上图,我弄两套Q,K,V,就是两个head。
比如我们有8个头,我们的输入经过八个头的self-attention后,会得到8个维度相同的输出:
不太形象的可视化
最后,放上一个比较乱但包含所有的可视化
位置编码改变的只是输入,见下图
第一个疑惑
第二个疑惑
我们继续回到主线上去,对于一个解码器,其实它不像上面那么简单,单纯就一个self-attention和一个前馈网络,它还需要再添加一些组件:
可以看到,它多了两个东西:求和与归一化和残差模块。为了很好的理解,我们同样让数据流过他们:
我觉得图已经很明白了,我们来简单形容一下:
我们将self-attention的input:z和它的output相加,再做一次normalization,注意,这里不是softmax,而是layer normalization。
我们简单和batch normalization区别一下:
其实他们的本质对待的维度不一样,batch normalization看名字也知道,是在batch之内做的,也就是不同样本的同一特征做normalization。而layer normalization是相反的,它针对每一个样本的所有特征做normalization。
ok,补充完毕,我们继续。
看到这里我真的很想问一句,为啥要加上这两个东西?参考[ 7 ]
Add操作借鉴了ResNet模型的结构,其主要作用是使得transformer的多层叠加而效果不退化
Layer Normalization操作对向量进行标准化,可以简化学习难度。
其实解码器基本和编码器的结构一模一样。如果我们只有两层的编码器:
我们唯一需要注意的就是数据流动的方式?
我个人觉得这张图说起来不舒服,我换一张经典的:
我们对decoder举一个简单宏观的例子:
首先要明确:解码阶段的每个步骤都会输出一个输出序列(在这个例子里,是英语翻译的句子)的元素
接下来的步骤重复了这个过程,直到到达一个特殊的终止符号,它表示transformer的解码器已经完成了它的输出。
每个步骤的输出在下一个时间步被提供给底端解码器,并且就像编码器之前做的那样,这些解码器会输出它们的解码结果 。另外,就像我们对编码器的输入所做的那样,我们会嵌入并添加位置编码给那些解码器,来表示每个单词的位置。
而那些解码器中的自注意力层表现的模式与编码器不同:在解码器中,自注意力层只被允许处理输出序列中更靠前的那些位置,也就是只看的到已经产生的单词,没有产生的单词要mask掉。在softmax步骤前,它会把后面的位置给隐去(把它们设为-inf)。
个人对mask操作和这里的sotfmax的操作还比较懵懂,个人暂时还得不出很好的结论,暂寄希望与源码可以弄清楚具体细节。
解码组件最后会输出一个实数向量。我们如何把浮点数变成一个单词?这便是线性变换层要做的工作,它之后就是Softmax层。
线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。这个操作我们前面用过,无非就是乘以一个权重而已,前面是压缩,这里是扩充,扩充成多少(当然是你的vocab_size大小啊)。
上面会得到每一个vocab的评分,我们一般习惯把它转化为概率,即,用Softmax 层便会把那些分数变成概率(都为正数、上限1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出。
整个过程如下图
单纯的理论学完其实都是浮在表面,落到实地的源码才是实质名归 !
[ 1 ] The Annotated Transformer
[ 2 ] BERT大火却不懂Transformer?读这一篇就够了
[ 3 ] Attention is all you need
[ 4 ]Transformer 李宏毅
[ 5 ]Transformer (变形金刚,大雾) 三部曲:RNN 的继承者
[ 6 ]Self-Attention with Relative Position Representations
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。