GPT1&BERT&ViT三种模型解析
来源:www.rethink.fun
一、GPT1模型解析
2018 年,OpenAI 发布了 GPT-1,正式拉开了大语言模型发展的序幕。随着模型参数规模和训练数据量的持续扩张,这一系列模型展现出了卓越的语言理解与生成能力。特别是 GPT-3,其拥有 1750 亿参数,被广泛认为是第一个真正意义上的“大语言模型”;而 GPT-4 更进一步,不仅显著提升了性能,还支持多模态输入(图像+文本),为人工智能的发展带来了更多可能性。
1.1 研究背景与动机
GPT-1 的论文《Improving Language Understanding by Generative Pre-Training》(通过生成式预训练改进自然语言理解)旨在解决当时自然语言处理(NLP)领域面临的两大核心挑战:
- 多数 NLP 任务严重依赖大量人工标注数据;
- 面向特定任务设计的模型缺乏通用性,难以迁移到其他任务。
受计算机视觉中“ImageNet 预训练 + 微调”成功模式的启发,OpenAI 提出了一种新方案,希望通过通用的预训练机制提高模型的泛化能力,并便于迁移到多种下游任务中。
面临的主要问题包括:
- 数据问题: NLP 领域缺乏类似 ImageNet 的大规模标注数据集,原始文本虽多,但缺少标注;
- 架构问题: 需要一种能在多种任务中泛化良好的模型架构,理想状态是只需加一个简单的线性分类器即可适配不同任务。
OpenAI 的解决思路如下:
- 利用自回归语言模型进行无监督预训练,训练目标是预测下一个词,这样就能直接使用海量原始文本;
- 采用 Transformer 架构,在捕捉序列模式和迁移能力方面优于以往模型,因此选定其作为基础结构。
1.2 模型架构

GPT-1 采用的是 Transformer 的 Decoder-only 架构,也就是说它省略了原始 Transformer 的编码器部分,进而也移除了与编码器相关的交叉注意力机制。
文本的处理流程如下:
- 分词与嵌入: 文本首先通过分词器切分成 token,再经过嵌入层转换为词向量,并添加可学习的位置编码,以引入位置信息。
- 掩码多头自注意力: 使用带掩码的多头注意力机制,确保模型在预测每个 token 时只能参考其前文,保持自回归性质。
- 残差连接与归一化: 每个模块使用残差连接与层归一化,随后通过前馈神经网络处理,再次进行残差与归一化。
- 堆叠结构: GPT-1 包含 12 层这样的解码器模块。
模型参数细节如下:
- 解码器层数:12
- 隐藏层维度:768
- 注意力头数:12
- 总参数量:1.17 亿
- 激活函数:GELU(Gaussian Error Linear Unit)
- 位置嵌入:采用可学习参数(非正弦函数)
- 词嵌入与输出层权重共享

GPT-1 将词嵌入矩阵(形状为
[vocab_size, hidden_size])与输出线性层的权重(形状为[hidden_size, vocab_size])共享,从而减少了参数量。这种策略在小模型中尤为重要,因为嵌入矩阵在总参数中占比很大。
1.3 下游任务适配方式
预训练完成后,OpenAI 采用极简的方式对 GPT-1 进行微调来适配不同下游任务,只需在文本中加入特殊 token。把组合好的序列输入预训练好的GPT-1。并拿出最后一个Token对应的输出向量, 然后接上一个简单的线性层来完成不同的任务。

