作者:杨夕
NLP论文学习笔记:https://github.com/km1994/nlp_paper_study
个人介绍:大佬们好,我叫杨夕,该项目主要是本人在研读顶会论文和复现经典论文过程中,所见、所思、所想、所闻,可能存在一些理解错误,希望大佬们多多指正。
NLP 百面百搭 地址:https://github.com/km1994/NLP-Interview-Notes
推荐系统 百面百搭 地址:https://github.com/km1994/RES-Interview-Notes
在很长的一段时间内,NLP的任务采用的都是 Pretrain + Fine-tuning(Model Tuning)的解决方案,但是这种方案,需要对于每个任务都重新 fine-tune 一个新的模型,且不能共用。
但是对于一个预训练的大语言模型来说,这就仿佛好像是对于每个任务都进行了定制化,十分不高效。
是否存在一种方式,可以将预训练语言模型作为电源,不同的任务当作电器,仅需要根据不同的电器(任务),选择不同的插座,对于模型来说,即插入不同的任务特定的参数,就可以使得模型适配该下游任务。
Prompt Learning 就是这个适配器,它能高效得进行预训练语言模型的使用。
这种方式大大地提升了预训练模型的使用效率,如下图:
- 左边是传统的 Model Tuning 的范式:对于不同的任务,都需要将整个预训练语言模型进行精调,每个任务都有自己的一整套参数。
- 右边是Prompt Tuning,对于不同的任务,仅需要插入不同的prompt 参数,每个任务都单独训练Prompt 参数,不训练预训练语言模型,这样子可以大大缩短训练时间,也极大的提升了模型的使用率。
所以什么是 Prompt, 字面上来讲,Prompt 就是提示:
例如我们有人忘记了某个事情,我们给予特定的提示,他就可以想起来,例如我们说:
白日依山尽,
大家自然而然地会想起来下一句诗:黄河入海流。
亦或者,搜索引擎,可以根据我们的输入,进行输出的提示:
那么在NLP中 Prompt 代表的是什么呢?
- prompt 就是给 预训练语言模型 的一个线索/提示,帮助它可以更好的理解 人类的问题。
例如,下图的BERT/BART/ERNIE 均为预训练语言模型,对于人类提出的问题,以及线索,预训练语言模型可以给出正确的答案。
- 根据提示,BERT能回答,JDK 是 Oracle 研发的
- 根据 TL;DR: 的提示,BART知道人类想要问的是文章的摘要
- 根据提示,ERNIE 知道人类想要问鸟类的能力--飞行
Prompt 更严谨的定义如下:
Prompt is the technique of making better use of the knowledge from the pre-trained model by adding additional texts to the input.
Prompt 是一种为了更好的使用预训练语言模型的知识,采用在输入段添加额外的文本的技术。
- 目的:更好挖掘预训练语言模型的能力
- 手段:在输入端添加文本,即重新定义任务(task reformulation)
- 特征工程阶段
- 依赖大量人工
- 需要监督数据
- 架构工程阶段
- 人工构建特征的工作量减少
- 设计各异的神经网络结构从而拿到结果需要监督数据
- 预训练-微调阶段
- 预训练可以不需要监督数据
- 工业界:更适合工业界的少数据场景,减少一部分标注成本
- 工业界对于少数据场景,往往(也有用半监督学习) 先rule-based走通业务逻辑,在线上线下标注数据,再进一步监督学习或微调。
- 目前工业界里还难打的过微调
- 定位:可以探索
- 学术界:适合学术研究
- 新任务+PL,效果不要太差,都是一次新的尝试与探索
- 定位:做研究
- Prompt 模版(Template)的构造
- Prompt 答案空间映射(Verbalizer)的构造
- 文本代入template,并且使用预训练语言模型进行预测
- 将预测的结果映射回label。
- 模板Template的作用:输入和输出进行重新构造,变成一个新的带有mask slots的文本
- 介绍:模板Template 构造,文本代入 Template
- 思路:
- step 1: 构建模板:[x] overall,it was a [z] movie.
- step 2: 构建文本x :l love this movie.
- step 3: 文本x 代入 Template [x]位置: l love this movie. overall, it was a [z] movie
- 动机:对于构造的prompt,需要知道预测词和 label 之间的关系,并且也不可能允许 z 是任意词
- 介绍:建立预测词-标签的映射关系
- eg:
fantastic、great、amazing -> positive
boring、bad -> negative
- 介绍:根据 Verbalizer ,使用预训练模型对 2.1 代后的 mask slots [z] 进行预测
- eg:
- I love this movie.overallit was a fantastic movie
注:预训练模型预测得到了结果 fantastic, 我们需要将其代入[z] 中
- 介绍:预测结果代入Verbalizer,得到映射后的结果
- eg:
fantastic -> positive
eg: I love this movie. overall, it was a [z] movie
- cloze prompt
- 介绍:[z] 在句中,适合使用了Mask任务的LM
- prefix prompt
- 介绍:[z] 在句末,适合生成LM、自回归LM (自编码LM(Bert) vs 自回归LM(GPT))
- 文本匹配任务,Prompt可以有两个[X]
- 手工设计
- 介绍:人工 手工设计 Template
- 优点:直观
- 缺点:成本高,需要实验、经验等
- 自动学习 模Template板
- 介绍:通过模型学习上下文,自动生成 Template
- 离散Prompt
- 介绍:自动生成自然语言词
- eg: 给定一个大的文本库,给定输入x和输出y,在文本库中离散地搜索出现频繁的中间词或连词等,从而得到一个模板。
- 连续Prompt
- 介绍:Template的设计不必拘泥于自然语言,直接变成embedding表示也是可以的,设置独立于LM的模板参数,可以根据下游任务进行微调
- eg:给定一个可训练的参数矩阵,将该参数矩阵与输入文本进行连接,从而丢入模型进行训练。
- 介绍:寻找合适的答案空间Z,以及答案与标签的映射
- eg:Knowledgeable Prompt-tuning:Incorporating Knowledge intoPrompt Verbalizer for Text Classification (KPT)
- 用KB去查询Label相关词作为候选集,然后去噪
注:Label Space Y 是: Positive, Negative, Answer Space Z 可以是表示positive或者negative 的词,例如 Interesting/Fantastic/Happy/Boring/1-Star/Bad,具体的 Answer Space Z 的选择范围可以由我们指定。可以指定一个 y 对应1-N个字符/词。
具体的答案空间的选择可以有以下三个分类标注:
- 根据形状
- 1.1 Token 类型
- 1.2 Span 类型
- 1.3 Sentence 类型
- 是否有界
- 2.1 有界
- 2.2 无界
- 是否人工选择
- 3.1 人工选择
- 3.2 自动搜素
- 3.2.1 离散空间
- 3.2.2 连续空间
- 动机:在定义完模版以及答案空间后,需要选择合适的预训练语言模型对 prompt 进行预测,如何选择一个合适的预训练语言模型也是需要人工经验判别的。
- 具体的预训练语言模型分类:
- autoregressive-models: 自回归模型,主要代表有 GPT,主要用于生成任务;
- autoencoding-models: 自编码模型,主要代表有 BERT,主要用于NLU任务;
- seq-to-seq-models:序列到序列任务,包含了an encoder 和 a decoder,主要代表有 BART,主要用于基于条件的生成任务,例如翻译,summary等;
- multimodal-models:多模态模型
- retrieval-based-models:基于召回的模型,主要用于开放域问答
注:想要做summary 任务,我们可以选择更合适的 BART 模型
- 动机:如何对已有的 Prompt 进行任务增强以及拓展
- 从以下几个方面进行探讨
- Prompt Ensemble:Prompt 集成,采用多种方式询问同一个问题
- Prompt Augmentation:Prompt 增强,采用类似的 prompt 提示进行增强
- Prompt Composition:Prompt 组合,例如将一个任务,拆成多个任务的组合,比如判别两个实体之间是否是父子关系,首先对于每个实体,先用Prompt 判别是人物,再进行实体关系的预测。
- Prompt Decomposition:将一个prompt 拆分成多个prompt
- 动机:Prompt-based 模型在训练中,有多种训练策略,可以选择哪些模型部分训练,哪些不训练
- 根据训练数据区分:
- Zero-shot: 对于下游任务,没有任何训练数据
- Few-shot: 对于下游任务只有很少的训练数据,例如100条
- Full-data: 有很多的训练数据,例如1万多条数据
- 根据不同的参数更新的部分:
- 预训练模型
- Prompts 参数
- 预训练语言模型:可以选择精调,或者不训练
- 对于prompts:
- Tuning-free Prompting
- 直接做zero-shot
- Fixed-LM Prompt Tuning
- 引入额外与Prompt相关的参数,固定LM参数,微调与Prompt相关参数
- Fixed-prompt LM Tuning
- 引入额外与Prompt相关的参数,固定与Prompt相关参数,微调LM
- Prompt + LM Tuning
- 引入额外与Prompt相关的参数,两者都微调
- Tuning-free Prompting
- 策略选择
- 数据量级是多少
- 是否有个超大的 Left-to-right 的语言模型(注:通常如果只有很少的数据的时候,往往希望不要去 fine-tune 预训练语言模型,而是使用LM的超强能力,只是去调prompt 参数。而让数据量足够多的时候,可以精调语言模型。)
手工设计prompt(基于token的prompt)还有一个问题是,模型对prompt很敏感,不同的模板得到的效果差别很大。
注:prompt一字之差效果也会差别很大 (来自文献[2])
所以研究学者就提出自动学习prompt向量的方法。因为我们输入进去的是人类能看懂的自然语言,那在机器眼里是啥,啥也不是, 也不能这么说吧,prompt经过网络后还是得到一个个向量嘛,既然是向量,当然可以用模型来学习了,甚至你输入一些特殊符号都行,模型似乎无所不能,什么都能学,只要你敢想,至于学得怎么样,学到了什么,还需进一步探究。
-
动机:手工设计prompt(基于token的prompt)存在问题,那么是否可以引入(基于vector的prompt),所以就有了 基于 token+vector 的 prompt
-
具体说明(如下图):
- 任务:模型来预测一个国家的首都
- 左边是全token的prompt,文献里称为“离散的prompt”,有的同学一听"离散"就感觉懵了,其实就是一个一个token组成的prompt就叫“离散的prompt”。
- 右边是token+vector形式的prompt,其实是保留了原token prompt里面的关键信息(capital, Britain),(capital, Britain)是和任务、输出结果最相关的信息,其他不关键的词汇(the, of ,is)留给模型来学习。
-
token形式的prompt: “The captital of Britain is [MASK]”
-
token+vector: “h_0 , h_1, ... h_i, captital, Britain, h_(i+1), ..., h_m [MASK]”
- 思路:全vecotor可以直接拼接在预训练模型的layer里面,而且这个模型可以做序列tagging任务(给输入序列中每个token打标签)
- 动机:以上Prompt采用vector形式之后,在训练集比较大(full-data)的时候效果是好的,但是在few-shot(训练集很小)场景下就不好了,因为数据量小不好学嘛。那怎么办呢?既然NLP任务都有预训练模型,那么prompt是否也可以先进行预训练再微调呢?
- 思路:拿大量无标签语料对Prompt先做个预训练,再在下游任务上做微调
Prompt Learning 的优势有哪些呢?我们可以从四个角度进行分析。
- Level 1. Prompt Learning 角度
- Level 2. Prompt Learning 和 Fine-tuning 的区别
- Level 3. 现代 NLP 历史
- Level 4. 超越NLP
- Prompt Learning 可以将所有的任务归一化预训练语言模型的任务
- 避免了预训练和fine-tuning 之间的gap,几乎所有 NLP 任务都可以直接使用,不需要训练数据。
- 在少样本的数据集上,能取得超过fine-tuning的效果。
- 使得所有的任务在方法上变得一致
- Fine-tuning 是使得预训练语言模型适配下游任务
- Prompting 是将下游任务进行任务重定义,使得其利用预训练语言模型的能力,即适配语言模型
Prompting 方法是现在NLP的第四范式。其中现在NLP的发展史包含
- Feature Engineering:即使用文本特征,例如词性,长度等,在使用机器学习的方法进行模型训练。(无预训练语言模型)
- Architecture Engineering:在W2V基础上,利用深度模型,加上固定的embedding。(有固定预训练embedding,但与下游任务无直接关系)
- Objective Engineering:在bert 的基础上,使用动态的embedding,在加上fine-tuning。(有预训练语言模型,但与下游任务有gap)
- Prompt Engineering:直接利用与训练语言模型辅以特定的prompt。(有预训练语言模型,但与下游任务无gap)
我们可以发现,在四个范式中,预训练语言模型,和下游任务之间的距离,变得越来越近,直到最后Prompt Learning是直接完全利用LM的能力。
Prompt 可以作为连接多模态的一个契机,例如 CLIP 模型,连接了文本和图片。相信在未来,可以连接声音和视频,这是一个广大的待探索的领域。
- prompt设计:可以手动设计模板,也可以自动学习prompt,这里可填坑的地方比较多;
- 预训练模型的选择:选择跟任务贴近的预训练模型即可;
- 预测结果到label的映射:如何设计映射函数,这里可填坑的地方也比较多
- 训练策略:根据prompt是否有参数,预训练模型参数要不要调,可以组合出各种训练模式,根据标注数据样本量,选择zero-shot, few-shot还是full-data。比如few-shot场景,训练数据不多,如果prompt有参数,可以固定住预训练模型的参数,只调prompt的参数,毕竟prompt参数量少嘛,可以避免过拟合。
- OpenPrompt
- 由清华推出了prompt-tuning工具包
- 特点:每个 class 都继承了 torch 的类或者 huggingface 的类,可以方便地部署自己的任务
- Prompt在中文分类few-shot场景中的尝试
- 特点:包含数据集构建、模板构建、训练测试一套完整代码
根据你的任务和数据来定义classes 和 InputExample。
以情感分类任务为例,classes包含2个label:"negative"和"positive"
from openprompt.data_utils import InputExample
classes = [ # There are two classes in Sentiment Analysis, one for negative and one for positive
"negative",
"positive"
]
dataset = [ # For simplicity, there's only two examples
# text_a is the input text of the data, some other datasets may have multiple input sentences in one example.
InputExample(
guid = 0,
text_a = "Albert Einstein was one of the greatest intellects of his time.",
),
InputExample(
guid = 1,
text_a = "The film was badly made.",
),
]
根据具体任务选择合适的预训练语言模型,这里采用的预训练模型是bert,因为根据prompt的设计,是想让模型输出[mask]位置的词语,属于填空问题。
from openprompt.plms import load_plm
plm, tokenizer, model_config, WrapperClass = load_plm("bert", "bert-base-cased")
这个例子是手动设计模板,模板放在ManualTemplate里面,text = '{"placeholder":"texta"} It was {"mask"}', 其中text_a就是InputExample里面的输入text_a,It was {"mask"} 就是prompt。
from openprompt.prompts import ManualTemplate
promptTemplate = ManualTemplate(
text = '{"placeholder":"text_a"} It was {"mask"}',
tokenizer = tokenizer,
)
在情感分类里面,[Mask]位置的输出是一个单词,我们要把这些单词映射成"positive","negative"标签,这个过程称为"Verbalizer",比如"bad"属于"negative", "good", "wonderful", "great"属于"positive"。
from openprompt.prompts import ManualVerbalizer
promptVerbalizer = ManualVerbalizer(
classes = classes,
label_words = {
"negative": ["bad"],
"positive": ["good", "wonderful", "great"],
},
tokenizer = tokenizer,
)
将前面几步构建的模板(promptTemplate)、预训练模型(plm)、输出映射(promptVerbalizer)组成promptModel
from openprompt import PromptForClassification
promptModel = PromptForClassification(
template = promptTemplate,
plm = plm,
verbalizer = promptVerbalizer,
)
将前面几步构建的模板(promptTemplate)、预训练模型(plm)、输出映射(promptVerbalizer)组成promptModel
from openprompt import PromptDataLoader
data_loader = PromptDataLoader(
dataset = dataset,
tokenizer = tokenizer,
template = promptTemplate,
tokenizer_wrapper_class=WrapperClass,
)
# making zero-shot inference using pretrained MLM with prompt
promptModel.eval()
with torch.no_grad():
for batch in data_loader:
logits = promptModel(batch)
preds = torch.argmax(logits, dim = -1)
print(classes[preds])
# predictions would be 1, 0 for classes 'positive', 'negative'
- Prompt Learning全面梳理扫盲
- 一文轻松入门Prompt(附代码)
- prompt工程指南
- [细读经典]P-tuning:用“连续提示微调”来增强“超大规模语言模型”的下游能力
- NLPer福利!清华推出Prompt-tuning开源工具包,取代传统的微调fine-tuning
- 一文跟进Prompt进展!综述+15篇最新论文逐一梳理
- Prompt在低资源NER中的应用
- Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing
- GPT Understands, Too
- P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks
- PPT: Pre-trained Prompt Tuning for Few-shot Learning
- Fine-tune之后的NLP新范式:Prompt越来越火,CMU华人博士后出了篇综述文章
- 【NLP】Prompt Learning 超强入门教程
- 大模型prompt tuning技术上
- Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing