草庐IT

预训练新范式!为什么Prompt会更有效?

kaiyuan_sjtu 2023-05-22 原文

作者 | 上杉翔二        

悠闲会 · 信息检索 

整理 | NewBeeNLP

「Prompt Tuning也许会是深度学习时代的Feature Engineering问题,如何给各大任务设计合理的Prompts将会是很有意思的科学问题」

--刘知远

虽然博主以前也看到了基于 Prompt-tuning 可以让GPT-3处理各种类型的任务,甚至拥有直接处理零样本和小样本学习能力。也整理过GPT,OpenAI CLIP,DALL-E[1] 这些文章,见 GPT Plus Money!B O O M

但但但,没想到这个领域冲的好快,新范式、综述和应用都层出不穷,并且看到小伙伴的应用实验效果奇佳。便想重新整理一些文章,再重新认识一下Prompt-tuning!

GPT-3

Language Models are Few shot Learners

众所周知GPT-3的优势在于它超级大大大大 ,可以针对特定任务来进行微调,即核心理念为:「将所有自然语言处理的任务转换为语言模型任务。」

所有任务都可以被统一建模,任务描述与任务输入视为语言模型的历史上下文,而输出则为语言模型需要预测的未来信息。即转变问题为根据文本的走向来 " 搞清楚 " 它们应该做什么事情,通过给模型一些提示(prompt),来让它根据任务直接学习。这种方法也被称为情境学习(in-context learning)或提示(prompting)。

NLP中Prompt综述

然后直接来看大佬关于Prompt的综述。

  • 论文:Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing

  • 地址:https://arxiv.org/abs/2107.13586

NLP技术的发展可分为4个阶段/范式,如下图。

  • 全监督学习(非神经网络)。仅在目标任务的输入输出样本数据集上训练特定任务模型,其严重依赖特征工程。

  • 全监督学习(神经网络)。使得特征学习与模型训练相结合,于是研究重点转向了架构工程,即通过设计一个网络架构(如CNN,RNN,Transformer)能够学习数据特征。

  • Pre-train,Fine-tune。先在大数据集上预训练,再根据特定任务对模型进行微调,以适应于不同的下游任务。在这种范式下,研究重点转向了目标工程,设计在预训练和微调阶段使用的训练目标(损失函数)。

  • Pre-train,Prompt,Predict。无需要fine-tune,让预训练模型直接适应下游任务。方便省事,不需要每个任务每套参数,突破数据约束。具体“Prompt”的做法是,将人为的规则给到预训练模型,使模型可以更好地理解人的指令的一项技术,以便更好地利用预训练模型。例如,在文本情感分类任务中,输入为"I love this movie.",希望输出的是"positive/negative"中的一个标签。那么可以设置一个Prompt,形如:"The movie is ___",然后让模型用来表示情感状态的答案(label),如positive/negative,甚至更细粒度一些的“fantastic”、“boring”等,将空补全作为输出。

Fine-tune和Prompt的区别?

prompting 更加依赖先验,而 fine-tuning 更加依赖后验。

  • Fine-tuning中:是预训练语言模型“迁就“各种下游任务。具体体现就是通过引入各种辅助任务loss,将其添加到预训练模型中,然后继续pre-training,以便让其更加适配下游任务,这个过程中,预训练语言模型做出了更多的牺牲。

  • Prompting中,是各种下游任务“迁就“预训练语言模型。具体体现也是上面介绍的,我们需要对不同任务进行重构,使得它达到适配预训练语言模型的效果,这个过程中,是下游任务做出了更多的牺牲。

这种方法的优点是给定一组合适的 prompt,以完全无监督的方式训练的单个 LM 就能够用于解决大量任务。然而该方法也存在一个问题——这种方法引入了 prompt 挖掘工程的必要性,即需要找出最合适的 prompt 来让 LM 解决面临的任务,即怎么做Prompt Engineering。

Fine-tune和Prompt的性能?