文本分类任务
- 在文本前后加入表示开始和结束的 token;
- 最后一个token的输出向量接入一个线性分类器,直接进行分类。
文本蕴含任务(Textual Entailment)
- 输入为两句话:前提 + 假设;
- 结构为
[开始][前提][分隔符][假设][结束]; - 输出为三分类:“支持”、“反对”或“不相关”。
语义相似度评估
- 同样使用
[开始][句子1][分隔符][句子2][结束]的输入格式; - 因任务对称,交换句子位置进行两次推理,将输出向量相加后输入到线性层中做分类。
多项选择题
- 将每个问题与每个选项组合为独立序列;
- 每个序列送入 GPT-1 得到得分,通过 softmax 计算每个选项的概率。
这种将任务指令融入输入序列的策略使 GPT-1 能以极少的结构修改适应不同任务,并充分利用其在预训练中学习到的语言能力。
1.4 多任务联合训练
在下游任务微调时,GPT-1 同时执行两个目标:
- 特定任务的分类目标;
- 自回归语言模型任务(预测下一个词)。
通过为这两个损失函数设置不同的权重,实现多任务学习,有助于模型保持语言建模能力的同时提升特定任务性能,增强泛化能力。
1.5 训练数据与设置
GPT-1 的预训练使用了 BooksCorpus 数据集:
- 包含约 7000 本未出版图书;
- 总词量约为 8 亿;
- 上下文窗口长度:512 个 token;
- 批处理大小:32。
1.5 效果
GPT1通过“预训练+微调”的范式,用通用的模型架构,通过对不同人物设计不同的输入的方式下,不对模型结构进行大幅修改,在12个NLP任务中,有9个刷新了当时的最好成绩,显示出其强大的通用性和迁移能力。
GPT-1 的发布,标志着以无监督预训练为核心的大模型路线正式确立。它证明了即使使用最基础的“预测下一个词”的目标,也能学到强大的语言表示能力,并且易于迁移到多种 NLP 任务中。这为后续如 GPT-2、GPT-3 和 GPT-4 等更强大的语言模型奠定了坚实的基础。
二、Bert模型解析
GPT-1的成功让人们看到了在NLP领域“预训练+微调”模式的可行性,GPT-1是Decoder-Only的架构。BERT(Bidirectional Encoder Representations from Transformers)的作者认为GPT-1的注意力是单向的,也就是每个token只能关注它前面token的信息。但是对于NLP任务,双向注意力可以看到序列里所有token的信息,这样对于上下文理解会更加全面,应该会取得更好的效果。
2.1 BERT模型架构
BERT是一个Encoder-Only的架构。它采用了Transformer里的Encoder部分的架构。

BERT里将Encoder模块的层数用L表示,Token Embedding和隐藏层大小用H表示,自注意力的头用A表示。然后推出了两种大小的BERT模型。
BERT_base:(L=12,H=768,A=12) 总参数量为110M。这个大小与GPT-1是同样参数规模的,就是用来和GPT-1比效果的。
BERT_large(L=24,H=1024,A=16)总参数量为340M。
BERT也是采用了“预训练+微调”的模式。在预训练期间,BERT通过不同的预训练任务在未标记的数据上进行模型训练。 对于微调,首先使用预训练的参数初始化 BERT 模型,然后使用下游任务中的标记数据对所有参数进行微调。 每个下游任务都有单独的微调模型,即使它们使用相同的预训练参数进行了初始化。BERT 的一个特征是其跨不同任务的统一结构。也就是BERT预训练的结构和最终的下游任务在模型结构之间的差异很小。
2.2 输入和输出
BERT可以解决NLP领域的两类问题,一类是序列级别的问题,一类是token级别的问题。
序列级别的问题
序列级别的问题也分为两类,一类是单个序列的,比如对一句话进行正面和负面情绪的判别。一类是两个序列的,比如判断两句话的蕴含关系。所以这就需要BERT在输入设计上能够支持单个序列,也要能支持两个序列。输出上需要有一个位置可以输出对单个或者多个序列进行总结的信息。
Token级别的问题
Token级别的问题类似NER(Named Entity Recognition 命名实体识别),它需要BERT能够为每个token输出它在上下文中的含义信息。
所以最终BERT的输入如下:

