长亭百川云 - 文章详情

通过Zero-shot学习重新审视自动化程序修复

拨开云雾

74

2024-07-13

分享一篇使用大模型进行自动化程序修复的文章,来自ESEC/FSE 2022,题目为:Less Training, More Repairing Please: Revisiting Automated Program Repair via Zero-shot Learning。

本文提出了AlphaRepair,一种完形填空式(Cloze-style)的APR方法,可以直接利用APR的大型预训练代码模型,而无需对历史错误修复进行任何微调/再训练。作者的见解是:可以基于上下文信息直接预测正确的代码是什么(即:完形填空任务),而不是对修复编辑(Repair Edit)应该看起来像什么(即:NMT任务)进行建模【这个想法有意思】

目前基于学习的APR技术存在如下难点:

(1)训练数据的质量: 当前基于学习的APR工具需要通过使用历史缺陷修复(即:成对的缺陷和补丁代码)来训练或微调模型。这些数据通常是通过抓取开源项目找到关于bug修复的特定提交而获得的。然而,这依赖于爬虫代码的可信度。例如,为了找到bug修复提交,经常使用诸如bug、fix、patch、solve、issue、problem等关键字来过滤提交。单个的bug修复提交也可以包括不相关的编辑,比如重构或者新特性实现[1]。因此,提取的数据可能包含各种不相关的提交和错误修复提交中不相关的代码更改,从而给训练数据集增加了噪声。

(2)训练数据的数量: 与大量的开源代码片段相比,缺陷修复数据的数量是有限的。为了减少上述包含来自bug修复的不相关变更的提交issue的影响,基于学习的APR方法通常将提交限制在其数据集中具有少量变更行的提交,这进一步限制了训练数据的数量。通过在这种有限的历史修复数据上进行训练,导致当前基于学习的APR方法的编辑多样性受限于训练数据。

(3)上下文表示: 为了正确修复有问题的代码片段,其前后的上下文所提供的语法/语义信息至关重要。当前基于学习的APR工具首先将包括错误代码元素的上下文作为纯文本或结构化表示传递到编码器中。然后,编码的上下文被直接使用或者与错误代码片段的单独编码相结合,作为解码器的输入。然而,这个过程是不自然的,因为对于模型来说,区分补丁在上下文中的位置,或者有效地合并独立的bug/上下文编码是具有挑战性的。这些技术可能会遗漏补丁与其上下文之间的复杂关系,例如:提供重要语法/语义信息的每个代码元素的接近程度。

背景知识

下图是基于NMT和大模型的APR方法概述。

(1)基于NMT的方法: 如图1a所示,编码器接受错误行及其周围的上下文作为输入,并输出编码的表示。解码器使用编码表示来生成新的代码行,以替换有问题的行。

(2)基于大模型的方法: 如图1b所示,有三种大模型:

  • GPT:仅使用解码器组件来预测给定所有先前token的下一个token输出。这种类型的解码器是自回归的,通过迭代地输入所有先前的token来生成序列。

  • BERT:仅包含编码器,旨在学习数据的表示,并使用MLM目标进行训练,即:训练数据中的一小部分token将被掩码token替换,目标是训练BERT来预测掩码token的真实值。

  • 编码器-解码器结构:如T5等模型是为序列到序列任务设计的,训练目标旨在恢复给定原始输入的正确输出序列(英语到法语,损坏到未损坏等)。

方法

本文所提方法的框图如图2所示。AlphaRepair使用CodeBERT这一预训练模型,将其目标设置为掩蔽语言模型(Masked Language Model,MLM)。

AlphaRepair的运行流程如下:

(1)输入处理: 接受一个有错误的项目,并根据错误定位信息分离周围的上下文和有错误的行。将前后的上下文编码成token表示。此外,还将有问题的行编码为自然语言注释作为CodeBERT的输入。

作者使用了CodeBERT的tokenizer,如图3所示。首先定义tokenization结构,一个作为CodeBERT输入的token列表。然后,Tokenize前后的上下文,并在它们之间插入掩码行。对于程序修复,bug行本身对于生成补丁也很重要。然而,将它作为上下文的一部分是没有意义的,因为AlphaRepair的目标是生成代码来替换它

为了捕获错误代码行的编码,作者利用了CodeBERT的双模态特性,它可以接受编程语言和自然语言(即:注释)。使用块注释字符(/* comment */)将原来有问题的行转换成注释。

