能否将本文方法运用到其他大模型上提升性能?
摘要
自动程序修复(APR)旨在自动修复软件错误,而无需人工调试,在软件开发和维护中起着至关重要的作用。尽管最近在修复错误的数量方面取得了重大进展,但APR仍然受到长期存在的过拟合问题的挑战(即生成的补丁看似合理,但过拟合)。因此,已经提出了各种技术来解决过拟合问题。其中,利用深度学习方法自动预测补丁正确性,以及最近可用的大规模补丁基准测试正在出现。然而,现有的基于学习的技术主要依赖于手动设计的代码特征,这在实践中可能非常昂贵且难以构建。在本文中,我们提出了APPT,这是一种基于预训练模型的自动补丁正确性评估技术,它将源代码视为一系列令牌,而不需要额外的开销来从不同的角度设计大量特征。特别是,APPT采用预训练模型作为编码器堆栈,然后是LSTM堆栈和深度学习分类器。虽然我们的想法是通用的,可以建立在各种现有的预训练模型上,但我们已经基于BERT模型实现了APPT。我们对1183个Defects4J补丁进行了广泛的实验,实验结果表明,APPT的预测准确率为79.0%,召回率为81.3%,比最先进的CACHE技术高出3.6%和4.8%。我们对49694个真实世界补丁的进一步调查表明,与现有的表示学习技术相比,APPT实现了最佳性能(在评估补丁分类技术的五个常见指标中超过99%)。我们还证明,采用先进的预训练模型可以进一步提供实质性的进步(例如,基于GraphCodeBERT的APPT在精确度和召回率方面分别将基于BERT的APPT提高了3.0%和2.6%),突出了APPT的普遍性。
关键词 自动程序修复、补丁正确性、预训练模型
1 引言
软件错误在现代软件系统中是不可避免的,并导致致命的后果,例如造成数万亿美元的经济损失,影响全球数十亿人[1],[2]。由于现代软件系统的规模和复杂性不断增加,开发人员修复这些错误是非常耗时和劳动密集型的[3]。自动程序修复(APR)旨在在没有人为干预的情况下自动修复暴露的软件错误,在过去几十年中引起了学术界和工业界的广泛关注[4],[5]。尽管有一个新兴的研究领域,但文献[6]、[7]中提出了各种APR技术,并在修复错误的数量方面不断取得有希望的结果。
然而,由于程序规范薄弱,很难对生成的补丁实现高精度[8]。现有的APR技术通常利用开发人员编写的测试用例作为评估所生成补丁正确性的标准。事实上,通过可用测试用例生成的补丁可能无法推广到其他潜在的测试用例,从而导致APR的长期挑战(即过拟合问题)[8]。例如,当在功能中检测到错误时,可以通过删除功能来生成补丁,而可用的测试用例通常无法执行删除的功能[9]。在这种情况下,开发人员需要花费大量时间和精力来过滤过拟合补丁,导致在实践中应用APR技术时调试性能不佳[10],[11]。
因此,已经提出了各种自动补丁正确性评估(APCA)技术来确定生成的补丁是否确实正确[12]。根据提取的特征,传统的APCA技术可分为两类:静态和动态技术[13]。静态技术倾向于根据句法和语义特征分析代码变化模式或代码相似性。例如,Tan等人[14]为有缺陷的程序定义了一组通用的禁止转换(例如,上述功能删除)。相比之下,动态技术通常针对自动测试生成工具(例如Evosuite[15]和Randoop[16])生成的额外测试用例执行合理的补丁。例如,Xiong等人[17]生成新的测试用例,并根据测试用例执行的行为相似性确定补丁的正确性。然而,静态技术可能会遇到预测精度问题,而动态技术生成额外的测试用例并执行所有修补程序则相当耗时[13]。
最近,受发布的大规模补丁基准测试[6]、[7]的启发,提出了一些基于学习的APCA技术,通过嵌入有缺陷和补丁的代码片段来评估补丁的正确性[12]、[18]、[19]。例如,Lin等人[20]利用抽象语法树(AST)路径来表示补丁,并构建深度学习分类器来预测补丁的正确性。同样,He等人[18]在AST级别静态提取代码特征,并训练概率模型来执行补丁预测。然而,尽管预测结果出色,但现有的基于学习的APCA技术主要采用复杂的代码感知特征(例如[20]中的AST路径)或手动设计的代码特征(例如[18]中的202个代码特征),在实践中进行和提取成本很高。
在这项工作中,我们提出了APPT,这是第一种基于预训练模型的自动补丁正确性评估技术,它采用预训练和微调来解决先前工作的上述局限性。我们首先采用大型预训练模型作为编码器堆栈来提取代码表示。然后,我们使用双向LSTM层来捕获有缺陷的代码段和修补的代码段之间的丰富依赖信息。最后,我们构建了一个深度学习分类器来预测补丁是否过拟合。APPT仅将源代码标记视为输入,并使用训练有素的编码器堆栈自动提取代码特征,从而消除了对代码感知特征和手动设计特征的需求。尽管APPT在概念上是通用的,可以建立在各种预训练模型上,但我们已经将APPT实现为基于BERT模型的实用APCA工具。我们在1183个Defects4J补丁上的实验结果表明,APPT将最先进的CACHE技术提高了3.6%的准确率、1.2%的准确度、4.8%的召回率、2.9%的F1评分和3.1%的AUC。我们对来自五个不同补丁基准的49694个真实世界补丁进行了额外调查,结果表明,APPT在准确性、精确度、召回率、F1得分和AUC指标方面超过了99%,优于现有的表示学习技术。我们还采用不同的预训练模型来进一步研究APPT的泛化能力。结果表明,具有先进预训练模型的APPT可以提高预测性能。例如,当配备GraphCodeBERT时,APPT的精确度和召回率可以提高3.0%和2.6%,比最先进的CACHE技术高4.2%和7.2%。
综上所述,我们做出了以下主要贡献:
•新方向。本文为补丁正确性预测开辟了一个新的方向,即通过预训练和微调直接利用大型预训练模型。与现有的基于学习的APCA技术相比,我们的方法不需要任何额外的努力来设计和提取复杂的代码特征。
•新技术。我们提出了APPT,这是一种基于BERT的APCA技术,它利用预训练和分类器来预测补丁的正确性。据我们所知,我们是第一个利用微调预训练模型来评估补丁正确性的人。
•广泛研究。我们进行了各种实证研究,以调查和评估不同斑块基准上的APPT。结果表明,APPT的整体性能明显优于现有的基于学习和传统的APCA技术。
•可用工件。我们发布了实验中使用的相关材料(包括源代码、补丁和结果),用于复制和未来的研究。
2 背景
2.1 APR
APR技术的主要目标是自动识别和修复程序错误。图1展示了典型APR技术的工作流程,通常由三个步骤组成:(1)定位短语使用现成的故障定位技术来识别可疑的代码元素(例如语句或方法)[21],[22];(2) 然后,修复短语根据一组转换规则修改这些元素,以生成各种新的程序变体,也称为候选补丁;(3) 验证短语采用原始测试用例作为oracle来检查候选补丁是否按预期执行。具体来说,通过原始测试用例的候选补丁称为可信补丁。语义上与开发人员补丁等效的合理补丁表示正确补丁;否则,这是一个过度拟合的补丁。
由于实践中程序行为的规范薄弱,确保合理补丁的正确性从根本上来说是具有挑战性的。现有的研究表明,手动识别过拟合补丁是耗时的,可能会损害开发人员的调试性能[10],[23]。因此,已经提出了各种技术来自动验证补丁的正确性。根据是否需要动态执行或机器学习[13],我们将其分为三大类:基于静态的技术、基于动态的技术和基于学习的技术。
•基于静态的APCA技术。这些技术旨在通过静态代码特征(如代码删除程序转换)将正确的补丁优先于过拟合的补丁。
•基于动态的APCA技术。这些技术旨在通过执行基于修复或修补程序生成的额外测试用例来过滤出过拟合的补丁。根据是否需要正确的补丁,这些技术可以进一步分为基于oracle的动态补丁和不基于oracle的静态补丁。
•基于学习的APCA技术。这些技术旨在预测通过机器学习技术增强的合理补丁的正确性。他们通常提取手动设计的代码特征,然后采用分类器进行补丁预测[18]。提出了一些采用代码嵌入技术自动提取代码特征的技术[20],也称为基于表示学习的APCA技术。
最近,越来越多的研究工作试图使用机器学习技术从现有的补丁基准中学习,以预测潜在的补丁正确性,取得了有希望的结果。在这项工作中,我们采用大型预训练模型(即BERT)对合理的补丁进行编码,并训练一个深度学习分类器来预测补丁的正确性。与现有技术相比,我们的论文是第一篇通过预训练和微调预训练模型来预测补丁正确性的工作。
2.2 预训练模型
最近,预训练的语言模型(如BERT)在广泛的自然语言处理(NLP)任务中显著提高了性能,如机器翻译和文本分类[24]-[26]。通常,这些模型经过预训练,通过对大规模未标记数据进行自监督训练来推导通用语言表示,然后通过对有限数据注释进行微调来转移到多个下游任务中。
现有的预训练模型通常采用编码器-解码器架构,其中编码器将输入序列编码为固定长度的向量表示,解码器根据输入表示生成输出序列。
仅使用编码器的模型(例如BERT[24])通常预先训练一个双向transformer,其中每个令牌可以相互关注。仅编码器模型擅长理解任务(例如代码搜索),但它们的双向性对于生成任务需要一个额外的解码器,在生成任务中,这个解码器从头开始初始化,无法从预训练任务中受益。
仅解码器模型(例如GPT[25])是使用单向语言建模进行预训练的,该建模只允许token处理前一个token,并自己预测下一个token。纯解码器模型擅长于代码完成等自回归任务,但单向框架对于理解任务来说不是最优的。
编码器-解码器模型(例如T5[26])通常使用去噪预训练目标,这些目标会破坏源输入并要求解码器恢复它们。与有利于理解和自回归任务的仅编码器和仅解码器模型相比,编码器-解码器模型可以支持代码摘要等生成任务。在这项工作中,我们将补丁正确性评估视为一项二元分类任务,并根据现有工作考虑仅使用编码器的模型来获得代码片段的嵌入[27]。
受NLP中预训练模型成功的启发,最近已经采用了许多尝试,用预训练模型(例如GraphCodeBERT)来增强许多与代码相关的任务(例如代码摘要和代码搜索)[28],[29]。尽管取得了可喜的结果,但很少有工作旨在探索预训练模型在支持补丁正确性评估方面的能力。在这项工作中,BERT被选为利用预训练模型进行自动补丁正确性评估,因为它已被广泛应用于各种与代码相关的任务中,并且对分类任务非常有效[28],[29]。还选择了两种先进的BERT风格模型(即CodeBERT和GraphCodeBERT)来研究APPT的泛化能力。
3 方法
图2显示了我们方法的总体框架。通常,APPT接受一个有缺陷的程序和一个看似合理的补丁,该补丁将可用的测试用例作为输入。APPT提取有缺陷的代码片段及其相应的修补代码片段,并采用四种策略截断代码token。然后,APPT使用预训练的BERT模型来嵌入截断的token。在获得有缺陷和修复的代码片段的表示后,APPT使用四个预定义的函数来集成表示。最后,APPT采用深度学习分类器返回最终结果(即正确或过拟合)。
3.1 代码提取(将缺陷代码和修复代码片段转换成token形式)——对应train.py前4个方法
给定一个有缺陷的程序,现有的APR工具可能会返回一个通过所有可用测试用例的合理补丁p(如果存在)。代码提取阶段旨在将返回的补丁和有缺陷的程序作为输入,并输出相应的有缺陷和已修补的代码token(如图2(a)所示)。
具体来说,我们通过解析补丁文件来获得有缺陷和补丁的代码片段(即Cb和Cp)。首先,我们选择删除和添加的行作为错误行和修补行,分别标记为“+”和“-”。其次,为了保留关于合理补丁的上下文信息,我们在每个代码片段中保留不变的行(即,开头没有“+”和“-”)。最后,有缺陷(或修补)的代码片段由有缺陷(修补)的行和公共上下文部分组成。
我们将有缺陷(或修补)的代码片段视为token序列,并在token代码片段时利用子词标记化方法将标识符进一步分解为子标记,从而解决词汇表外(OOV)问题[30]。在这项工作中,我们保留了原始的标记化词汇表,而不是使用字节对编码(BPE)算法构建新的词汇表,因为我们希望APPT继承自然语言理解能力,并从一个良好的初始点开始学习预测。
在提取出有缺陷(或修复)的代码token后,我们试图将它们作为token嵌入短语的输入。然而,预训练模型通常仅限于特定的输入长度。例如,BERT最多只能接收512个令牌的输入序列。我们进一步截断了标记化后长度大于512的输入。根据现有的工作[31],我们使用不同的方法来截断方法对。
•仅限head:将前512个令牌保留在Cb和Cp中。
•仅尾部:将最后512个令牌保留在Cb和Cp中。
•仅中间:在Cb和Cp中选择中间的512个令牌。
•混合:选择Cb和C