Redian新闻
>
Prompt总结 | 从MLM预训任务到Prompt Learning原理解析与Zero-shot分类、NER简单实践

Prompt总结 | 从MLM预训任务到Prompt Learning原理解析与Zero-shot分类、NER简单实践

公众号新闻

每天给你送来NLP技术干货!


来自:老刘说NLP

Prompt Learning是当前NLP的一个重要话题,已经有许多文章进行论述。

从本质上来说,Prompt Learning 可以理解为一种下游任务的重定义方法,将几乎所有的下游任务均统一为预训练语言模型任务,从而避免了预训练模型和下游任务之间存在的 gap。

如此一来,几乎所有的下游 NLP 任务均可以使用,不需要训练数据,在小样本数据集的基础上也可以取得超越 Fine-Tuning 的效果,使得所有任务在使用方法上变得更加一致,而局限于字面意义上的理解还远远不够,我们可以通过一种简单、明了的方式进行讲述。

为了解决这一问题,本文主要从预训练语言模型看MLM预测任务、引入prompt_template的MLM预测任务、引入verblize类别映射的Prompt-MLM预测、基于zero-shot的prompt情感分类实践以及基于zero-shot的promptNER实体识别实践五个方面,进行代码介绍,供大家一起思考。


一、从预训练语言模型看MLM预测任务

MLM和NSP两个任务是目前BERT等预训练语言模型预训任务,其中MLM要求指定周围词来预测中心词,其模型机构十分简单,如下所示:

import torch.nn as nn 
from transformers import BertModel,BertForMaskedLM
class Bert_Model(nn.Module):
    def __init__(self,  bert_path ,config_file ):
        super(Bert_Model, self).__init__()
        self.bert = BertForMaskedLM.from_pretrained(bert_path,config=config_file)  # 加载预训练模型权重
    def forward(self, input_ids, attention_mask, token_type_ids):
        outputs = self.bert(input_ids, attention_mask, token_type_ids) #masked LM 输出的是 mask的值 对应的ids的概率 ,输出 会是词表大小,里面是概率 
        logit = outputs[0]  # 池化后的输出 [bs, config.hidden_size]
        return logit

下面一段代码,简单的使用了hugging face中的bert-base-uncased进行空缺词预测,先可以得到预训练模型对指定[MASK]位置上概率最大的词语【词语来自于预训练语言模型的词表】。

例如给定句子"natural language processing is a [MASK] technology.",要求预测出其中的[MASK]的词:

>>> from transformers import pipeline
>>> unmasker = pipeline('fill-mask', model='bert-base-uncased')
>>> unmasker("natural language processing is a [MASK] technology.")
[{'score': 0.18927036225795746, 'token': 3274, 'token_str''computer''sequence''natural language processing is a computer technology.'}, 
{'score': 0.14354903995990753, 'token': 4807, 'token_str''communication''sequence''natural language processing is a communication technology.'},
{'score': 0.09429361671209335, 'token': 2047, 'token_str''new''sequence''natural language processing is a new technology.'}, 
{'score': 0.05184786394238472, 'token': 2653, 'token_str''language''sequence''natural language processing is a language technology.'}, 
{'score': 0.04084266722202301, 'token': 15078, 'token_str''computational''sequence''natural language processing is a computational technology.'}]

从结果中,可以显然的看到,[MASK]按照概率从大到小排序后得到的结果是,computer、communication、new、language以及computational,这直接反馈出了预训练语言模型能够有效刻画出NLP是一种计算机、交流以及语言技术。

二、引入prompt_template的MLM预测任务

因此,既然语言模型中的MLM预测结果能够较好地预测出指定的结果,那么其就必定包含了很重要的上下文知识,即上下文特征,那么,我们是否可以进一步地让它来执行文本分类任务?即使用[MASK]的预测方式来预测相应分类类别的词,然后再将词做下一步与具体类别的预测?

实际上,这种思想就是prompt的思想,将下游任务对齐为预训练语言模型的预训练任务,如NPS和MLM,至于怎么对齐,其中引入两个概念,一个是prompt_template,即提示模版,以告诉模型要生成与任务相关的词语。因此,将任务原文text和prompt_template进行拼接,就可以构造与预训练语言模型相同的预训练任务。

