基于transfomer架构的模型[GPT、BERT、VIT、ST、MAE等等]总结

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Transformer

首先我们来回顾一下Transformer

模型架构图

对于Transformer从宏观角度可以可以理解为6个Encoder+6个Decoder组成

各部分介绍

输入部分

主要就是词嵌入+位置编码

对于词嵌入比较简单就是对一个句子里的每个词做一个嵌入操作映射到相应的维度。一般来说就是先把句子中的词转为对应的数字索引然后根据这个索引做embedding操作得到一个相应维度的向量。

对于位置编码Transformer这里的位置编码是不可学习的(不会更新)直接用公式计算出来的。

其中dmodel是词嵌入得到单个词的向量的维度大小;

pos表示词的位置比如"我爱学习啊"这句话有5个词其位置就分别为0,1,2,3,4;

2i2i+1分别表示在维度的偶数位置和奇数位置(i表示的是维度这个维度的大小是dmodel假如dmodel的大小为512那么维度位置取值为0~511那么i的取值为0~2552i的取值为0,2,4···510,2i+1的取值为1,3,5···511)也就是说在偶数位置使用sin奇数位置使用cos;

最后得到的每个词的位置编码维度大小和其词嵌入维度大小一样(均为dmodel)因此将每个词的位置编码和词嵌入加起来得到最终输入。

Encoder

主要包含多头注意力和前馈神经网络。

首先是Muti-Head Attention其包含多个Scaled Dot-Product Attention将多个Scaled Dot-Product Attention的输出结果拼接在一起然后传入Linear层将其映射为和输入矩阵维度一样。

其次对于Scaled Dot-Product Attention则是重点了。图中的Q、K、V是由该模块输入X分别乘WQ,WK,WV得到的而WQ,WK,WV是随机初始化得到并在训练过程和其他参数一样根据梯度下降进行更新。那么Attention的计算公式如下(dk是Q矩阵的列数):

简单来说就是Q乘K的转置然后除以根号dk来防止内积过大并做softmax操作得到概率最后乘V得到单词之间的Attention系数。