上图中最上边的Input是原始输入,我们给所有的输入前增加一个特殊的[cls] token,它是一个占位符,将来在BERT Encoder Block最后一层第一个位置的输出向量,就代表了整个输入的全局信息。利用这个信息,加一个分类头就可以对序列(单序列或者多序列)进行分类。我们发现Input中间还有一个[SEP] token,它就是序列的分隔符,用来隔开两个序列的。通过这个token,让模型知道这个token的前后属于不同的序列。
对于token级别的任务,每个token位置最后一层Encoder的输出的向量就代表了这个token结合上下文后的表示信息,加一个分类头就可以进行NER分类了。对于所有token都共享一个NER分类头。
上图中下边有3个不同的Embedding。它们按位相加,构成了序列最终的输入向量。
-
Token Embedding,就是每个token的词向量,代表了每个token的原始含义。此时还没有上下文信息。字典里每个token都有一个Embedding,并且是可学习的参数。
-
Segment Embedding,用于区分每个token属于哪个序列的Embedding,每个序列共享一个Embedding,也是可学习参数。
-
Position Embedding,用于给每个位置一个Embedding,不同与Transformer里用sin和cos函数生成固定位置编码,这里的位置编码也是可学习参数,让模型在训练中自己学习。
2.3 预训练BERT
因为BERT预设是可以同时解决序列级别问题和token级别的问题。所以预训练时就必须设计能提取token上下文信息和序列信息的任务。
Token级别任务:遮蔽语言模型
遮蔽语言模型(Masked Language Model),是随机将输入token中的一些进行mask,替换为[MASK] token。然后在BERT输出时,根据[MASK]token的输出向量加一个分类头,预测出原始被遮蔽的token id。这个任务就是填空题,锻炼模型根据上下文信息猜出空缺token的能力。这样训练模型就让模型能更高的提取token级别的上下文信息。
但是为了防止模型只是在输入[MASK]这个token时才刻意提取它的上下文信息,实际BERT在训练时,先随机选择15%的token做最终的token id预测。在这15%的token里,80%的概率用[MASK] token替换原来的token,10%的概率随机替换为其他token,10%的概率保持原有token不变。经过这样的数据设计,BERT模型就可以很好的提取每个token的上下文信息了。
序列级别任务:下一句预测
很多NLP下游任务,比如问答,自然语言推理,都是基于理解两个句子的关系。BERT设计的序列级别的任务是判断两个句子是否是连续的。在预训练样本中采集两个连续的句子作为正样本,两个不连续的句子作为负样本,提取[cls] token 最后一层的输出向量加一个分类头,进行二分类判断。这个任务可以很好的让[cls]token提取序列级别的上下文信息。
对于预训练数据,BERT使用了BooksCorpus(800万个单词)和英文的Wikipedia(25亿个单词)。
2.4 微调BERT
BERT的微调很简单,只需要按照问题类型组织输入token即可。比如是两个序列,就在序列之间加[SEP] token,并且设置合适的Segment Embedding。
另外根据不同的任务,利用输出不同位置token的输出,加上分类头来进行下游任务。序列级别的就提取[CLS] token的输出,token级别的就提取每个token的输出。
BERT进行微调时,只有分类头是全新的随机初始化的,其他参数,包括Encoder和Embedding都是预训练好的,进行全参数量的微调。
2.5 BERT的意义
BERT 在概念上很简单,在实际应用中非常强大。 它在 11 种自然语言处理任务上获得了当时最优结果,包括将 GLUE 得分提高到 80.5%(提升绝对值 7.7%),MultiNLI 准确度达到 86.7%(提升绝对值 4.6%),SQuAD v1.1 问答测试 F1 达到 93.2(提升绝对值 1.5 个点)以及 SQuAD v2.0 测试集 F1 达到 83.1(提升绝对值 5.1 个点)。
BERT是NLP领域第一个火出圈,现象级的预训练模型,在大语言模型出来之前,BERT就是NLP领域最受人瞩目的模型。大多数的NLP问题,仅需要少量的标注数据,甚至几百个,就可以达到非常不错的效果。
三、ViT模型解析
3.1为什么ViT如此重要?
接下来学习vision transformer,也就是VIT模型。ViT可以说是确立了把transformer模型引入到计算机视觉领域的模型。在他之前确实也有人做过尝试,但都是规模太小,效果不好,或者对transformer做了一定的修改。而ViT作者的目标就是想验证在不对transformer做任何修改的前提下,是否可以将transformer引入到计算机视觉领域。并且在不同规模的数据集训练下,和卷积神经网络相比,transformer有什么优势?
首先我们先来看结论,作者通过对图像分类任务进行实验发现:1、transform模型可以不做任何改动就来解决计算机视觉问题。2、在小规模数据集上,它的表现略输于卷积神经网络。在中等或者大规模数据集上进行训练,表现和卷积神经网络相当或者略高于卷积神经网络。3、训练同等精度的模型。Transformer模型比卷积神经网络在计算效率上更具优势。
ViT的意义在于它证明了transformer架构的通用性。Transformer架构可以说是目前人类发现的最佳模型架构。它的训练效率高,可以通过注意力提取复杂语义,可以支持多种模态,并且结构简单,可以自由扩展模型大小。即使使用千亿级别参数规模的模型,依然没有出现性能饱和。ViT为多模态大模型铺平了道路。Transformer的架构的统一也让基于transformer架构的工程优化,比如flash attention、vLLM等,可以直接应用于计算机视觉和多模态领域。
下面我们就来具体讲解ViT模型。如果你了解transformer模型,那么理解ViT模型就没有任何问题。如果你还不了解transformer模型,请先看我之前介绍transformer的blog。
因为作者希望对transformer模型尽量不做任何修改,来完成对图像的分类任务。既然模型不能改变,那就要让图片数据变得像文本。所以ViT这篇论文的题目就是一张图像等于16 * 16个词。
ViT是如何将图片转化成为和文本一样的序列呢?答案是首先将图片按照固定大小分为一个patch。比如论文中输入图片为224乘224,如果patch大小为14乘14,则可以分为16乘16的一个序列。
不按照单个像素来划分的原因是这样:会导致序列过长,计算复杂度太高。还有一个原因就是一个像素只有RGB3个值,语义信息太少,用一个长度几百上千的向量去做一个像素的embedding也是浪费。所以这里选择用一个patch作为一个语义单元,对应文本里面的一个token,一个图像。
3.2如何把图像翻译给transformer
3.2.1Patch转embedding向量
Patch怎么转换成一个对应的embedding向量呢?可以将这个patch的长 * 宽 * 通道数的多维矩阵表示把它展平,然后通过一个共享的线性层投射到transformer模型里的特征维度。比如1024,这样就完成了把一个图片转换成一个向量序列的转换。图像切片相当于文本里面的分词,线性投射层相当于embedding层。