例如,

>>> from transformers import pipeline
>>> unmasker = pipeline('fill-mask', model='bert-base-uncased')
>>> text = "I really like the film a lot."
>>> prompt_template = "Because it was [MASK]."  
>>> pred1 = unmasker(text + prompt_template)
>>> pred1
[
{'score': 0.14730973541736603, 'token': 2307, 'token_str''great''sequence''i really like the film a lot. because it was great.'}, 
{'score': 0.10884211212396622, 'token': 6429, 'token_str''amazing''sequence''i really like the film a lot. because it was amazing.'}, 
{'score': 0.09781625121831894, 'token': 2204, 'token_str''good''sequence''i really like the film a lot. because it was good.'}, 
{'score': 0.04627735912799835, 'token': 4569, 'token_str''fun''sequence''i really like the film a lot. because it was fun.'}, 
{'score': 0.043138038367033005, 'token': 10392, 'token_str''fantastic''sequence''i really like the film a lot. because it was fantastic.'}]

>>> text = "this movie makes me very disgusting. "
>>> prompt_template = "Because it was [MASK]."  
>>> pred2 = unmasker(text + prompt_template)
>>> pred2
[
{'score': 0.05464331805706024, 'token': 9643, 'token_str''awful''sequence''this movie makes me very disgusting. because it was awful.'}, 
{'score': 0.050322480499744415, 'token': 2204, 'token_str''good''sequence''this movie makes me very disgusting. because it was good.'}, 
{'score': 0.04008950665593147, 'token': 9202, 'token_str''horrible''sequence''this movie makes me very disgusting. because it was horrible.'}, 
{'score': 0.03569378703832626, 'token': 3308, 'token_str''wrong''sequence''this movie makes me very disgusting. because it was wrong.'},
{'score': 0.033358603715896606, 'token': 2613, 'token_str''real''sequence''this movie makes me very disgusting. because it was real.'}]

上面,我们使用了表达正面和负面的两个句子,模型得到最高的均是与类型相关的词语,这也验证了这种方法的可行性。

三、引入verblize类别映射的Prompt-MLM预测

与构造prompt-template之外,另一个重要的点是verblize,做词语到类型的映射,因为MLM模型预测的词语很不确定,需要将词语与具体的类别进行对齐,比如将"great", "amazing", "good", "fun", "fantastic", "better"等词对齐到"positive"上,当模型预测结果出现这些词时,就可以将整个预测的类别设定为positive;

同理,将"awful", "horrible", "bad", "wrong", "ugly"等词映射为“negative”时,即可以将整个预测的类别设定为negative;

>>> verblize_dict = {"pos": ["great""amazing""good""fun""fantastic""better"], "neg": ["awful""horrible""bad""wrong""ugly"]
... }
>>> hash_dict = dict()
>>> for k, v in verblize_dict.items():
...     for v_ in v:
...         hash_dict[v_] = k
>>> hash_dict
{'great''pos''amazing''pos''good''pos''fun''pos''fantastic''pos''better''pos''awful''neg''horrible''neg''bad''neg''wrong''neg''ugly''neg'}

因此,我们可以将这类方法直接加入到上面的预测结果当中进行修正,得到以下结果,

>>> [{"label":hash_dict[i["token_str"]], "score":i["score"]} for i in pred1]
[{'label''pos''score': 0.14730973541736603}, {'label''pos''score': 0.10884211212396622}, {'label''pos''score': 0.09781625121831894}, {'label''pos''score': 0.04627735912799835}, {'label''pos''score': 0.043138038367033005}]

>>> [{"label":hash_dict.get(i["token_str"], i["token_str"]), "score":i["score"]} for i in pred2]
[{'label''neg''score': 0.05464331805706024}, {'label''pos''score': 0.050322480499744415}, {'label''neg''score': 0.04008950665593147}, {'label''neg''score': 0.03569378703832626}, {'label''real''score': 0.033358603715896606}]

通过取top1,可直接得到类别分类结果,当然也可以综合多个预测结果,可以获top10中各个类别的比重,以得到最终结果:

