2022年年底OpenAI发布ChatGPT,将LLM(Large Language Model)带向了一个新的高度,而2023年OpenAI继续放出大招:更强大的GPT-4问世,瞬间引爆了整个互联网圈。在这个大模型时代,作为一名NLPer,持续吸收着层出不穷的新技术,确实有些吃不消。俗话说,好记性不如烂笔头,在此记录下LLM相关技术及进展。顺便说一句,你可以说它不全面,但不能说它不通俗易懂。
虽然本篇博客的主要目的是介绍包括GPT系列在内的各种LLM的架构,但是在介绍LLM之前,我们有必要了解下Tuning(微调)的发展历程,它们推动着LLM朝向更智能的方向发展。
目前学术界一般将NLP任务的发展分为四个阶段,即NLP四范式:
在整个NLP领域,你会发现整个发展是朝着精度更高、少监督,甚至无监督的方向发展的。下面我们对第三范式、第四范式进行详细介绍。
Fine-Tuning是一种迁移学习,在自然语言处理(NLP)中,Fine-Tuning是用于将预训练的语言模型适应于特定任务或领域。Fine-Tuning的基本思想是采用已经在大量文本上进行训练的预训练语言模型,然后在小规模的任务特定文本上继续训练它。
Fine-tuning的概念已经存在很多年,并在各种背景下被使用。Fine-tuning在NLP中最早的已知应用是在神经机器翻译(NMT)的背景下,其中研究人员使用预训练的神经网络来初始化一个更小的网络的权重,然后对其进行了特定的翻译任务的微调。
经典的fine-tuning方法包括将预训练模型与少量特定任务数据一起继续训练。在这个过程中,预训练模型的权重被更新,以更好地适应任务。所需的fine-tuning量取决于预训练语料库和任务特定语料库之间的相似性。如果两者相似,可能只需要少量的fine-tuning。如果两者不相似,则可能需要更多的fine-tuning。
Bert模型2018年横空出世之后,将fine-tuning推向了新的高度。不过目前来看,Fine-Tuning逐渐退出了tuning研究的舞台中心:LLM蓬勃发展,Fine-Tuning这种大规模更新参数的范式属实无法站稳脚跟。而更适应于LLM的tuning范式,便是接下来我们要介绍的Prompt-Tuning、Instruction-Tuning等。
Fine-Tuning这块需要介绍的不多,不过fine-tuning的基座——PLM,比如Bert、Transformer,我们还是有必要了解的,这里给出两篇写的比较全面的博客,供大家参考:This post is all you need(上卷)——层层剥开Transformer、This post is all you need(下卷)——步步走进BERT
在介绍prompt-tuning之前,我们有必要认识下in-context learning,prompt-tuning和in-context learning是prompt learning的两种模式。In-context learning是指在大规模预训练模型上进行推理时,不需要提前在下游目标任务上进行微调,即不改变预训练模型参数就可实现推理,其认为超大规模的模型只要配合好合适的模板就可以极大化地发挥其推理和理解能力。常用的in-context learning方法有few-shot、one-shot、zero-shot;prompt-tuning是指在下游目标任务上进行推理前,需要对全部或者部分参数进行更新,这里全部/部分的区别就在于预训练模型参数是否改变(其实本质上的prompt-tuning是不更新预训练模型参数的,这里有个特例方法称为Prompt-Oriented Fine-Tuning,其实该方法更适合称为升级版的fine-tuning,后面会详细介绍这个方法)。无论是in-context learning还是prompt-tuning,它们的目标都是将下游任务转换为预训练模型的预训练任务,以此来广泛激发出预训练模型中的知识。总的来说,基于fine-tuning的方法是让预训练模型去迁就下游任务。而基于prompt-tuning的方法可以让下游任务去迁就预训练模型。
我们先以二分类的情感分析作为例子,描述prompt-tuning的工作原理。给定一个句子[CLS] I like the Disney films very much. [SEP] ,传统的Fine-tuning方法是将其通过Bert获得 [CLS]表征之后再喂入新增加的MLP分类器进行二分类,预测该句子是积极的(positive)还是消极的(negative),因此需要一定量的训练数据来训练。而Prompt-Tuning则执行如下步骤:
基于上述内容,我们对prompt learning有个初步了解,接下来我们详细介绍下两种prompt learning:In-context learning和prompt-tuning。
In-context learning(ICL)又称为上下文学习,最早是在GPT-3《Language Models are Few-Shot Learners》中被提出来的。In-context learning(ICL)的关键思想是从类比中学习。下图给出了一个描述语言模型如何使用ICL进行决策的例子。首先,ICL需要一些示例来形成一个演示上下文。这些示例通常是用自然语言模板编写的。然后ICL将查询的问题(即你需要预测标签的input)和一个上下文演示(一些相关的cases)连接在一起,形成带有提示的输入(可称之为prompt),并将其输入到语言模型中进行预测。值得注意的是,与需要使用反向梯度更新模型参数的训练阶段的监督学习不同,ICL不需要参数更新,并直接对预先训练好的语言模型进行预测(这是与prompt-tuning不同的地方,ICL不需要在下游任务中prompt-tuning或fine-tuning)。它希望模型能自动学习隐藏在演示中的模式,并据此做出正确的预测。
Training:在推理前,通过持续学习让语言模型的ICL能力得到进一步提升,这个过程称之为model warmup(模型预热),model warmup会优化语言模型对应参数或者新增参数,区别于传统的fine-tuning,fine-tuning旨在提升LLM在特定任务上的表现,而model warmup则是提升模型整体的ICL性能。
Supervised in-context training跟self-supervised in-context training旨在通过引入更加接近于in-context learning的训练目标从而缩小预训练跟ICL之间的差距。比起需要示例的in-context learning,只涉及任务描述的instruction tuning更加简单且受欢迎。另外,在model warmup这个阶段,语言模型只需要从少量数据训练就能明显提升ICL能力,不断增加相关数据并不能带来ICL能力的持续提升。从某种角度上看,这些方法通过更加模型参数可以提升ICL能力也表明了原始的LLM具备这种潜力。虽然ICL不要求model warmup,但是一般推荐在推理前增加一个model warmup过程(解释一下:ICL最初的含义指的是大规模语言模型涌现出一种能力:不需要更新模型参数,仅仅修改输入prompt即添加一些例子就可以提升模型的学习能力。ICL相比之前需要对模型在某个特定下游任务进行fine-tuning大大节省了成本。之后ICL问题演变成研究怎么提升模型以具备更好更通用的ICL能力,这里就可以用上之前fine-tuning的方式,即指model warmup阶段对模型更新参数。)。
Inference:很多研究表明LLM的ICL性能严重依赖于演示示例的格式,以及示例顺序等等,在使用目前很多LLM模型时我们也会发现,在推理时,同一个问题如果加上不同的示例,可能会得到不同的模型生成结果。
无监督方法:首先就是根据句向量距离或者互信息等方式选择跟当前输入x最相似的样本作为演示示例,另外还有利用自使用方法去选择最佳的示例排列,有的方法还会考虑到演示示例的泛化能力,尽可能去提高示例的多样性。除了上述这些从人工撰写的样本中选择示例的方式外,还可以利用语言模型自身去生成合适的演示示例。
监督方法:第一种是先利用无监督检索器召回若干相似的样本,再通过监督学习训练的Efficient Prompt Retriever进行打分,从而筛选出最合适的样本。此外还有基于prompt tuning跟强化学习的方式去选择样本。
以上内容对In-context learning做了简单介绍,详细内容大家可参考论文《A Survey on In-context Learning》、A Survey for In-context Learning翻译。对于In-context learning及后面会讲到的instruction-tuning方法来说,如何设计输入的prompt是很重要的一点,有关prompt设计的方法,除了上面讲到的内容,这里还有一篇写的很好的文章供大家参考[译] Prompt Engineering: 循循善诱。
不过以GPT-3为首的这类方法有一个明显的缺陷是——其建立在超大规模的预训练语言模型上,此时的模型参数数量通常超过100亿,在真实场景中很难应用,因此众多研究者开始探索GPT-3的这套思路在小规模的语言模型(如Bert)上还是否适用?事实上,这套方法在小规模的语言模型上是可行的,但是需要注意:
在介绍prompt-tuning之前,我们先介绍下实现prompt-tuning的重要组件——Pattern-Verbalizer-Pair(PVP)。Pattern-Verbalizer-Pair模式来源于大名鼎鼎的PET模型,PET(Pattern-Exploiting Training)出自《Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference》。这里先简单介绍下论文的核心内容(下面蓝色字体内容可选择性学习,不感兴趣可直接跳到后面的PVP介绍部分):由于在实际任务中,模型往往只会接触到少量的labeled examples(few-shot learning),而直接将监督学习运用到小样本学习会使得模型表现不佳,针对这个问题,论文中提出了Pattern-Exploiting Training (PET),使用natural language patterns将input examples规范为完型填空形式的半监督训练机制。通过这种方法,成功地在few-shot settings上将task descriptions与标准监督学习结合。具体的步骤是:
另外在该论文中,作者提出,在每一个PLM上只进行一次微调+soft labels生成,通常得到的新的数据集(即用soft labels标记的unlabeled dataset)会有很多错误的数据,因此扩展提出iPET模型(Iterative PET),即添加了迭代过程:首先随机从集成的预训练模型集合中抽取部分预训练模型,在未标注数据集(unlabeled dataset) D \mathcal{D} D 上标注数据,并扩增到初始有标签数据集 T \mathcal{T} T上,其次再根据扩增后的 T \mathcal{T} T分别微调预训练模型。上述过程一直迭代多次:
上述内容具体可参考:论文解读:Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference、论文阅读:PET系列。论文中有关PET及IPET部分的介绍,不是本部分关心的重点,大家选择性学习。下面着重介绍本部分最关心,也是PET最核心的部分Pattern-Verbalizer-Pair(PVP),PET设计了两个很重要的组件:
通过上个部分的介绍,我们已经了解,prompt-tuning是用来自动构建pattern的方法,接下来我们根据使用场景的不同,分别介绍几种成熟的prompt-tuning方法。
Prompt-Oriented Fine-Tuning:这个就是前面提到的需要更新全部参数(包括预训练模型参数)的prompt-tuning方法。Prompt-Oriented Fine-Tuning训练方法的本质,上面已经提到过,其实是将目标任务转换为适应预训练模型的预训练任务,以适应预训练模型的学习体系。例如我们在Bert模型上做情感分类任务,正常的fine-tuning流程,是将训练文本经过bert编码后,生成向量表征,再利用该向量表征,连接全连接层,实现最终的情感类别识别。这种方式存在一个显式的弊端:预训练任务与下游任务存在gap,我们知道Bert的预训练任务包括两个:MLM与NSP(具体可参考BERT预训练的任务MLM和NSP),简单来说,MLM任务是通过分类模型识别被MASK掉的词,类别大小即为整个词表大小;NSP任务是预测两个句子之间的关系;而Prompt-Oriented Fine-Tuning训练方法,是将情感分类任务转换为类似于MLM任务的[MASK]预测任务,具体来说,我们构建如下的prompt文本:prompt = It was [MASK].
,将prompt文本与输入text文本text = The film is attractive.
进行拼接生成It was [MASK].The film is attractive.
,输入至预训练模型中,训练任务目标和MLM任务的目标一致,即识别被[MASK]掉的词。通过这种方式,可以将下游任务转换为和预训练任务较为一致的任务,已有实验证明,Prompt-Oriented Fine-Tuning相对于常规的Fine-Tuning,效果确实会得到提升(Prompt进行情感分类)。
通过以上描述我们可以知道,Prompt-Oriented Fine-Tuning方法中,预训练模型参数是可变的。其实将Prompt-Oriented Fine-Tuning方法放在Prompt-Tuning这个部分合理也不合理,因为它其实是prompt-tuning+fine-tuning的结合体,将它视为fine-tuning的升级版是最合适的。Prompt-Oriented Fine-Tuning方法在Bert类相对较小的模型上表现较好,但是随着模型越来越大,如果每次针对下游任务,都需要更新预训练模型的参数,资源成本及时间成本都会很高,因此后续陆续提出了不更新预训练模型参数,单纯只针对prompt进行调优的方法,例如Hard Prompt和Soft Prompt。
这里再给出一些常见下游任务的prompt设计:
Hard Prompt & Soft Prompt:承接上文,Hard Prompt和Soft Prompt的提出,是为了解决预训练模型过大,难以针对下游任务进行训练的痛点。目前常见的Hard Prompt和Soft Prompt方法,分为以下五种:
Hard Prompt:前面三种称为离散的模板构建法(记作Hard Template、Hard Prompt、Discrete Template、Discrete Prompt),其旨在直接与原始文本拼接显式离散的字符,且在训练中始终保持不变。这里的保持不变是指这些离散字符的词向量(Word Embedding)在训练过程中保持固定。通常情况下,离散法不需要引入任何参数。主要适用场景是GPT-3类相对较大的模型,Bert类相对较小的模型也可以用,只是个人觉得Bert等预训练模型,针对下游任务训练的成本并不是很高,完全可以同时微调预训练模型参数。上述三种Hard Prompt方法,实际场景中用的比较少,这里就不一一介绍了,大家有兴趣可以参考Prompt-Tuning——深度解读一种新的微调范式。
Soft Prompt:后面两种则被称为连续的模板构建法(记作Soft Template、Soft Prompt、Continuous Template、Continuous Prompt),其旨在让模型在训练过程中根据具体的上下文语义和任务目标对模板参数进行连续可调。反观Hard Prompt方法,不论是启发式方法,还是通过生成的方法,都需要为每一个任务单独设计对应的模板,因为这些模板都是可读的离散的token,这导致很难寻找到最佳的模板。另外,即便是同一个任务,不同的句子也会有其所谓最佳的模板,而且有时候,即便是人类理解的相似的模板,也会对模型预测结果产生很大差异。例如下图,以SNLI推断任务为例,仅仅只是修改了模板,测试结果差异很明显,因此离散的模板存在方差大、不稳定等问题。
如何避免这种问题呢,Soft Prompt方法便是来解决这种问题的,其将模板转换为可以进行优化的连续向量,换句话说,我们不需要显式地指定这些模板中各个token具体是什么,而只需要在语义空间中表示一个向量即可,这样,不同的任务、数据可以自适应地在语义空间中寻找若干合适的向量,来代表模板中的每一个词,相较于显式的token,这类token称为伪标记(Pseudo Token)。下面给出基于Soft Prompt的模板定义:
假设针对分类任务,给定一个输入句子 x x x,连续提示的模板可以定义为:
T = [ x ] , [ v 1 ] , [ v 2 ] , … , v m ] [ M A S K ] \mathcal{T} =[x],[v_{1}],[v_{2}],…,v_{m}][MASK]\ T=[x],[v1],[v2],…,vm][MASK] 其中 [ v 1 ] [v_{1}] [v1]则是伪标记,其仅代表一个抽象的token,并没有实际的含义,本质上是一个向量。
总结来说:Soft Prompt方法,是将模板变为可训练的参数,不同的样本可以在连续的向量空间中寻找合适的伪标记,同时也增加模型的泛化能力。因此,连续法需要引入少量的参数并在训练时进行参数更新,但预训练模型参数是不变的,变的是prompt token对应的词向量(Word Embedding)表征及其他引入的少量参数。主要适用场景同Hard Prompt一致。目前具有代表性的三种Soft Prompt方法如下,下面我们进行逐一介绍:
Parameter-Efficient Prompt Tuning:该方法率先提出了伪标记和连续提示的概念,支持模型能够动态地对模板在语义空间内进行调整。主要针对的是NLU任务,形式化的描述如下:
给定 n n n个token,记作 x 1 , . . . , x n x_{1}, ..., x_{n} x1,...,xn,通过一个预训练模型对应的embedding table,将 n n n个token表征为向量矩阵 X e ∈ R n × e X_{e} \in R^{n\times e} Xe∈Rn×e,其中 e e e是向量的维度(其与预训练模型的配置有关,例如Bert-base是768)。连续模板中的每个伪标记 v i v_{i} vi可以视为参数,也可以视为一个token,因此,可以通过另一个embedding table将 p p p个伪标记token表征为向量矩阵 P e ∈ R p × e P_{e} \in R^{p\times e} Pe∈Rp×e 。将文本和prompt进行拼接获得新的输入 [ P e : X e ] ∈ R ( p + n ) × e [P_{e} :X_{e}] \in R^{(p+n) \times e} [Pe:Xe]∈R(p+n)×e。这个新的输入将会进入T5的encoder-decoder结构来训练和推理。注意,只有prompt对应的向量表征参数 P e P_{e} Pe会随着训练进行更新。
论文中提到,每个伪标记的初始化可以有下列三种情况,分别是Random Uniform,Sampled Vocab和Class Label。
最后发现,非随机初始化方法要显著好于随机初始化,而Class Label效果相对更好,当然,只要模型足够大,哪种初始化方法的差异就比较小了。具体论文参考2021年谷歌发表的《The Power of Scale for Parameter-Efficient Prompt Tuning》。
P-Tuning:P-Tuning是另一个具有代表性的连续提示方法,主要针对的是NLU任务,方法图如下所示(图中的 P i P_{i} Pi等价于上文的 v i v_{i} vi,表示伪标记),谷歌于2021年发表。
P-Tuning方法中的四个技巧点:
具体可参考:2021年发表的《GPT Understands, Too》、《论文解读:GPT Understands, Too》、《细读经典:P-tuning》
PPT(Pre-trained Prompt Tuning):Prompt-Tuning通常适用于低资源场景,但是由于连续的模板是随机初始化的,即其存在新的参数,少量样本可能依然很难确保这些模板被很好地优化。因此简单的方法就是对这些连续的模板进行预训练。PPT旨在通过先让这些连续提示在大量无标注的预训练语料进行预训练,然后将其加载到对应下游任务的PLM上进行训练。具体来说,作者对3种prompt tuning的优化策略在few-shot learning问题上分别进行了效果对比,包括hard prompt和soft prompt结合、label到text映射方法选择以及使用真实单词的embedding进行soft prompt的随机初始化。通过对比实验发现,hard+soft prompt结合的方法可以提升效果,但是仍然比finetune效果差。Label到text的映射方法对于效果影响很大,选择能够表达label对应含义的常用单词会带来最好效果。而使用单词embedding进行soft prompt的初始化在大模型上并没有明显的效果提升。
基于以上实验结果,作者提出了Pre-trained Pormpt Tuning解决few-shot learning问题,核心思路是对soft prompt进行预训练,得到一个更好的soft prompt初始化表示。对于每种类型的任务,设计一个和其匹配的预训练任务,得到soft prompt embedding的预训练表示。
论文中以sentence-pair classification、multiple-choice classification、single sentence classification三种任务介绍了如何针对每种下游任务设计预训练任务学习soft prompt embedding。例如对于sentence-pair classification,作者设计了如下预训练任务。将2个句子对拼接在一起,如果两个句子来自同一个文档相邻两句话,则label为yes(完全一致);如果两个句子来自同一个文档但距离较远,则label为maybe;其他句子对label为no,如下图所示(图中的 P P P即连续的提示模板, < x >
另外论文中还给出了四种微调方案,如下图所示,[a]展示了模型的预训练过程,[b]和[c]展示了两种主流的Fine-Tuning方法(前文已经介绍过),[d]展示了提示学习( Prompt Tuning, PT )方法,具体可参考2022年清华大学发表的《PPT: Pre-trained Prompt Tuning for Few-shot Learning》、小样本学习:Pre-trained Prompt Tuning for Few-shot Learning,Prompt 如何更好地应用于工业界?。
至此,我们已经深入了解了fine-tuning和prompt-tuning两种微调方法,也或多或少能观察到二者之间的区别,我们在这里进行下总结。众多周知,prompt-tuning是在fine-tuning后发展起来的,可以说是解决NLP领域各种下游问题更好的一种方式要提出一个好的方式那必然是用来「解决另一种方式存在的缺陷或不足」,那我们就先从预训练模型PLM+fine-tuning范式说起,这个范式常用的结构是Bert+fine-tuning,这种范式若想要预训练模型更好的应用在下游任务,需要利用下游数据对模型参数微调;首先,模型在预训练的时候,采用的训练形式:自回归、自编码,这与下游任务形式存在极大的 gap,不能完全发挥预训练模型本身的能力,必然导致:较多的数据来适应新的任务形式(少样本学习能力差、容易过拟合)。其次,现在的预训练模型参数量越来越大,为了一个特定的任务去fine-tuning一个模型,会占用特别多的训练资源,对一些中小企业或者用户来说并不现实,也会造成资源的一定浪费。
而prompt-tuning则很好的解决了这些问题,它将所有下游任务统一成预训练任务,以特定的模板,将下游任务的数据转成自然语言形式,充分挖掘预训练模型本身的能力。本质上就是设计一个比较契合上游预训练任务的模板,通过模板的设计来挖掘出上游预训练模型的潜力,让上游的预训练模型在尽量不需要标注数据的情况下比较好的完成下游的任务,即只需要少量数据的 Prompt Tuning,就可以实现很好的效果,具有较强的零样本/少样本学习能力。具体可参考Prompt-Tuning VS Fine-Tuning。
前文中已经多次提到过instruction-tuning,可以说在大规模语言模型领域,它是目前最火的研究范式,性能超过包括in-context learning在内的prompt learning。
回顾instruction-tuning的发展历程,首先是Google2021年的FLAN模型《FINETUNED LANGUAGE MODELS ARE ZERO-SHOT LEARNERS》,这篇文章明确提出instruction-tuning(指令微调)的技术,它的本质目的是想将 NLP 任务转换为自然语言指令,再将其投入模型进行训练,通过给模型提供指令和选项的方式,使其能够提升Zero-Shot任务的性能表现。
Instruction-Tuning提出的动机在于大规模的语言模型如GPT-3可以非常好地学习few-shot,但它在zero-shot上却不那么成功。例如, GPT-3在阅读理解、问题回答和自然语言推理等任务上的表现很一般,作者认为一个潜在的原因是,如果在没有少量示例的zero-shot条件下,模型很难在prompts上表现很好,因为prompts可能和预训练数据的格式相差很大。
既然如此,那么为什么不直接用自然语言指令做输入呢?通过设计instruction,让大规模语言模型理解指令,进而完成任务目标,而不是直接依据演示实例做文本生成。如下图所示,不管是commonsense reasoning任务还是machine translation任务,都可以变为instruction的形式,然后利用大模型进行学习。在这种方式下,当一个unseen task进入时,通过理解其自然语言语义可以轻松实现zero-shot的扩展,如natural language inference任务。
接下来,我们介绍下FLAN的具体训练流程。
具体来说,作者提出的Finetuned Language Net(FLAN)模型将62个NLP任务分为12个簇,同一个簇内是相同的任务类型,如下图所示。
对于每个task,将为其手动构建10个独特template,作为以自然语言描述该任务的instructions。为了增加多样性,对于每个数据集,还包括最多三个“turned the task around/变更任务”的模板(例如,对于情感分类,要求其生成电影评论的模板)。所有数据集的混合将用于后续预训练语言模型做instruction-tuning,其中每个数据集的template都是随机选取的。如下图所示,Premise、Hypothesis、Options会被填充到不同的template中作为训练数据。
最后基于LaMDA-PT模型进行微调。LaMDA-PT是一个包含137B参数的自回归语言模型,这个模型在web文档(包括代码)、对话数据和维基百科上进行了预训练,同时有大约10%的数据是非英语数据。然后FLAN混合了所有构造的数据集在128核的TPUv3芯片上微调了60个小时。
至此,我们详细介绍了包括FLAN在内的instruction-tuning方法,总结来说,instruction-tuning也是in-context-learning的一种,只是instruction-tuning是将大模型在多种任务上进行微调,提升大模型的自然语言理解能力,最终实现在新任务上的zero-shot。目前另外一个采用了instruction-tuning技术的大规模语言模型是instructGPT,后面我们会详细介绍instructGPT的具体实现方式。
Fine-Tuning:先在大规模语料上进行预训练,然后再在某个下游任务上进行微调,如Bert+Fine-Tuning;
Prompt-Tuning:先选择某个通用的大规模预训练模型,然后为具体的任务生成一个prompt模板以适应大模型进行微调,如GPT-3+Prompt-Tuning;
Instruction-Tuning:仍然在预训练语言模型的基础上,先在多个已知任务上进行指令微调,然后在某个新任务上进行zero-shot,如GPT-3+Instruction-Tuning;
Prompt-Tuning vs Instruction-Tuning:Prompt和instruction都是指导语言模型生成输出的文本片段,但它们有着不同的含义和用途。
因此,Prompt和instruction都是用于指导模型生成输出的文本,但它们的目的和使用方式是不同的。Prompt更多地用于帮助模型理解任务和上下文,而Instruction则更多地用于指导模型执行具体操作或完成任务。
对于prompt-tuning和instruction-tuning还有一个不同点,就是prompt在没精调的模型上也能有一定效果(模型不经过prompt-tuning,直接针对下游任务进行推理),而instruction-tuning则必须对模型精调,让模型知道这种指令模式。但是,prompt也有精调,经过prompt-tuning之后,模型也就学习到了这个prompt模式,精调之后跟instruction-tuning有什么区别呢?这就是instruction-tuning巧妙的地方了,prompt-tuning都是针对一个任务的,比如做个情感分析任务的prompt-tuning,精调完的模型只能用于情感分析任务,而经过instruction-tuning多任务精调后,可以用于其他任务的zero-shot。
这里聊一聊自己的见解,两者的对比主要是基于大模型。Prompt是通过对任务进行一定的描述,或者给一些示例(ICL),来完成既定任务目标,但是如果不给模型示例(zero-shot),prompt表现的很一般,这怎么办呢?能不能让大模型理解任务是做什么的,这样不用示例也能完成任务目标,instruction就是来做这个任务的,它为了让模型具备理解任务的能力,采用大量的指令数据,对模型进行微调,即instruction-tuning。因此,instruction和prompt的不同之处在于:instruction是在prompt的基础上,进一步挖掘模型理解任务的能力。(仅供参考)
随着LLM的越来越大,以及tuning技术的快速发展,LLM在包括情感分析在内的传统自然语言任务上表现越来越好,但是单纯的扩大LLM模型的参数量无法让模型在算术推理/常识推理/符号推理等推理任务上取得理想的效果。 如何提升LLM在这些推理任务上性能呢?在此前关于LLM的推理任务中,有两种方法:
但是这两种方法都有着局限性,前者微调计算成本太高,后者采用传统的输入输出样例在推理任务上效果很差,而且不会随着语言模型规模的增加而有实质性的改善。此时,Chain-of-Thought应运而生。下面我们根据三篇比较有代表性的论文,详细介绍CoT的发展历程。
Manual-CoT是Chain-of-Thought技术的开山之作,由Google在2022年初提出《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》。其旨在进一步提高超大规模模型在一些复杂任务上的推理能力。其认为现有的超大规模语言模型可能存在下面潜在的问题:
针对这些问题,作者提出了chain of thought (CoT)这种方法来利用大语言模型求解推理任务。
下面这个例子可以很好的说明思维链到底在做什么。左图是传统的one-shot prompting,就是拼接一个例子在query的前面。右图则是CoT的改进,就是将example中的Answer部分的一系列的推理步骤(人工构建)写出来后,再给出最终答案。逻辑就是希望模型学会一步一步的输出推理步骤,然后给出结果。
论文中首先在算数推理(arithmetic reasoning)领域做了实验,使用了5个数学算术推理数据集:GSM8K / SVAMP / ASDiv / AQuA / MAWPS,具体的实验过程这里不再赘述,感兴趣的同学可以直接参考论文,这里直接给出实验结论(如下图):
除此之外,论文中为了证明CoT的有效性,相继做了消融实验(Ablation Study)、鲁棒性实验( Robustness of Chain of Thought)、常识推理(Commonsense Reasoning)实验、符号推理(Symbolic Reasoning)实验,下面分别做以简单介绍:
消融实验:我们知道,消融实验是通过研究移除某个组件之后的性能,证明该组件的有效性。论文中通过引入CoT的三个变种,证明CoT的有效性,结果如下图所示:
鲁棒性实验:论文中通过annotators(标注者),exemplars(样例选择)和models(模型)三个方面对CoT进行了鲁棒性分析。如下图所示,总体结论是思维链普遍有效,但是不同的CoT构建方式/exemplars的选择/exemplars的数量/exemplars的顺序,在一定程度上影响着CoT的效果。
关于鲁棒性实验,论文中最后指出:Prompt Engineering仍然很重要,不同的prompt(CoT)的设计/数量/顺序都会对模型产生不同的影响,且方差还是很大的。 因此未来的一个方向可能是探索一种能够获取稳健CoT(Prompts)的范式。 或许可以用一个LLM自动生成CoT用于Prompting,后面我们将介绍这种技术:Auto-CoT。
常识推理实验 & 符号推理实验:此处我们不做过多介绍,这里给出三种推理模式的exemplars示例(绿色:算数推理,橙色:常识推理,蓝色:符号推理),供大家参考:
这篇CoT开山之作首次提出思维链(CoT)的概念,思维链简单的说就是一系列中间推理步骤。这篇论文最大的贡献就是发现了在LLM生成推理任务的结果之前,先生成思维链,会使模型的推理性能有大幅度的提升,特别是在复杂的推理任务上,但是有个前提就是LLM的规模要大于10B,否则CoT没用甚至起副作用。CoT的一大好处是无需微调模型参数,仅仅是改变输入就可以改进模型的性能。随着LLM越来越大,高校和小企业可能无法承担训练LLM的成本,因此无法参与其中进行科研与实践,但CoT这个研究方向仍然可以做。对于CoT的更多细节,大家可参考《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》及思维链(Chain-of-Thought, CoT)的开山之作
2022年6月东京大学和谷歌共同发表了一篇论文《Large Language Models are Zero-Shot Reasoners》,这是一篇关于预训练大型语言模型(Pretrained Large Language Models, LLMs)推理能力的探究论文。目前,LLMs被广泛运用在很多NLP任务上。同时,在提供了特定任务的示例之后,LLMs是一个非常优秀的学习者。随着思考链的提示方式(chain of thought prompting, CoT)被提出,对LLMs推理能力的探究上升到一个新的高度,这种提示方式可以引导模型通过示例中一步一步的推理方式,去解决复杂的多步推理,在数学推理(arithmetic reasoning)和符号推理(symbolic reasoning)中取得了SOTA的成果。作者在研究中发现,对拥有175B参数的GPT-3,通过简单的添加”Let’s think step by step“,可以提升模型的zero-shot能力。Zero-shot-CoT的具体格式如下图所示,论文中的具体细节这里不做过多赘述,感兴趣的同学可详读论文内容。需要注意一点的是,同等条件下,Zero-shot-CoT的性能是不及Manual-CoT的。
前文已经提到过,传统CoT的一个未来研究方向:可以用一个LLM自动生成CoT用于Prompting,李沐老师团队在2022年10月发表的论文《AUTOMATIC CHAIN OF THOUGHT PROMPTING IN LARGE LANGUAGE MODELS》证明了这一技术方向的有效性,称为Auto-CoT。
目前较为流行的CoT方法有两种,一种是Manual-CoT,一种是Zero-shot-CoT,两种方式的输入格式如下图所示。前文我们提到过,Manual-CoT的性能是要优于Zero-shot-CoT的,关键原因在于Manual-CoT包含一些人工设计的问题、推理步骤及答案,但是这部分要花费一定的人工成本,而Auto-CoT则解决了这一痛点,具体做法是:
总体来说,Auto-CoT是Manual-CoT和Zero-shot-CoT的结合体,如下图所示。实验证明,在十个数据集上Auto-CoT是可以匹配甚至超越Manual-CoT的性能,也就说明自动构造的CoT的问题、中间推理步骤和答案样例比人工设计的还要好,而且还节省了人工成本。
至此,我们详细介绍了三种CoT技术:Manual-CoT、Zero-shot-CoT以及Auto-CoT,有关CoT的技术还有很多,需要我们慢慢学习,后续持续更新。
通过前文的介绍,我们可以把Tuning分为两类:
我们知道,部分参数微调模式的提出,一方面是由于资源限制,无法更新整体大模型参数,另一方面,要保证在资源有限的条件下,能够尽可能的提升大模型在下游任务上的效果。目前,针对部分参数微调的研究,正处于蓬勃发展阶段,这个研究领域有个统一的名称:Parameter-Efficient Fine-Tuning (PEFT),即参数有效性微调,PEFT方法仅微调少量或额外的模型参数,固定大部分预训练参数,大大降低了计算和存储成本,同时最先进的 PEFT 技术也能实现了与全量微调相当的性能。前文提到的Prompt-Tuning,包括p-tuning等,都可以视为PEFT的一种。总体来说,参数有效性微调可分为三个类别:
接下来,我们对其中流行的PEFT算法进行详细介绍。
Prefix-Tuning:Prefix-Tuning也是一种prompt-tuning,是最早提出soft-prompt的论文之一《Prefix-Tuning: Optimizing Continuous Prompts for Generation》,斯坦福大学于2021年发表。Prefix-Tuning在模型输入前添加一个连续的且任务特定的向量序列(continuous task-specific vectors),称之为前缀(prefix)。前缀同样是一系列“虚拟 tokens”,即没有真实语义。与更新所有 PLM 参数的全量微调不同,Prefix-Tuning固定PLM的所有参数,只更新优化特定任务的prefix。Prefix-Tuning与传统Fine-tuning的对比图如下所示:
如下图所示,prefix-tuning有两种模式,一种是自回归模型(例如GPT-2),在输入前添加一个前缀得到 [ P R E F I X ; x ; y ] [PREFIX;x;y] [PREFIX;x;y];另一种是encoder-decoder模型(例如Bart),在编码器和解码器前加前缀得到 [ P R E F I X ; x ; P R E F I X ′ ; y ] [PREFIX;x;PREFIX^{'};y] [PREFIX;x;PREFIX′;y]。接下来我们以GPT-2的自回归语言模型为例,介绍下prefix-tuning的流程。
首先,对于传统的GPT-2模型来说,将输入 x x x和输出 y y y拼接为 z = [ x ; y ] z=[x;y] z=[x;y],其中 X i d x X_{idx} Xidx和 Y i d x Y_{idx} Yidx分别为输入和输出序列的索引, h i ∈ R d h_{i} \in R_{d} hi∈Rd是每个时间步 i i i下的激活向量(隐藏层向量), h i = [ h i 1 ; … … ; h i n ] h_{i}=[h_{i}^{1}; ……;h_{i}^{n}] hi=[hi1;……;hin]表示在当前时间步的所有激活层的拼接, h i j h_{i}^{j} hij是时间步 i i i的第 j j j层激活层。自回归模型通过如下公式计算 h i h_{i} hi,其中 ϕ \phi ϕ是模型参数:
h i = L M ϕ ( z i , h < i ) h_{i} =LM_{\phi}(z_{i},h_{hi=LMϕ(zi,h<i)
h i h_{i} hi的最后一层,用来计算下一个token的概率分布:
p ϕ ( z i + 1 ∣ h ≤ i ) = s o f t m a x ( W ϕ h i n ) p_{\phi}(z_{i+1}|h_{≤i}) =softmax(W_{\phi}h_{i}^{n})\ pϕ(zi+1∣h≤i)=softmax(Wϕhin)
其中 W ϕ W_{\phi} Wϕ是将 h i n h_{i}^{n} hin根据词表大小进行映射。
在采用prefix-tuning技术后,则在输入前添加前缀,即将prefix和输入以及输出进行拼接得到 z = [ P R E F I X ; x ; y ] z=[PREFIX;x;y] z=[PREFIX;x;y], P i d x P_{idx} Pidx为前缀序列的索引, ∣ P i d x ∣ |P_{idx}| ∣Pidx∣为前缀序列的长度,这里需要注意的是,prefix-tuning是在模型的每一层都添加prefix(注意不是只有输入层,中间层也会添加prefix,目的增加可训练参数)。前缀序列索引对应着由 θ \theta θ参数化的向量矩阵 P θ P_{\theta} Pθ,维度为 ∣ P i d x ∣ × d i m ( h i ) |P_{idx}|\times dim(h_{i}) ∣Pidx∣×dim(hi)。隐层表示的计算如下式所示,若索引为前缀索引 P i d x P_{idx} Pidx,直接从 P θ P_{\theta} Pθ复制对应的向量作为 h i h_{i} hi(在模型每一层都添加前缀向量);否则直接通过LM计算得到,同时,经过LM计算的 h i h_{i} hi也依赖于其左侧的前缀参数 P θ P_{\theta} Pθ,即通过前缀来影响后续的序列激活向量值(隐层向量值)。
h i = { P θ [ i , : ] if i ∈ P i d x L M ϕ ( z i , h < i ) otherwise h_{i}= \begin{cases} P_{\theta}[i,:]& \text{if} \ \ \ i\in P_{idx}\\ LM_{\phi}(z_{i},h_{hi={Pθ[i,:]LMϕ(zi,h<i)if i∈Pidxotherwise
在训练时,Prefix-Tuning的优化目标与正常微调相同,但只需要更新前缀向量的参数。在论文中,作者发现直接更新前缀向量的参数会导致训练的不稳定与结果的略微下降,因此采用了重参数化的方法,通过一个更小的矩阵 P θ ′ P_{\theta}^{'} Pθ′和一个大型前馈神经网络 MLP θ \text{MLP}_{\theta} MLPθ对 P θ P_{\theta} Pθ进行重参数化: P θ [ i , : ] = MLP θ ( P θ ′ [ i , : ] ) P_{\theta}[i,:]=\text{MLP}_{\theta}(P_{\theta}^{'}[i,:]) Pθ[i,:]=MLPθ(Pθ′[i,:]),可训练参数包括 P θ ′ P_{\theta}^{'} Pθ′和 MLP θ \text{MLP}_{\theta} MLPθ的参数,其中, P θ P_{\theta} Pθ和 P θ ′ P_{\theta}^{'} Pθ′有相同的行维度(也就是相同的prefix length), 但不同的列维度。在训练时,LM 的参数 ϕ \phi ϕ被固定,只有前缀参数 P θ ′ P_{\theta}^{'} Pθ′和 MLP θ \text{MLP}_{\theta} MLPθ的参数为可训练的参数。训练完成后, P θ ′ P_{\theta}^{'} Pθ′和 MLP θ \text{MLP}_{\theta} MLPθ的参数被丢掉,只有前缀参数 P θ P_{\theta} Pθ被保存。
上述内容详细介绍了prefix-tuning的主要训练流程,下面我们给出论文中通过实验得出的三个主要结论:
我们回顾下前文提到的parameter-efficient prompt tuning(下面简称为prompt tuning),其论文中有提到,它可以看作是prefix-tuning的简化版。总结下两者的不同点:
P-Tuning v2针对prefix-tuning、p-tuning解决的问题:
P-Tuning v2的优点:
P-Tuning v2的核心点:
P-Tuning v2的其他优化及实施点:
通过前文对Tuning技术的介绍,我们能够了解到,Tuning技术依赖于LLM的发展,同时也在推动着LLM的发展。通常,LLM指的是包含数百亿(或更多)参数的语言模型,这些模型在大量的文本数据上训练。接下来我们介绍几个耳熟能详的大语言模型,其他LLM的相关内容可参考LLMSurvey、Open LLM Leaderboard
、开源大语言模型(LLM)汇总(持续更新中)
2017年,Google推出Transformer,利用Attention完全替代过往深度学习中的Recurrence和Convolutions结构,直白地展现出了“大一统模型”的野心,"xxx is ALL you need"也成了一个玩不烂的梗。
2018年6月,OpenAI推出基于Transformer Decoder改造的第一代GPT(Generative Pre-Training),有效证明了在NLP领域上使用预训练+微调方式的有效性。紧随其后,同年10月Google推出基于Transformer Encoder部分的Bert,在同样参数大小的前提下,其效果领跑于GPT-1,一时成为NLP领域的领头羊。
不甘示弱的OpenAI在4个月后,推出更大的模型GPT-2(GPT-1: 110M,Bert: 340M,GPT-2: 1.5B),同时,OpenAI也知道,光靠增加模型大小和训练数据集来获得一个和Bert差不多效果的模型,其实是没有技术含量的。于是,在GPT-2里,OpenAI引入zero-shot并证明了其有效性。
此后,OpenAI在LLM上义无反顾地走了下去,在2020年6月推出巨人GPT-3,参数量高达175B,各类实验效果达到顶峰,据说一次训练费用为1200w美元,“贵”也成了普通工业界踏足GPT系列的壁垒之一。
在正式介绍GPT系列模型之前,我们先介绍下语言模型的概念,语言模型是GPT系列模型的基座。什么是语言模型?简单来说,就是看一个句子是人话的可能性。专业一点来说,给定一个句子,其字符是 W = ( w 1 , w 2 , ⋯ , w L ) W=(w_{1},w_{2},\cdots,w_{L}) W=(w1,w2,⋯,wL),那么,从语言模型来看,这个句子是人话的可能性就是:
P ( W ) = P ( w 1 , w 2 , ⋯ , w L ) = P ( w 1 ) P ( w 2 ∣ w 1 ) P ( w 3 ∣ w 1 , w 2 ) ⋯ P ( w L ∣ w 1 , w 2 , ⋯ , w L − 1 ) \begin{aligned} P(W)&=P(w_{1},w_{2},\cdots,w_{L})\\ &=P(w_{1})P(w_{2}|w_{1})P(w_{3}|w_{1},w_{2})\cdots P(w_{L}|w_{1},w_{2},\cdots,w_{L-1})\\ \end{aligned} P(W)=P(w1,w2,⋯,wL)=P(w1)P(w2∣w1)P(w3∣w1,w2)⋯P(wL∣w1,w2,⋯,wL−1)
但是, L L L太长就会很稀疏,直接算这个概率不好计算,我们就可以用近似计算:
P ( W ) = P ( w 1 , w 2 , ⋯ , w L ) = P ( w 1 ) P ( w 2 ∣ w 1 ) P ( w 3 ∣ w 1 , w 2 ) ⋯ P ( w L ∣ w 1 , w 2 , ⋯ , w L − 1 ) = P ( w 1 ) P ( w 2 ∣ w 1 ) ⋯ P ( w L ∣ w L − N , ⋯ , w L − 1 ) \begin{aligned} P(W)&=P(w_{1},w_{2},\cdots,w_{L})\\ &=P(w_{1})P(w_{2}|w_{1})P(w_{3}|w_{1},w_{2})\cdots P(w_{L}|w_{1},w_{2},\cdots,w_{L-1})\\ &=P(w_{1})P(w_{2}|w_{1})\cdots P(w_{L}|w_{L-N},\cdots,w_{L-1}) \end{aligned} P(W)=P(w1,w2,⋯,wL)=P(w1)P(w2∣w1)P(w3∣w1,w2)⋯P(wL∣w1,w2,⋯,wL−1)=P(w1)P(w2∣w1)⋯P(wL∣wL−N,⋯,wL−1)
这就是常说的N-gram统计语言模型,N通常是2,3,4。特别的,当N=1时,语言模型就退化为各个字符出现的概率之积。当N=4时语言模型就比较大了,实际应用中一般最大也就是4了。根据条件概率 P ( w L ∣ w L − N , ⋯ , w L − 1 ) P(w_{L}|w_{L-N},\cdots,w_{L-1}) P(wL∣wL−N,⋯,wL−1),我们就能知道给定前N个字,下一个字是什么字的概率了。语言模型的评价指标可以采用PPL(困惑度,Perplexity,语言模型)。
接下来,我们分别介绍GPT-1、GPT-2、GPT-3的模型原理及预训练方法等相关知识。
GPT-1:GPT-1是OpenAI在论文《Improving Language Understanding by Generative Pre-Training》中提出的生成式预训练语言模型。该模型的核心思想:通过二段式的训练,第一个阶段是利用语言模型进行预训练(无监督形式),第二阶段通过fine-tuning的模式解决下游任务(监督模式下)。GPT-1可以很好地完成若干下游任务,包括文本分类、自然语言推理、问答、语义相似度等。在多个下游任务中,微调后的GPT-1性能均超过了当时针对特定任务训练的SOTA模型。
自然语言推理(Natural Language Inference 或者 Textual Entailment):判断两个句子是包含关系(entailment),矛盾关系(contradiction),或者中立关系(neutral);
问答和常识推理(Question answering and commonsense reasoning):类似于多选题,输入一个文章,一个问题以及若干个候选答案,输出为每个答案的预测概率;
语义相似度(Semantic Similarity):判断两个句子是否语义上是相关的;
分类(Classification):判断输入文本是指定的哪个类别。
下面具体介绍下GPT-1的模型结构及训练流程。
模型结构:GPT-1基础架构是基于Transformer的Decoder部分,只是删除了Encoder-Decoder Attention层,只保留了Masked Multi-Head Attention层和Feed Forward层了。Transformer结构提出之始便用于机器翻译任务,机器翻译是一个序列到序列的任务,因此Transformer设计了Encoder用于提取源端语言的语义特征,而用Decoder提取目标端语言的语义特征,并生成相对应的译文。GPT-1目标是服务于单序列文本的生成式任务,所以含弃了关于Encoder部分,包括Decoder的Encoder-Decoder Attention层。整体是12层的Transformer-Decoder变体,如下图所示:
除此之外,GPT-1还将Atention 的维数扩大到768(原来为512),将attention的头数增加到12个(原来为8个),将Feed Forward层的隐层维数增加到3072(原来为2048),总参数达到110M。GPT-1还优化了学习率预热算法,使用更大的BPE码表(词表大小为40478,478个 base characters + 40000个结合的字符),激活函数ReLU改为对梯度更新更友好的高斯误差线性单元GeLU,将正余弦构造的位置编码改为了带学习的位置编码。
模型训练:上文已经提到,GPT-1模型训练整体上分为两步:1)在大规模无标注文本数据上学习到一个高容量的语言模型;2)在标注数据上进行微调。其中第二步是针对具体的下游任务来进行训练的。
无监督预训练:总体训练任务目标是根据已知的词预测未知的词。在这里设定一定的窗口大小,即根据有限的词预测下一个词:给定一个语料的句子序列 U = { u 1 , ⋯ , u n } \mathcal{U}=\{u_{1},\cdots,u_{n}\} U={u1,⋯,un},已知前 k k k个词预测当前词 u i u_{i} ui,用一个标准的语言模型目标去极大化这个似然函数:
L 1 ( U ) = ∑ i l o g P ( u i ∣ u i − k , ⋯ , u i − 1 ; Θ ) L_{1}(\mathcal{U})=\sum_{i} logP(u_{i}|u_{i-k},\cdots, u_{i-1};\Theta)\ L1(U)=i∑logP(ui∣ui−k,⋯,ui−1;Θ)
其中: k k k是滑动窗口大小, Θ \Theta Θ是要优化的参数。 P ( u ) P(u) P(u)的计算方法是:
h 0 = U W e + W p h i = t r a n s f o r m e r _ b l o c k ( h i − 1 ) , ∀ i ∈ [ 1 , n ] P ( u ) = s o f t m a x ( h n W e T ) \begin{aligned} h_{0}&=UW_{e}+W_{p}\\ h_{i}&=transformer\_block(h_{i-1}),\forall i\in[1,n]\\ P(u)&=softmax(h_{n}W_{e}^{T}) \end{aligned} h0hiP(u)=UWe+Wp=transformer_block(hi−1),∀i∈[1,n]=softmax(hnWeT)
其中: W e W_{e} We是词向量矩阵(token embedding matrix), W p W_{p} Wp是位置向量矩阵(position embedding matrix), U = u − k , ⋯ , u − 1 ) U=u_{-k},\cdots,u_{-1}) U=u−k,⋯,u−1)是tokens的上下文向量(源代码中, u i u_{i} ui都是one-hot编码向量,相当于做一个查询操作, U U U存储索引, W e W_{e} We存储着词向量值), n n n是Decoder层的数量。
上面是论文中的描述,我们举一个简单的例子,来说明GPT-1实际上是如何进行无监督预训练的。例如输入文本是:【今天很开心】,这段文本经过切词转换为一个个token后,输入GPT-1的transformer-decoder结构,在最后一层,会输出每个token对应的表征向量,即上文的 h n ∈ R m × d h_{n}\in R^{m\times d} hn∈Rm×d,其中 m m m是token数量,这个例子中就是5, d d d是模型维度,GPT-1中就是768;接下来, h n h_{n} hn再经过一个全连接层,生成 z n ∈ R m × v z_{n}\in R^{m\times v} zn∈Rm×v,其中 v v v是词表的大小;最后, z n z_{n} zn会经过softmax操作,然后选取它每一行中数值最大的索引到词表中搜索对应的token,搜索到的token怎么用呢?我们的目标是下一个词的预测,输入是【今天很开心】,输出也是5个token,因为输入的第一个token是【今】,因此我们希望输出的第一个token是【天】,输入的第二个token是【天】,则希望输出的第二个token是【很】,依此类推,直到最后一个输入token【心】,不过因为它没有下一个词,所以在预训练过程中,不在我们的损失计算范围内。所以,我们会更新模型参数,尽可能的让最终的输出token的前四个字是【天很开心】,这就是预训练任务的整体流程。回过头来,我们也理解了为什么预训练叫做无监督训练,就是因为我们其实没有标注样本,而是拿下一个词当做标签进行模型训练,这种方式也被称作自监督训练。
监督训练:当得到无监督的预训练模型之后,我们将它的值直接应用到有监督任务中。对于一个有标签的数据集 C \mathcal{C} C,每个实例有 m m m个输入token: { x 1 , ⋯ , x m } \{x^{1},\cdots,x^{m}\} {x1,⋯,xm},它对应的标签是 y y y。首先将这些token输入到训练好的预训练模型中,获取最后一个transformer decoder的输出,得到最终的特征向量 h l m h_{l}^{m} hlm。然后再通过一个全连接层得到预测结果 y y y:
P ( y ∣ x 1 , ⋯ , x m ) = s o f t m a x ( h l m W y ) P(y|x^{1},\cdots,x^{m})=softmax(h_{l}^{m}W_{y})\ P(y∣x1,⋯,xm)=softmax(hlmWy)
其中 W y W_{y} Wy为全连接层的参数。有监督的目标则是最大化下式的值:
L 2 ( C ) = ∑ x , y P ( y ∣ x 1 , ⋯ , x m ) L_{2}(\mathcal{C})=\sum_{x,y}P(y|x^{1},\cdots,x^{m})\ L2(C)=x,y∑P(y∣x1,⋯,xm)
注意:这里的 h l m h^m_l hlm是每一个词对应的Decoder输出拼接起来的, h l m = { h l < 1 > , ⋯ , h l < m > } h^m_l=\{h^{<1>}_l,\cdots,h^{
GPT-1的实验中发现,加入语言模型学习目标作为辅助任务,也就是损失函数中加入能带来两点好处:1)提升监督模型的泛化能力;2)加快收敛;因此,最终的优化目标如下( λ \lambda λ一般取0.5):
L 3 ( C ) = L 2 ( C ) + λ L 1 ( C ) L_{3}(\mathcal{C})=L_{2}(\mathcal{C})+\lambda L_{1}(\mathcal{C})\ L3(C)=L2(C)+λL1(C)
下游任务:GPT-1论文中给出了四个下游适配任务,分别是文本分类、自然语言推理、问答、语义相似度,同时给出了针对这四个任务,如何进行针对性的微调。这四个任务虽然本质上都是属于自然语言理解的文本分类任务,但是GPT-1的结构是很适配做自然语言生成任务的。下面我们介绍下GPT-1如何在上述四个任务上进行微调,如下图所示。
这里我们同样通过一个文本分类的例子,来介绍下GPT-1在下游任务上是如何微调的。例如下游任务是情感文本分类任务,包括喜、怒、哀、惧、其他五个类别,其中一个样本是【今天很开心】,真实标签是【喜】。通过前面的介绍,我们知道GPT-1在下游任务进行微调时,损失函数包含两部分,一部分是与预训练保持一致的下一个词预测损失,这部分就不介绍了。另一部分是分类损失,对于分类任务来说,我们最终也会获取到GPT-1最后一层的向量表征 h l ∈ R m × d h_{l}\in R^{m\times d} hl∈Rm×d,其中 m m m是token数量,这个例子中就是5, d d d是模型维度,GPT-1中就是768, l l l是模型层数;接下来, h l h_{l} hl的最后一行再经过一个全连接层(注意,预训练任务是 h l h_{l} hl整体都要经过全连接层,我们这里只需用到最后一个token,即图片中的Extract对应的向量表征),生成 z l ∈ R c z_{l}\in R^{c} zl∈Rc,其中 c c c是类别数目;最后, z l z_{l} zl会经过softmax操作,获取【今天很开心】这段文本对应的每一个类别的概率值,我们的期望是【喜】的概率值要尽可能的大,也就是 z l z_{l} zl的第一个元素的值要尽可能大,这也就是我们的优化目标。
GPT-1特点:
GPT-1与ELMo,Bert的区别:
GPT-1的数据集:GPT-1使用了BooksCorpus数据集,这个数据集包含7000本没有发布的书籍。作者选这个数据集的原因有二:1)数据集拥有更长的上下文依赖关系,使得模型能学得更长期的依赖关系;2)这些书籍因为没有发布,所以很难在下游数据集上见到,更能验证模型的泛化能力。
GPT-2:我们知道,GPT-1和Bert的训练都是分两步走:pre-training + supervised fine-tuning。这套方法的缺点:
另外,在Bert模型提出之后,Encoder vs Decoder,Bert vs GPT-1,两两之间的比较就开始了,但是此时GPT-1仍处在劣势。Bert提出之后,除了生成任务外,NLP任务的范式基本就是Bert的预训练+fine-tuning了。OpenAI放弃了吗?并没有!我们知道,基于Decoder的模型,模型和数据量越大,效果越好。但OpenAI如果只做到这一点,从技术上来说又太逊色了,性价比也不高。因此,OpenAI从训练数据上进行改进,引入了zero-shot这一创新点,GPT-2就诞生了《Language Models are Unsupervised Multitask Learners》。
论文中认为现在的训练方式训练出来的模型只能算是一个小任务上的专家系统,而且还都不够鲁棒。造成这个问题的原因是模型都是在单一领域内的单一任务上进行训练的,缺乏泛化性。跟人一样,见识和知识太少时,就很难对事情有全面的了解。要解决这个问题,一个可行的思路是多任务学习,而且是大量不同领域的不同任务。但是,这样的多任务学习是有监督的训练,需要大量的数据,这个就比较难实现了。
GPT-2在GPT-1的基础上,提出了新的发展思路来解决这个问题。简单来说,GPT-2的思路就是充分相信语言模型,不再对下游任务进行fine-tuning或者增加任务头了,就用预训练的语言模型来解决所有任务,直接做zero-shot的任务。具体来说,就是上高质量的大数据,堆叠更多的参数,不同任务改造成生成任务。
GPT-2本质上还是一个语言模型,但是不一样的是,它证明了语言模型可以在 zero-shot 的情况下执行下游任务,也就是说,GPT-2在做下游任务的时候可以无需任何标注的信息,也无需任何参数或架构的修改。后来的GPT-3也是沿用了这个思路,这个时候,已经可以看出一些ChatGPT的影子了。
模型结构:GPT-2的模型在GPT-1的基础上做了一些改进,如下:
论文给了不同层数的模型,最大的模型称为GPT-2模型,参数有1.5B;最小的即是GPT-1,对标Bert-base;倒数第二小的对标Bert-large。不同模型大小如下:
模型训练:GPT-2只有预训练过程。
GPT-2的数据集:许多之前的工作是在单个文本域上训练语言模型,例如新闻文章,维基百科或小说等等。GPT-2则是希望使得训练数据集的领域和上下文更多一点。在网站上爬取文本是一个方案,比如说Common Crawl网站。虽然这些网站手机的数据集在量级上很大,但它们存在严重的数据质量问题,这上面的内容有很多是信噪比很低的,难以理解的内容。为了解决数据集质量的问题,GPT-2只爬取人类过滤之后的网页。但是,手动过滤的网络爬取很昂贵,所以GPT-2从社交媒体平台Reddit 上抓取了至少收到了3个karma的链接。karma可以被认为是一种启发式指标,用于判断其他用户是否认为该链接有趣、有教育意义或只是有趣。得到的这个数据集称之为WebText,是一个包含了4500万个链接的文本数据集。经过重复数据删除和一些基于启发式的清理后,它包含略多于800万个文档,总文本容量为40GB。作者从WebText中删除了所有维基百科文档,因为它可能涉及到 test evaluation tasks。目前全量的数据是没有开放下载的,可通过GPT-2训练数据集下载部分训练数据。
GPT-2特点:
GPT-3:GPT-2的最大贡献是验证了通过海量数据和大量参数训练出来的词向量模型有迁移到其它类别任务中而不需要额外的训练。但是很多实验也表明,GPT-2的无监督学习的能力还有很大的提升空间,甚至在有些任务上的表现不比随机的好。尽管在有些zero-shot的任务上的表现不错,但是我们仍不清楚GPT-2的这种策略究竟能做成什么样子。GPT-2表明随着模型容量和数据量的增大,其潜能还有进一步开发的空间,基于这个思想,诞生了我们下面要介绍的GPT-3《Language Models are Few-Shot Learners》。
GPT-2在GPT-1的基础上往前走了一大步:完全抛弃了微调,并采用了zero-shot的方式。Zero-shot的方式被GPT-2认证可行后,OpenAI就不得不开始考虑模型是否能真正做到强大了,毕竟现在只是和Bert持平而已。这一刻OpenAI开始悟过来,既然LLM要一路走到底,既然模型变大避免不了,那不如来得更彻底一些。GPT-3沿用了去除fine-tuning,只做通用语言模型的思路,同时技术上小做替换(sparse Transformer);对于下游任务,在不做微调的前提下采用了few-shot的方式(毕竟完全不给模型任何显性提示,效果确实没达到预期)。最终生成了一个大小高达175B的大模型,当然效果也是一骑绝尘的。
模型结构:GPT-3的模型与GPT-2的模型基本一致,主要改进只有一点:
Sparse Attention:在模型结构中的注意力层,GPT-3采用Sparse Transformer中的Sparse Attention方案,sparse attention与传统self-attention(称为 dense attention)的区别在于:
具体来说,sparse attention除了相对距离不超过 k k k以及相对距离为 k , 2 k , 3 k , ⋯ k,2k,3k,\cdots k,2k,3k,⋯的token,其他所有token的注意力都设为0,如下图所示:
我们来具体观察一下,实际上图中的第二行就是涉及到的attention的token内容,可以看出首先关注了附近四个token,其次是 2 k , 3 k 2k,3k 2k,3k距离的token,那么为什么这么做呢?使用 sparse attention 的好处主要有以下两点:
模型训练:GPT-3也只有预训练过程。
无监督训练:GPT-3仍采用GPT-2提出的仅做预训练、不做微调的思路。GPT-3采用了in-context learning。借用meta-learning(元学习)的思想,在pre-training期间让模型学习广泛的技能和模式识别能力,而在推理期间利用这些技能和能力迅速适配到期望的任务上。在之前的章节中,我们已经介绍过in-context learning,下面简单介绍下GPT-3中的in-context learning。
In-context learning是这篇论文中介绍的一个重要概念,要理解in-context learning,我们需要先理解meta-learning(元学习)。对于一个少样本的任务来说,模型的初始化值非常重要,从一个好的初始化值作为起点,模型能够尽快收敛,使得到的结果非常快的逼近全局最优解。元学习的核心思想在于通过少量的数据寻找一个合适的初始化范围,使得模型能够在有限的数据集上快速拟合,并获得不错的效果。
这里的介绍使用的是MAML(Model-Agnostic Meta-Learning)算法,正常的监督学习是将一个批次的数据打包成一个batch进行学习。但是元学习是将一个个任务打包成batch,每个batch分为支持集(support set)和质询集(query set),类似于学习任务中的训练集和测试集。
对一个网络模型 f f f,其参数表示为 θ \theta θ,它的初始化值被叫做meta-initialization。MAML的目标则是学习一组meta-initialization,能够快速应用到其它任务中。MAML的迭代涉及两次参数更新,分别是内循环(inner loop)和外循环(outer loop)。内循环是根据任务标签快速的对具体的任务进行学习和适应,而外学习则是对meta-initialization进行更新。直观的理解,我用一组meta-initialization去学习多个任务,如果每个任务都学得比较好,则说明这组meta-initialization是一个不错的初始化值,否则我们就去对这组值进行更新。
GPT-3中介绍的in-context learning则是元学习的内循环,基于语言模型的SGD则是外循环,如下图所示。
下游任务:在训练阶段,预训练通用的语言模型,使模型能够具备识别不同NLP任务的能力,此时模型具备了一定的ICL能力。而在推理阶段,依赖于模型的ICL能力,针对各NLP任务,向模型中输入特定上下文,上下文包括任务描述、若干个任务样本和任务提示,模型根据上下文进行推理给出任务输出。根据上下文包含的任务样本数量可进一步将上下文学习分为Zero-Shot(无任务样本)、One-Shot(仅一个任务样本)和Few-Shot(多个任务样本)三类。
GPT-3的数据集:GPT-3的训练数据包括低质量的Common Crawl,高质量的WebText2、Books1、Books2和Wikipedia。GPT-3根据数据集的不同质量赋予了不同的权值,权值越高的在训练的时候越容易抽样到(见下图)。为了清理脏数据,OpenAI做了以下的数据处理:
最终处理完成后使用的数据规模约570G。
如上图所示,在实际实验过程中,对不同数据集按照一定的比例进行采样,这个比例不是按照原始数据量多少来划分的,不然这里基本采样到的就都是Common Crawl的数据了,可以看到这里Common Crawl的数据量比其他几个多很多。进行采样的原因主要考虑到,就算做了一些数据清洗还是觉得Common Crawl的数据质量不如其他几个。最终采样的时候,虽然Common Crawl的数据量是其他几个数据集的上百倍,但是实际占比是60%,有40%的数据是能够保证质量的。
GPT-3的特点:
优点:GPT-3的强大之处在于它的泛化能力。不需要微调,只需要在输入序列里用自然语言表述任务要求,就可以让模型执行不同的子任务。GPT-3在部分任务上达到或超过了SOTA,并且验证了模型规模越大、任务效果越好,且大部分情况下,GPT-3的Few-Shot优于One-Shot和Zero-Shot。
缺点:
GPT-3虽然在各大NLP任务以及文本生成的能力上令人惊艳,但是他仍然还是会生成一些带有偏见的,不真实的,有害的造成负面社会影响的信息,而且很多时候,他并不按人类喜欢的表达方式去说话。在这个背景下,OpenAI提出了一个概念“Alignment”,意思是模型输出与人类真实意图对齐,符合人类偏好。因此,为了让模型输出与用户意图更加对齐,就有了InstructGPT这个工作《Training language models to follow instructionswith human feedback》。InstructGPT提出了一个理想化语言模型的三大目标:helpful(能帮助用户解决问题)、honest(不能捏造事实,不能误导用户)、harmless(不能对用户或环境造成物理、精神、社会层面的伤害)。
为了实现上述的目标,论文提出了一种基于人类反馈来微调语言模型的方法,使其能够更好地遵循用户的指示,并在各种任务上表现出更高的质量和可信度。基本流程分为以下三个步骤:
最终得到的InstrctGPT相较于GPT-3:
下面详细介绍下InstructGPT数据集构建以及训练流程。
InstructGPT数据集构建:InstructGPT数据集构建可以分为三个阶段。第一个阶段是为了构建初始的指令prompt数据集,具体做法是让标注人员构建下面三种prompt:
基于上面三种指令prompt,OpenAI团队训练了初始版本的InstructGPT模型,然后将这个InstructGPT模型放到Playground(Playground可理解为测试API,非生产API)里供用户使用,这就引申至InstructGPT数据集构建的第二个阶段:用户在使用过程中,会继续问一些问题,OpenAI团队将这些问题收集回来,并进行过滤等操作,具体来说,将每个用户ID的对应的指令prompt数量限制为200个,同时过滤掉个人信息,并根据用户ID拆分训练、验证和测试集(同一个用户问题会比较类似,不适合同时出现在训练集和验证集中)。可以看出,第一阶段和第二阶段是一个循环过程:先拿部分数据训练模型,然后通过模型获取新数据,再用新数据继续优化模型,这种思路也很适合我们以后的模型训练过程。
至此,通过上述两阶段的处理,OpenAI团队已经获取了一定量的指令prompt(包括标注人员构建的prompt以及从用户侧收集的prompt),接下来即是针对不同训练任务构建不同的数据集,也就是第三阶段。基于第二阶段获取的指令prompt,构建三个数据集,分别用于后续的三个训练任务SFT、RM、PPO。
SFT Dataset和RM Dataset都需要人工标注,区别在于前者的生成式的标注要比后者的判别式的标注贵很多,同样的标注时间和成本,联合前者和后者得到的数据要比只用前者得到的数据多很多,在这上面训练出来的模型性能可能会好一些。
InstructGPT训练流程:上文已经介绍,关于InstructGPT的训练流程,论文中分为了三个步骤:有监督微调,奖励模型训练,强化学习训练,如下图所示。实际上可以把它拆分成两种技术方案,一个是有监督微调(SFT),一个是基于人类反馈的强化学习(RLHF),下面我们简单介绍这两种技术方案。
奖励模型(RM):模型结构是把SFT模型最后的unembedding层去掉,即最后一层不用softmax,改成一个线性层,这样训练好的RM模型就可以做到输入问题+答案,输出一个标量的分数。RM模型使用6B,而不是175B,主要原因是:
前文已经介绍,RM数据集在标注阶段,标注人员被要求对每一个prompt下的不同回答进行排序。如下图,某个prompt下有A、B、C三个回答,标注人员认为A>B>C。在训练阶段,假设一个prompt下有K个回答,则两两回答一组,组成一条训练数据,例如(prompt, A, B),则一共有 C k 2 C_{k}^{2} Ck2条训练数据。这些训练数据将组成一个batch,通过构造并最小化Pairwise Ranking Loss的方法,来训练奖励模型,整体过程如下:先以RM Dataset中的指令prompt作为输入,通过第一阶段微调好的SFT模型,生成K个不同的回答,形成
接下来我们根据模型的损失函数Pairwise Ranking Loss,详细了解下RM模型的训练过程。Pairwise Ranking Loss表达式如下所示:
l o s s ( θ ) = − 1 C k 2 E ( x , y w , y l ) ∼ D [ l o g ( σ ( r θ ( x , y w ) − r θ ( x , y l ) ) ) ] loss(\theta)=-\frac{1}{C_{k}^{2}}E_{(x,y_{w},y_{l})\sim D}[log(\sigma(r_{\theta}(x,y_{w})-r_{\theta}(x,y_{l})))]\ loss(θ)=−Ck21E(x,yw,yl)∼D[log(σ(rθ(x,yw)−rθ(x,yl)))]
其中,
也可以参考下图(有个小错误,就是 s i g m o i d sigmoid sigmoid函数是将值映射至 ( 0 , 1 ) (0,1) (0,1),而不是 ( − 1 , 1 ) (-1,1) (−1,1),不过无伤大雅)。
论文中期望当回答 y y y的排序相对较高时, r θ ( x , y ) r_{\theta}(x,y) rθ(x,y)的得分也能越高。为了不让K的个数影响训练模型,论文中在前面乘上 1 C k 2 \frac{1}{C_{k}^{2}} Ck21,将loss平均到每一个答案组合上。除此之前,还有几点需要我们注意:
强化学习模型(RL):这个阶段先将RL模型的权重初始化为SFT模型的权重,然后通过改良后的PPO算法(PPO-ptx算法)继续对RL模型进行优化,最终得到InstructGPT。强化学习的大致流程可以总结为:模型在做出行动后,需要人来对模型进行反馈,然后模型做出对应的更新。具体来说,论文中训练RM就是为了学习人来对模型进行反馈,SFT模型在拿到prompt并生成对应的答案后,由RM进行打分,再根据这个打分去更新模型,然后用更新的模型生成新的答案,并进行下一步学习,这就是强化学习的过程。强化学习的目标函数 o b j e c t i v e ( ϕ ) objective(\phi) objective(ϕ)如下所示,RL模型最终的训练目标是让 o b j e c t i v e ( ϕ ) objective(\phi) objective(ϕ)越大越好。
o b j e c t i v e ( ϕ ) = E ( x , y ) ∼ D π ϕ R L [ r θ ( x , y ) − β l o g ( π ϕ R L ( y ∣ x ) / π S F T ( y ∣ x ) ) ] + γ E x ∼ D p r e t r a i n [ l o g ( π ϕ R L ( x ) ) ] \begin{aligned} objective(\phi)=&E_{(x,y)\sim D_{\pi_{\phi}^{RL}}}[r_{\theta}(x,y)-\beta log(\pi_{\phi}^{RL}(y|x)/\pi^{SFT}(y|x))]+\\ &\gamma E_{x\sim D_{pretrain}}[log(\pi_{\phi}^{RL}(x))] \end{aligned} objective(ϕ)=E(x,y)∼DπϕRL[rθ(x,y)−βlog(πϕRL(y∣x)/πSFT(y∣x))]+γEx∼Dpretrain[log(πϕRL(x))]
其中:
整体的目标是最大化上述的目标函数,现在分别介绍下目标函数的每一项,也可以参考下面的图片:
最后再给出对目标函数的理解,优化目标是使得上述目标函数越大越好,通过上述介绍,我们知道, o b j e c t i v e ( ϕ ) objective(\phi) objective(ϕ)可分成三个部分,RM打分部分+KL散度部分+GPT-3预训练部分:
回顾下InstructGPT的训练流程,共包含两次对模型的微调:GPT-3模型 ⇒ \Rightarrow ⇒SFT模型 ⇒ \Rightarrow ⇒RL模型,其实这里始终都是同一个模型,只是不同过程中名称不一样。除此之外,在SFT模型 ⇒ \Rightarrow ⇒RL模型阶段,还会依赖于另一个在SFT模型基础上训练的RM模型。InstructGPT训练SFT、RM、RL三个模型的原因为:
最后,我们展示下论文中提到的InstructGPT性能对比结果,可以发现,参数量为13B的InstructGPT模型,性能都要远远好于参数量为175B的GPT-3模型:
InstructGPT是在GPT-3的基础上通过SFT+RLHF两个阶段训练完成;ChatGPT则是在GPT-3.5的基础上通过SFT+RLHF两个阶段训练完成,显著提升了模型的对话能力。SF和RLHF两个阶段,在InstructGPT章节中我们已经做了详细介绍,这里不做过多赘述。关于GPT-3和GPT-3.5,它们其实是两个模型系列,分别称为GPT-3系列和GPT-3.5系列,下面我们参考综述拆解追溯 GPT-3.5 各项能力的起源,简单展示下OpenAI团队所构建的GPT-3系列和GPT-3.5系列是如何进化的。
2022年3月,OpenAI团队又放大招,发布了更强的LLM:GPT-4。虽然无从得知GPT-4的训练细节,但是可以肯定的是,GPT-4采用了更大的模型结构,增加了更多的训练数据。我们可以通过官方博客GPT-4了解下GPT-4的强大能力。目前GPT-4的主要能力点如下:
毫不夸张的说,尽管需要耗费巨大的资源,但是目前国内外各大公司都在或多或少的参与着LLM的军备竞赛,这在一定程度上促进着NLP技术的发展。归因于此,目前已经有一系列LLM陆陆续续问世了。我们无法对这些LLM进行一一介绍,这里挑选一些我们在其基础上做过微调或有使用经验的模型,对它们进行简单介绍。
大模型 | 团队 | 发布时间 | 模型规模 | 是否开源 | 资源链接 |
---|---|---|---|---|---|
ChatGLM-6B | 清华大学 | 2023 | 6B | 已开源,不可商用 | ChatGLM-6B |
LLAMA-7B | 2023 | 7B | 已开源,不可商用 | LLaMA | |
baichuan-7B | 百川智能 | 2023 | 7B | 已开源,可商用 | baichuan-7B |
文心一言 | 百度 | 2023 | 千亿 | 未开源,不可商用 | 暂无 |
LLM之所以主要都用Decoder-only架构,除了训练效率和工程实现上的优势外,一方面,在理论上是因为Encoder的双向注意力会存在低秩问题,这可能会削弱模型表达能力;另一方面,就生成任务而言,引入双向注意力并无实质好处。而Encoder-Decoder架构之所以能够在某些场景下表现更好,大概只是因为它多了一倍参数。所以,在同等参数量、同等推理成本下,Decoder-only架构就是最优选择了。具体参考LLM为什么都用Decoder only架构?
了解LLM,看这一篇就够了!!!