接下来需要考虑位置编码了。VIT里的做法是为每个位置加上一个可学习的位置编码,比如图里有九个不同的可学习的位置编码。

因为VIT做的任务是要对图片进行分类,它参考了自然语言处理里的BERT模型的做法,在最前边加了一个可学习的用来分类的token,并且有自己可学习的位置编码。因为后边采用的是transformer的encoder架构,每个token不论在序列里的什么位置都可以看到所有的其他token。所以即使把它固定在第一个位置上,它也可以汇集所有图像patch的信息。

3.2.2网络结构
网络结构就采用的是transformer里面的encoder,不同于一般大模型采用decoder的。因为大模型一般是做生成任务的,所以选择decoder的。而图片分类是做信息提取的,所以用类似BERT的架构采用了encoder模块。最后是通过第一个位置的分类token的信息加上一个简单的MLP的头进行图片的分类,类似于BERT,ViT也训练了不同大小的模型,分为base、large、huge, 分别有不同的层数,hidden size、MLP size和注意力头。

对于ViT模体型的表示,一般会用类似于ViT-L/16来表示,表示这是一个ViT large模型,其中patch的大小为16乘16,patch越小,一个图片分出来的patch就越多,输入到transformer里的序列就越长,计算复杂度就越高。当然一般模型效果也越好。
下面看一下具体的实现效果,作者选择了当时在不同数据集上表现最好的分类模型Resnet和EfficientNet进行比较。可以看到在JFT数据集上进行预训练,然后在不同的数据集上进行测试,ViT-H/14模型几乎都取得了最好的成绩。特别需要注意的是最后一行在TPUV3的核数乘以训练天数表示的计算代价上,VIT模型具有非常大的优势。所以说在大规模图像数据集上训练VIT模型更有优势。

下来我们讲一些实现的细节。首先是如何将图像转换成embedding的序列,训练图片大小为224乘224 patch,大小为16 * 16,patch的数量为14 * 14。Transformer里的特征维度也就是hidden sizes为1024。
将图像转换为embedding序列有两种方法,一种是之前说过的,首先把图片拆分为多个patch,对于每一个patch它的shape为16,16,3,将它展开为一个长度为768的一维向量,然后再通过一个共享的输入为768,输出为1024的线性层进行编码,形成embedding的序列。但是实际实现时有一个更简单的方法,不用对图片进行patch切分,直接对原始图片定义1024个卷积核,每个卷积核的大小就是patch的大小,16乘16,步长也为16,padding为valid。这两个操作实际上是完全等价的。

3.2.3位置编码与模型架构的尝试
作者在论文中也对位置编码进行了不同的尝试,分别是不加位置编码、一维位置编码、二维位置编码和相对位置编码。比如图中将图片切成为九个patch,对于一维位置编码而言,就是生成九个可学习的位置编码,每个位置编码的长度都和特征维度一样为1024。对于二维编码生成三个表示行的可学习的位置编码,三个表示列的可学习位置编码,但它们的长度都为特征长度的一半——512,通过行列特征的拼接来构成一个二维位置编码。比如对于第二行第一列的patch,它的位置编码就是取第二个行位置编码拼接上第一个列位置编码构成的