{
  "text":"I really like the film a lot.""label""pos"  
  "text":"this movie makes me very disgusting. ""label":"neg"
}

至此,我们可以大致就可以大致了解在zero-shot场景下,prompt的核心所在。而我们可以进一步的想到,如果我们有标注数据,又如何进行继续训练,如何更好的设计prompt-template以及做好这个词语映射词表,这也是prompt-learning的后续研究问题。

因此,我们可以进一步地形成一个完整的基于训练数据的prompt分类模型,其代码实现样例具体如下,从中我们可以大致在看出具体的算法思想,我们命名为prompt.py

from transformers import AutoModelForMaskedLM , AutoTokenizer
import torch

class Prompting(object):

  def __init__(self, **kwargs):
    model_path=kwargs['model']
    tokenizer_path= kwargs['model']
    if "tokenizer" in kwargs.keys():
      tokenizer_path= kwargs['tokenizer']
    self.model = AutoModelForMaskedLM.from_pretrained(model_path)
    self.tokenizer = AutoTokenizer.from_pretrained(model_path)

  def prompt_pred(self,text):
    """
    输入带有[MASK]的序列,输出LM模型Vocab中的词语列表及其概率
    "
""
    indexed_tokens=self.tokenizer(text, return_tensors="pt").input_ids
    tokenized_text= self.tokenizer.convert_ids_to_tokens (indexed_tokens[0])
    mask_pos=tokenized_text.index(self.tokenizer.mask_token)
    self.model.eval()
    with torch.no_grad():
      outputs = self.model(indexed_tokens)
      predictions = outputs[0]
    values, indices=torch.sort(predictions[0, mask_pos],  descending=True)
    result=list(zip(self.tokenizer.convert_ids_to_tokens(indices), values))
    self.scores_dict={a:b for a,b in result}
    return result

  def compute_tokens_prob(self, text, token_list1, token_list2):
    """
    给定两个词表,token_list1表示表示正面情感positive的词,如good, great,token_list2表示表示负面情感positive的词,如good, great,bad, terrible.   
    在计算概率时候,统计每个类别词所占的比例,score1/(score1+score2)并归一化,作为最终类别概率。
    "