Prompt当然参数量要小很多啦。如下图是对于T5的两者对比图。

  • Fine-tune需要为每个下游任务都存下整个预训练模型的副本,并且推理必须在单独的批次中执行。

  • Prompt只需要为每个任务存储一个特定于下游任务的小单元,并使用原始的预先训练过的模型启用混合任务推理即可。

Fine-tune模型的每个副本都需要110亿个参数。而Prompt每个任务只需要20480个参数——减少超过5个数量级。

Prompt Engineering

主要目标是设计Prompt。比如 Overall it was a [z] movie,[z]是需要模型进行填充的空位,[z]的位置和数量决定了Prompt的类型。回顾一下CLIP就是使用的“A photo of a [z]”的prompt。

  • cloze prompt([z]在句中)。适合使用掩码 (Mask) LM 解决的任务。

  • prefix prompt([z]在句末)。适合有关生成的任务或使用标准自回归 LM 解决的任务,因为它们与模型从左到右的性质刚好吻合。

对于一些涉及多个输入的任务例如文本对分类,prompt 模板则必须包含至少两个输入的空间。人工设计模版或者自动化搜索模版一般是常用的方式。

Answer Engineering

与 prompt 工程相反,answer 工程的目的是搜索一个 answer 空间 Z 和一个到原始输出 Y 的映射,从而得到更细粒度的预测模型。比如把纯“positive”或“negative”的标签转变成选择恰当的词汇填空如“fantastic”,使情感分类任务转变为语言模型构建任务。下图是一些可参考的模版,当然此后的一些新方法都是想通过自动构建模版,以进一步释放语言模型潜能。

为什么Prompt会更有效?

实际上,prompt 可以看做是对预训练语言模型中已经记忆的知识的一种检索方式,由于 prompt 任务形式就是预训练任务,所以相比于 fine-tuning,当使用 prompt 形式向模型中输入样本时,预测时得到了“提示”,因此所需使用到的信息量是更多的。因为要用到更多的信息量,所以样本的类别分布在 prompt 视角之下都是稀疏的,这应该也是 prompt 在 few-shot 上效果显著的原因。

Prefix-Tuning

之前的工作如Prompting主要是人工设计模版或者自动化搜索模版(如上一篇综述所展示了很多模版)来进行预训练的,但人工设计的未知性比较影响模型性能+自动化搜索成本较高,为什么不从离散tokens转向连续的“soft prompt”呢?

  • 论文:Prefix-Tuning: Optimizing Continuous Prompts for Generation

  • paper:https://arxiv.org/abs/2101.00190

  • code:https://github.com/XiangLi1999/PrefixTuning

因此prefix-tuning 的主要贡献是提出任务特定的trainable前缀prefix,这样直接为不同任务保存不同的前缀即可。如上图的下方,只需要不同任务设置不同的Prefix即可,因此实现上只需要存储一个大型transformer模型和多个学习的任务特定的prefix参数。

基于prompting方法的直觉,作者认为具有适当的上下文可以在不改变参数的情况下来引导LM。因此作者给模型若干的字词作为提示,比如想让模型生成“Obama”,那可以在其常见的搭配前加上上下文(例如abama的名字Barack),那么LM就会把更高的可能性分配给想要的单词,就能自动生成一个模版。符号化表示可以有两种形式:

  • 对于自回归模型,加入前缀后的模型输入表示:

  • 或者为编码器和编码器都添加prefix:。

<

然后其他的不变,语言模型参数固定,只更新前缀参数。这种连续性的生成方法在后续的P-tuning中也持续被探索。

GPT Understands, Too

P-tuning V1的动机和prefix-tuning是类似的,也是探索可学习的可微模版,主要贡献是首次提出了用连续空间搜索的 embedding 做 prompt。如这篇清华文章的题目“GPT Understands, Too”,代表着它的表现想颠覆一直以来“GPT只能做生成,不能做理解”的结论。

  • paper:https://arxiv.org/abs/2103.10385

  • code:https://github.com/THUDM/P-tuning