通过实验发现不加位置编码效果最差,其他不论是一维二维还是相对位置编码效果都差不多,但是你是否觉得不加位置编码为什么效果也还行,没差多少?这是因为图片被切成了patch,patch内部是含有位置信息的。就像上面这个图片,它的patch即使被打乱了位置,你也可以知道这是一个建筑的图片。
对于位置编码,作者还做了进一步的研究,他发现通过学习一维位置编码,也是可以学到二维信息的。比如这里这个位置编码的相关性的图,可以看到和每个patch位置编码最相关的还是自己附近的以及自己所在行列的patch。另外作者研究发现,随着encoder层的增加,每个注意头关注的平均像素距离可以看到不同于卷积神经网络,在网络浅层,有的头关注近距离的像素,但是也有很多头已经关注到了远距离的像素。随着模型层数的加深,模型越来越关注远距离的全局信息了。

对于模型结构的尝试,作者选择了三种结构进行对比,一种是原始的卷积神经网络,比如ResNet,一种就是使用transformer的ViT,还有一种就是卷积神经网络和transformer的混合模型。

这里介绍一下混合模型的模型架构。它首先由卷积神经网络来提取特征,最后在提取的特征图上,每个空间位置就是一个图像的patch。然后再做线性映射进入transformer encoder。图中,圆形表示ViT,方形表示ResNet,+ 表示混合模型。可以看到在相同的预训练代价下,刚开始混合模型有优势,但是随着计算代价的增大,也就是模型的增大,最终ViT模型的效果还是略好于混合模型。所以证明了transformer架构在视觉领域完全可以取代卷积神经网络。

对于图片分类通常也有两种做法,一种是之前说的,通过在序列第一个位置增加一个分类的token来提取图像的全局信息。还有一个做法就是不额外增加token,就用所有图像patch最后一层输出的全局平均池化来做全局信息。作者做了比较,两个效果也是类似的。

下面我们来看一下训练数据集大小对模型的影响。在小规模数据集上,ResNet表现好于ViT。而随着数据规模的增大,ViT效果是好于ResNet的。我们大致有个概念,就是在百万级别的数据集上ResNet好于ViT。千万级别的数据上ResNet和ViT差不多,亿级别数据上ResNet不如ViT为什么在小的数据集上的训练ViT不如ResNet呢?这是由归纳偏置引起的。
3.3归纳偏置思考
什么是归纳偏置呢?归纳偏置就是在训练模型时,人为引入的一些先验知识给模型。这些知识是人给的,不是模型从数据中学来的。

比如卷积操作中,每一层都有两个归纳偏置,一个是局部性,一个是平移不变性。卷积核为什么只作用在图片的一个局部呢?因为我们知道图片相关信息都集中在局部。卷积核为什么在图片上进行平移呢?因为我们知道物体不论在图片的什么位置,它的特征是不变的。我们把这两点先验知识都给了模型,所以他学习起来相对简单。这个归纳偏置在卷积神经网络的每一层都起作用。
但是ViT采用的是transformer架构,引入的归纳偏置比较少,就是在切分patch时引入了局部性。因为我们是把图片分成了块,而不是随机取一些像素。还有就是对所有的patch都用同样的线性层进行embedding,这里也引入了平移不变性。但是ViT只是在切分patch和对patch进行编码时引入了归纳偏置,后面注意力计算是完全没有引入归纳偏置的。所以这导致了ViT在小规模数据集上学习比ResNet要慢一些。
最后ViT的作者还尝试着让图片进行自监督学习,因为有标记的数据总是少数,想让模型取得大规模的数据集凸取得突破性的进展,一定要是自监督学习。就像BERT的成功和GPT的成功,作者在自监督学习时也借鉴了BERT的做法。它将50%的图像patch进行标记,在这些标记的patch里,80%将换成可学习的mask标签,10%的embedding替换为其他的patch的embedding,10%的embedding保持不变,最终让利用标记的这些patch的输出预测原始图片的像素值。

为了简化问题,将原来RGB255乘255乘2551共1658万多种颜色简化到RGB,分别对应八个值,8乘8、256种颜色,最终效果也是非常不错。Vi t打开了transformer架构处理计算机视觉和多模态数据的大门,让多模态通用人工智能成为可能。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)