然后Add & Norm比较好理解第一个Add & Norm就是应用了残差连接将Multi-Head Attention的输入和输出相加然后做layerNorm操作(一种将每一层神经元的输入都转成均值方差都一样的归一化操作;第二个Add & Norm同理将FeedForward的输入和输出相加然后做layerNorm操作。

最后是Feed Forward层比较简单主要就是两个linear层。

Decoder

基本和Encoder大差不差。主要的几点不同如下:

  1. 第一个Muti-Head Attention多了个Masked该操作在Scaled Dot-Product Attention中的Attention计算公式的softmax操作之前使用。该操作就是屏蔽单词后面的内容使得训练的时候与预测时都不能看到之后的单词信息而保持一致。

  1. 第二个Muti-Head Attention的K、V矩阵则是由Encoder的输出提供的其输入只生成Q矩阵。

输出部分

一般来说就是通过linear层将其映射到词表维度最后做softmax求各个词的概率。

GPT

GPT的核心思想是把Transformer的decoder拿出来在没有标号的大量文本数据上训练一个语言模型来获得一个预训练模型然后用其在子任务上做微调得到每一个任务所需的分类器。

模型架构图

其由12个Transformer中的Decoder模块经修改后组成相比于原始的Decoder其去掉了第二个Muti-Head Attention。由于语言模型是利用上文预测下一个单词的那么就保留采用Mask Multi-Head Attention其中的Masked会对单词的下文遮挡。

实现过程与原理

其主要分为两个阶段:无监督Pre-training和有监督Fine-tuning。

无监督Pre-training

对于第一个阶段。假设有一个未标记的文本u其中每个词表示为ui从u1一直到un取一个超参数k代表上下文窗口大小(或输入序列的长度)。既然要预测第i个出现的概率第i个词表示为ui通过下面公式即使用模型通过ui前面的k个词来预测ui的概率并把所有的i得到的概率加起来由于做了log操作所以相当于所有词出现的概率相乘即这个文本出现的联合概率。总的来说就是训练一个模型使其能够最大概率的输出跟我的文本长得一样的文本。

使用的模型则是上面说transformer的decoder变种。假如我们要预测词u的概率将其前k个词记为U将其做一个词嵌入的投影并加上位置信息的编码得到h0(We是词的Embedding矩阵Wp是位置编码的Embedding矩阵),后面n层每一层的输入来自上一层的输出最后一层输出做一个词嵌入的投影并通过softmax得到概率分布。

有监督Fine-tuning

对于第二个阶段。假设一个集合输入一个C集合其输出为一个分类y的标号将该集合输入到训练好的模型中得到其中transformer_block最后一层的输出然后乘以参数Wy(预测输出时的参数该参数随着微调过程会调整)做一个softmax操作就可以得到概率了

那么基于上述把所有带标号的序列输入进去后计算其是真实分类标号的联合概率那么微调的时候应该使结果L2更大即联合概率更大。

不仅需要调整参数使得L2更大结合考虑之前预训练的L1可以提高模型泛化性和加速收敛。也就是在微调时考虑两个目标函数即不仅需要给定一个序列预测序列的下一个词还需要给定一个完整序列预测其对应的标号。通过一个超参数将这两个目标函数联系起来。不过注意这里的L1使用的是和L2一样的集合C只是不需要使用到标号y

关于GPT-2和GPT-3

GPT-2在模型方面和GPT差不多主要不同的地方是在子任务的处理时不提供任何相关的训练样本而是直接用预训练的模型去对子任务做预测精炼点说就是用无监督的预训练模型去做有监督任务。GPT2.0旨在通过一个数量更大更丰富广泛通用性强质量高的无监督训练数据训练更好更大的预训练模型把GPT第二阶段Fine-tuning有监督地做下游任务换成了无监督地做下游任务。总的来说GPT模型其发展倾向于用更少的有监督数据+更多的无监督数据去训练模型。

GPT-1在子任务上使用训练样本微调更新参数GPT-2则不使用相关训练样本而GPT-3则在子任务上使用少量的相关训练样本但不更新参数。为了更好的了解GPT-2和GPT-3的区别我们要解释两个概念“zero-shot”表示不提供样本“few-shot”表示每个子任务提供10-100个训练样本。那么GPT-2就是一个参数量较小的zero-shot而GPT-3就是一个参数量更大的few-shot。当然更具体的可以去看论文这里就不多赘述了

BERT

模型架构图

BERT主要使用了Transformer的Encoder架构将多个Encoder堆叠在一起其中使用了12个Encoder使用了24个Encoder。

修改点

输入部分

BERT相对于GPT系列的单向采用双向表示使得其对于文本生成类任务不太适用了并且ICLR2020这篇Incorporating BERT into Neural Machine Translation也尝试了简单的用bert做机器翻译也不会得到好的效果。虽然不适合上述任务但其可以很好的用于比如NSP(Next Sentence Prediction )任务,一般包含问答(QA)和自然语言推理(NLI)等等。而这类任务一般包含一个句子对的输入。那么每个句子对的开头用一个特殊的分类标记([CLS])然后用一个特殊的标记([SEP])将句子分开。

相对于原始的Transformer的输入部分主要有两处修改:

①位置编码被修改为了可学习的位置嵌入;

②新增了段嵌入以表明该词属于哪个句子。

也就是说最终输入为词嵌入+段嵌入+可学习的位置嵌入

词嵌入比较简单不多赘述。

对于段嵌入比如图中[CLS] my dog is cute [seq]为第一句话he likes play ##ing{其实##代表ing是play后缀所以实际为playing不过因为play比较常见所以进行了拆分} [seq]为第二句话也就是说总共有两段,那么这里可以理解为对这两段做embedding操作所以得到的结果中处于同一段(句)的词的段嵌入一定相同。

对于位置嵌入可以理解为对每个词在原来句子中的位置做embedding操作其实这三个操作的实现几乎一样都是基于Embedding只不过嵌入字典的大小不同说这么多可能还是有点晦涩难懂看一下代码实现保证你就懂了如下图:

预训练

没有使用传统的从左到右或从右到左的语言模型来预训练BERT而是使用两个无监督任务预训练BERT即MLM和NSP。

GPT是典型的自回归模型(AR,autoregressive)只考虑单侧信息而BERT是自编码模型(AE,autoencoding)可以利用上下文信息从损坏的输入数据中预测重建原始数据。

MLM

那么BERT是如何损坏文本信息然后让其重建呢?主要是用到了MASK操作。又因为[MASK]在微调期间不会出现所以会在预训练和微调之间造成了不匹配。为了缓解这一点我们并不总是用实际的[MASK]替换单词。

mask操作的策略如下:

首先随机选取15%的词进行MASK(但实际上这15%并不是全部MASK,呼应了前面的那句话)对于这15%的词再进行如下操作权衡:

  1. 80%替换为[MASK];

  1. 10%替换成随机词(ps:有很小概率随机到自己);

  1. 10%不变

所以总得来说MLM任务类似于把文本随机挖空一些词然后做完形填空。比如输入是一句话“新年快乐”。

对于上面提到的GPT模型其优化目标为:

P(新年快乐)=P(新)P(年|新)P(快|新年)P(乐|新年快);

那么此时BERT的MLM任务的优化目标:

假如经过MASK操作后变为“新[MASK]快乐”

P(新年快乐|新[MASK]快乐)=P([MASK]=年|新[MASK]快乐)

NSP

为了训练一个能够理解句子关系的模型我们对一个二元化的NSP任务进行了预训练该任务可以从任何单语言语料库中简单地生成。具体来说在为每个预训练例子选择句子A和B时50%的情况下B是A之后的实际下一句句子(标记为IsNext即正样本) 另外50%的情况下B是语料库中的随机句子(标记为NotNext即负样本)。如下图所示C用于NSP(就是用得到的[CLS]表示来做二分类任务)。

微调

上图分别是在各种下游任务(句子对分类任务、单个句子分类任务、问答任务、序列标注任务)上微调BERT。

对于每个任务我们只需将特定于任务的输入和输出插入BERT并对所有端到端的参数进行微调。在输出端词表示被送入输出层用于词级别的任务例如序列标记或问答而[CLS]表示被送入输出层用于分类任务例如包含或情感分析。

与预训练相比微调成本相对较低。

从完全相同的预训练模型开始本文中的所有结果可以在一个云TPU上花费最多1小时复现或者在一个GPU上花几个小时。

扩展

文中提到了的参数量我们不妨借助下面的图计算推导一下:

  1. 首先嵌入层的字典大小就是词表大小,为30k(ps:这里的k不是指参数就是千的意思)然后映射到的维度是H;

  1. 然后多头自注意力层对于每个头得到Q、K、V需要进行映射其大小均为H*64由于有A个头且A*64=H所以把它们在每个头之间合并起来就相当于3个H*H的矩阵之后还需要对输出做一个Linear层其参数大小为H*H所以总共是

  1. 最后是前馈神经网络层主要就是两个Linear第一个Linear输入是H输出是4H第二个Linear输入是4H输出是H那么总共是

所以说总的参数个数为30k*H + *L

那么把的H和L分别代入得到:

30000*768+12*768*768*12=107,974,656≈110M

30000*1024+12*1024*1024*24=332,709,888≈340M

VIT

模型架构图

VIT(Vision Transformer)就是将多个Encoder堆叠起来不过根据下面的图来看其Encoder和我们认识中的传统的Transformer的Encoder有细微的差别具体的留到下面小节分析。

修改点

输入部分

之前的Transformer架构相关模型都是用在NLP领域所以输入都是由多个词组成的句子而现在的输入变成了由多个像素组成的图片那么自然第一想法就是把像素类比为词输入进去但是一张图片的像素个数往往远大于句子的长度这样会导致输入的序列长度非常长所以显然是不行的。

VIT的思想是把图片切成一个个patch一个patch含有多个像素对应句子中的token。

  1. 假如图像为HxWxC其中H为高W为宽C为通道数我们把其切为N个大小为PxPxC的patch其中N=HW/P^2N就是Transformer输入的实际有效序列长度;

  1. 在Transformer块中使用的隐向量大小为dmodel那么需要通过一个线性投影将patch展平然后映射到dmodel维度;

  1. 类似于BERT,开头也加上了[class]所以需要得到其词嵌入此时序列长度为N+1;

  1. 对整个序列使用标准的可学习1D位置嵌入得到各个“词”的位置嵌入(可以理解为nn.Embedding(N+1,d_model))其中[class]的词嵌入与其位置嵌入相加其余patch通过步骤2得到的dmodel维度结果与其位置嵌入相加。得到的最终结果就是真正的输入了。

Encoder部分

传统的Encoder

VIT中的Encoder

在该Encoder部分无论是多头自注意力层还是MLP层其都把norm操作提前了。

我个人认为是在做NLP时最开始生成的embedding都是归一化过的现在改成图像了所以需要先把图像中每个patch对应的输入先做归一化。

Swin Transformer

模型架构图

宏观来看主要是使用到了多个transformer的encoder部分首先从上面图可以看出这里encoder的归一化LN操作也提前了且对传统的多头自注意力层做了修改其次相对于前面谈到的模型encoder块之间不是简单的堆叠而是中间穿插了Patch Merging操作。

流程剖析

Patch Partition

这一步的思路和VIT相似就是把图片切成一个个4x4的patch(VIT中是16x16)。由于输入图片高H、宽W以及通道数3所以切分后一个patch就是4x4x3有HW/16个patch对应图中表示的维度为

Linear Embedding

这层比较简单就是应用了线性嵌入层将其投影到任意维度(记为C)经过这一层得到的维度为

Swin Transformer Block

窗口多头自注意力(W-MSA)

相比于VIT中把含有多个patch的图片类比为含有多个token的句子输入到transformer块中Swin Transfomer主要是把一个图片分为多个window(一个window中有多个patch)也就是说每次输入到transformer块中的东西变为了一个含有多个patch的window而一张图片中的多个window就类比为了多个句子更进一步类比为输入的批。

假设一个图片有hxw个patch(那么类比为输入到transformer块中的序列长度为hwC为transformer块中的隐层维度我们来计算一下W-MSA和VIT中MSA的复杂度:

首先对于VIT中的MSA:

  1. 输入X分别与参数相乘得到Q、K、V。其中X维度为(hw,C)的维度均为(C,C)(ps:由于有多头假设有A个头使得实际单个头里这些W的维度是(C,B),但由于A*B=C把各个头的W矩阵拼接起来就是维度(C,C)了,那么等价于我们忽略多头来估算处理的情况下面第2步涉及到C的也是用到了类似思想。此时复杂度为

  1. 对于attention公式涉及到以及softmax操作后与V相乘第一个矩阵乘法的左右乘数的矩阵维度为(hw,C)和(C,hw)那么复杂度为第二个矩阵乘法的左右乘数的矩阵维度为(hw,hw)和(hw,c)那么复杂度为。这一步总复杂度为

  1. 最后结果(维度是(hw,C))还需要过一个(C,C)的Linear层得到attention层输出复杂度为

所以VIT中W-MSA总的复杂度为:

其次对于W-MSA:

VIT中一次输入一个图片patch个数为hw类比为序列长度为hw那么W-MSA一次输入一个窗口patch个数为一个图片中有个windows所以可以理解为批量为的"序列长度"为版本的MSA。式(1)中hw替换为然后整个结果乘

化简之后得到复杂度如下:

根据公式很明显得出如果M*M<hw那么W-MSA的复杂度比MSA更小。

移动窗口多头自注意力(SW-MSA)

W-MSA缺乏跨窗口之间的通信这限制了它的建模能力。为了引入跨窗口间通信同时保持非重叠窗口的高效计算。因此提出了SW-MSA该方法在连续Swin Transformer块中的两个分区配置之间交替(如下图也就是说每两个encoder块中第一个使用W-MSA第二个使用SW-MSA)。

具体做法就是对原始图片进行循环移位比如下图中把原图的A、B、C部分(其中A的高或宽为M/2)进行了移位得到了新的图像矩阵对新的图像进行窗口划分即可这样巧妙的引入了跨窗口间的通信但又带来了新的问题即一个窗口中可能包含原本不相邻的patch这些不相邻的patch不应该一起做自注意力的因为它们之间没有太大联系且这个移位操作是我们人为引入的模型不应该学习它们的关系。那么如下图所示需要给含有不相邻patch的窗口引入掩码模板以便在计算attention的softmax操作之前把不相邻的部分变成一个很小的负值使得其在softmax操作中几乎对其他项没有影响。最后需要把循环移位还原回去因为我们需要保持原来图片的相对位置保持其整体图片的语义信息如果没有这一步那么图片会在操作过程中一直往右下角移最终导致其语义信息被破坏掉。

掩码模板就是把不相邻的不需要的那部分设置为-100其他需要的为0这样和原始softmax操作之前的矩阵结果加起来使得不需要的部分数值非常小经过softmax操作后变为0。这些掩码模板官方给出了很好的示例具体如下:

第一个窗口不存在不相邻patch所以掩码模板全为0其他窗口都存在不相邻patch所以有特定的掩码模板(模板大小就是seq_len*seq_len其中seq_len是输入到transformer块中的序列长度可以理解为一个窗口中的patch数量)。

图中例子图片大小是14x14一个窗口有7x7个patch(可以类比为每次输入序列长度为49)即M=7所以移动的部分shift为M/2=3。

掩码模板的更具体实现可参考官方issuehttps://github.com/microsoft/Swin-Transformer/issues/38

相对位置编码

上面说到一个窗口中有多个patch可以类比为一个序列中有多个token也就是每次输入到块中的是一个含有多个patch的窗口显然需要为窗口中的patch加上位置编码而该模型使用的是相对位置编码并且不是在输入的时候加上位置编码而是在计算attention的公式中在进行softmax操作之前得到矩阵上加上位置编码如下图中的B。

假如一个窗口有MxM个patch(可以类比为NLP中一句话有MxM个token所以相当于序列长度为)显然B矩阵的形状为

那么B如何得到呢?具体举例如下:

为了方便举例画图假设一个窗口有4个patch那么可以类比为序列长度为4那么此时通过得到的结果矩阵维度是4x4假设其如下:

分别以patch1、patch2、patch3、patch4为(0,0)原点得到的二维相对位置分别如下:

然后分别把得到的四个矩阵展平为一行拼接成新的4x4的矩阵:

然后设一个window中有MxM个patch这个例子中我们M=2那么操作如下最终得到4x4的索引矩阵

最后根据这个矩阵中的各个索引去做嵌入操作得到值组成的4x4矩阵就是我们公式中的B矩阵。

其中嵌入操作可以理解为nn.Embedding((2M-1)*(2M-1), 1)相当于对这些索引做一个词典大小为(2M-1)*(2M-1)嵌入向量维度大小为1的嵌入操作当然了由于这个索引是不可学习的考虑到自注意力机制都是多头的假设有num_heads个头一般为了方便可以一次生成所有头的B矩阵(此时维度是4x4xnum_heads)因此嵌入操作修改为nn.Embedding((2M-1)*(2M-1), num_heads)。

Patch Merging

顾名思义就是把邻近的小patch合并成一个patch是一个下采样的过程。假设原始张量维度为HxWxC这里为了方便假设C为1本文中主要是下采样两倍那么就每隔一个点选一个组成patch使得原始张量变为了4个维度变为了然后用2C个1x1的卷积核操作把通道数变为了2C最后维度为。具体变换过程如下图:

MAE

模型架构与流程

MAE(Masked Autoencoders)用到了Transformer中的encoder和decoder不同于之前的模型大部分都只用到了两者中的一个。该模型主要就是抽取图像特征并进行图像重建恢复。

  1. 将图像划分为多个patch(这一步借鉴于VIT)然后随机mask掉75%的patch;

  1. 然后把未mask的剩余patch作为序列输入到encoder中(这里使用的是前面讲的VIT模型所以对于输入的处理也相同即线性投影和位置嵌入);

  1. 因为transformer块的输入和输出形状相同所以将encoder输出与之前mask掉的patch按照原图的顺序拼接起来作为decoder的输入(对于输入处理当然还是需要加入位置嵌入的);

  1. decoder输出后最后一层是线性投影其输出通道的数量等于一个patch中的像素值的数量以便于重建图像。损失函数则是在像素空间中计算重建图像和原始图像之间的均方误差(MSE)这里只计算被mask的patch上的损失。

其中MAE的解码端只在预训练期间用于执行图像重建任务在下游任务微调不需要使用。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6