RAP-Gen: Retrieval-Augmented Patch Generation with CodeT5 for Automatic Program Repair

基本信息

博客贡献人

JokerLin

作者

Weishi Wang, Yue Wang, Shafiq Joty, Steven C.H. Hoi

标签

Automated program repair, Neural networks, Retrieval-augmented generation, Pretrained language models

摘要

自动程序修复(APR)对于减少开发人员的手动调试工作并提高软件可靠性至关重要。虽然传统的基于搜索的技术通常依赖于启发式规则或冗余假设来挖掘修复模式,但近年来,基于深度学习 (DL) 的方法的激增,以数据驱动的方式自动化程序修复过程。然而,它们的性能通常受到一组固定参数的限制,无法对 APR 的高度复杂搜索空间进行建模。

为了减轻参数模型的负担,在这项工作中,我们提出了一种新颖的检索增强补丁生成框架(RAP-Gen),通过显式地利用从以前的错误修复对的代码库中检索到的相关修复模式。具体来说,我们构建了一个混合补丁检索器,以与语言无关的方式基于原始源代码来解释词汇和语义匹配,该方式不依赖于任何特定于代码的功能。此外,我们采用代码感知语言模型 CodeT5 作为我们的基础模型,以统一的方式促进补丁检索和生成任务。我们采用分阶段的方法,其中补丁检索器首先检索相关的外部错误修复对,以增强 CodeT5 补丁生成器的错误输入,后者合成修复补丁候选的排名列表。值得注意的是,RAP-Gen是一个通用的APR框架,可以灵活地集成不同的补丁检索器和生成器来修复各种类型的错误。

我们在两种编程语言的三个基准测试上彻底评估了 RAP-Gen,包括 JavaScript 中的 TFix 基准测试、Java 中的 Code Refinement 和 Defects4J 基准测试,其中可能提供也可能不提供错误本地化信息。实验结果表明,RAP-Gen 在所有基准测试中都显着优于以前最先进的 (SoTA) 方法,例如将 TFix 上 T5-large 的准确率从 49.70% 提高到 54.15%(修复了 478 个以上错误),并修复了 818 个 Defects4J 错误中的 15 个错误。进一步的分析表明,我们的补丁检索器可以搜索相关的修复模式来指导 APR 系统。

简介

程序修复是维持软件质量的最重要阶段之一,然而,在现代软件开发中,这是一个耗时且成本高昂的过程。因此,对自动程序修复(APR)工具的需求巨大,以减轻开发人员修复程序的难度和成本,其用例包括在程序开发时 、构建时或运行时搜索补丁。

APR 的一类值得注意的传统技术被称为基于搜索(也称为生成和验证)的方法 。他们经常根据通过手动启发式规则 或基于冗余的技术挖掘的修复模式来搜索修复。后一组方法做出了冗余假设,即固定补丁通常可以从代码库的其他地方(捐赠者代码片段)找到(或重建)。这一假设已通过研究 得到实证验证,表明很大一部分提交 (3% - 17%) 确实由现有代码库组成。

同时,随着深度学习技术的最新进展,出现了大量基于深度学习 (DL) 的 APR 方法 通过参数模型以纯粹数据驱动的方式自动化修复过程。在这种范例中,APR 任务通常被表述为神经机器翻译(或序列到序列学习)问题 ,以便将有缺陷的(源)程序转换为正确的(目标)版本。尽管它们在软件智能任务中取得了有希望的结果,但它们的性能通常受到一组固定的模型参数的限制,无法学习用于程序修复的高度复杂的分布模式,即使有数亿个参数。

为了减轻参数神经模型的负担,在这项工作中,我们提出了一种名为 RAP-Gen 的新型检索增强补丁生成框架,以额外利用补丁检索器中的相关修复模式。早期基于冗余假设的 APR 技术表明,从现有代码库甚至 StackOverflow 的外部问答中挖掘修复模式可以作为 APR 的关键修复成分。我们的模型本质上是半参数的,旨在结合隐式(参数)端到端程序修复学习和显式(非参数)修复模式挖掘的优点。与之前的修复模式挖掘工作的一个区别是,我们利用最相关的错误修复对作为错误补丁的指导修复模式,而不是使用手工设计的启发式方法对修复模板进行聚类。这种检索引导策略也是受到程序开发人员调试行为的推动,他们经常搜索相关的错误修复示例,以提炼一些修复错误的线索。图1展示了一个激励示例,我们可以发现检索到的先前修复示例通知了一种修复模式,即用“Error”对象包装字符串作为“throw”语句,这指导开发人员修复正在考虑的目标错误。

在这里插入图片描述

此外,我们建议采用基于 Transformer 的 编码器解码器模型 CodeT5 作为 RAP-Gen 的统一基础模型,用于补丁检索和生成任务。 CodeT5 是一种通用的代码感知语言模型,在 GitHub 上整理的八种流行编程语言(包括 JavaScript 和 Java)的大型源代码语料库上进行了预训练,在代码理解和生成任务中实现了最先进的 (SoTA) 性能。 RAP-Gen 采用分阶段学习策略来连接补丁检索器和补丁生成器:补丁检索器首先搜索相关的错误修复模式,然后将其传递给 CodeT5 补丁生成器,根据源错误代码和检索到的外部错误修复知识合成候选修复补丁的排名列表。虽然这种检索增强生成范式已经在其他任务中进行了探索,例如问答以及代码生成和摘要,但我们是第一个基于大规模预训练代码语言模型研究其APR系统有效性的人。

对于检索器,我们提出了一种混合方法,通过基于原始源代码的稀疏(BM25)和密集(DPR)检索来解释词汇和语义匹配。我们采用 CodeT5 的编码器作为我们的密集 DPR 检索器,并建议使用以前的错误修复对通过对比学习目标来训练它,因为修复补丁通常与其错误补丁共享大部分语义。密集的 DPR 检索器有望捕获更深层次的代码语义,而基于稀疏关键字的 BM25 检索器更关注词汇相似性,这对代码标识符的命名选择很敏感。值得注意的是,混合检索器与语言无关,因为它不需要任何特定于代码的功能,例如抽象语法树(AST)。实验表明,我们的补丁检索器能够检索词汇和语义相关的修复模式来指导 APR 系统。

我们研究了 RAP-Gen 在不同 APR 场景中的有效性,包括 JavaScript linter 引发的诊断 (TFix )、Java 错误修复提交 (Code Refinement ) 以及开源项目中伴随测试用例的真实 Java 错误 (Defects4J )。在这些基准测试中,我们将 APR 问题表述为给定有错误的代码补丁,APR 模型学习预测修复补丁,该补丁可以从开发人员编写的先前错误修复对的代码库中修复该错误。预测修复补丁的正确性通过静态分析器 (TFix) 或单元测试 (Defects4J) 进行验证,或者通过与开发人员编写的真实修复进行直接比较来验证。总的来说,广泛的实验结果表明,我们的 RAP-Gen 在所有这三个 APR 基准上都显着优于现有的基于 DL 的方法。

