在LLM出现之前,有很多基于Transformer的经典模型,主要有三种类型:以BERT为代表的基于Encoder的模型,以BART为代表的基于Encoder-Decoder的模型,还有以GPT系列为代表的基于Decoder的模型。
BERT
BERT模型是基于Encoder Only架构最经典的模型,其它的模型只是预训练任务或者注意力模块等存在细微差别。
原文链接:BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
BERT是一个语言表示模型,其目的是学到通用的文本表示,预训练之后的BERT针对不同的任务设置额外的输出层,在微调之后能够达到很好的效果。
Word2Vec已经证明了预训练之后的语言模型(Language Model)能够提升自然语言处理任务的性能,Pre-train+Fine-tuning的范式在GPT1(使用Transformer的Decoder部分)上也得到了应用。BERT的创新在于,设置了一个掩码语言模型(masked language model
,MLM)的预训练目标,使用Transformer的Encoder部分关注两个方向的上下文(从左到右和从右到左)。
整体流程
整体流程分为两部分,一部分是预训练,另一部分是微调。
- 预训练:在无标注数据上用不同的预训练任务训练模型
- 微调:用预训练得到的参数初始化BERT模型,随后用下游任务的标注数据微调所有模型参数
预训练任务
有两个训练任务:
masked language model
具体来说,输入文本随机mask一些tokens(15%),然后基于上下文预测出这些被mask掉的tokens。此外,由于fine-tuning阶段并没有[MASK]
这个token,为了弥补预训练和微调之间的不匹配,随机mask的token并不总是用[MASK]
标记来表示。
实际训练时,如果某个位置的token要被mask掉,有80%的可能性用[MASK]
标记替代,有10%的可能性随机挑选一个token替代,还有10%的可能性保持原始token不变。next sentence prediction
具体来说,给定一个文本对,预测第二个句子是否是第一个句子的下一句。实际训练挑选句子$A$和句子$B$时,有50%的可能性$B$确实是跟在$A$后面的,标记为IsNext
;还有50%的可能性$B$是从语料中随机挑选的句子,标记为NotNext
模型结构
BERT的模型架构采用了多层的、双向的Transformer encoder,其代码实现可以参考The Annotated Transformer,这是哈佛大学关于Transformer的教程。也可以看这篇Transformer论文精读。
如果用$L$表示Transformer blocks的数目,$H$表示隐藏层的维度,$A$表示多头注意力中head的数目,那么这是模型的参数:
BERT-BASE | BERT-Large | |
---|---|---|
L | 12 | 24 |
H | 768 | 1024 |
A | 12 | 16 |
Param | 110M | 340M |
不过由于采用了Next Sentence Prediction
这个预训练任务,BERT在处理输入时和Transformer略有不同。
其中[CLS]
是每个句子的第一个token,该token对应的最终隐藏状态可以看作序列表示,可以用于分类任务;[SEP]
用于分割句子,以区分两个不同的句子。
我们可以看到BERT的输入部分有三种Embedding:
- Token Embedding
- Segment Embedding
- Position Embedding
通过将这三种Embedding相加,可以将三种信息融合,模型在不增加额外参数的情况下将多种信息编码到同一个向量中。这种设计提高了BERT模型的兼容性,它可以组合不同的Embedding来适应不同的输入格式和任务需求。
从语义层面,为什么这三种Embedding可以相加?
相加之后的向量大小和方向就改变了,模型拿到的是三个向量之和,它怎么知道原来的三个向量?
苏剑林:embedding的数学本质就是以one-hot为输入的单层全连接网络,也就是说世界上本没有什么embedding,有的只是one-hot。从这个角度理解,这三个Embedding相加等价于三个原始的one-hot向量拼接再经过一个全连接网络。
训练语料
预训练的语料来源有:
BooksCorpus
:800M wordsEnglish Wikipedia
:2,500M words
使用WordPiece
作为Tokenizer,建立了30,000个token的词汇表。
微调
对于每一个不同的下游任务,需要将不同的输入和输出插入BERT,然后端到端地微调所有参数。
对于输入来说,预训练阶段的句子$A$和句子$B$和这些下游任务的输入是一样的:1)question answering
中的(问题,段落)
对;2)文本分类中的(text - $\varnothing$)对;3)序列标注中的(text - $\varnothing$)对。
对于输出来说,token的表示可以作为输出层的输入,从而应用于token级别的任务,如序列标注或者问答;[CLS]
的表示可以输入到输出层,用于分类任务,例如情感分析或文本蕴含。
相比于预训练,微调花费的资源要小很多。
BART
BART全称BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension
。
2019年10月,Facebook提出了BART,这篇文章在抽象总结和对话等生成任务上都取得了更好的下游性能,相比于BERT做出了如下改变:
- 在双向编码器架构中添加了因果解码器
- 用更复杂的预训练任务代替BERT的完形填空任务
预训练任务
BART会使用任意噪声函数破坏文本,然后预训练的任务就是让模型重建原始文本。
具体来说,BART会随机打乱原始句子的顺序,然后使用单个[MASK]
来替换文本片段,这个文本片段可以是多个Token。通过这种方式,迫使模型对句子的整体长度进行更多的推理,并且对输入进行更长范围的转换,概括了BERT中的两个预训练任务。

