Transformer详解

序言

主要的序列转导模型是基于复杂的循环或卷积神经网络,包括一个编码器和一个解码器。Transformer则是放弃了递归和卷积,完全基于注意力机制。它的发展过程:Encoder-Decoder->Attention->Self-Attention->Transformer。下面对其进行逐步详细讲解。

1.Encoder-Decoder

Encoder-Decoder 并不是一个具体的模型,而是一个通用的框架,编码器和解码器可以任意选择与组合。
Encoder 和 Decoder 部分可以是任意文字,语音,图像,视频数据。
模型可以是 CNN,RNN,LSTM,GRU,Attention 等等。
所谓编码,就是将输入序列转化转化成一个固定长度向量, 解码,就是讲之前生成的固定向量再转化出输出序列。
不管输入序列和输出序列长度是什么,中间的「向量 c」长度都是固定的。 这是Encoder-decoder框架的的缺点。
Encoder-Decoder 是一个 End-to-End 的学习算法,以机器翻译为力,可以将法语翻译成英语。这样的模型也可以叫做 Seq2Seq。

选自MIT Deep Learning
上方的Encoder对输入序列建模,然后在网络的出口处提取最后一个时间步的激活值输出,最后一个时间步的激活值输出因为走过了整个源文本序列,所以可以认为它蕴含了需要翻译的整个句子的信息,得到C(即为输入序列的表)。下方的Decoder则是以C为输入,生成当前条件下最大概率的目标语言句子。

2.Attention

传统的方式将整个的序列信息压缩到了一个语义编码C中,当序列过长的时候就会出现严重的信息损失,也容易出现梯度消失问题,产生信息瓶颈,这时候就多加几个C,出现了基于Encoder-Decoder的attention model。

在上图中结构是没有注意力机制的,所以可以把它看作是注意力不集中的分心模型,每个单词的生成过程如下:
在这里插入图片描述
其中f是Decoder的非线性变换函数。从这里可以看出,在生成目标句子的单词时,不论生成哪个单词,它们使用的输入句子Source的语义编码C都是一样的,没有任何区别。
而语义编码C是由句子Source的每个单词经过Encoder 编码产生的,这意味着不论是生成哪个单词,还是,其实句子Source中任意单词对生成某个目标单词yi来说影响力都是相同的,这是为何说这个模型没有体现出注意力的缘由。这类似于人类看到眼前的画面,但是眼中却没有注意焦点一样。
如果拿机器翻译来解释这个分心模型的Encoder-Decoder框架更好理解,比如输入的是英文句子:Tom chase Jerry,Encoder-Decoder框架逐步生成中文单词:“汤姆”,“追逐”,“杰瑞”。
在翻译“杰瑞”这个中文单词的时候,分心模型里面的每个英文单词对于翻译目标单词“杰瑞”贡献是相同的,很明显这里不太合理,显然“Jerry”对于翻译成“杰瑞”更重要,但是分心模型是无法体现这一点的,这就是为何说它没有引入注意力的原因。
没有引入注意力的模型在输入句子比较短的时候问题不大,但是如果输入句子比较长,此时所有语义完全通过一个中间语义向量来表示,单词自身的信息已经消失,可想而知会丢失很多细节信息,这也是为何要引入注意力模型的重要原因。

引入了Attention 机制的Encoder-Decoder框架
上图就是引入了Attention机制的框架,有了多个语义编码C,在预测Y1时,可能Y1的注意力是放在C1上,那咱们就用C1作为语义编码,当预测Y2时,Y2的注意力集中在C2上,那咱们就用C2作为语义编码,以此类推,就模拟了人类的注意力机制。
以机器翻译例子"Tom chase Jerry" - "汤姆追逐杰瑞"来说明:
当我们在翻译"杰瑞"的时候,为了体现出输入序列中英文单词对于翻译当前中文单词不同的影响程度,比如给出类似下面一个概率分布值:
(Tom,0.3)(Chase,0.2)(Jerry,0.5)。
每个英文单词的概率代表了翻译当前单词“杰瑞”时,注意力分配模型分配给不同英文单词的注意力大小。这对于正确翻译目标语单词肯定是有帮助的,因为引入了新的信息。同理,目标句子中的每个单词都应该学会其对应的源语句子中单词的注意力分配概率信息。这意味着在生成每个单词Yi的时候,原先都是相同的中间语义表示C会替换成根据当前生成单词而不断变化的Ci。理解AM模型的关键就是这里,即由固定的中间语义表示C换成了根据当前输出单词来调整成加入注意力模型的变化的Ci。

