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,情感分析、意见挖掘、句法分析、机器翻译、人机对话、知识图谱、语音识别等
记得备注~

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

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