总之,本文做出了以下贡献:

(1) 我们为 APR 提出了一种新颖的检索增强补丁生成框架(RAP-Gen)。它是一个通用框架,可以轻松与任何序列到序列学习模型集成。据我们所知,这是第一个在基于 DL 的 APR 系统中利用固定模式挖掘检索功能的工作。

(2) 我们提出了一种用于修复模式挖掘的混合补丁检索器,它通过稀疏和密集检索器的组合来解释词汇和语义匹配。它是一个与语言无关的补丁检索器,使用原始源代码,不需要任何特定于代码的功能。

(3) 我们建议采用通用的预训练代码感知语言模型 CodeT5 作为 RAP-Gen 的基础模型来修复各种错误。此外,我们以统一的方式利用它来进行补丁检索和生成任务。

(4) 我们在 JavaScript 和 Java 的三个 APR 基准上广泛评估 RAP-Gen。结果表明,RAP-Gen 在所有基准测试中均显着优于基于 SoTA DL 的方法。特别是,与之前的 SoTA T5large 模型相比,我们的最佳模型在 TFix 上取得了显着的改进(精确匹配精度为 49.70 → 54.15,错误消除精度为 69.30 → 78.80),模型尺寸比我们的模型大 3.5 倍。在代码优化方面,RAP-Gen 为中小型子集设置了新的 SoTA 精确匹配结果 24.80 和 15.84,超过 CodeT5 的 21.61 和 13.96。在 Defects4J 上,RAP-Gen 实现了新的 SoTA 性能,与之前的 SoTA 模型相比,修复了 15 个具有完美 FL 的错误 (110 → 125) 和 6 个没有完美 FL 的错误 (68 → 74)。

相关工作

自动程序修复

在过去的几十年里,自动程序修复(APR)引起了越来越多的关注,并且已经提出了各种APR技术来减少调试中的手动工作。一类值得注意的 APR 传统技术称为基于搜索(或生成和验证)的方法 。早期基于搜索的 APR 技术通常基于启发式算法 或遗传编程 的程序修改或突变,以生成大量候选修复程序以通过单元测试进行验证。搜索策略已进一步扩展为采用使用基于冗余的技术挖掘的固定模式。这些方法做出了冗余假设 ,即固定补丁通常可以从代码库中的其他地方重建,这已通过研究 进行了实证验证,表明很大一部分提交 (3%-17%) 确实由现有代码库组成。更多基于冗余的技术表明,从现有代码库 甚至 StackOverflow 的外部问答中挖掘修复模式可以在很大程度上使 APR 系统受益。

最近,随着自然语言处理(NLP)深度学习(DL)方法的最新进展,许多基于DL的APR技术被提出,以端到端数据驱动的方式自动化程序修复过程。受神经机器翻译(NMT)成功的推动,这些技术通常将 APR 表述为序列到序列 NMT 问题 ,即将有错误的程序翻译为固定版本。基于学习的 APR 技术已经探索了各种神经架构。早期的技术 基于循环神经网络 ,通过许多最近的基于 DL 的模型(包括 TFix 、CURE 、Recoder 、RewardRepair 和 SelfAPR )进一步扩展到 CoCoNuT 中的卷积神经网络 和基于 Transformer 的模型 。值得注意的是,许多基于深度学习的方法通过利用抽象语法树 (AST) 和测试执行诊断 等特定于代码的功能来探索提高 APR。具体来说,Recoder 学习 AST 上的语法引导编辑,以确保生成的修复补丁的语法正确性,而 DEAR 使用基于树的长短期记忆 (LSTM) 模型 来更好地编码代码结构,并使用周围的 AST 子树构建合适的修复上下文。对于测试执行信息的使用,SelfAPR 将测试执行诊断编码到输入表示中,而RewardRepair 通过基于程序编译和测试执行信息的损失函数来改进APR。

就 APR 基准而言,最受欢迎的是 Defects4J ,它包含来自开源 GitHub 项目的真正错误修复补丁,并已被大量 APR 工作广泛采用 。该基准测试的一个显着特点是它包含一个测试套件来验证错误是否已修复。然而,由于这些 APR 方法依赖于测试用例,它们不适用于新发现的错误或难以确定性测试的错误 。此外,获取包含测试用例的大规模 APR 数据集仍然是一个关键挑战,例如,最大的 Defects4J 数据集之一仅包含不到 1000 个bugs 和另一个流行的 QuixBugs 只有 40 个 bug。为了摆脱测试用例的要求,还有另一组APR研究专注于静态分析错误或违规,它们可以通过静态分析工具进行标记,并且更容易管理更多的错误修复数据。此外,另一种类型的APR是基于错误修复提交,通过检查提交注释是否包含某些关键字,例如“repair”和“fix”。我们在这项工作中考虑了所有这些类型的 APR 用例。

代码的预训练语言模型

GPT 、BERT 和 T5 等预训练语言模型 (LM) 显着提高了广泛的 NLP 任务的性能。受其成功的启发,最近的许多工作尝试将 NLP 预训练方法适应编程语言。它们通常依赖于仅编码器的 BERT 型模型(CodeBERT 和 GraphCodeBERT )或仅解码器的 GPT 型模型(CodeGPT 和 Codex ),或编码器-解码器模型(PLBART 和 CodeT5 )。特别是,CodeT5 是一个统一的语言模型,在涵盖 8 种不同编程语言的大规模代码语料库上以代码感知预训练目标进行预训练,已被证明可以在广泛的代码理解和生成任务上实现 SoTA 性能 。与之前基于深度学习的 APR 方法(例如 CURE 和 TFix )相比,这些方法主要利用自然语言语料库上预训练的 LM,我们建议利用 CodeT5 的代码感知 LM 来实现具有更好代码理解能力的 APR。最近有人尝试探索 APR 的大语言模型 (LLM) 的小样本学习。根据普伦纳等人的说法,他们基于 Codex 的方法在 TFix 的 200 个实例的随机样本上实现了 46% 的 EM,而微调后的 T5 的 EM 为 59%,这表明少样本学习和微调结果之间仍然存在差距。此外,LLM的小样本学习需要更多的工程工作来促进调整和后处理,这是劳动密集型的。另一个担忧是,诸如 Codex 之类的 LLM 不是开源的,使用其 API 可能会很昂贵,例如,Davinci 版本每 1K 代币的成本为 0.02 美元。

检索增强生成

一般的检索增强生成范式由三个部分组成,包括信息检索、数据增强和生成模型。它在 NLP 领域得到了广泛的研究,并被证明可以在各种 NLP 任务中实现 SoTA 性能,包括问答和问题生成以及机器翻译 。受其成功的启发,许多研究工作采用了这种范式(也称为检索和编辑/优化框架)来使软件智能任务受益,包括代码自动完成 、代码摘要和代码生成 。

方法