即生成目标句子单词的过程成了下面的形式:
在这里插入图片描述
而每个可能对应着不同的源语句子单词的注意力分配概率分布,比如对于上面的英汉翻译来说,其对应的信息可能如下:
在这里插入图片描述
其中,f2函数代表Encoder对输入英文单词的某种变换函数,比如如果Encoder是用的RNN模型的话,这个f2函数的结果往往是某个时刻输入后隐层节点的状态值;g代表Encoder根据单词的中间表示合成整个句子中间语义表示的变换函数,一般的做法中,g函数就是对构成元素加权求和,即下列公式:
在这里插入图片描述

Lx代表输入句子Source的长度,aij代表在Target输出第i个单词时Source输入句子中第j个单词的注意力分配系数,hj是Source输入句子中第j个单词的语义编码。假设Ci下标i就是上面例子所说的“ 汤姆” ,那么Lx就是3,h1=f(“Tom”),h2=f(“Chase”),h3=f(“Jerry”)分别是输入句子每个单词的语义编码,对应的注意力模型权值则分别是0.6,0.2,0.2,所以g函数本质上就是个加权求和函数。如果形象表示的话,翻译中文单词“汤姆”的时候,数学公式对应的中间语义表示的形成过程类似下图。
在这里插入图片描述

还有一个大问题,句子中各个单词注意力分配概率分布值是怎么获得的。

对于采用RNN的Decoder来说,在时刻i,如果要生成yi单词,我们是可以知道Target在生成yi之前的时刻i-1时,隐层节点i-1时刻的输出值Hi-1的,而我们的目的是要计算生成yi时输入句子中的单词“Tom”、“Chase”、“Jerry”对来说的注意力分配概率分布,那么可以用Target输出句子i-1时刻的隐层节点状态Hi-1去一一和输入句子Source中每个单词对应的RNN隐层节点状态hj进行对比,即通过函数F(hj,Hi-1)来获得目标单词yi和每个输入单词对应的对齐可能性,这个F函数在不同论文里可能会采取不同的方法,然后函数F的输出经过Softmax进行归一化就得到了符合概率分布取值区间的注意力分配概率分布数值。计算过程如下图:
在这里插入图片描述

3.Self-Attention

1.框架发展到以上阶段,只能在Decoder阶段实现并行运算,Encoder部分依旧采用的是RNN,LSTM这些按照顺序编码的模型,Encoder部分还是无法实现并行运算,效率还是不够高。
2.因为Encoder部分目前仍旧依赖于RNN,所以对于中长距离之间,两个词相互之间的关系没有办法很好的获取。

为了改进上面两个缺点,更加完美的Self-Attention出现了。
在一般任务的Encoder-Decoder框架中,输入Source和输出Target内容是不一样的,比如对于英-中机器翻译来说,Source是英文句子,Target是对应的翻译出的中文句子,Attention机制发生在Target的元素和Source中的所有元素之间。而Self Attention顾名思义,指的不是Target和Source之间的Attention机制,而是Source内部元素之间或者Target内部元素之间发生的Attention机制。
在这里插入图片描述
例如上图是self-attention的一个例子:
我们想知道这句话中的its,在这句话里its指代的是什么,与哪一些单词相关,那么就可以将its作为Query,然后将这一句话作为Key和Value来计算attention值,找到与这句话中its最相关的单词。通过self-attention我们发现its在这句话中与之最相关的是Law和application,通过我们分析语句意思也十分吻合。
如此引入Self-Attention后会更容易捕获句子中长距离的相互依赖的特征,因为如果是RNN或者LSTM,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小。但是Self-Attention在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征。除此外,Self-Attention对于增加计算的并行性也有直接帮助作用。正好弥补了attention机制的两个缺点,这就是为何Self-Attention逐渐被广泛使用的主要原因。

