大家好,我是半虹,这篇文章来讲 GPT\text{GPT}GPT (Generative Pre-Training\textbf{G}\text{enerative}\ \textbf{P}\text{re-}\textbf{T}\text{raining}Generative Pre-Training)
实际上,GPT\text{GPT}GPT 包括一系列论文,具体有 :
GPT\text{GPT}GPT 本质上就是一个预训练语言模型,其用大规模无标注语料、以自回归的方式进行预训练
GPT\text{GPT}GPT 通过不断增加模型参数以及预训练语料规模进行更新迭代,并最终取得令人惊艳的效果
值得一提的是,GPT\text{GPT}GPT 的模型参数以及预训练语料规模非常夸张,我们来看一组数据就会清楚
注意量级换算,K(Kilo)=103,M(Mega)=106,G(Giga)=109,T(Tera)=1012\text{K}(\text{Kilo}) = 10^3,\ \text{M}(\text{Mega}) = 10^6,\ \text{G}(\text{Giga}) = 10^9,\ \text{T}(\text{Tera}) = 10^{12}K(Kilo)=103, M(Mega)=106, G(Giga)=109, T(Tera)=1012
GPT-1
模型参数量为 117M
,预训练语料有 5GB
GPT-2
模型参数量为 1.5G
,预训练语料有 40GB
GPT-3
模型参数量为 175G
,预训练语料有 45TB
最后再来简单回顾一下,GPT\text{GPT}GPT 系列模型及其相关模型的发展历程
201720172017 年 060606 月,Transformer\text{Transformer}Transformer 横空出世,在自然语言处理领域中掀起轩然大波
201820182018 年 060606 月,GPT-1\text{GPT-1}GPT-1 正式发布,其用 Transformer\text{Transformer}Transformer 的解码器训练单向语言模型,引领预训练模型热潮
201820182018 年 101010 月,BERT\text{BERT}BERT 随之发布,其用 Transformer\text{Transformer}Transformer 的编码器训练掩码语言模型,效果更超越 GPT-1\text{GPT-1}GPT-1
201920192019 年 020202 月,GPT-2\text{GPT-2}GPT-2 沿着原有路线,增加更多数据,扩展更大模型,尝试解决 zero-shot\text{zero-shot}zero-shot 任务
202020202020 年 050505 月,GPT-3\text{GPT-3}GPT-3 是暴力出奇迹,深度挖掘预训练语言模型潜力,且最后在 few-shot\text{few-shot}few-shot 任务表现优秀
下面我们将会逐一介绍,GPT\text{GPT}GPT 系列模型的详细内容及其细节
关键词:无标注文本、自监督学习
在自然语言处理领域,存在各种各样的任务,这些任务都需要大量的标注数据进行训练才有不错的模型效果
但针对特定任务的高质量标注数据难以获得,通常都需要花费大量的人力物力才能得到数量不多的有效数据
另一方面,在目前的互联网上有海量的文本,但这些文本是没有针对特定任务做标注的,被称为无标注文本
所以很难将这些文本应用到特定任务的训练,这未免有些遗憾和浪费
基于此情,GPT-1\text{GPT-1}GPT-1 将预训练和微调的范式引入到自然语言处理领域,使得模型能够利用海量的无标注文本
具体来说就是先让模型在大规模无标注数据上针对通用任务进行训练,使模型具备理解语言的基础能力
然后将预训练好的模型在特定的有标注数据上针对下游任务进行微调,使模型能够适应不同的下游任务
其中最重要的是设计预训练任务让模型能够利用无标注文本进行训练,这关系到预训练模型基础能力的强弱
怎么利用无标注文本呢?GPT-1\text{GPT-1}GPT-1 的想法就是做语言模型,简单来说,就是根据之前的词语预测下一个词语
这是一个自监督的学习,因此不需要对文本做特殊的标注,只需要把要预测的词语掩盖就行
事实上,GPT-1\text{GPT-1}GPT-1 可以看作是 Transformer\text{Transformer}Transformer 的解码器,然后在此基础上去掉解码层中的多头外注意力机制
解码器由若干个解码层堆叠而成,而且模型中上一个解码层的输出会作为下一个解码层的输入
解码层包括带掩码的多头自注意力机制和前馈神经网络两个子模块,每个子模块后有残差连接和层正则化
由于我们在之前的文章中已经详细介绍过各个模块细节,所以这里就不再重复了,整体的结构如下图所示
具体来说,GPT-1\text{GPT-1}GPT-1 包含 121212 个改过的 Transformer\text{Transformer}Transformer 的解码层,这些解码层会堆叠起来
最后一个解码层的输出表示会经过线性预测层得到要预测单词的概率分布
而第一个解码层的输入表示由词元嵌入和位置嵌入相加得到,这里的嵌入是可学习向量,要通过数据训练
输入表示的示意图如下所示:
GPT-1\text{GPT-1}GPT-1 的思想是先在无标注语料上预训练语言模型,再用预训练模型在有标注语料对下游任务进行微调
GPT-1\text{GPT-1}GPT-1 首先要解决的问题是:怎么预训练语言模型
注意这里的语言模型是单向的,简单来说就是要模型根据之前若干个词语预测未来下一个词语是什么
形式化表达如下:
给定一个无标注语料库 U\mathcal{U}U,其中包含文本序列 {u1,...,un}\{u_1, ..., u_n\}{u1,...,un}
语言模型的优化目标是最大化下面的似然函数,其中 kkk 表示上下文窗口大小
L1(U)=∑ilogP(ui∣ui−k,...,ui−1)L_1(\mathcal{U}) = \sum_{i}{\log P(u_i|u_{i-k},...,u_{i-1})} L1(U)=i∑logP(ui∣ui−k,...,ui−1)
GPT-1\text{GPT-1}GPT-1 面临的下一个问题是:怎么对下游任务微调
所谓微调就是用预训练模型在有标注的数据集对特定下游任务继续训练,使得模型能够适应不同下游任务
形式化表达如下:
给定一个有标注数据集 C\mathcal{C}C,其中每个样本包含文本序列 {x1,...,xm}\{x^1,...,x^m\}{x1,...,xm} 和对应的标签 yyy
微调模型的优化目标是最大化下面的似然函数
L2(C)=∑(x,y)logP(y∣x1,...,xm)L_2(\mathcal{C}) = \sum_{(x,\ y)}{\log P(y|x^1,...,x^m)} L2(C)=(x, y)∑logP(y∣x1,...,xm)
具体来说,我们会将预训练模型用于初始化,并在此基础上添加一个额外的线性预测层用于标签预测
输入序列经过预训练模型后,取序列最后一个单词在模型最后一层输出作为整个序列的特征向量表示
最终这个特征向量经过新增的线性预测层后,就能得到预测标签的概率分布
在对下游任务进行微调时,将语言模型作为辅助目标,有利于加速模型收敛,并且提高模型的泛化性
因此,最终微调目标如下,其中 λ\lambdaλ 为任务的权重系数
L3(C)=L2(C)+λL1(C)L_3(\mathcal{C}) = L_2(\mathcal{C}) + \lambda L_1(\mathcal{C}) L3(C)=L2(C)+λL1(C)
上述介绍的微调过程可直接用于分类任务,以此类推,我们可以拓展出如何处理其他类型的下游任务
核心是将不同类型下游任务的输入都转化为文本序列,下面是几个例子:
文本分类:给定单个句子,判断这个句子的类别
解决方法:将这个句子直接输入预训练模型得到特征向量,然后经过预测层做分类
文本蕴涵:给定两个句子,判断两个句子的关系
解决方法:将两个句子拼接输入预训练模型得到特征向量,然后经过预测层做分类
语义相似:给定两个句子,计算两个句子相似度
解决方法:将两个句子前和后两次拼接输入预训练模型得到两个特征向量
将两个特征向量逐元素相加后经过预测层计算相似度得分
多项选择:给定问题和多个选项,选择一个作为正确答案
解决方法:将问题和多个选项分别拼接输入预训练模型得到多个特征向量
将多个特征向量分别经过预测层计算匹配得分后选最大值作为答案
文本序列在输入模型微调前都需要添加特殊标记,GPT-1\text{GPT-1}GPT-1 中使用的特殊标记如下:
添加在输入文本开头
添加在输入文本结尾$
添加在拼接文本之间下面是示意图:
关键词:零样本任务、多任务学习
GPT-1\text{GPT-1}GPT-1 已具备很强大的能力,但仍存在着一些不足,其中最重要的一点是:下游任务不能泛化
GPT-1\text{GPT-1}GPT-1 面对不同的下游任务,都需要重新进行训练,这要求各种下游任务要有对应的标注数据
从另一方面来说,GPT-1\text{GPT-1}GPT-1 出现后不久,BERT\text{BERT}BERT 以更多的数据、更大的模型全面超越了 GPT-1\text{GPT-1}GPT-1
而为了进行反击,GPT-2\text{GPT-2}GPT-2 增加了数据和模型规模,并尝试解决更困难的零样本任务 (zero-shot\text{zero-shot}zero-shot)
GPT-2\text{GPT-2}GPT-2 的模型结构与之前相比基本没变,只是做了一些细微的修改
概括来说,层正则化被移动到子模块之前,另有一个新的层正则化被添加到最后的自注意力模块之后
GPT-2\text{GPT-2}GPT-2 并没有对模型结构有太大的创新,其最重要的改变在于使用了更大的数据和更多的模型参数
具体来说,作者设计了以下几种模型大小:
模型总参数 | 解码层数量 | 隐藏层维度 | 备注 |
---|---|---|---|
117M117\ \text{M}117 M | 121212 | 768768768 | 与 BERTBASE\text{BERT}_\text{BASE}BERTBASE、GPT-1\text{GPT-1}GPT-1 对标 |
345M345\ \text{M}345 M | 242424 | 102410241024 | 与 BERTLARGE\text{BERT}_\text{LARGE}BERTLARGE 对标 |
762M762\ \text{M}762 M | 363636 | 128012801280 | 无 |
1542M1542\ \text{M}1542 M | 484848 | 160016001600 | 称 GPT-2\text{GPT-2}GPT-2 |
GPT-2\text{GPT-2}GPT-2 的思想是在大规模无标注语料上预训练语言模型,然后直接把预训练模型应用到各种下游任务
这样的任务设定可以进一步引申为零样本任务
所谓零样本任务就是说预训练模型对下游任务进行预测时,不提供下游任务的训练样本,而是直接开始预测
这意味着预训练模型需要有泛化性,不用经过微调就可以应对各种下游任务
怎么使得预训练模型具备此能力呢?要从预训练过程入手,作者引入多任务学习的概念
所谓多任务学习就是说训练模型时,用多个任务的数据集,并且保证模型在各个任务的数据集上都可以收敛
说回模型,GPT-2\text{GPT-2}GPT-2 还是将单向语言模型作为预训练任务,其训练方式还是和 GPT-1\text{GPT-1}GPT-1 一样
不同在于,GPT-2\text{GPT-2}GPT-2 会在更大型更丰富的语料上进行训练,可以认为这些语料隐式包含翻译、问答等的任务
与之对应,GPT-2\text{GPT-2}GPT-2 的模型参数也增加了许多,使其能拥有更强大的学习能力
作者认为,当语言模型的容量足够大且训练语料的数量足够多时,通过无监督训练就能覆盖所有有监督任务
换句话说,所有有监督任务都是语言模型的子集,直观的解释是:理想的语言模型能真正理解语言
最后还有一点,预训练模型进行预测时,是不能出现特殊标记的,因为模型无法理解预训练时没见过的东西
模型在预测时的输入应该是纯自然语言,用于明确待做任务类型,这在后来的文献中称为提示 ( prompt\text{prompt}prompt )
下面是示意图,GPT-2\text{GPT-2}GPT-2 所做的事情是:根据提示和输入来续写,从而完成各种下游任务
关键词:少样本任务、上下文学习
从 GPT-2\text{GPT-2}GPT-2 的实验结果能看到:随着模型参数和训练语料的增加,模型的性能会不断提升
故 GPT-3\text{GPT-3}GPT-3 继续沿着原有道路,进一步增加模型参数和训练语料,在大规模预训练模型上越走越远
另一方面,GPT-3\text{GPT-3}GPT-3 不再追求零样本任务,而是转为少样本任务 (few-shot\text{few-shot}few-shot)
这与人类的习惯更加相似,具有一定知识的人类,在给出若干个例子后,就可以学会某种能力
GPT-3\text{GPT-3}GPT-3 的模型结构与之前相比基本没变, 只是做了一些细微的修改
概括来说,就是用 Sparse Transformer\text{Sparse Transformer}Sparse Transformer 中提出的稀疏自注意力机制代替原有的自注意力机制
GPT-3\text{GPT-3}GPT-3 也没有对模型结构有太大的创新,最重要的改变在于使用了更大的数据和更多的模型参数
具体来说,作者设计了以下几种模型大小:
名称 | 模型总参数 | 解码层数量 | 隐藏层维度 |
---|---|---|---|
GPT-3 Small\text{GPT-3 Small}GPT-3 Small | 125M125\ \text{M}125 M | 121212 | 768768768 |
GPT-3 Medium\text{GPT-3 Medium}GPT-3 Medium | 350M350\ \text{M}350 M | 242424 | 102410241024 |
GPT-3 Large\text{GPT-3 Large}GPT-3 Large | 760M760\ \text{M}760 M | 242424 | 153615361536 |
GPT-3 XL\text{GPT-3 XL}GPT-3 XL | 1.3B1.3\ \text{B}1.3 B | 242424 | 204820482048 |
GPT-3 2.7B\text{GPT-3 2.7B}GPT-3 2.7B | 2.7B2.7\ \text{B}2.7 B | 323232 | 256025602560 |
GPT-3 6.7B\text{GPT-3 6.7B}GPT-3 6.7B | 6.7B6.7\ \text{B}6.7 B | 323232 | 409640964096 |
GPT-3 13B\text{GPT-3 13B}GPT-3 13B | 13B13\ \text{B}13 B | 404040 | 514051405140 |
GPT-3 175B\text{GPT-3 175B}GPT-3 175B / GPT-3\text{GPT-3}GPT-3 | 175B175\ \text{B}175 B | 969696 | 122881228812288 |
GPT-3\text{GPT-3}GPT-3 的思想是在大规模无标注语料上预训练语言模型,然后直接把预训练模型应用到各种下游任务
这和 GPT-2\text{GPT-2}GPT-2 是一样的,不同之处在于 :GPT-2\text{GPT-2}GPT-2 设定为零样本任务,GPT-3\text{GPT-3}GPT-3 设定为少样本任务
所谓少样本任务就是说预训练模型对下游任务进行预测时,只提供极少量的任务样本供预训练模型使用
通常 GPT-3\text{GPT-3}GPT-3 不会将这些极少量的任务样本用于微调模型,而是会将其当作提示输入到预训练模型
这是因为对参数量如此大的模型进行微调也是非常困难的
这样在预测时将任务样本放到提示中给模型来学习的行为,可以称作是上下文学习 (in-context learning\text{in-context learning}in-context learning)
简单来说就是预训练模型需要在提示的上下文语境中学习要完成的下游任务
因此对于使用者来说,使用预训练模型完成下游任务最需要考虑的事情就是:如何设计提示
下面是一个例子:
GPT-3\text{GPT-3}GPT-3 的最终效果虽然已十分惊艳,但仍不可避免地存在一些局限性,下面来简单讨论下:
好啦,本文到此结束,感谢您的阅读!
如果你觉得这篇文章有需要修改完善的地方,欢迎在评论区留下你宝贵的意见或者建议
如果你觉得这篇文章还不错的话,欢迎点赞、收藏、关注,你的支持是对我最大的鼓励 (/ω\)
上一篇:【C++学习笔记】:代码风格