我们提出了 RAP-Gen,这是一种新颖的 APR 检索增强补丁生成框架,旨在通过利用从先前错误修复对的代码库中检索到的相关错误修复模式来提高 APR 性能。如图2所示,我们的RAP-Gen框架包含三个阶段:1)补丁检索器训练阶段,学习混合检索器,可以根据词汇和语义相似性找到相关代码补丁; 2) 补丁生成器训练阶段,用于训练 CodeT5 模型以根据错误输入和检索到的错误修复示例生成修复补丁; 3)推理阶段,用于预测多个修复补丁,其中排名第一的修复补丁将被传递给开发人员进行验证。

在这里插入图片描述

请注意,虽然检索增强生成技术已在许多 NLP 任务中得到探索 ,但将此类技术应用于 APR 任务并不是一件容易的事,并且需要系统的适应来解决一些独特的挑战。第一个挑战是如何检索相关修复模式以有效指导 APR,我们构建了一个基于词汇和语义信息的混合检索器,并与表 8 中的其他检索器进行了分析和比较。第二个挑战是如何针对各种语言和 APR 场景构建性能最佳的 APR 模型。我们利用与语言无关的预训练模型 CodeT5 进行检索和补丁生成,与需要不同检索器和生成器的先前工作相比,这是一种更加统一的方法。

在本节的其余部分中,我们首先在 3.1 节中介绍 APR 的检索增强补丁生成的任务公式,然后在 3.2 节中重新审视 CodeT5 的主干模型,然后在 3.3 节中详细介绍混合补丁检索器,在 3.4 节中详细介绍检索增强补丁生成器。

任务制定

D = { ( X i , Y i ) } i = 1 ∣ D ∣ \mathcal{D} = \{(X_i, Y_i)\}_{i=1}^{|\mathcal{D}|} D={(Xi,Yi)}i=1D 为一个包含 ∣ D ∣ |\mathcal{D}| D 个错误-修复对(bug-fix pairs) ( X i , Y i ) (X_i, Y_i) (Xi,Yi) 的程序修复数据集,其中 X i X_i Xi Y i Y_i Yi 分别是第 i i i 个有错误的和已修复的程序补丁。假设我们有一个代码库,包含大量先前的错误-修复对集合 C = { ( B j , F j ) } j = 1 ∣ C ∣ C = \{(B_j, F_j)\}_{j=1}^{|C|} C={(Bj,Fj)}j=1C,其中 ( B j , F j ) (B_j, F_j) (Bj,Fj) 表示第 j j j 个先前的错误-修复对。给定 D \mathcal{D} D 中的一个有错误的程序补丁 X i X_i Xi,检索器基于由 ϕ \phi ϕ 参数化的相关性打分函数 f ϕ ( X i , B j ) f_\phi(X_i, B_j) fϕ(Xi,Bj),在代码库 C C C 中检索出最相关的错误-修复对 ( B j , F j ) (B_j, F_j) (Bj,Fj)。然后,原始输入序列 X i X_i Xi 与检索到的错误-修复对进行增强,形成一个新的输入序列 X ^ i = X i ⊕ B j ⊕ F j \hat{X}_i = X_i \oplus B_j \oplus F_j X^i=XiBjFj,其中 ⊕ \oplus 表示拼接操作。序列到序列(seq2seq)生成器随后以自回归的方式从 X ^ i \hat{X}_i X^i 生成 Y i Y_i Yi。形式上,我们的目标是使用由 θ \theta θ 参数化的补丁 seq2seq 生成器来学习以下概率:
P θ ( Y i ∣ X ^ i ) = ∏ k = 1 n P θ ( Y i , k ∣ X ^ i , Y i , 1 : Y i , k − 1 ) P_\theta(Y_i|\hat{X}_i) = \prod_{k=1}^{n} P_\theta(Y_{i,k}|\hat{X}_i, Y_{i,1} : Y_{i,k-1}) Pθ(YiX^i)=k=1nPθ(Yi,kX^i,Yi,1:Yi,k1)
其中 Y i , 1 : Y i , k − 1 Y_{i,1} : Y_{i,k-1} Yi,1:Yi,k1 是第 k k k 个 token 之前的序列, n n n 表示目标序列 Y i Y_i Yi 中的 token 数量。请注意,我们将外部代码库 C C C 视为一种非参数化记忆,并将检索到的错误-修复对视为生成器的指导性修复模式。在概率术语中,检索可以被形式化为一个潜变量 Z j = ( B j , F j ) Z_j = (B_j, F_j) Zj=(Bj,Fj),在我们的情况中,它通过 top-1 被近似。形式上,该概率可以分解为:
P ( Y i ∣ X i ) = ∑ j = 1 ∣ C ∣ P ϕ ( Z j ∣ X i ) ⏟ Retriever P θ ( Y i ∣ X i , Z j ) ⏟ Generator ≈ P θ ( Y i ∣ X i , Z j ∗ ) P(Y_i|X_i) = \sum_{j=1}^{|C|} \underbrace{P_\phi(Z_j|X_i)}_{\text{Retriever}} \underbrace{P_\theta(Y_i|X_i, Z_j)}_{\text{Generator}} \approx P_\theta(Y_i|X_i, Z_j^*) P(YiXi)=j=1CRetriever Pϕ(ZjXi)Generator Pθ(YiXi,Zj)Pθ(YiXi,Zj)
其中 Z j ∗ Z_j^* Zj 是来自检索器 P ϕ ( Z j ∣ X i ) P_\phi(Z_j|X_i) Pϕ(ZjXi) 的 top-1 检索输出。我们采用这种 top-1 近似,因为在较大的 k k k 上进行边缘化(marginalization)会使得训练和推理变得复杂且低效。我们同时也尝试了将 top- k k k k = 2 , 3 , 5 k = 2, 3, 5 k=2,3,5)与 Fusion-in-Decoding 或 FiD 方法结合使用,但没有观察到显著的性能提升。

CodeT5