重建原始文本的重建损失就是解码器输出和原始输入之间的交叉熵。
下游任务
BART常用语Seq2Seq任务,如机器翻译,摘要生成等自然语言处理任务。
GPT
GPT系列文章是由OpenAI提出的,是基于Decoder Only架构的生成式语言模型的代表作。
GPT-1
GPT-1原文是Improving Language Understanding by Generative Pre-Training
。
GPT-1验证了生成式预训练+判别式微调这种范式的可行性。
GPT-1采用了BPE(bytepair encoding
)作为Tokenizer。
预训练
预训练的最终目标就是学到好的文本表示。什么是好的文本表示很难定义,不过我们希望这个文本表示不仅需要包含词级别的信息,还需要包含上下文级别的信息。
对此,GPT-1采用标准的语言建模(Language Modeling
)任务,以期望学到通用、带有高级语义信息的文本表示。
给定无标注的语料$C=\{u_1,…,u_n\}$,标准语言建模的目标是最大化以下的似然函数:
其中$k$是上下文窗口的大小,$\Theta$是神经网络的参数,用于表示条件概率$P$。具体来说,GPT-1采用多层Transformer decoder blocks
来作为这个神经网络。
假设上下文对应的向量是$U=(u_{-k},…,u_{-1})$,$n$是Transformer decoder blocks
的数目,$W_e$是token embedding matrix
,$W_{p}$是position embedding matrix
,那么通过以下方式计算给定上下文如何得到目标token的输出分布:
微调
在微调阶段,不管是什么任务,都需要将其输入转换为token序列。假设输入序列是$(x^1, …, x^m)$,对应的标签是$y$。
输入序列+预训练模型——>最后一层transformer block
的激活$h_l^m$,随后该向量作为一个额外线性输出层的输入:
此时的训练目标是:
不过,在实验阶段论文作者发现将language modeling
作为辅助任务有助于提升监督模型的泛化性,还可以加速收敛。
所以最终微调阶段的优化目标是这样的:
其中$D$是微调阶段的标注数据集。
GPT-2
GPT-2原文是Language Models are Unsupervised Multitask Learners
。
GPT-2用更多的数据预训练,验证了预训练模型强大的Zero-Shot
能力,一定程度上验证了通用模型的可行性。
GPT-2采用BPE作为Tokenizer,不过实现的时候加了一些规则阻止部分token合并。
预训练
通用的系统可以执行多种不同任务,即使输入相同,它需要根据不同的任务,输出不同的内容。因此,在构建模型时,需要建模$p(output|input, task)$。
模型结构在Transformer decoder block
的基础上做了一些小改动,Layer Normalization
移到每一个sub-block
的输入,类似pre-activation
,在最终的self-attention block
之后添加了额外的Layer Normalization
模块。
上下文长度也从512增加到1024。
下游任务
GPT-2采用了Zero-Shot
的方式进行预测,由输入格式决定要完成什么任务。此前这些格式的数据已经在预训练阶段见过。
GPT-3
GPT-3的原文是Language Models are Few-Shot Learners
。
GPT-3用更多的数据、更大的模型(175B)进行预训练,验证了预训练模型强大的Few-Shot
能力和in-context learning
能力,更进一步验证了通用模型的可行性。
此外,GPT-3还揭示了随着模型参数规模增大,in-context learning
的能力会进一步增强。
预训练
和GPT-2的预训练过程类似,只不过增大了数据规模、模型参数和训练长度等。
应用
GPT-3并没有采用微调的方式处理下游应用,而是利用模型in context learning
的能力,给模型传达指令。这种方式不会改变模型的权重,只是改变了输入格式。
- Few Shot
- One Shot
- Zero Shot