4.Transformer

整体结构
在这里插入图片描述

4.1 inputs

在这里插入图片描述
Transformer输入是一个序列数据,以水输入"Thinking Machines" 翻译成中文"思考机器"为例:
Encoder 的 inputs就是"Thinking Machines" 分词后的词向量。
输入inputs embedding后需要给每个word的词向量添加位置编码positional encoding,这是因为获取词语出现在句子中的位置信息是一件很重要的事情。举例来讲,我欠他100W 和 他欠我100W。这两句话的意思是完全相反的。而Transformer 是完全基于self-Attention地,而self-attention是不能获取词语位置信息地,就算打乱一句话中词语的位置,每个词还是能与其他词之间计算attention值,就相当于是一个功能强大的词袋模型,对结果没有任何影响。
关于位置编码的获取方式并不是唯一的,在《Attention Is All You Need》论文中Transformer使用的是正余弦位置编码,如下图所示:
在这里插入图片描述
关于位置编码信息的处理是将其与词向量相加求和,因为当词向量长度很大的时候,如果再拼接上位置信息,那么输入维度将会翻倍,效率会大大降低,所以将其位置信息与其词向量相加,且两者效果差不多。

4.2 Encoder Block

在这里插入图片描述
从图中我们可以看出一个encoder由Multi-Head Attention 和 全连接神经网络Feed Forward Network构成。
先回顾一下self-attention,假如输入序列是"Thinking Machines",x1,x2就是对应地"Thinking"和"Machines"添加过位置编码之后的词向量,然后词向量通过三个权值矩阵,转变成为计算Attention值所需的Query,Keys,Values向量。
在这里插入图片描述
以上图为例,X矩阵是由"Tinking"和"Machines"词向量组成的矩阵,然后跟过变换得到Q,K,V。假设词向量是512维,X矩阵的维度是(2,512),W Q , W K , W v均是(512,64)维,得到的Q、K、V就都是(2,64)维,然后利用其计算Attention数值。
在这里插入图片描述
输入序列中每个单词之间的相关性得分,上篇中说过计算相关性得分可以使用点积法,就是用Q中每一个向量与K中每一个向量计算点积,socre是一个(2,2)的矩阵。对于输入序列中每个单词之间的相关性得分进行归一化,归一化的目的主要是为了训练时梯度能够稳定。dk就是K的维度,以上面假设为例,dk=64。 通过softmax函数,将每个单词之间的得分向量转换成[0,1]之间的概率分布,同时更加凸显单词之间的关系。经过softmax后,score转换成一个值分布在[0,1]之间的(2,2)α概率分布矩阵。根据每个单词之间的概率分布,然后乘上对应的Values值,α与V进行点积,V的为维度是(2,64),(2,2)x(2,64)最后得到的Z是(2,64)维的矩阵。

Multi-Head Attention 很简单,就是在self-attention的基础上,对于输入的embedding矩阵,self-attention只使用了一组权值矩阵来进行变换得到Query,Keys,Values。而Multi-Head Attention使用多组权值矩阵得到多组Query,Keys,Values,然后每组分别计算得到一个Z矩阵,最后将得到的多个Z矩阵进行拼接。如下图:
在这里插入图片描述
在经过Multi-Head Attention得到矩阵Z之后,经过了一步:Add&Normalize操作。
Add,就是在Z的基础上加了一个残差块X,加入残差块X的目的是为了防止在深度神经网络训练中发生退化问题,退化的意思就是深度神经网络通过增加网络的层数,Loss逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss反而增大。
Normalize,在神经网络进行训练之前,都需要对于输入数据进行Normalize归一化,目的有二:1,能够加快训练的速度。2.提高训练的稳定性。
在这里使用Layer Normalization(LN)而不使用Batch Normalization(BN),LN是在同一个样本中不同神经元之间进行归一化,而BN是在同一个batch中不同样本之间的同一位置的神经元之间进行归一化。
BN是对于相同的维度进行归一化,但是咱们NLP中输入的都是词向量,一个300维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是适合地,因此这里选用的是LN。
Feed-Forward Networks
全连接层公式如下:
在这里插入图片描述
这里的全连接层是一个两层的神经网络,先线性变换,然后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进行筛选,筛选完后再变回原来的维度。