将等式(2)作为MLM的损失函数,其中和分别表示自然语言和代码的token序列。这样,CodeBERT就学习了函数表示的两种形式(自然语言和函数代码)。AlphaRepair利用了这种额外的理解,将有问题的行转换成注释。注释错误行、前上下文、掩码行和后上下文一起被tokenize为CodeBERT的输入。为了最大化可以编码的上下文,作者从错误行开始,增加上下文大小(也就是远离错误代码的行),直到达到最大CodeBERT输入令牌大小512。

(2)掩码生成: 通过使用有缺陷的代码行,借助模板生成多个掩码行(用于替换整行,替换行的开始/结束部分等)。每个掩码行替换错误行,并与周围的上下文一起被tokenize为CodeBERT的输入。 

为了生成补丁,作者用掩码行替换bug行。掩码行被定义为具有一个或多个掩码标记<mask>的行。使用CodeBERT通过替换代码token填充每个掩码token。填充的掩码行作为生成的补丁行。图4显示了作者用来生成掩码行的3种策略:完全、部分和模板掩模。

  • 完全掩码:

用只包含掩码token的行替换整个错误的行。作者称之为行替换,因为要求CodeBERT生成一个新的行来替换错误的行。同时,还在错误行之前/之后添加掩码token来生成掩码行。这表示在错误位置之前/之后插入新行来进行错误修复。

  • 部分掩码:

重用错误行的部分代码。首先将有问题的行分成单独的token,然后保留最后/第一个token,并用掩码token替换之前/之后的所有其他token。然后,重复这个过程,但是从原始的错误行添加更多的token以生成部分掩码行的所有可能版本。

例如,在图4中,通过保留return来生成掩码行return <mask><mask>...<mask>,进一步通过保留return foundDigit来生成掩码行return foundDigit <mask><mask>...<mask>。总之,对于部分生成后和部分生成前方法,生成L-1个掩码行,其中L是错误行中的token数。

这种方法是由补丁驱动的,补丁中的修复将重用部分有缺陷的行。通过附加带有一部分错误行的掩码行,可以减少CodeBERT需要为正确修复生成的token数。此外,部分错误代码就像CodeBERT的初始起点,通过提供重要的上下文来开始生成token。

  • 模板掩码:

作者实现了几个基于模板的掩码行生成策略,目标是作用于条件和方法调用语句,因为它们是两个最常见的错误模式。此外,一些传统的APR工具只专注于修复与条件语句相关的错误,这显示了针对常见错误模式的重要性。与前两种策略不同,只能为特定的错误行生成模板掩码。

第一组模板旨在针对有问题的方法调用,用掩码token替换方法调用名。这表示要求CodeBERT使用与之前相同的参数生成替换方法调用。作者还使用几个基于参数的改变:用掩码token替换整个输入,用掩码token替换一个参数,以及添加附加参数(由于改变了掩码token的数量,所以可以添加一个以上的参数,因此CodeBERT可以添加多个参数)。

对于布尔表达式形式的条件语句的模板,生成替换整个布尔表达式的掩码行,或者通过在语句后面附加掩码token来添加额外的and/or表达式。此外,还确定了常见的操作符(<>>===&&||等)并用掩码token直接替换它们。

基于模板的掩码行生成的灵感来自于许多错误的常见修复以及以前的APR工具,这些工具利用预设模板来修复错误。这些简单生成的模板提供了与部分掩码类似的功能,为CodeBERT提供了更多的起始代码来生成潜在的补丁。

对于每个生成的掩码行,从1开始增加生成的token数,直到该行中的token总数变为L+10,其中L是原始错误行中的token数。例如,如果在L=12的错误行上使用partial-after策略,并且保留前5个原始token,掩码token的数量将从1变到17。对于应用的每个掩码策略,都要完成这个过程(替换操作符策略除外,在该策略中,只使用单个掩码token替换公共操作符)。进一步对每个掩码行应用上述输入处理步骤以获得CodeBERT的输入列表。

(3)补丁生成: 迭代地查询CodeBERT以使用掩码行生成候选补丁。每个补丁用生成的代码行替换掩码行。

利用CodeBERT中的掩码替换的原始训练目标,通过"预测被给定其周围环境的掩码token所替换的正确token值"来训练CodeBERT。对于每个掩码token,CodeBERT输出其词汇表中每个token替换掩码token的概率。通常,在训练期间,一小部分token被屏蔽掉(<15%),模型尝试预测已被替换的实际token。

