写在最前面
本文为邹德清教授的《网络安全专题》课堂笔记系列的文章,本次专题主题为大模型。
范晓萱同学分享了Improving Few-shot Prompts with Relevant Static Analysis Products《用相关静态分析产品改进少样本提示》
论文:https://arxiv.org/pdf/2304.06815.pdf
论文12页信息量比较大,PPT相当充实,长达49页
分享时语速快而不乱,好飒啊
方向给出更好提示的结构,添加语义信息,从而让大模型有更好的结果
也是我感兴趣的,感觉未来无论是日常Prompt或者论文都有可能会用到
原文1.8w字,侧重自己感兴趣的部分,方便后面找论文灵感
论文名片
- 大型语言模型(LLM)是一类新的计算引擎,通过提示工程进行“编程”。
- 开发人员在处理编码任务时往往会有意识和无意识地记住一系列
语义事实
,这些都是快速阅读产生的肤浅、简单的事实。 - 转换器式 LLM 可能并没有天生能够
执行简单级别的“代码分析”
,需要明确添加此类型信息
来帮助处理代码。 - 目标是使用
代码摘要任务
来研究这个问题,并评估用语义事实
显式地自动增强 LLM 的提示是否真的有帮助。 - 先前的研究表明,LLM 在代码摘要方面的 性能受益于 从同一项目or通过信息检索方法找到的示例中
抽取的少量样本
。 添加语义事实
确实有帮助,可以提高先前工作建议的几种不同设置的性能,包括两种不同的大型语言模型。
在大多数情况下,性能的改善接近或超过2 BLEU;
对于具有挑战性的CodeSearchNet数据集中的PHP语言,这种增强产生了超过30 BLEU的性能。
未来论文的思考
- 添加语义信息的方法 能够提高LLMs在代码摘要任务中的性能。
- 探究如何通过在LLMs的prompt中添加语义信息,来提高代码摘要任务的性能。
背景
大型语言模型(LLM) 在任务上的表现通常优于较小的、自定义训练的模型
,特别是在使用“少量”示例集进行提示时。
LLM使用大量数据在自我监督(掩蔽或去噪)任务上进行预训练,并随着训练数据和参数数量的增加而表现出令人惊讶的优异行为。
LLM只需few-shot (or even zero-shot) learning
,即可在许多任务中表现出色:
只需在prompt中首先插入几个范例输入输出对
,模型就可以为给定的输入生成非常好的输出!
LLM in SE 预训练语言模型在软件工程中的应用和发展趋势
重点:
- 预训练LLM的成本,两个关键因素,即
微调的硬件支持和数据集的难度
,推动了预训练LLM在软件工程中的使用。- LLM在few-shot时表现更好,与小模型相比具有更大的优势。
- few-shot learning的定义和使用方法。
- LLM已经广泛应用于软件工程中的许多不同问题,包括
代码生成、测试、突变生成、程序修复、事件管理,甚至代码摘要生成
。这些建立在预先训练的LLM之上的工具正在推动技术的发展。 - 两个关键因素决定了预训练LLM日益占据主导地位,这两个因素都集中在
成本
上。
第一个因素 是训练自己的大型模型,甚至对预训练的LLM进行广泛的微调,都需要昂贵的硬件。
第二个因素 是为许多重要的软件工程任务生成受监督的数据集是困难和耗时的,除了最大的组织之外,通常超出了其他所有组织的能力。 - 尽管存在一些较小的模型,如
Polycoder或Codegen,专门用于代码
,已经获得了普及。
但与整体LLM趋势相反,这些小模型在few-shot时表现不佳
,因此在只有少量数据可用时没有帮助。
因此,文章关注的是LLM而不是小模型。
few-shot learning
通过few-shot learning,模型实际的参数保持不变
。相反,根据之前的定义,我们将一些问题实例与解决方案一起呈现给模型(即,作为“范例”的问题-解决方案对),并要求它完成最后一个实例(“测试输入”)的答案,我们没有为其提供解决方案。
因此,对于每个示例由<input, output>对组成,并且只有一个侧式输入input𝑡(没有相应的,期望的输出output𝑡),最终提示看起来像:
有了这个提示,LLM生成output𝑡,模拟提示中的示例所示的输入输出行为。在实践中,这种方法表现得相当好。
Prompt Engineering及其例子
Prompt Engineering 代理了传统Bert类模型的微调训练的方式,使用更为高效的Prompt来指导LLM,使其产生出期望的结果,而无需改变模型的权重
。
prompt 是人与大模型交互的媒介,是与LLM进行交互最直接最常用的方式:直接告诉模型我们要它干什么,模型便会干什么。
例如,我们可以问模型“用最多20个词总结下列文字”、“中国的首都在哪里?”、“判断下列文字的情感是正向还是负向”等等。我们输入的这些prompt,将会被模型识别、处理,最终输出为我们要的答案。
注意,有时内容为空,如比较简短的prompt:“中国的首都在哪里”、“模仿百年孤独的开头写一段话”这种言简意赅的prompt就只有指令、没有内容。
prompt格式:文本、视觉(bounding box、关键像素点)、语音
主流的prompt一般采用文本格式
。但文本并非唯一的形式。
我们在视觉任务中往往可以采用视觉prompt
,如bounding box、关键像素点等。以最近大火的segment anything model[1]为例,其便使用了不同的视觉prompt。
除此之外,我们在其他任务中也可以使用语音
等多种形式的prompt。
prompt的两大好处
总体来说,prompt有两大好处:
- 有研究表明,1个prompt相当于100个真实数据样本[2],充分说明了prompt蕴含的信息量之巨大。
- prompt在下游任务
数据缺乏
的场景下、甚至是zero-shot场景下,有着无可比拟的优势。因为大模型通常无法在小数据上微调,因此,基于prompt的微调技术便成为了首要选择。
[1] Segment anything https://ai.facebook.com/research/publications/segment-anything/
[2] https://arxiv.org/abs/2103.08493
Prompt的不同分类
Zero-shot prompt
零样本的prompt,即在评测大模型能力时,不给模型提供参考示例,仅通过任务描述或问题来推断出结果。
优点:这种方式灵活性高,不需要为每个新任务重新训练模型。
缺点:模型可能无法准确理解任务的微妙差别或特定要求。
> 例子:判断以下句子的情绪的类别
Few-shot
- Few-shot 学习是通过少量标注样本来完成任务的一种学习方式
- 标注样本通常包括输入和预期输出,帮助模型更好地理解任务性质和要求
- Few-shot 学习比 zero-shot 学习性能更好,但 代价 是
消耗更多 token,可能会达到上下文长度限制
- 在自然语言处理中,Few-shot 学习可以应用于
文本分类、指代消解、机器翻译、对话生成
等任务 - Few-shot 学习可以减少标注数据成本,具有更好的泛化性能,但需要为每个新任务提供示例文本
- Few-shot 学习可能不适用于需要快速处理大量未知任务的场景,而且需要更多计算资源和时间进行模型训练和调整
- 在实际应用前需要综合评估具体场景
> 例子: > 以下是一些示例句子及其情绪类别,请根据这些示例句子的情绪类别,判断下面句子的情绪类别。 > 示例1:这部电影太令人难过了。情绪类别:负面情绪 > 示例2:我非常兴奋明天的聚会。情绪类别:正面情绪 > 示例3:今天的天气真是美妙。情绪类别:正面情绪 > 示例4:我感到非常生气,因为他没有履行承诺。情绪类别:负面情绪 > > 请判断下面句子的情绪类别: > 句子1:这个好笑的视频让我笑了很久。 > 句子2:我非常失望他没有来参加我的生日派对。 > 句子3:这个好消息真是让我高兴。 > 句子4:我感到非常焦虑,因为明天有一场重要的面试。
Instruct prompt 指导提示
- 上述通过举例让LLM来理解具体的任务是什么,相当于给LLM进行示范,让他按照上述的模板来进行词语接龙
- 因此
直接用语言对我们想要的任务进行描述
,让模型理解我们想要做的事情是什么 - 将Few shot和 Instruct 结合起来的方式就是给出几个示例,但这些示例是描述具体的任务,而不是一步步来做具体的分析演示
- 这个方法称为
in-context instruction learning
,即通过在任务的上下文中给出具体的指令,让LLM对任务有更深入的理解能力,提问方式如下所示。
> 例子: > 定义:确定对话的说话人,“代理人”或“客户”。 输入:我已经成功地为您订了票。 输出:代理人。 > 定义:确定问题所要求的类别,“数量”或“位置”。 参赛作品:美国最古老的建筑是什么? 输出:位置。 > 定义:将电影评论的情绪分类为“积极的”或“消极的”。 我敢打赌,这个视频游戏比电影有趣多了。 输出:情绪分类。
Chain-of-thought prompt 思维链提示(这个看到过好几次了hh)
- 当面对一个复杂问题时,人类常常采取分解问题的方法来解决它。
- 这种方法包括将大问题分解为小问题,然后逐一解决。
- 举例来说,如果你要组织一个大型的生日聚会,你可能会先考虑场地、食物、娱乐等方面,然后逐步解决这些小问题。很自然地就
从人类的思维逻辑过程迁移至大模型的prompt
(提示)上。 - 这种思维模式被称为"Chain of Thought",也适用于逻辑推理任务的提示。
假设你要用一个语言模型来解决一个法律问题:
> 例子: > 第一步:了解背景 Prompt: "请简要描述这个法律问题的背景和主要矛盾点。" > 第二步:相关法律和案例 Prompt: "在这个法律背景下,有哪些相关的法律条款或先例案例?" > 第三步:应用法律到具体问题 Prompt: "这些法律条款或案例如何应用于我的具体问题?有没有可能的解释或者争议点?" > 第四步:可能的解决方案 Prompt: "根据以上的信息,最可能的解决方案或建议是什么
这样的Chain of Thought不仅能帮助模型更好地理解问题,还能帮助使用者更清晰地看到问题解决的全过程,就像我们人类解决问题时会做的那样。
Prompt使用技巧
温度和Top_K
在使用语言模型时,有两个关键参数需要注意,它们是温度和Top_K。
温度(Temperature):是介于0和1之间的数值,用于控制模型输出的多样性。
当温度为0时,模型会输出最有可能的答案;
当温度大于0时,模型会输出更多样化但可能不那么精确的答案。
数值越大,模型随机性越强,适合生成任务。
> 例子: > 温度为 0:LLM 可能会输出“苹果是一种水果。” > 温度为 1:LLM 可能会输出“苹果是一种多汁、美味的水果,常用于制作各种美食。”
Top_K:是一个整数,用于限制模型在生成每个单词时考虑的候选单词数量。
例如,当Top_K设置为50时,模型在生成下一个单词时只会考虑概率最高的前50个选项。
例子: Top_K 为 10:模型输出可能更加一致和准确。 Top_K 为 100:模型输出可能更加多样,但准确性可能会下降。
Retrieval检索器
重点
- LLM无法获取最新的知识和信息,并可能产生幻觉
- Retrieval方法可以规避这一问题
- Retrieval的核心目标是通过对query编码,将其与外部文档进行检索,找出最相关的文档片段
- 文本检索常用向量化和向量数据库进行检索
- 向量数据库支持近似搜索功能
- 仅使用模型内部检索也对于问题的问答有用
- LLM经过训练之后,对于最新的知识和信息就不再能够获取,另外对于内部的一些信息和知识有可能会产生幻觉,通过retrieval的方法能够很好的规避这个问题。
- Retrieval的思想非常简单,就是通过对query的编码,然后将其与外部文档进行一个检索,将检索的内容加入至外部文档中,根据最新的检索的内容和query的问题,重新组合传入大语言模型中。这里的核心目标是根据问题
找出文档中和问题最相关的片段
,文本检索里边比较常用的是利用向量进行检索,我们可以把文档片段全部向量化(通过语言模型,如bert等),然后存到向量数据库(如Annoy、 FAISS、hnswlib等)里边,来了一个问题之后,也对问题语句进行向量话,以余弦相似度或点积等指标,计算在向量数据库中和问题向量最相似的top k个文档片段,作为上文输入到大模型中。向量数据库都支持近似搜索功能。 - 另外有研究表示,即使不针对外部的知识,仅仅使用模型内部检索,对于问题的问答也是有用的。
Summarizing Code 代码摘要生成
- 文档完备的代码更容易维护,例如函数摘要头。
随着项目的发展,摘要注释可能会过时。自动代码摘要生成应运而生,已经取得了相当大的进展。 - 最初,基于模板的方法很流行,但创建具有良好覆盖率的模板列表很具有挑战性。
后来,研究人员专注于基于检索(IR)的方法,主要是基于相似性的度量检索现有代码(带有摘要)。
但是,只有在可用池中可以找到类似的代码注释对时,这种有前途的方法才有效。 - 最近,像CodeBERT和CodeT5这样的预训练语言模型在代码摘要生成方面表现最好。
然而,LLM现在在许多问题中通常优于预训练的较小的模型。
Ahmed和Devanbu[3]报告说,LLM可以用一个简单的提示,只包含同一个项目中的几个样本,就胜过预训练的语言模型。
这项工作说明了,谨慎建造提示词结构(即“提示词工程”)的前景
。
[3] Toufique Ahmed and Premkumar Devanbu. 2022. Few-shot training LLMs for project-specific code summarization. In 37th IEEE/ACM International Conference on Automated Software Engineering. 1–5.
创新点
ASAP
- 提出了一种自动语义增强提示ASAP,用于构建软件工程任务提示。
- ASAP方法基于一个类比,即有效的提示与开发人员在手动执行任务时所考虑的事实有关。
假设使用开发人员在手动执行任务时考虑的语法和语义事实提示LLM,将提高LLM在该任务上的性能
。 - 本文在代码摘要生成上说明ASAP方法的应用。该任务需要代码,通常是一个函数,并使用自然语言对其进行总结,以促进需求的可追溯性和维护。
此外,使用代码补全任务来确认假设。
具体实现
ASAP的具体实现过程可以分为以下几个步骤:
通过使用ASAP,可以使用源代码中的语义信息来增强提示符,提高语言模型的准确性和效率,从而改善代码自动生成的结果。
- ASAP的目的是使用语义代码分析进一步增强提示符。由于观察到开发人员使用代码的属性(如参数名称、局部变量名称、调用的方法和数据流),文章建议使用
从源代码中自动提取的语义事实来增加提示符
。 - 将
这些语义事实
与所需的输出一起添加到few-shot提示中,为语言模型提供相关示例,并说明这些提取的事实如何有助于构建一个好的摘要。
向模型提供目标代码和从中提取的语义事实,并要求模型发出摘要。
- 这些语义事实具体包括
函数的全限定名、参数名及其数据流图
。这些事实在few-shot例子中作为单独的、确定的字段呈现给LLM。
效果
- ASAP使用代码中导出的事实用于SE任务。
- 本文使用code-davinci-002, textdavinci-003.和GPT-3.5-turbo模型评估ASAP方法。
- 本文发现ASAP方法在代码摘要生成任务上显著提高了LLM的性能。
在几乎所有情况下,本文观察到统计上显著的改善几乎或超过2 BLEU。
对于PHP,ASAP方法第一次在这个具有挑战性的数据集上突破了30 BLEU。 - 本文发现ASAP方法还可以提高代码补全任务的性能。
模型与方法
在早期的工作中,基于transformer的预训练语言模型在NLP和软件工程中都提供了显著的收益。
预训练的语言模型可以分为三类:仅编码器、编码器-解码器和仅解码器模型
。
虽然编码器-解码器模型最初在许多任务上显示出成功,但现在仅解码器的LLM在许多任务上更具可扩展性和有效性。
编码器-解码器模型
:BERT是最早的预训练语言模型之一,使用两个自监督任务:带掩码机制的语言模型(MLM)和下一句预测(NSP)对其进行预训练。
后来,RoBERTa仅对BERT进行一些小调整,CodeBERT和GraphCodeBERT将这些思想引入软件工程,以非常相似的预训练目标进行训练,并解决更复杂的问题。
尽管CodeBERT和GraphCodeBERT是仅限编码器的模型,但它们可以应用于调优后的代码汇总,级联到在调优期间训练的解码器。
Ahmed和Devanbu报告说,多语言模型,通过多语言数据进行微调,比单语言模型表现更好。
他们还报告说,标识符在代码摘要生成任务中起着关键作用
。
PLBART[2]和CodeT5[64]也包括预训练的解码器,据报道,它们在代码汇总任务中工作得很好。
最近,已经发现非常大规模(仅解码器)的自回归LLM(具有175B+参数)在没有任何显式训练的情况下,通过少量学习就可以成功地进行代码总结。
仅解码器模型
仅解码器模型
(自回归模型):在生成式预训练中,任务是在给定之前的标记
的情况下自动回归预测下一个标记,生成一段连续的文本。
这个模型一般包含一个解码器和一个嵌入层,用于将输入的标记嵌入到向量空间中。
解码器的每一步输入是上一步的输出和一个特殊的Token,表示当前位置,以此来区分不同位置的单词。
在预训练完这个模型之后,可以通过对解码器进行微调来进行特定任务的文本生成或者其他相关任务。
这种单向的自回归训练,可以防止模型从未来的tokens中收集信息
。
较新的生成模型,如GPT, GPT-2和GPT-3,也以这种方式训练,但它们有更多的参数,并且是在更大的数据集上训练的。
当前的大型语言模型,如GPT-3
,有大约(或超过)175B个参数。
这些强大的模型在很少的提示下表现得非常好,以至于人们对通过微调进行特定任务参数调整的兴趣降低了。
Codex
是GPT-3的变体,在代码和自然语言注释方面进行了密集训练。
Codex家族包括两个版本:Codex-cushman,较小,有12B个参数;
CodexDavinci,最大,有175B个参数。 Codex模型被广泛用于各种任务。
本文的实验主要针对Code-davinci模型,特别是Code-davinci-002,它擅长于将自然语言翻译成代码,并支持代码补全和代码插入。
还有一些新型号,如TextDavinci-003和GPT-3.5-turbo;与Codex变体不同,这些模型理解并生成自然语言和代码。
虽然针对聊天功能进行了优化,但GPT-3.5-turbo在传统补全任务中也表现出色。
Text-Davinci-003是一个类似于Code-Davinci-002的补全模型。
本文使用Code-davinci-002,Text-Davinci-003和GPT-3.5-turbo模型
研究ASAP提示增强是如何工作的
。
Retrieving Exemplars from Training Data 从训练数据中检索样本
如前所述,当与非常大的模型一起使用时,few-shot学习效果相当好。
我们用少量的<problem, solution>范例提示模型,并要求它解决一个新问题。
然而,为少量的学习精心选择范例是有帮助的。
Nashid等人发现,基于检索的范例选择有助于解决断言生成和程序修复等问题[4]。根据他们的建议,使用BM25 信息检索(Information retrieval,IR)算法从训练集中选择相关的小样本。
BM25[55]是一种基于频率的检索方法,可以有效提高TF-IDF。
我们注意到,在few-shot学习中,与相同的固定示例相比,有了实质性的改进,实验部分也提供了响应的证明。
Nashid等人[4]比较了几种检索方法,发现BM25的效果最好,因此,后续实验也使用它。
[4] Noor Nashid, Mifta Sintaha, and Ali Mesbah. 2023. Retrieval-Based Prompt Selection for Code-Related Few-Shot Learning. In Proceedings, 45th ICSE.
Automatic Semantic Prompt Augmentation 自动语义提示增强
存储库名称和路径
使用特定域的信息增强提示可以提高LLM在各种任务上的性能。
先前的工作表明,使用来自同一存储库的代码增强提示可以提高代码摘要生成任务的性能。
我们认为,基本的存储库级元信息,如存储库名称和到存储库的完整路径,提供了额外的上下文
。
例如,像“tony19/logback- android”,“apache/parquet-mr”和“ngageoint/ geo-package-android”这样的存储库名称都将函数连接到特定的域(例如,android, apache, geo-location),这可以增强对要总结的目标代码的理解。
展示了如何使用存储库级信息增强提示符的示例。与存储库名称类似,函数的路径也可以对模型做出贡献。
标记标识符
先前的工作表明,语言模型在生成代码摘要时发现标识符比代码结构更有价值。
然而,标识符在代码中扮演着不同的角色。局部变量、函数名、参数、全局变量等,在它们出现的方法的功能中起着不同的作用;阅读代码的开发人员当然知道标识符的角色,只需确定其作用范围和用途。
因此,使用标识符的特定角色来增加提示可以帮助模型更好地“理解”功能
。ASAP使用TreeSitter遍历函数的AST并收集标识符及其角色。
尽管模型可以访问代码的token序列,因此也可以访问所有标识符,但它们以标记标识符的形式访问模型可能:a)为模型节省一些计算工作,b)更好地调节模型的输出。
展示了如何使用带标记的标识符增强函数的提示符。
数据流图(DFG)
Guo等人引入了GraphcodeBERT模型,该模型在预训练阶段中使用数据流图(DFG)代替抽象语法树(AST)等语法级结构。GraphcodeBERT在各种软件工程(SE)任务上的表现优于CodeBERT。
作者将这些DFG信息整合到few-shot的示例中,并推测,这为模型提供了对每个范例和目标示例的更好的语义理解。
每行包含一个标识符及其索引,以及该特定数据流向的标识符的索引。
与repo和带标签的标识符不同,数据流图可能非常长,因此不方便将完整的数据流添加到提示符中。
在长提示符的情况下,只在提示符中保留DFG的前30行。
除了标识符之外,DFG还提供了对标识符在函数中的重要性的更好理解。
显示了用于实验的数据流程图(DFG)