首先关于“什么是模版”,直观上模版就是由自然语言构成的前缀/后缀。比如人工定义的话会变成类似「The capital of Britain is [MASK]」这种形式,然后也可是「Britain' capital is [MASK]」或者「[MASK] is the capital of Britain 」等等。通过这些模版才能使得下游任务跟预训练任务一致,这样才能更加充分地利用原始预训练模型。

而P-tuning的目标是:不关心模版长什么样, 只需要知道模版由哪些 token 组成,该插入到哪里,插入后能不能完成我们的下游任务,输出的候选空间是什么就可以了。即不关心模版的“自然语言”要求,它只是为了更好地实现“一致性”但不是必须的。因此P-tuning 的做法是用上下文 x (「Britain」)、目标 y(「[MASK]」) 和去组合成一个prompt模版 t (「The capital of ... is ....」)。如下图,上下文指蓝色的“Britain”,目标是红色的“[MASK]”,橘色的区域是想生成的模版。左边的图a是离散化搜索的做法,不一定是最优的。右边的是P-tuning,它通过预训练的嵌入层将一组离散输入 token (即pseudo prompts)映射到embedding,然后结合(capital,Britain)生成得到结果,再优化生成的encoder部分。这种方法可以找到更好的连续 prompt,并通过下游损失函数对连续 prompt 进行优化。

离散 vs 连续

最早研究自动模版的工作,有些是挖掘实体关系,有些需要大量样本进行基于梯度搜索(如autoprompt),在离散空间下搜索的自然语言模版。也有梯度粗筛+代入模版精筛等等。而连续的Prefix-Tuning和P-tuning 放弃了“模版由自然语言构成”这一要求,从而将其变成了可以简单梯度下降求解的连续参数问题,效果还更好。可能prompt的关键在于它是怎么用的,不在于它由什么构成。

P-Tuning v2

P-tuning在一些复杂的自然语言理解NLU任务上效果很差(任务不通用)+预训练的参数量不能小(规模不通用)。因此V2版本主要是基于P-tuning和Prefix-tuning,引入Deep Prompt Encoding和Multi-task Learning等策略进行优化的。

  • P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks

  • paper:https://arxiv.org/abs/2110.07602

  • code:https://github.com/thudm/p-tuning-v2

直接看模型图如下,左边是p-tuning的做法,右边是v2的改进。对比起来主要的变动有:

  • Deep Prompt Encoding:采用 Prefix-tuning 的做法,在输入前面的每层加入可微调的参数。

  • 无重参数化编码器:对pseudo token,不再使用重参数化进行表征(如用于 prefix-tunning 的 MLP 和用于 P-tuning 的 LSTM),且不再替换pre-trained model word embedding,取而代之的是直接对pseudo token对应的深层模型的参数进行微调。

  • Multi-task learning:对于pseudo token的continous prompt,随机初始化比较难以优化,因此采用multi-task方法同时训练多个数据集,prefix continuous prompt共享。

  • 回到CLS:可以在[CLS]部分输出sentence-level class,以及每个token位置上输出token-level class,可以解决一些没有语义标签的问题。

OpenPrompt 工具包

Pipeline工具来了!先放link:

  • https://github.com/thunlp/OpenPrompt

一起交流

想和你一起学习进步!『NewBeeNLP』目前已经建立了多个不同方向交流群(机器学习 / 深度学习 / 自然语言处理 / 搜索推荐 / 图网络 / 面试交流 / 等),名额有限,赶紧添加下方微信加入一起讨论交流吧!(注意一定o要备注信息才能通过)

本文参考资料

[1]

GPT,OpenAI CLIP,DALL-E: https://nakaizura.blog.csdn.net/article/details/116903995

END -



2022年 预训练的下一步是什么?

2022-01-11

NLP 预训练家族再思考

2021-12-22

万字综述!从21篇最新论文看多模态预训练模型研究进展

2021-11-17

NLP 语义匹配:经典前沿方案整理

2021-11-10

有关预训练新范式!为什么Prompt会更有效?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  9. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  10. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

随机推荐