""
    _=self.prompt_pred(text)
    score1=[self.scores_dict[token1] if token1 in self.scores_dict.keys() else 0\
            for token1 in token_list1]
    score1= sum(score1)
    score2=[self.scores_dict[token2] if token2 in self.scores_dict.keys() else 0\
            for token2 in token_list2]
    score2= sum(score2)
    softmax_rt=torch.nn.functional.softmax(torch.Tensor([score1,score2]), dim=0)
    return softmax_rt

  def fine_tune(self, sentences, labels, prompt=" Since it was [MASK].",goodToken="good",badToken="bad"):
    """  
    对已有标注数据进行Fine tune训练。
    "
""
    good=tokenizer.convert_tokens_to_ids(goodToken)
    bad=tokenizer.convert_tokens_to_ids(badToken)
    from transformers import AdamW
    optimizer = AdamW(self.model.parameters(),lr=1e-3)
    for sen, label in zip(sentences, labels):
      tokenized_text = self.tokenizer.tokenize(sen+prompt)
      indexed_tokens = self.tokenizer.convert_tokens_to_ids(tokenized_text)
      tokens_tensor = torch.tensor([indexed_tokens])
      mask_pos=tokenized_text.index(self.tokenizer.mask_token)
      outputs = self.model(tokens_tensor)
      predictions = outputs[0]
      pred=predictions[0, mask_pos][[good,bad]]
      prob=torch.nn.functional.softmax(pred, dim=0)
      lossFunc = torch.nn.CrossEntropyLoss()
      loss=lossFunc(prob.unsqueeze(0), torch.tensor([label]))
      loss.backward()
      optimizer.step()

四、基于zero-shot的prompt情感分类实践

下面我们直接以imdb中的例子进行zero-shot的prompt分类实践,大家可以看看其中的大致逻辑:

1、加入

>>from transformers import AutoModelForMaskedLM , AutoTokenizer
>>import torch
>>model_path="bert-base-uncased"
>>tokenizer = AutoTokenizer.from_pretrained(model_path)
>>from prompt import Prompting
>>prompting= Prompting(model=model_path)

2、使用prompt_pred直接进行情感预测

>>prompt="Because it was [MASK]."
>>text="I really like the film a lot."
>>prompting.prompt_pred(text+prompt)[:10]
[('great', tensor(9.5558)),
 ('amazing', tensor(9.2532)),
 ('good', tensor(9.1464)),
 ('fun', tensor(8.3979)),
 ('fantastic', tensor(8.3277)),
 ('wonderful', tensor(8.2719)),
 ('beautiful', tensor(8.1584)),
 ('awesome', tensor(8.1071)),
 ('incredible', tensor(8.0140)),
 ('funny', tensor(7.8785))]
>>text="I did not like the film."
>>prompting.prompt_pred(text+prompt)[:10]
[('bad', tensor(8.6784)),
 ('funny', tensor(8.1660)),
 ('good', tensor(7.9858)),
 ('awful', tensor(7.7454)),
 ('scary', tensor(7.3526)),
 ('boring', tensor(7.1553)),
 ('wrong', tensor(7.1402)),
 ('terrible', tensor(7.1296)),
 ('horrible', tensor(6.9923)),
 ('ridiculous', tensor(6.7731))]

2、加入neg/pos词语vervlize进行情感预测

>>text="not worth watching"
>>prompting.compute_tokens_prob(text+prompt, token_list1=["great","amazin","good"], token_list2= ["bad","awfull","terrible"])
tensor([0.1496, 0.8504])

>>text="I strongly recommend that moview"
>>prompting.compute_tokens_prob(text+prompt, token_list1=["great","amazin","good"], token_list2= ["bad","awfull","terrible"])
tensor([0.9321, 0.0679])

>>text="I strongly recommend that moview"
>>prompting.compute_tokens_prob(text+prompt, token_list1=["good"], token_list2= ["bad"])
tensor([0.9223, 0.0777])

五、基于zero-shot的promptNER实体识别实践

进一步的,我们可以想到,既然分类任务可以进行分类任务,那么是否可以进一步用这种方法来做实体识别任务呢?

实际上是可行的,暴力的方式,通过获取候选span,然后询问其中实体所属的类型集合。

1、设定prompt-template

同样的,我们可以设定template,以一个人物为例,John是一个非常常见的名字,模型可以直接知道它是一个人,而不需要上下文

Sentence. John is a type of [MASK]

2、使用prompt_pred直接进行预测 我们直接进行处理,可以看看效果:

>>prompting.prompt_pred("John went to Paris to visit the University. John is a type of [MASK].")[:5]
[('man', tensor(8.1382)),
 ('john', tensor(7.1325)),
 ('guy', tensor(6.9672)),
 ('writer', tensor(6.4336)),
 ('philosopher', tensor(6.3823))]
>>prompting.prompt_pred("Savaş went to Paris to visit the university. Savaş is a type of [MASK].")[:5]
[('philosopher', tensor(7.6558)),
 ('poet', tensor(7.5621)),
 ('saint', tensor(7.0104)),
 ('man', tensor(6.8890)),
 ('pigeon', tensor(6.6780))]

2、加入类别词语vervlize进行情感预测
进一步的,我们加入类别词,进行预测,因为我们需要做的识别是人物person识别,因此我们可以将person类别相关的词作为token_list1,如["person","man"],其他类型的,作为其他词语,如token_list2为["location","city","place"]),而在其他类别时,也可以通过构造wordlist字典完成预测。

>>> prompting.compute_tokens_prob("It is a type of [MASK].",
                              token_list1=["person","man"], token_list2=["location","city","place"])
tensor([0.7603, 0.2397])

>>> prompting.compute_tokens_prob("Savaş went to Paris to visit the parliament. Savaş is a type of [MASK].",
                              token_list1=["person","man"], token_list2=["location","city","place"])//确定概率为0.76,将大于0.76的作为判定为person的概率
tensor([9.9987e-01, 1.2744e-04])

从上面的结果中,我们可以看到,利用分类方式来实现zero shot实体识别,是直接有效的,“Savaş”判定为person的概率为0.99,

prompting.compute_tokens_prob("Savaş went to Laris to visit the parliament. Laris is a type of [MASK].",
                              token_list1=["person","man"], token_list2=["location","city","place"])
tensor([0.3263, 0.6737])

而在这个例子中,将“Laris”这一地点判定为person的概率仅仅为0.3263,也证明其有效性。

总结

本文主要从预训练语言模型看MLM预测任务、引入prompt_template的MLM预测任务、引入verblize类别映射的Prompt-MLM预测、基于zero-shot的prompt情感分类实践以及基于zero-shot的promptNER实体识别实践五个方面,进行了代码介绍。

关于prompt-learning,我们可以看到,其核心就在于将下游任务统一建模为了预训练语言模型的训练任务,从而能够最大地挖掘出预训模型的潜力,而其中的prompt-template以及对应词的构造,这个十分有趣,大家可以多关注。

参考文献

1、https://huggingface.co/bert-base-uncased
2、https://github.com/savasy/prompt-based-learning



📝论文解读投稿,让你的文章被更多不同背景、不同方向的人看到,不被石沉大海,或许还能增加不少引用的呦~ 投稿加下面微信备注“投稿”即可。

最近文章

COLING'22 | SelfMix:针对带噪数据集的半监督学习方法

ACMMM 2022 | 首个针对跨语言跨模态检索的噪声鲁棒研究工作

ACM MM 2022 Oral  | PRVR: 新的文本到视频跨模态检索子任务




投稿或交流学习,备注:昵称-学校(公司)-方向,进入DL&NLP交流群。
方向有很多:机器学习、深度学习,python,情感分析、意见挖掘、句法分析、机器翻译、人机对话、知识图谱、语音识别等
记得备注~

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
DALL-E和Flamingo能相互理解吗?三个预训练SOTA神经网络统一图像和文本NeurIPS 2022 | 首个将视觉、语言和音频分类任务进行统一的半监督分类学习基准又见百小僧,.NET分布式毫秒级定时任务Sundial面世3D版DALL-E来了!谷歌发布文本3D生成模型DreamFusion,重点是zero-shotAI推理加速原理解析与工程实践分享 | Q推荐After Losing Her Son to a Chicago Shooting, She Tries to Move OnSpring Boot 3 步完成日志脱敏,简单实用~AI 训练加速原理解析与工程实践分享 | Q推荐[电脑] AMD Zen4 —— R9 7900X & ASUS ROG CROSSHAIR X670E HERO 测试分享!!一键从Prompt到PowerPoint,斯坦福博士生自制的PPT生成神器火了Uber简直老谋深算...自制咳嗽排痰器,简单实用5 种瀑布流场景的实现原理解析PromptCLUE:大规模多任务Prompt预训练中文开源模型CLUE社区最新神器!PromptCLUE:大规模多任务Prompt预训练中文开源模型无性婚姻,为了身份嫁给了白老头--美低端生活(十)通过睡觉改变运气的奥秘(简单实用)Chinese Netizens Ask: Should Boys be Allowed in Women-Only AreasShanghai to Offer Inhaled COVID Vaccine as Booster ShotAfter the Yangtze, China Passes Law to Protect Yellow Riverlearning learning itself我在斯坦福当教练和评委 My Learnings from Coaching Stanford Entrepreneurs!爷爷 奶奶 populated college essays“白开水”英语是“white water”吗?| 1 min learning English重症高峰来袭,多地ICU告急:床位越少,分类、分流越重要Does the third world benefit from the US-China confrontation?语文课文背诵16招,简单实用!(老师请转给家长)At Home and at Work, China’s Weaning Moms Face a BottleneckVideoMAE:简单高效的视频自监督预训练新范式|NeurIPS 2022"miss the boat"竟然有这层意思!| 1 min learning English下一个“嫖娼”的会是谁?解除Hiring Freeze!Zoom开启秋招NG、Intern岗位!李飞飞两位高徒联合指导:能看懂「多模态提示」的机器人,zero-shot性能提升2.9倍两条路Erayak Power Solution Group(雷亚电子)路演PPT分享
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。