CodeT5 是一个基于 Transformer 的统一预训练编码器解码器语言模型,在代码理解和生成任务中均实现了 SoTA 结果。它使用从 GitHub 收集的 8 种不同编程语言(即 Ruby、JavaScript、Go、Python、Java、PHP、C、C#)的 830 万个函数进行了预训练。 CodeT5 采用一组识别标识符的预训练目标,将特定于代码的知识合并到语言模型中。在这项工作中,我们采用 CodeT5 作为我们的密集 DPR 检索器和补丁生成器,以利用其强大的代码理解能力。

BPE 子字标记化(BPE Subword Tokenization) 使用 CodeT5 的好处之一是它提供了特定于代码的字节对编码 (BPE) 标记器。它可以避免代码域中普遍存在的词汇表外(OOV)问题,因为程序员倾向于编写任意标识符,并且不可能构建固定词汇表来容纳任意标记(通常称为开放词汇表问题)。 BPE 是一种学习如何根据令牌的频率分布有效地将令牌拆分为子词的算法。它还可以帮助减少词汇表大小,因为它将稀有标记拆分为多个子词,而不是直接将整个标记添加到词汇表中。此外,由于 CodeT5 标记生成器针对八种流行的编程语言进行了预训练和优化,因此生成的标记化具有良好的通用性。正如所指出的,与默认的 T5 分词器 相比,它平均减少了分词序列 30% - 45%。

编码器和解码器架构(Encoder and Decoder Architecture) CodeT5 由一组用于其编码器和解码器的 Transformer 层组成。每个 Transformer 层都包含一个用于特征聚合的多头自注意力,然后是一个针对前一层输出的前馈层。最后一层生成所有输入标记的隐藏状态,可以用作分类或生成任务的代码表示。对于 CodeT5 编码器,它利用双向注意掩码来学习与 BERT 类似的更好的上下文表示,而 CodeT5 解码器则采用因果注意掩码来确保每个标记只能关注先前的标记,以实现更好的序列生成。在 RAP-Gen 框架中,我们采用 CodeT5 作为补丁生成器及其编码器,专门用于密集检索器。

混合补丁检索器

RAP-Gen 中的检索器模块旨在检索相关修复模式以指导 APR 流程。它建立在相关性评分函数 f ϕ ( X i , B j ) f_\phi(X_i, B_j) fϕ(Xi,Bj) 的基础上,计算 D D D 中的(查询)错误 X i X_i Xi 与代码库 C C C 中的先前(关键)错误 B j B_j Bj 之间的相关性。如 2-1 所示,我们利用混合方法将基于词汇的 BM25 检索器和基于语义的 DPR 检索器结合起来,以同时考虑词汇和语义信息。像这样的先前工作表明,稀疏和密集检索器可以相互补充,以实现更强大的文本检索。

基于词汇的检索器 (Lexical-based Retriever)。我们采用了 BM25,这是一种著名的基于词汇的检索器,它使用稀疏向量表示进行词汇匹配。BM25 将每个代码补丁转换为词袋表示(bag-of-words representation),并计算查询补丁 X i X_i Xi 和候选补丁 B j B_j Bj 之间的词汇相似度。计算出的相似度得分表示为 f ϕ ( X i , B j ) = B M 25 ( X i , B j ) f_\phi(X_i, B_j) = BM25(X_i, B_j) fϕ(Xi,Bj)=BM25(Xi,Bj)。作为一种基于词汇的稀疏检索器,BM25 对源代码中标识符的命名选择非常敏感,而这并不影响代码的语义。

基于语义的检索器 (Semantic-based Retriever)。我们采用稠密段落检索器 (Dense Passage Retriever, DPR),通过测量语义相似度来检索相关的补丁。为了对代码补丁进行编码,我们使用基于 Transformer 的编码器将每个补丁映射到一个固定大小的稠密向量。具体来说,我们从预训练的 CodeT5 编码器初始化 DPR,并在代码到代码的检索任务中对其进行训练。为了训练 DPR,我们建议利用代码库中的错误-修复对,将带有错误的代码 B j B_j Bj 作为查询 (query),并将相应的已修复代码 F j F_j Fj 作为键 (key)。这是基于这样一个假设:包含错误的补丁及其修复后的补丁通常具有相似的语义(例如标识符和代码结构)。这个技巧避免了整理错误到错误(bug-to-bug)搜索数据集所需的大量人工标注工作。

对于每个查询补丁和候选补丁,我们在其词元化(tokenized)序列的开头加上一个特殊的 [CLS] token,并将该 [CLS] token 的最后一层隐藏状态用作补丁表示。我们使用一个共享的 DPR 来分别将 D \mathcal{D} D 中的查询补丁 X i X_i Xi C C C 中的候选补丁 B j B_j Bj 编码为 C L S X i CLS_{X_i} CLSXi C L S B j CLS_{B_j} CLSBj。然后,这两个补丁表示之间的相似度由它们的内积计算得出,如下所示:
f ϕ ( X i , B j ) = s i m ( X i , B j ) = [ C L S X i ] T [ C L S B j ] f_\phi(X_i, B_j) = sim(X_i, B_j) = [CLS_{X_i}]^T [CLS_{B_j}] fϕ(Xi,Bj)=sim(Xi,Bj)=[CLSXi]T[CLSBj]
为了训练 DPR 检索器,我们利用批次内负样本 (in-batch negatives) 来优化 InfoNCE 对比损失,定义如下:
L n c e = 1 N ∑ i = 1 N − log ⁡ exp ⁡ ( s i m ( B i , F i ) ) exp ⁡ ( s i m ( B i , F i ) ) + ∑ j ∈ M , j ≠ i exp ⁡ ( s i m ( B i , F j ) ) \mathcal{L}_{nce} = \frac{1}{N} \sum_{i=1}^N -\log \frac{\exp(sim(B_i, F_i))}{\exp(sim(B_i, F_i)) + \sum_{j \in \mathcal{M}, j \neq i} \exp(sim(B_i, F_j))} Lnce=N1i=1Nlogexp(sim(Bi,Fi))+jM,j=iexp(sim(Bi,Fj))exp(sim(Bi,Fi))
其中 M \mathcal{M} M 是当前的微批次 (minibatch), N N N 表示微批次中正训练样本的数量。该目标旨在最大化正样本之间的相似度,同时最小化负样本之间的相似度。每个正样本将有 ∣ M ∣ − 1 |\mathcal{M}| - 1 M1 个负样本。请注意,由于训练数据的噪声性质,我们没有像其他论文那样采用困难负样本挖掘 (hard negative mining) 策略。

在推理阶段,给定一个有错误的查询补丁 X i X_i Xi,DPR 通过计算 X i X_i Xi(query)和 B j B_j Bj(key)之间的相似度来检索出一个相关的错误-修复对 ( B j , F j ) (B_j, F_j) (Bj,Fj)。我们也尝试过基于 X i X_i Xi F j F_j Fj 之间的相似度,但没有产生更好的结果。

混合检索器 (Hybrid Retriever)。为了同时考虑词汇和语义信息,我们采用了一种混合方法,将 BM25 和 DPR 结合起来。相似度得分计算为 f ϕ ( X i , B j ) = s i m ( X i , B j ) + λ B M 25 ( X i , B j ) f_\phi(X_i, B_j) = sim(X_i, B_j) + \lambda BM25(X_i, B_j) fϕ(Xi,Bj)=sim(Xi,Bj)+λBM25(Xi,Bj),其中 λ \lambda λ 是一个用于平衡两个检索器的权重,在我们的实验中凭经验设置为 1。基于这个组合的相似度得分,我们选择 top-1 相关的错误-修复对 ( B j , F j ) (B_j, F_j) (Bj,Fj) 作为修复模式,以指导补丁生成器进行错误修复。与仅依赖词汇或仅依赖语义信息的检索器相比,混合检索器预计将更加稳健。

检索增强补丁生成器

如图 2 所示,给定一个有错误的补丁 X i X_i Xi,我们搜索一个最相关的修复模式 ( B j , F j ) (B_j, F_j) (Bj,Fj),并将其传递给补丁生成器以生成一个修复后的代码补丁 Y i Y_i Yi。我们采用了一种简单但有效的策略,通过将检索到的错误-修复对追加到原始的错误补丁中,将 X i X_i Xi 增强为 X ^ i = X i ⊕ B j ⊕ F j \hat{X}_i = X_i \oplus B_j \oplus F_j X^i=XiBjFj。请注意,补丁生成器模块可以是任何序列生成模型。与先前直接采用在自然语言上优化的生成器的研究不同,我们提出使用 CodeT5,这是一个为代码优化的、具备代码感知的编程语言模型。

训练 (Training) 我们将提供给 CodeT5 补丁生成器的检索增强输入准备为 X ^ i = “[CLS]  X i  [BUG]  B j  [FIX]  F j ” \hat{X}_i = \text{“[CLS] } X_i \text{ [BUG] } B_j \text{ [FIX] } F_j\text{”} X^i=“[CLS] Xi [BUG] Bj [FIX] Fj,其中 [BUG][FIX] 是特殊的 token,用于将检索到的错误-修复对与错误补丁分离开来。CodeT5 的编码器将 X ^ i \hat{X}_i X^i 作为输入,并从其解码器以自回归的方式输出修复后的补丁 Y i Y_i Yi(见第 3.1 节)。我们考虑了有错误的补丁 X i X_i Xi 的两种设置,即它可能包含或可能不包含错误定位信息。如果它包含错误类型、错误消息和错误行等错误信息,那么这个有错误的补丁将被增强为 “error information [SEP] X i X_i Xi”,从而结合错误信息来帮助修复 bug。为了训练补丁生成器,我们采用教师强制 (teacher forcing) 来最小化所有训练实例上的交叉熵损失 L c e \mathcal{L}_{ce} Lce,其定义为:
L c e = − ∑ i = 1 ∣ D ∣ log ⁡ ( P θ ( Y i ∣ X ^ i ) ) \mathcal{L}_{ce} = - \sum_{i=1}^{|\mathcal{D}|} \log(P_\theta(Y_i|\hat{X}_i)) Lce=i=1Dlog(Pθ(YiX^i))
在教师强制中,解码器使用真实的 (ground-truth) 上下文以实现更快的收敛。我们使用训练集作为搜索代码库。为了避免信息泄漏 (information leakage),我们不允许检索器访问真实的错误-修复对,否则由于生成器可以直接复制检索到的修复作为目标输出,训练损失很容易降到接近 0。这种策略使得训练和评估过程更加兼容,因为评估集同样不与训练集重叠。

通过束搜索进行推理(Inference with Beam Search) 在推理过程中,如图 2-3 所示,我们使用波束搜索为输入的错误补丁生成固定补丁候选的排序列表,其中预测的数量由波束大小 B 决定。具体来说,在每个解码时间步长,波束搜索使用最佳优先搜索策略以最高概率选择最有希望的修复候选。当发出通知句子结束的 [EOS] 令牌时,搜索过程终止。将通过与真实修复补丁进行比较或通过测试套件进行验证或通过软件开发人员的手动验证来检查排名最高的修复补丁的正确性。

实验设计

数据集

我们在三个 APR 数据集上评估 RAP-Gen,即 JavaScript 中的 TFix 、Code Refinement 和 Java 中的 Defects4J (v1.2) 。所有数据集最初都是从开源 GitHub 提交中收集的,但基于不同的错误识别标准,其中 TFix 基于 JavaScript 静态分析器的诊断,Code Refinement 基于与修复相关的提交消息,Defects4J 基于运行测试套件。我们在表 1 中报告了他们的数据统计。

在这里插入图片描述

在这里插入图片描述

TFix TFix 是一个大型程序修复数据集,包含从 550 万个 GitHub 提交中精选的 JavaScript 代码补丁对。它包括由静态分析器 ESLint2 检测到的 52 种错误类型(参见表 4)。除了错误类型之外,它还提供了丰富的错误注释,例如错误消息和本地化错误行,因此不需要像之前的工作那样进行故障定位。为了准备输入序列,如图 3(a) 所示,我们将所有错误信息与错误代码补丁一起组合成单个文本,如下所示:

在这里插入图片描述

在这里插入图片描述

其中错误上下文由给定的本地化错误行及其两个相邻代码行组成,形成有错误的代码补丁。对于目标序列,通过将错误行替换为错误上下文中的固定行来获得。在数据处理过程中,我们观察到每个数据拆分内部以及数据拆分之间存在重复问题。具体来说,训练、验证和测试分割中分别有 114、2 和 4 个重复项,训练和测试、训练和测试、验证和测试分割之间的分割间重复项分别有 28、34 和 4 个重复项。我们过滤了所有这 243 个重复项,以获得 TFix 的重复数据删除版本,如表 1 所示。

基线模型 我们将 RAP-Gen 与现有的基于 DL 的 APR 模型(包括 SequenceR 和 CoCoNuT )进行比较。此外,我们还比较了大型预训练模型 T5-large ,该模型已在 TFix 上进行了微调,以实现 SoTA 性能。

评估指标 我们按照 TFix 上的报告精确匹配 (EM) 准确性和 BLEU-4 分数来评估程序修复性能。 BLEU-4 是评估子字重叠程度的宽松指标,而 EM 是更严格的指标,要求预测与实际提交中的真实补丁相同。由于有错误的程序可能有不同的修复方法,因此我们进一步采用之后的错误消除指标来考虑各种形式的修复。如果现有错误被删除并且修复后没有引入新的错误(由静态分析器 ESLint 检测到),则错误删除的预测被视为正确。对于所有指标,我们以 0-100 (%) 的范围表示结果,分数越高代表性能越好。

Code Refinement Code Refinement 包含函数级别的错误修复对,这些错误修复对最初是在 2011 年 3 月至 2017 年 10 月期间从公共 GitHub Archive3 收集的。他们使用 Google BigQuery API 来识别所有具有包含以下模式的消息的 Java 提交:(“修复”或“解决”)和(“错误”或“问题”或“问题”或“错误”),以确保收集的错误修复函数对的质量。他们通过使用索引标记(例如 TYPE1、VAR1、METHOD1 等)混淆标识符来规范化函数。图 3 (b) 中可以找到一个数据示例。数据集包含两个数据子集,这两个数据子集由标记的数量确定,即小集的代码标记数≤ 50,中集的代码标记数 ≤ 50 < ≤ 100。由于未提供错误定位,因此将整个代码片段作为我们模型的源输入序列。目标序列是整个代码片段的精炼版本。

基线和指标 我们将 RAP-Gen 与基于 Transformers 的预训练编程语言模型进行比较。其中一组模型是仅编码器模型,例如 RoBERTa(code)、CodeBERT 和 GraphCodeBERT 。这些仅编码器模型需要随机初始化的解码器来生成修复。此外,我们还与 PLBART 和 CoTexT 等编码器-解码器 Transformer 模型进行比较。 NSEdit 是一个语言模型,编码器和解码器分别从 CodeBERT 和 CodeGPT 初始化。它经过微调,可通过神经符号编辑序列生成修复程序,并在代码细化中列为当前的 SoTA 模型。我们按照应用BLEU-4和精确匹配来评估代码细化数据集。

Defects4J Defects4J 是最广泛采用的 APR 基准测试之一,其中包含 17 个开源 GitHub 项目中的 835 个真实的错误修复补丁。每个错误修复示例都附有测试用例来验证修复。 Defects4J 错误的一个示例可以在图 3© 中找到,其中“-”表示要修复的错误行,“+”表示开发人员提交的正确修复。有缺陷的行及其相应的代码上下文组合起来形成源输入序列,而目标序列是固定行。由于 Defects4J 只有测试集,我们使用 SelfAPR 使用自我监督学习方法整理的项目特定训练数据。具体来说,叶等人。 在 Defects4J 的正确过去版本上提出了 16 条扰动规则,以构建 1,039,873 个合成错误修复 Java 补丁。我们使用在线提供的 830,240 个训练数据的子集。4 为了进行测试,我们按照它们的精确设置来评估来自 Defects4J v1.2 和 v2.0 的 818 个缺陷的模型(表 1),其中涵盖了地面实况故障定位(完美 FL)和基于频谱的 FL 工具(例如 Gzoltar )的预测 FL 的设置。

基线和指标 我们将 RAP-Gen 与一系列基于 SoTA DL 的 APR 模型进行比较,包括 SequenceR 、CoCoNuT 、CURE 、RewardRepair 、Recoder 、DLFIx 、DEAR 、BugLab 和 SelfAPR 。为了进行评估,我们根据先前工作的单元测试和手动验证来计算在 Defects4J 上可以正确修复多少错误。我们首先运行测试套件来自动识别每个错误的合理正确补丁,然后进行手动检查以完全验证其正确性。我们的 RAP-Gen 的正确预测包含在我们的工件中。对于基线结果,我们引用了 DEAR 中的 DLFix 和 DEAR 的结果,以及 SelfAPR 中的其他结果。

实施细节

对于 RAP-Gen,我们采用 CodeT5-base ,其中包含 12 个编码器层和 12 个解码器层,参数大小为 220M。我们使用 PyTorch 实现 RAP-Gen 并使用 AdamW 优化器对其进行训练。为了训练其神经组件,我们在 Google Cloud Platform 上使用 NVIDIA A100-40G GPU 运行这些实验。对于每个基准,我们使用对比损失 LinfoNCE(使用 64 的批量大小和 2e-5 的学习率)对 DPR 检索器进行 50 个时期的微调。我们使用序列生成损失 Lce 将 RAP-Gen 生成器微调 30 个时期,批量大小为 32,学习率为 5e-5。这些最佳设置是通过超参数调整的网格搜索获得的:批量大小为 (16, 32, 64),学习率为 (1e-4, 5e-5, 2e-5)。 DPR检索器的训练时间为5-9小时,具体取决于数据集的训练大小,而RAP-Gen生成器的训练时间在2天内。对于基于词汇的检索器,我们使用 BM25 的开源 Python 库5,它可以在一小时内通过多处理在 CPU 上进行高效训练。在推理过程中,我们对 TFix 和 Code Refinement 使用波束大小为 5 的波束搜索,对 Defects4J 使用波束大小为 100 的波束搜索。

研究问题

为了调查 RAP-Gen 在 APR 任务上的有效性,我们寻求回答以下研究问题 (RQ):

  • RQ1:与 TFix 上基于 DL 的 APR 模型的比较研究。与其他基于 DL 的 APR 方法相比,RAP-Gen 如何修复 TFix 上的 JavaScript linter 标记的编码错误?
  • RQ2:RAP-Gen 对 TFix 的预测分析。 RAPGen 如何针对不同错误类型和补丁长度修复 TFix 错误? RAP-Gen在修复Bug时采用了哪些修复操作?
  • RQ3:与基于 DL 的 APR 模型在代码优化方面的比较研究。与其他基于 DL 的 APR 方法相比,RAP-Gen 在修复 Java 提交相关错误方面表现如何?
  • RQ4:我们的混合补丁检索器的分析。我们的混合补丁检索器能否找到相关的修复模式来指导 APR?
  • RQ5:Defects4J 上基于 DL 的 APR 模型的比较研究?与其他基于 DL 的 APR 方法相比,RAP-Gen 在修复开源项目中的 Java bug 方面表现如何?

实验结果

RQ1:TFix 上基于 DL 的 APR 模型的比较研究

改进了 TFix 评估 原始的TFix基准测试采用52种错误类型的直接平均精确匹配(EM)准确率作为主要指标。然而,如表4所示,这些错误类型的分布相当不平衡,例如,主要错误类型“no-invalid-this”有16,166个实例,而最少的错误类型“no-new-symbol”只有10个实例。因此,采用加权平均值来考虑错误类型的分布更为合理。此外,我们还发现了其精确匹配评估的另一个局限性:如果预测修复中的空白字符(如空格或换行符)比真实修复多一个,就会被视为错误的精确匹配。然而,额外的空白字符并不会影响JavaScript程序的正确性。因此,我们建议使用无空格精确匹配(EM)的加权平均值,即在计算EM之前对空白字符进行归一化处理,以排除空白字符不匹配的影响。由于我们发现TFix数据集中存在重复问题,我们还报告了去重后的版本的结果。

CodeT5 结果 我们将 CodeT5 模型与 TFix 上其他基于 DL 的基线进行比较,并在表 2 中显示结果。对于平均 EM w/spaces 的原始指标,CodeT5-base (50.88) 也比 T5-large (49.33) 产生更好的精度,因为它具有更大的模型大小(∼ CodeT5-base 的 3.5 倍:770M 与 220M)。如果我们关注更合理的平均 EM w/o spaces,CodeT5-base 显着提高了性能,与 T5-large 相比,绝对精度提高了约 5 个(49.35→54.30)。基于无 spaces 的加权平均 EM,CodeT5-small (50.31) 和 CodeT5-base (53.57) 均优于包括 T5-large (49.70) 在内的所有基线。这表明,在大规模源代码上进行代码感知预训练的CodeT5模型对程序的理解更为深入。对于 TFix 评估,我们使用 EM 表示不带 spaces 的加权平均 EM。我们进行了一项消融研究,从输入序列中删除错误信息,包括错误类型和错误消息,我们观察到 CodeT5-small 和 CodeT5-base 模型都有一致的性能降级,这表明告知 APR 模型需要修复哪些类型的错误是有帮助的。

在这里插入图片描述

RAP-Gen 结果 我们在表 3 中报告了 RAP-Gen 模型在重复数据删除 TFix 基准上的结果,其中由于重复后数据大小的变化,结果略有不同。结果显示 RAP-Gen 显着优于 T5-large (49.58→54.15 EM)。这表明检索增强生成是 APR 的一种可行且有效的方法,并且语义信息和词汇信息对于检索相关修复模式至关重要。我们在图 3(a)中展示了一种情况,我们可以观察到 RAP-gen 在检索到的修复模式的指导下成功修复了错误,而没有检索的 CodeT5 则给出了错误的修复。

在这里插入图片描述

错误消除评估 尽管精确匹配可以确保机器生成补丁的正确性,但它可能是一个过于严格的指标,无法考虑其他形式的正确修复。因此,我们按照采用错误消除指标,其中如果错误被消除并且没有引入新的错误,则修复被视为正确。错误检测基于静态分析器 ESLint。我们在图 4 中报告了 6,793 个实例6 的大子集上的错误消除以及 EM 和 BLEU-4 结果。我们观察到 RAP-Gen 比 T5-large 显着提高了错误消除精度(69.30→78.80)。与 EM 和 BLEU-4 相比,更大的增益意味着 RAP-Gen 更有能力产生各种形式的良好修复。此外,EM 与更宽松的错误消除指标非常一致。

在这里插入图片描述

RQ2:TFix 上 RAP-Gen 的分析

错误类型的性能细分 我们在表 4 中列出了重复数据删除 TFix 上 52 种错误类型的性能细分。RAP-Gen 在 40/52 错误类型方面优于之前的 SoTA T5-large。特别是对于主要错误类型“no-invalid-this”,RAP-Gen将其精确匹配度从T5-large的37.48提高到44.13,即修复了更多107个实例。总的来说,RAP-Gen 比 T5-large 正确修复了 478 个错误,模型尺寸小得多。

修复操作分析 我们分析我们的模型在 TFix 上执行了哪些修复模式。与代码插入和替换操作相比,我们观察到很大一部分修复由删除操作组成。我们发现修复操作包括代码插入(12.5%)、替换(8.1%)、删除(47.9%)、插入和替换(6.9%)、插入和删除(8.2%)、替换和删除(7.2%)以及所有三种方式(9.2%)。早期的研究也反映出删除操作是最常见的修复模式之一。此外,我们发现一种占主导地位的修复操作是错误行(EL)删除,即简单地从有缺陷的代码中删除错误行,在测试集中约占 23%。我们在表 5 中展示了模型如何执行此操作。我们观察到 RAP-Gen 实现了最佳的精度、召回率和 F1 分数,与 CodeT5 的 67 个和 T5-large 的 71 个相比,误报数最低为 56 个。这表明 RAP-Gen 能够学习更多样化的错误修复模式,而不是过度依赖琐碎的错误行删除模式。

在这里插入图片描述

补丁长度分析 我们分析了补丁长度的影响(图 5)。图 5(a)显示了错误补丁的累积分数(按补丁长度根据其结果进行分组)。我们发现 RAP-Gen 成功修复的补丁往往比失败的补丁要短。图 5 (b) 显示了按错误补丁长度划分的正确修复的分布,其中 RAP-Gen 可以修复比 T5-large 更多的错误,特别是对于具有 40 到 60 个标记的补丁。

在这里插入图片描述

RQ3:与基于 DL 的 APR 模型在代码细化方面的比较研究

我们在表 6 中报告了 Code Refinement 的比较结果。所有基线结果都直接从原始论文中获得。我们在表 6 中报告了 Code Refinement 的比较结果。所有基线结果都直接从原始论文中获得。我们首先观察到“Naive Copy”给出了相当高的 BLEU-4 分数,但精确匹配为零,表明有错误的代码及其修复有很大的重叠,应该采用精确匹配作为主要指标。在基线中,NSEdit 是一个非常有竞争力的基线,在小子集上获得了最佳结果 (24.04 EM),而 CodeT5 在中等集上给出了最佳结果 (13.96 EM)。与小集相比,中集的结果较低,表明较长的错误函数更难以修复,这与图 5 (a) 中的观察结果一致。总体而言,RAP-Gen 在两个子集上实现了新的 SoTA 结果,小型集为 24.80 EM,中集为 15.84 EM。这再次证实了检索到的修复模式提供了有用的信号来指导程序修复,并且混合检索器通过使用词汇和语义信息而更加强大。图 3 (b) 显示了一种情况,其中检索到的修复模式(错误行删除)帮助 RAP-Gen 成功修复错误。

在这里插入图片描述

RQ4:混合补丁检索器的分析

我们研究了不同的检索模块如何影响表 7 中检索增强生成设置中的 APR 性能。我们首先通过从代码库中随机检索错误修复对来与随机基线进行比较。与“无检索器”相比,一致的性能降级意味着随机检索的修复模式无法为 APR 提供有用的指导信号。然后我们将 RAP-Gen 中的混合检索器与不同的检索器进行比较:稀疏 BM25 检索器和基于 CodeBERT 或 CodeT5 的密集检索器。我们观察到基于 CodeT5 的检索器优于基于 BM25 或基于 CodeBERT 的检索器,而我们结合 BM25 和 CodeT5 的混合检索器实现了最佳 APR 性能,验证了我们在 RAP-Gen 中检索器模块设计的有效性。

在这里插入图片描述

我们进一步分析检索器在查询和检索最多的补丁之间的词汇和语义匹配方面的性能。我们使用 BLEU-4 分数来测量它们的子标记重叠以进行词汇匹配,而对于语义匹配,我们计算由我们微调的 DPR 检索器编码的密集向量之间的余弦相似度 (CosSim)。表 8 显示了我们的检索器在 TFix 和 Code Refinement 基准测试中的性能。第一行表示通过从代码库中随机检索错误修复对的性能下限,我们观察到该随机基线在词汇和语义匹配方面的得分要低得多。

在这里插入图片描述

对于词法匹配,BM25 在 TFix 上优于 DPR(基于 CodeT5),但在两个 Code Refinement 子集上表现不佳。我们认为,这是由于TFix和Code Refinement之间的数据差异所致,后者使用了模糊标识符(如VAR1、VAR2等),这阻碍了基于词汇的BM25检索器的性能。混合检索器在所有数据集上实现了最佳词汇匹配,揭示了语义信息可以补充词汇信息。对于语义匹配,DPR 在所有数据集上取得了最佳结果,这并不奇怪,因为它是针对相同的目标进行优化的。值得注意的是,我们的混合检索器取得的结果略低于 DPR,但比 BM25 好得多,这意味着它可以平衡词汇和语义信息,并且比基于词汇的检索器更强大,后者对标识符命名的选择很敏感。

RQ5:与基于 DL 的 APR 模型对 Defects4J 的比较研究

RAP-Gen 结果 我们在表 9 中将 RAP-Gen 与 Defects4J v1.2 和 v2.0 上的其他基于 SoTA DL 的 APR 基线进行比较。我们考虑基于频谱的故障定位 (FL) 和完美 FL 的两种设置。请注意,所有基线结果均引用自 SelfAPR 和 DEAR 。为了公平比较,我们遵循惯例,采用与 RAP-Gen 的 Recoder 相同的 5 小时超时、波束大小 100、集成策略。

在这里插入图片描述

如表 9 所示,与其他基线相比,我们的 RAP-Gen 通过修复最大的一组错误(v1.2 中的 72 个错误和 v2.0 中的 53 个错误),在完美 FL 下实现了新的 SoTA 性能。特别是,它在 v1.2 和 v2.0 中分别比之前的 SoTA SelfAPR 多修复了 7 个和 8 个错误。对于基于频谱的 FL 的结果,RAP-Gen 实现了第二好的性能,这与 v1.2(48 vs. Recoder 的 49)和 v2.0(26 vs. SelfAPR 28)上的 SoTA 模型非常有竞争力。考虑到 v1.2 和 v2.0 的错误,它总共修复了 74 个错误,超过了 Recoder 的 68 个错误或 SelfAPR 的 67 个错误。总的来说,有或没有完美 FL 的结果都验证了我们的 RAP-Gen 相对于其他基于 DL 的基线的优越性。值得注意的是,与许多此类模型相比,我们的 RAP-Gen 展示了另一个优势,即作为与语言无关的模型,可以推广到其他 APR 用例。相比之下,Recoder 需要学习对 AST 的编辑,而 SelfAPR 需要测试执行诊断,这使得它们不适用于或仅限于处理无法在没有测试用例的情况下解析为 AST 或其他 APR 场景的碎片代码片段。

我们研究了 RAP-Gen 在多大程度上可以补充现有的 APR 模型,包括 Recoder 、RewardRepair 和 SelfAPR 。与这些基于 SoTA DL 的 APR 方法相比,RAP-Gen 分别修复了 Defects4J v1.2 和 v2.0 的 13 个和 12 个独特错误,这些错误从未被任何其他基于 DL 的 APR 方法正确解决,这证明我们的 RAP-Gen 可以补充其他性能最佳的 APR 方法。我们在图3©中进一步展示了一个案例,并发现RAP-Gen成功修复了Chart-9漏洞,但修复的形式与开发人员的修复不同。

从各种修复模式中检索的效果 我们分析从各种修复模式中检索错误修复样本将如何影响 APR 性能。对于此分析,如图 6 所示,我们从 Defects4J v1.2 和 v2.0 中选择了在完美 FL 设置下 CodeT5 无法修复(红色)而 RAP-Gen 可以修复(绿色)的 39 个错误。对于修复模式的分类,我们基于 SelfAPR 设计的 16 条扰动规则,并使用每个规则的训练集作为单独的检索代码库。我们从代码库中为每个规则或修复模式(表示为 P1 到 P16)检索 top-1 错误修复样本,并在使用 RAP-Gen 中此类检索的指导信号后检查它是否可以提高 CodeT5 的性能。

在这里插入图片描述

从图 6 中,我们观察到从 RAP-Gen 中的各种修复模式检索通常有助于纠正 CodeT5 对 Defects4J 错误的预测。我们发现 v1.2 中的大多数错误可以在从许多不同的模式检索后修复,而对于 v2.0,有一些错误仅适用于少数修复模式,例如 Closure-150 的 P16 和 JacksonDatabind-54 的 P5。在各种修复模式中,我们发现“插入现有块”的 P13 和“删除语句”的 P14 适用于大多数 bug,表明这些是修复 Defects4J bug 的关键修复模式。

有效性的威胁

构造有效性 我们在三个 APR 基准测试上评估了 RAP-Gen:JavaScript 中的 TFix、Code Refinement 和 Java 中的 Defects4J。在 TFix 上,我们发现了一个重复问题,并从总共 104,804 个数据实例中删除了 243 个内部拆分或内部拆分重复项。这可能会稍微影响我们的模型和 TFix(T5-large)模型之间的比较。我们通过报告我们的模型在原始 TFix 数据集上的结果以及 TFix 模型在重复数据删除测试集上的结果来减轻这种威胁。在代码优化方面,与 TFix 中的对可以通过静态分析器验证不同,它的错误修复对是从 GitHub 提交中通过错误修复相关的提交消息来策划的,并且只有其中一部分是手动验证。有可能某些对是无效的(与错误修复无关),这对该数据集评估的可靠性带来潜在威胁。

内部有效性 对内部有效性的威胁主要在于 RAP-Gen 的超参数搜索阶段。作为一种神经模型,其性能很大程度上受到超参数选择的影响。为了减轻此类威胁,我们进行网格搜索来调整一组更好的超参数,但我们仍然不能声称它们是最好的。

外部有效性 我们仅在 JavaScript 和 Java 程序上评估了 RAP-Gen 模型,并未研究其对其他编程语言 (PL) 的泛化。然而,我们的方法与语言无关,因为我们不采用任何特定于代码的功能(例如 AST),并且可以以直接插入的方式应用于其他 PL。此外,我们对两个 PL 中的三个 APR 数据集的评估应该足够全面,以验证我们方法的有效性。

总结

我们提出了一种新颖的检索增强补丁生成(RAPGen)框架,用于自动程序修复,这是软件工程中的一项基本任务,旨在减少开发人员在调试中的手动工作。 RAP-Gen 由两个组件组成:一个混合补丁检索器,用于检索查询有缺陷补丁的相关修复模式;以及一个补丁生成器,用于根据有缺陷补丁及其检索到的指导修复模式合成固定补丁。此外,我们建议利用强大的代码感知预训练语言模型 CodeT5 作为 RAP-Gen 的骨干,以统一的方式促进补丁检索和生成。 JavaScript 和 Java 中的三个不同 APR 基准的综合结果证明了我们的 RAP-Gen 模型相对于现有的基于深度学习的 APR 方法的有效性和优越性。

数据可用性

我们的代码和模型可以在此链接(https://figshare.com/s/a4e95baee01bba14bf4b)中找到,以重现本文中的结果。

启发

传统的深度学习修复方法完全依赖模型内部固定的参数来应对高度复杂的修复搜索空间,RAP-Gen通过使用 RAG 从代码库中检索相关的修复模式(bug - fix 对),模拟了人类开发者的真实调试行为(查阅 StackOverflow 等),从而指导模型正确修复,大幅度降低依赖简单的删除报错行或者乱修复的情况出现。

相关知识链接

@inproceedings{10.1145/3611643.3616256,
author = {Wang, Weishi and Wang, Yue and Joty, Shafiq and Hoi, Steven C.H.},
title = {RAP-Gen: Retrieval-Augmented Patch Generation with CodeT5 for Automatic Program Repair},
year = {2023},
isbn = {9798400703270},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3611643.3616256},
doi = {10.1145/3611643.3616256},
booktitle = {Proceedings of the 31st ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering},
pages = {146–158},
numpages = {13},
keywords = {Automated program repair, Neural networks, Pretrained language models, Retrieval-augmented generation},
location = {San Francisco, CA, USA},
series = {ESEC/FSE 2023}
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