草庐IT

解密prompt系列5. APE+SELF=自动化指令集构建代码实现

风雨中的小七 2023-04-17 原文

上一章我们介绍了不同的指令微调方案, 这一章我们介绍如何降低指令数据集的人工标注成本!这样每个人都可以构建自己的专属指令集, 哈哈当然我也在造数据集进行时~

介绍两种方案SELF Instruct和Automatic Prompt Engineer,前者是基于多样的种子指令,利用大模型的上下文和指令理解能力,以及生成的多样性来Bootstrap生成更多样的指令样本,后者是prompt逆向工程,基于输入和输出,使用LLM来生成和挑选最优的prompt指令。

于是我把这两个方法强行组了CP,用APE把原始任务转化成种子指令,再用SELF去扩充,在医学和金融NLP任务上进行了尝试。也在huggingface上用gradio做成了可视化的应用, 有API Key的盆友可以自己尝试下效果:https://huggingface.co/spaces/xl2533/MakeInstruction ,记得fork到自己的space再尝试哟~

Automatic Prompt Engineer(APE)

指令生成

这里作者基于原始的输入+输出,部分样本只有输出,例如自由生成类的任务,来让大模型预测,原始指令是什么。作者把指令生成的模板分成了3类,不过个人感觉其实只要一类即可,就是few-shot样本在前,待生成的指令在最后的向前生成类型,如下图

原始论文使用的是text-davinci-002来完成这个指令生成的任务,每个样本使用5条few-shot样例作为上下文,让模型输出可能的指令。这里我把生成指令的模型改成了ChatGPT(只是因为便宜),prompt模板也根据ChatGPT的特点做了调整。核心是ChatGPT作为对话模型比davinci-002,003废话要多。如果还按上面的指令来写,你可能会得到ChatGPT的回答是:我认为这个朋友收到的指令时blablabla.....

在实际测试中我还发现了几个有意思的点

  1. 对于相对抽象,偏生成类的任务,few-shot样本要给够,模型才有可能猜到'无偏'的指令

例如相似新闻标题生成任务:我输入了2条样本作为few-shot如下,模型预测:"将输入中的公司或组织名称规范化为全称"

我又采样了两条样本如下,模型预测:"将公司公告或新闻标题简化成简短的标题,包括公司名称和主要内容"

哈哈预测的指令确实都没毛病,只不过都是相似新闻标题生成的子集,所以你需要根据任务输入输出的多样性程度来调整你的few-shot样本数,多样性越高你需要的few-shot样例越多

  1. 构建指令样本,说人话很重要

例如我把以上的相似标题生成任务,简化成了判断两个标题是否描述同一事件的分类任务。最初我的输入如下。看起来也没毛病是不是?然模型的预测是:"我无法确定这个任务指令的具体内容,但它可能与文本分类或者自然语言处理相关。给出输入文本,需要判断该文本是否符合某种特定的模式或标准,从而得出输出结果"

但是当我把样本中的输出改成符合任务语意的相同/不相同时,模型预测是:"判断两个新闻标题是否相同,如果相同输出"相同",否则输出"不相同"

当然考虑生成模型解码的随机性,我在第一类样本构建上多次采样也得到了类似相似度判断的指令,但整体效果都差于下面的构建方式,所以和MRC构建很相似,一切以符合语意为第一标准

  1. 不要你以为,要模型以为!

最初我对这种机器生成指令的方式是不太感冒的,但是在医学术语标准化这个任务上,我对比了APE得到的最优指令,和我人工写的指令,在单测时确实是模型指令,得到正确答案的概率更高。所以我大胆猜测,因为模型之间的一致性,所以合理使用模型生成的指令,能提供更精准的上下文任务描述,且理论上都应该不差于人类指令。

指令打分

这里作者使用了两种打分方式,来评估多组样本生成的多个候选指令的优劣

  1. Accuracy:使用模型预测的正确率,例如对于QA问题,根据不同指令在相同样本上模型回答的准确率来评价指令的效果。
  2. Log Probability:使用模型预测的logprobs作为评价指标。注意这个指标有些tricky,我最初认为和上面的Accuracy一样是让模型去预测,把预测正确的token的logprobs求和。后来发现是把输入+输出+指令都喂给模型,计算模型生成原始输出的概率,很好解决了生成类任务解码随机不同指令无法比较的问题。