本文的任务类似于CodeBERT的原始训练目标,虽然也对输入进行预处理,屏蔽掉一小组token,但是输入和屏蔽的训练数据之间的关键区别是本文屏蔽的token被分组在一起。CodeBERT和BERT系列模型的一个显著特征是双向性,其中每个token表征不仅基于之前的上下文,还基于之后的上下文。为了生成掩码token的替换token,CodeBERT查看掩码位置之前和之后的token。这对于训练目标很有效,因为掩码token被展开,其中每个token在其之前/之后具有足够的token来给出上下文。然而,对于本文的输入数据,掩码token是在一起的。

为了便于为分组的掩码token生成token,通过用先前生成的token替换掩码token来迭代地输出token。图5a显示了如何完成这一过程。

从初始输入的掩码行if(end index < 0 || <mask> <mask> <mask>){开始,使用CodeBERT来确定第一个掩码token的前N个最可能的替换token。N是beam width。在这个例子中,N=3,意味着取前3个最可能的token及其条件概率。在下一次迭代中,通过用前3个替换token(即:startIndexendIndexemptyRange)替换第一个掩码token来再次查询CodeBERT,以找到具有最高联合条件概率的前3个token对(即:startIndex <startIndex ==endIndex >=)。作者称这个联合条件概率值为临时联合得分。给定n作为掩码token长度,作为具有迄今为止生成的p个token的掩码行(p<=n),设为T中t后(包括t)的所有token都被掩盖时,生成t的CodeBERT条件概率,临时联合分数被定义为:

这里需要注意,一旦生成了完整的行(也就是所有掩码token都已被替换),临时联合分数就不表示所生成的token的实际概率。这是因为当在确定替换token的可能性时,CodeBERT使用前后上下文,概率值不考虑将来要生成的掩码token。当在掩码行T中计算token序列的临时联合分数时(),CodeBERT能够看到了之前的token值(),然而之后的所有token都被屏蔽掉()。也就是说,临时联合分数以值尚未决定的掩码token为条件,并且条件概率不考虑那些掩码token未来的具体值。因此,仅临时使用该概率值作为代理,并在之后进一步重新分配概率以获得更精确的补丁排序。在每次迭代中,使用临时联合分数来仅保留前3个最高分数生成的token序列。重复这个过程,直到为输入中的所有掩码token生成token。

通过顺序生成token,保证在生成任何掩码token时,CodeBERT至少具有直接上下文的一侧。这有助于生成更多语法正确的候选补丁,因为CodeBERT可以使用先前的直接上下文来通知最佳的下一个token应该是什么。这个过程类似于代码或自然语言生成任务中常用的beam search。区别是,传统的代码生成可以将序列的似然性精确地计算为所生成token的对数条件概率的平均值。而对于本文的方法,原始平均值(原文是naive average,一时间不知道怎么翻译了)仅仅是可能性的近似值,因为掩码行开始处的token的概率输出不包括未来生成的token。为了解决这个问题,作者通过重新查询CodeBERT来进一步重新排列每个候选补丁,以获得精确的似然值。

(4)补丁重排名: 再次使用CodeBERT,通过使用生成的token的联合概率计算生成的补丁的分数来提供补丁排名。

从包含所有生成的token的完整补丁开始,只屏蔽掉其中一个token,并查询CodeBERT以获得该token的条件概率。对所有其他先前的掩码token位置应用相同的过程,并计算联合分数,该分数是各个token概率的平均值。给定一个序列中生成的n个token:,设为屏蔽掉t后CodeBERT的条件概率,则联合分数定义为:

可以将联合分数理解为生成序列的条件概率(即:给定前后两种上下文,根据CodeBERT,生成补丁的可能性是多少)。对于通过所有掩码生成策略生成的所有修补程序,都会执行此操作。因为不同的掩码行具有不同数量的掩码token,所以将其除以n以解决token长度差异。

图5b示出了重新排序过程的例子。使用来自补丁生成示例的3个补丁,并为它们中的每一个屏蔽掉第一个token,并从CodeBERT获得概率值。对其他两个token重复这个过程,最后得到所有3个补丁的联合分数。使用联合分数来为每个补丁进行排名。

(5)补丁验证: 编译每个候选补丁,并根据测试套件进行验证。最后输出一个看似合理的补丁列表供开发人员检查。

参考文献

[1] Jiang Y, Liu H, Niu N, et al. Extracting concise bug-fixing patches from human-written patches in version control systems[C]//2021 IEEE/ACM 43rd International Conference on Software Engineering (ICSE). IEEE, 2021: 686-698.

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2