4.3 Decoder Block and Output

在这里插入图片描述

Decoder的输入分为两类:
一种是训练时的输入,一种是预测时的输入。
训练时的输入就是已经对准备好对应的target数据。例如翻译任务,Encoder输入"Tom chase Jerry",Decoder输入"汤姆追逐杰瑞"。
预测时的输入,一开始输入的是起始符,然后每次输入是上一时刻Transformer的输出。例如,输入"“,输出"汤姆”,输入"汤姆",输出"汤姆追逐",输入"汤姆追逐",输出"汤姆追逐杰瑞",输入"汤姆追逐杰瑞",输出"汤姆追逐杰瑞"结束。
关于输出的解释:
解码组件最后会输出一个实数向量。我们如何把浮点数变成一个单词?这便是线性变换层要做的工作,它之后就是Softmax层。
线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。假设我们的模型从训练集中学习一万个不同的英语单词(我们模型的“输出词表”)。因此对数几率向量为一万个单元格长度的向量——每个单元格对应某一个单词的分数。接下来的Softmax 层便会把那些分数变成概率(都为正数、上限1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出。

Decoder的Masked Multi-Head Attention
Transformer 模型里面涉及两种 mask:
1.padding mask:让padding(不够长补0)的部分不参与attention操作。(在Encoder中的Multi-Head Attention也是需要进行mask地,只不过Encoder中只需要padding mask即可)
2.sequence mask:生成当前词语的概率分布时,让程序不会注意到这个词背后的部分。(可以理解为防止剧透)
详细的矩阵运算角度参考:链接: https://blog.csdn.net/qq_35169059/article/details/101678207?depth_1-

基于Encoder-Decoder 的Multi-Head Attention
Encoder中的Multi-Head Attention是基于Self-Attention地,Decoder中的第二个Multi-Head Attention就只是基于Attention,它的输入Quer来自于Masked Multi-Head Attention的输出,Keys和Values来自于Encoder中最后一层的输出。
经过了第二个Multi-Head Attention之后的Feed Forward Network与Encoder中一样,然后就是输出进入下一个decoder,如此经过6层decoder之后到达最后的输出层。

4.Transformer应用于CV

Transformer算是挺老的一个模型,为什么他活了下来,且应用范围变的特别广?
最本质的原因在于传统算法在提取特征的时候,想的都是很局部的一些信息,比如在图像当中卷积是一个个窗口,递归网络当中是一个个的词,其实这些都像是单独的、较小的一个独立的个体,很难有一些全局的特征,比如说窗口跟窗口之间的关系、词根词之间的关系,而Transformer就能做到提取全局向量、全局信息。举个对Transformer进行修改,将其搬到CV领域的例子:
An Image Is Worth 16X16 Words: Transformers for Image Recognition at Scale[2]
Rethinking Semantic Segmentation from a Sequence-to-Sequence Perspective with Transformers
第一篇文章作者,就是尽可能地将NLP领域的transformer不作修改地搬到CV领域来。但是NLP处理的语言数据是序列化的,而CV中处理的图像数据是三维的(height、width和channels)。所以需要通过某种方法将图像这种三维数据转化为序列化的数据。
图像分块:将原始HxWxC的图像分为PxPxC的patches,然后进行展平为序列,序列长度为Nx(P^2xC),其中N为patch数目
patch embedding: 类似于NLP的embedding操作,实际是进行高维度向量向低维度向量的转换。这里采用了券链接层,最后得到的维度为ND;另外这里还追加了一个分类向量,主要用于描述各个patch的分类信息。最后加入位置编码(position encoding)信息。
将得到的embedding后的序列输入到 encoder-decoder 结构中
分类head:基于MLP实现。
在这里插入图片描述
在此基础上,作者提出了Vision Transformer模型。

第二篇文章用ViT作为的图像的encoder,然后加一个CNN的decoder来完成语义图的预测。在这里插入图片描述
论文介绍了两种decoder结构: (b),reshape为图片维度后不断通过卷积进行上采样;(c)不同层的feature进行提取,然后合并提取(multi-level feature aggragation)最终的输入。

Swim-transformer
Swim-transformer一定程度可以认为是 transformer 类型的集大成者,其网络结构见下图。基于 Swim-transformer 的网络在各个任务上的都达到了SOTA的精度。除了常规的视觉任务(物体检测和语义分割等),其在其他任务上也表现亮眼,如:视频动作识别,自监督学习,图像复原等。
先谈谈前面基于 Vit 的网络结构的缺陷:
基于分块的输入维度很高:patch^2 ,时间复杂度高
输入序列长度单一:对于CV任务来说,多尺度的信息往往是重要的

Swim-transformer 一定程度上解决了上述问题:
将时间复杂度降至线性
能够提取多尺度的信息
Swim Transformer对CNN的降维打击这篇博客对 Swim-transformer 进行较为详尽的描述。
链接: https://zhuanlan.zhihu.com/p/360513527

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Transformer 是一种用于自然语言处理任务的深度学习模型,它是由 Google 在 2017 年提出的。相比于传统的循环神经网络和卷积神经网络,Transformer 采用了全新的 Encoder-Decoder 架构,使用了自注意力机制(Self-Attention Mechanism)来处理输入序列,从而在机器翻译、文本摘要、对话生成等任务上取得了很好的效果。 Transformer 本质上是一个 Encoder-Decoder 架构,其中 Encoder 和 Decoder 都是由多个相同的层堆叠而成。每个层都包含了两个子层,分别是 Multi-Head Attention 和 Feed Forward Neural Network。其中 Multi-Head Attention 是 Transformer 的核心,它使用了自注意力机制来计算输入序列中每个词与其他词之间的关系,从而更好地捕捉上下文信息。 Transformer 的训练过程分为两个阶段:第一阶段是无监督的预训练,使用大量的无标注数据来训练模型;第二阶段是有监督的微调,使用有标注的数据来微调模型,使其适应特定的任务。 下面是一个简单的 Transformer 模型的实现代码: ```python import torch import torch.nn as nn import torch.nn.functional as F class Transformer(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim, num_layers, num_heads, dropout): super().__init__() self.embedding = nn.Embedding(input_dim, hidden_dim) self.encoder_layers = nn.ModuleList([EncoderLayer(hidden_dim, num_heads, dropout) for _ in range(num_layers)]) self.decoder_layers = nn.ModuleList([DecoderLayer(hidden_dim, num_heads, dropout) for _ in range(num_layers)]) self.fc_out = nn.Linear(hidden_dim, output_dim) self.dropout = nn.Dropout(dropout) self.scale = torch.sqrt(torch.FloatTensor([hidden_dim])).to(device) def forward(self, src, trg, src_mask, trg_mask): src_len, batch_size = src.shape trg_len, batch_size = trg.shape src_pos = torch.arange(0, src_len).unsqueeze(1).repeat(1, batch_size).to(device) trg_pos = torch.arange(0, trg_len).unsqueeze(1).repeat(1, batch_size).to(device) src = self.dropout((self.embedding(src) * self.scale) + src_pos) trg = self.dropout((self.embedding(trg) * self.scale) + trg_pos) for layer in self.encoder_layers: src = layer(src, src_mask) for layer in self.decoder_layers: trg = layer(trg, src, trg_mask, src_mask) output = self.fc_out(trg) return output ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值