如何调openai接口获取输入的logprobs:
把echo=True,logprobs=1, 就能返回所有采样token的logprobs,logprobs取值对应TopN的返回,openai最多只给你返回Top5 token,包括实际被采样的token。max_tokens=0, 不让模型生成新的文本,就可以让模型原样返回我们喂进去的输入,以及对应的模型计算的每个token的条件概率啦

同时作者加入了随机搜索,既对模型生成的指令,过滤低分的部分,对于高分的指令集,让模型基于以下指令模板,为高分指令生成相似的指令,和原始生成的指令一起排序选出最优指令。

这块实现时,我把相似指令的部分拿掉了,改成人工加入,针对得到的高分指令,补充上自己认为缺少核心的信息后使用log prob的打分方式来评估是否有提升。所以应用里,我把Generated Prompt的窗口改成了可交互的,可以直接对生成的指令做修改,再Eval效果即可。

例如在医疗搜索意图的任务上,很明显模型无法理解"多问"标签是啥意思,所以最初多组样本得到的最优指令是下图的第二个,而我人工加入"多问"的指令后,得到了效果更好的第一个指令

效果

这里作者使用了REF[1]里面使用的24个指令任务,每类任务挑选5对样本,使用以上的方案得到最优的指令,再在剩余样本上,和人工指令以及REF[1]论文中使用的方案(没有搜索和打分排序的APE)以下称为greedy,进行效果对比。APE的效果在24个任务上基本可以打平人工模板甚至在部分任务上还要超越人工指令。在BigBench这类难度更高的样本上,APE在17(共21)个任务上也超越了人工指令的效果。

我在4个医学数据集上APE+人工优化得到的最优指令如下

任务 指令
搜索意图 生成医学相关问题的答案。给定一个输入问题,需要根据问题生成相应的输出答案。答案包括临床表现、病因、治疗方法、作用、定义等等,如果有多个问题,返回多问
医疗术语标准化 将医学手术名称的术语表述标准化。输入是医学手术的名称,输出是对该手术的名称进行修正、标准化,以供医学专业人员更好地理解
医疗药物功能实体抽取 给定药品信息和用途说明,根据用途说明提取出药品的主治功能。
医疗文献QA生成 训练一个问答系统,给定一些医学文本,能够回答用户提问关于该文本内容的问题。每个输入-输出对是一组文本和对应的问题及答案。输出的形式是以下Json格式{"问题":\(问题, "回答":\)回答}

以医学术语标准化为例我简化了APE提供的gradio应用,效果如下

SELF-Instruct

上一步我们依赖APE得到了种子指令集,但是丰富度和多样性是远远不够的。这里SELF提出了一种Bootstrap方案来让LLM基于种子指令生成新的指令的指令扩充方案。这个方案也后续被用于Alpaca项目中生成微调指令集,主要包括以下3个步骤

1. 新指令生成

首先作者人工构建了175个种子指令,这些种子指令由1条指令和1个样本构成。每个Step作者会从中采样8个指令,其中6个来自以上种子,2个来自LLM生成的指令,当然step1全部都是种子指令。然后基于如下的prompt模板构建模型输入

"""
Come up with a series of tasks:
Task 1: {instruction for existing task 1}
Task 2: {instruction for existing task 2}
Task 3: {instruction for existing task 3}
Task 4: {instruction for existing task 4}
Task 5: {instruction for existing task 5}
Task 6: {instruction for existing task 6}
Task 7: {instruction for existing task 7}
Task 8: {instruction for existing task 8}
Task 9:
"""

如果你看着这个模板,感觉和ChatGPT的模板格格不入,那就对了。因为作者是基于GPT3实现的,就是那个天真的续写模型。

而Alpaca项目在使用SELF构建指令样本时升级到了davinci-003模型,因为模型可以更好的理解指令,因此以上纯few-shot的prompt模板也改成了如下(用ChatGPT翻成了中文),同时输入的few-shot数量缩减到了3个

"""
你需要想出20个不同的任务指令。这些任务指令将输入GPT模型,我们将评估GPT模型完成指令的情况。
以下是要求:
1. 尽量不要在每个指令中重复使用动词,以最大化多样性
2. 指令的表达形式需要多样化。例如你可以把问题和祈使句结合起来
3. 指令的类型应该多样化,包括但不限于开放式生成、分类、抽取、问答、文本编辑等等
4. 指令应该是GPT模型可以完成的任务。例如,指令不能是输出图像或者视频,另一个例子,不要让助手在下午5点叫醒你或设置提醒,因为GPT不能执行任何动作
5. 指令必须是中文
6. 指令应该是1到2句话,可以是祈使句或问句。
20个任务的列表:
"""

我第一遍读完SELF是一脑门子问号

后来我(似乎&努力)说服了自己,就是LLM做了类似非线性差值的工作,因为每个Step都会采样不同的指令作为上下文,而模型也会根据随机采样得到的不同的上下文融合出新的指令。

2. 样本生成

只有指令还不够,还需要生成指令对应的输入和输出。在SELF原论文中,作者多加了一步分类任务,也就是让LLM先判断指令本身是否为分类任务,如果是则先生成输出,再生成输入。如果否则先生成输入再生成输出。原因是作者发现,对于分类任务模型倾向于只生成其中一个标签的输入。

但在Alpaca项目中,因为模型升级到Davinci-003,以上问题也似乎不再显著。并且随着模型输入长度变长,Alpaca直接把生成样本和生成指令的步骤合在了一起,一步到位。于是以上指令生成的模板被扩充成了以下

"""
7. 你应该为指令生成一个合适的输入。输入字段应该包含为指令提供的一个具体示例。它应该涉及真实的数据,而不应该包含简单的占位符。输入应该提供足够的内容,使指令具有挑战性,但理想情况下不应超过100个单词。
8. 不是所有的指令都需要输入。例如,当一个指令询问一些一般信息时,“世界上最高的山峰是什么”,就不需要提供具体的上下文。在这种情况下,我们只需在输入字段中放置“<无输入>”。
9. 输出应该是对指令和输入的合适回应。确保输出少于100个单词。
20个任务的列表:
"""

于是每一步其实是直接生成指令,以及指令对应的输入和输出样本。对于自由生成类的任务,也支持没有输入只有输出。

3. 过滤和后处理

针对以上生成的指令和样本还需要进行一些系列的后处理,主要包括模型预测解析,不合理指令的过滤,以及相似指令的过滤,解析的部分建议直接看代码

  1. 不可用指令过滤
  • 因为输出长度被截断的指令:response停止原因是length,则过滤最后一个指令
  • 过滤包含不可用关键词的指令:例如图片,音频等GPT模型无法完成的任务
  • 过滤过长,过短的指令
  • 过滤指令前几个字非英文/中文的指令
  1. 相似指令过滤

为了保证指令集的多样性,每轮都会计算新生成指令和已有指令的Rouge-L相似度,也就是摘要任务评估中用最长公共子串的长度计算的F值。只保留F值小于0.7的新的指令。同时也可以根据最近几轮生成指令的整体相似度,来判断是否要停止生成。

我用上面APE生成的4个医学NLP任务作为种子指令集,每轮采样2个人工,1个机器指令作为few-shot,生成2个新的指令任务。我对以上指令做了些调整,限制只生成医学领域的任务,得到了如下的结果。哈哈我决定去给账户充钱了。。。

大致就是这么多,感兴趣的盆友们一起玩起来(造数据的痛苦见者有份)~

更多Prompt相关论文·教程,开源数据·模型,以及AIGC相关玩法戳这里DecryptPrompt


Reference

  1. Instruction induction: From few examples to natural language task descriptions.
  2. Fairness-guided Few-shot Prompting for Large Language Models
  3. Flipped Learning: Guess the Instruction! Flipped Learning Makes Language Models Stronger Zero-Shot Learners

有关解密prompt系列5. APE+SELF=自动化指令集构建代码实现的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  4. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  5. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  10. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

随机推荐