Self-Edit:故障感知的代码生成编辑器

互联不一般哥 2024-03-13 06:34:01

Self-Edit: Fault-Aware Code Editor for Code Generation

Kechi Zhang, Zhuo Li, Jia Li , Ge Li∗, Zhi Jin∗

Key Lab of High Confidence Software Technology (PKU), Ministry of Education

School of Computer Science, Peking University, China

引用

Zhang, K., Li, Z., Li, J., Li, G., & Jin, Z. (2023). Self-Edit: Fault-Aware Code Editor for Code Generation. arXiv preprint arXiv:2305.04087.

论文:https://arxiv.org/pdf/2305.04087.pdf

摘要

大型语言模型(LLMs)已经展示了在竞赛编程任务上生成代码的卓越能力。但由于样本数量有限,LLMs仍然面临着较低的准确性,因此受人类编程过程的启发,本文提出了一种名为Self-Edit的生成和编辑方法,该方法利用LLMs生成的代码的执行结果来提高竞赛编程任务中的代码质量。本文在问题中提供的示例测试用例上执行生成的代码,并将执行结果包装成补充性注释。同时利用这个注释作为指导,本文使用具有错误感知能力的代码编辑器来纠正生成代码中的错误。本文提出的方法在两个竞技编程数据集上进行了广泛的评估,涵盖了九种不同大小参数的LLMs,与其他后处理方法相比,本文的方法展示了更高的准确性和效率。

1 引言

最近,大型语言模型(LLMs)已被应用于竞赛编程任务。该任务要求理解一个复杂的自然语言问题描述,包括示例测试用例,并正确实现可以涵盖数百行的解决方案。解决方案通过在隐藏的测试用例上执行来进行评估。然而,现有的LLMs在这个任务中通常具有较低的准确性和通过率。例如,在流行的竞技编程基准APPS-test上(Hendrycks等,2021),几乎最强大的模型GPT3(Brown等,2020)在只允许提交一个程序的情况下(称为pass@1)仅达到7%的准确性。为了提高LLMs在竞赛编程任务上的性能,我们从人类编程的过程中汲取灵感。解决竞技编程问题时,程序员通常会编写初始程序,执行一些示例测试用例,并根据测试结果优化代码。

在这个过程中,程序员可以从测试结果中获取关键信息(例如,程序输出或编译/运行时错误消息),这有助于调试程序。我们通过采用类似的流水线(如图1(a)所示)来实现这个想法,使用基于神经网络的编辑器。通过分析预训练LLM生成的代码,我们发现一些生成的代码可以通过轻微修改来改进。图1(b)显示了GPT3在APPS-test数据集上生成的代码示例。GPT3生成的代码与问题描述不一致。我们注意到错误消息直接指出了代码中的错误,我们可以利用这个信息快速修复错误。这激发了我们调查利用执行结果编辑和改进LLMs生成代码质量的方法。在这项工作中,我们提出了一种新颖的生成和编辑方法,用于增强LLMs在竞技编程任务中的性能,称为Self-Edit。为了模仿上述人类程序员的行为,我们的方法在三个步骤中结合了LLMs的能力:

图1 a) 我们的方法受到人类程序员解决问题的过程的启发。

图1 b) GPT3模型在APPS-test数据集上的输出以及相应的错误消息,该消息是通过在示例测试案例上运行获得的。

① 使用LLMs生成。我们使用大型语言模型作为黑匣子生成器,根据问题描述生成程序。

② 执行。给定LLMs生成的代码,我们在示例测试用例上执行它,以获取执行结果。我们进一步将执行结果与模板一起封装为附加注释,以提供编辑所需的额外有用信息。

③ 编辑。我们开发了一个故障感知的神经代码编辑器,该编辑器接受生成的代码和附加注释作为输入,并对代码进行细化。我们的代码编辑器旨在通过LLMs提高代码生成的质量和准确性。

我们在两个公共竞技编程基准上进行了广泛的实验,包括APPS(Hendrycks等,2021)和HumanEval(Chen等,2021)。我们将我们的方法应用于9个流行的LLMs,参数大小从110M到175B不等,以展示其通用性。与直接从LLMs生成相比,我们有几个发现:

① 我们的方法显著提高了LLMs的性能。特别是,我们的方法在APPS-dev上将pass@1的平均值提高了89%,在APPS-test上提高了31%。即使对于选择的最大语言模型GPT3-175B,我们相对较小的编辑器模型也可以将在APPS-dev基准上的pass@1从26.6%提高到32.4%。

② 我们的方法在不同样式的数据集HumanEval上具有泛化能力,将pass@1的平均值提高了48%,显示了在分布外基准上的迁移能力。

最近,也提出了一些用于后处理LLMs生成的程序的方法(Shi等,2022;Inala等,2022;Chen等,2022;Zhang等,2022)。这些方法从LLMs中进行大规模采样,重新排名这些采样的程序,并输出最终的程序。相比之下,我们的self-edit框架具有两个优势:

① 我们的方法保持恒定的样本预算,显著减少了LLMs的计算开销。

② 我们的编辑器直接修改程序,并优于这些基于重新排名的方法,特别是在样本预算有限的情况下,如pass@1。据我们所知,我们是首次采用基于编辑的后处理方法用于竞技编程任务。

本文的贡献如下:

① 本文提出了一种名为Self-Edit的生成和编辑方法,用于大型语言模型(LLMs)生成高质量的竞技编程任务代码。

② 本文开发了一个故障感知的神经代码编辑器,它以生成的代码和错误消息作为输入,并利用它们来细化代码,提高其质量和准确性。

③ 本文在两个流行的数据集和九个LLMs上进行实验,以展示我们方法的有效性和通用性。

2 技术介绍

Self-Edit的工作流程如图2所示。给定问题描述,我们首先使用LLM生成初始代码。然后,我们执行示例测试用例以获取测试结果并构建补充注释。最后,我们训练一个故障感知的代码编辑器模型,以根据问题描述、生成的代码和补充注释来细化代码。

图2:Self-Edit整体工作流程

2.1 LLMs作为黑匣子生成器

在我们的设计中,我们使用具有固定参数的大型语言模型作为黑匣子生成器。这个设计选择是因为训练LLMs成本高昂,而且通常访问LLMs是受限的。(例如,OpenAI只提供有偿API以推断GPT3。)使用LLMs作为黑匣子生成器使我们的方法灵活,可以使用不同的LLMs。我们研究了九个大小从110M到175B的LLMs进行代码生成,详细的比较描述在实验部分的数据表中。

2.2 执行器和补充注释

在使用LLMs生成代码后,我们使用执行器运行示例测试用例。我们将执行结果分类为三种类型:

① 通过:程序通过了测试用例。

② 错误答案:程序正常运行但输出不正确。

③ 错误:程序由于语法错误、运行时异常或超过时间限制而异常终止。

我们分析了在APPS-train数据集上由相对较小的模型PyCodeGPT-110M和较大模型GPT3-175B生成的代码的测试结果分布,如图3所示。我们观察到由不同模型生成的程序产生不同的测试结果分布。由较小模型(PyCodeGPT)生成的代码更容易遇到SyntaxError问题,而较大模型(GPT3)显示较少的SyntaxErrors,较少的RuntimeErrors,但更多正常执行的情况。

图3 在使用PyCodeGPT-110M-finetuned和GPT3模型时,APPS-train数据集中前10个辅助评论类别的分布,以每个类别生成的程序总数的百分比表示。

为了为代码编辑器模型构建有意义的补充注释,以了解各种执行结果,我们设计了三种类型测试结果的注释模板(图4)。注释模板可以使用附加有用信息包装潜在的错误消息,以进行编辑。①对于通过示例测试用例的代码,我们使用注释1:“通过了示例测试用例”。②对于产生不正确输出的代码,我们使用注释2,其中包括相关的输入、期望输出和实际输出。我们还附加指令“重新编写代码”以指导编辑器模型重新实现算法以产生正确的输出。③对于由于错误而终止的代码,我们使用注释3,其中包括错误行号、行上下文和完整错误消息。这些补充注释为生成的代码提供了额外的上下文和清晰度,并用于指导代码的编辑。

图4 不同情境下的示例辅助评论

2.3 故障感知的代码编辑器

一旦我们构建了补充注释,我们就训练一个故障感知的编辑器,该编辑器以自然语言描述、生成的代码和补充注释作为输入,并生成更高质量的细化代码。

2.3.1 代码编辑器模型

故障感知的代码编辑任务被正式定义为一个序列到序列的任务:给定自然语言描述N,由LLM生成的程序S,以及附带的补充注释C(第3.2节),模型需要生成更高质量的代码Cˆ,实现自然语言描述并通过测试用例。在我们的实验中,输入对(N,S,C)被分成三部分,并使用特殊的分隔符标记连接起来,表示为[SOS],n1,n2,...,n|N|,[CODE],s1,...,s|S|,[CMNT],c1,...,c|C|,[EOS],其中小写字母表示输入对(N,S,C)中相应内容的令牌。我们训练一个只有解码器的模型来完成代码编辑任务。具体而言,我们通过在此任务上微调PyCodeGPT-110M来实现代码编辑器。

在推断时,我们首先使用自然语言描述作为输入,从LLMs生成多个程序。对于每个生成的程序,我们将描述中提供的示例测试用例输入执行器以获取一个故障感知的注释。然后,我们使用编辑器生成一个新的程序,这是进一步评估的最终版本。与现有的大规模采样和过滤/重新排名方法相比,这种推断方法保持了较小的样本预算。

2.3.2 代码编辑器的数据集构建

为了训练一个故障感知的代码编辑器,我们需要包含生成的程序和相应补充注释的数据集。为了收集这样的数据集,我们使用不同的LLMs(第4.1节)为APPS-train数据集中的问题生成候选程序。对于每个问题,我们从LLM中采样10个程序,然后执行示例测试用例以获取测试结果并构建补充注释。此时,我们获得了不同LLMs的三元组(N,S,C)的数据集。为了进一步获得地面真实程序Cˆ,我们收集了原始APPS训练数据集中的标准地面真实程序和通过所有隐藏测试用例的生成程序。

对于每个LLM,我们创建了一个包含近4.5k个生成程序的独立编辑器数据集,每个程序都附有注释。对于每个生成的程序,我们设置最多15个地面真实程序。正如我们在图3中描述的,来自不同LLMs的生成程序具有相应注释的不同分布。为了优化每个LLM的故障感知代码编辑器的性能,有必要使用与相应LLM特定的训练数据集。

2.3.3 代码编辑器的数据集构建

基于输入对(N,S,C)进行高质量程序的编辑是一对多的任务,因为多个正确的目标程序满足要求。标准的最大似然目标旨在通过考虑训练集中的所有解决方案(类似于召回)来最小化损失,而我们关注的是模型在有限的尝试预算内基于现有生成的代码编辑单个正确解决方案的能力(类似于精确度)。为了解决这种差异,我们遵循先前的工作,并采用GOLD的一种变体(Pang和He,2021;Li等,2022c),它在标准的最大似然目标梯度中引入了一个脱机策略的重要性权重:

其中θ代表模型参数,logPθ(t)是下一个令牌预测的标准对数似然目标。附加权重Pθ(t)使模型能够关注已经具有较高似然性的令牌,因此模型可以集中精力学习这些更容易学习的地面真实解决方案,并增加获得至少一个正确输出的机会。这样的损失设置允许编辑器学习从现有生成的程序中复制部分内容以获得更好的输出。

3 实验评估

3.1 实验设置

研究问题。在本文中,我们研究以下研究问题:

RQ1:故障感知代码编辑器在竞赛编程中能够如何改进各种代码生成模型?

RQ2:基于编辑器的方法相对于现有排名方法的优势?

RQ3:补充注释在多大程度上有助于优化程序?

RQ4: 编辑轮数如何影响最终结果?

数据集。我们考虑在两个现有的代码生成数据集上评估我们的方法:APPS(Hendrycks et al., 2021): 收集自编程竞赛和面试问题的5000个训练和5000个测试任务的集合。测试集有三个不同的难度级别:入门、面试和竞赛。HumanEval(Chen et al., 2021): 包含164个测试编程问题,每个问题都有一个函数签名、文档字符串、主体和若干单元测试。

我们的实验仅使用APPS-train数据集来微调代码生成模型和代码编辑器模型,因为它是最大的训练数据集。我们采用先前研究的相同划分方法,并使用从APPS训练数据集中排除的598个任务用于验证。

数据集的详细统计信息如表1所示。隐藏测试用例是用于评估的测试用例,不包含在问题描述中,因此与用于获取补充注释的示例测试用例有所区别。

基础LLMs。在本文中,我们调查了几种用于代码生成的广泛使用的语言模型的效果,包括text-davinci-002(175B)(Brown et al., 2020)、CodeGen(2B, 350M)(Nijkamp et al., 2022)、InCoder(1B)(Fried et al., 2022)、GPT-Neo(1.3B, 125M)(Black et al., 2021)、GPT-J(6B)(Wang and Komatsuzaki, 2021)和PycodeGPT(110M)(Zan et al., 2022)。这些模型在zero-shot或微调实验条件下进行评估。

编辑器模型。我们通过对PyCodeGPT-110M进行微调来实现代码编辑器。我们选择这个模型是因为它的参数相对较小,性能较高。在早期实验中,我们还尝试了CodeGen-350M模型,但发现训练速度和最终性能不如我们选择的模型好。

考虑到LLMs表现出强大的上下文学习能力,不需要训练过程,我们还探索了设计我们的自我编辑方法的上下文学习变体。我们使用text-davinci-002作为基础模型和编辑器模型。关于上下文学习自我编辑的性能将在第5.2节中讨论。

我们使用通过pass rate pass@k的指标进行性能评估,并利用隐藏测试用例来确定代码解决方案的功能正确性。对于每个问题,我们提交k个代码解决方案进行评估。如果其中任何一个通过所有地面真实测试用例,就认为问题已解决。然后pass@k是已解决问题的百分比。在我们的实验中,我们设置k = {1, 5, 10}。

为了展示我们的编辑器纠正的程序数量,我们设计了一个新的指标sol@k,表示每个问题给定k个样本的总正确程序数量。例如,在APPS-test中的5000个问题中,我们将生成5000 * k个代码解决方案,其中我们将计算sol@k中的正确解决方案数量。在我们的实验中,我们设置k = 10。我们展示了基础模型的性能以及编辑后的性能(标记为edit-pass@k和edit-sol@k)。

训练/推断设置。对于每个微调的LLM,我们将最大epoch限制为10,学习率为1e-5,并根据APPS-dev上的验证损失选择最佳检查点。我们采用相同的训练策略在相应的编辑器数据集上训练故障感知的代码编辑器。我们为我们的编辑器设置最大输入长度为1024,输出长度为512。为了提取补充注释,即使问题描述中包含多个示例测试用例,我们也只选择其中一个。在推断时,我们对LLM和编辑器输出都使用温度采样,其中T = 0.8。我们将LLMs的样本预算限制为10。对于LLM输出的每个代码,我们只生成一个代码作为最终版本与我们的编辑器。因此,编辑器的使用保持了一个恒定的样本预算。所有实验均在4个Tesla V100-32GB GPU上进行。

RQ1与基础LLMs比较

APPS-dev和APPS-test。首先,我们与直接从LLMs生成代码进行比较,以分析故障感知代码编辑器如何改进九种流行的代码生成模型。表2展示了在APPS-dev数据集上的九个不同代码生成模型的主要结果。故障感知编辑器改进了所有代码生成模型,尽管它们的大小和训练设置不同。在九个模型中,pass@1的平均值从6.17%增加到11.67%,表示出色的89%的改进。对于具有特别大参数数量的LLMs,我们的编辑器也能够取得显著的改进。对于175B参数的GPT3,我们的编辑器在pass@{1,5,10}上的改进也分别达到了5.9%,5.0%,8.4%。

在表3中展示了在APPS-test数据集上的结果。测试问题比APPS-dev更具挑战性,这可以通过更小的pass@k数字看出。我们的编辑器保持了对不同大小模型的显著改进。 pass@1的绝对改进范围从0.12%到0.7%,表明编辑器可以在这一具有挑战性的基准上解决6到35个问题。至于sol@10,我们的编辑器还可以额外纠正数百个由LLMs生成的代码。

在某些情况下,我们观察到edit-pass@1的表现优于pass@5。这表明编辑候选代码非常高效。使用编辑器模型,可以减少从LLMs采样所需的程序数量。

另一个有趣的观察是,配备我们的编辑器的较小LLM可以达到与超大模型相当的性能。例如,GPT-Neo-125M,GPT-Neo-1.3B和GPT-J都是使用相同数据集预训练和微调的。使用编辑器可以填补这一系列模型参数大小的差距。具有110M编辑器的125M预训练模型可以在某些情况下显着优于1.3B预训练模型,甚至优于6B预训练模型。这一发现也可以在其他实验中观察到,表明我们的编辑器可以提供大约相当于多次预训练模型大小增加的增益。

表2在APPS-dev数据集上的结果显示,我们的故障感知编辑器如何在不同的样本预算下改善不同代码生成模型的通过率。"finetuned"表示我们在APPS-train数据集上对这些模型进行了微调。"zero-shot"表示我们在零射击设置中使用这些模型。我们将在其他实验中使用基于该验证集的LLMs和编辑器模型的最佳检查点。

在不同难度级别问题上的表现。考虑到APPS-test数据集有三个难度级别,我们在表5中进一步分析了在不同难度问题上的改进。我们选择GPT-J-6B-finetuned作为基础模型,因为它在这个具有挑战性的基准上表现出色,并具有一定的代表性。编辑器可以提高所有难度级别问题的基础模型,但在简单的“入门”问题上,pass rate的改进相对较高。我们发现LLMs在非常困难的问题上的输出较差,使得编辑器难以纠正这些解决方案。即便如此,在将样本预算从1增加到10时,我们的方法在“竞赛”问题上也略微改进。

表3 在APPS-test数据集上的结果

HumanEval。我们还在HumanEval上测量了我们编辑器的迁移能力,这是一个不同风格的数据集,如表4所示。HumanEval数据集要求模型根据函数签名、注释和示例测试用例给出函数主体。在这个数据集中,我们只编辑无法通过示例测试用例的输出,遵循先前工作中的可执行性过滤器(Zhang等,2022)。我们还修改了输入格式,使其类似于APPS数据集中的格式。我们选择了一些具有代表性的LLMs进行在我们的计算能力范围内的评估。我们可以再次看到,编辑器改善了所有代码生成模型的性能。我们注意到,在更大的样本预算条件下,即使对于CodeGen-2B,pass@10的增加也不明显,我们的编辑器仍然可以纠正更多的生成解决方案。这些结果证明了我们的编辑器在纠正分布之外的输出代码方面的能力和普遍性。

RQ2 与后处理基线方法的比较

该实验比较了我们的自我编辑方法与现有的用于代码生成的后处理方法。我们选择与CodeRanker(Inala等,2022)进行比较,这是APPS数据集上最先进的重新排名方法。CodeRanker对CodeBERT(125M)进行了微调,用于分类潜在的错误类型,并使用此分类预测对LLMs生成的代码进行重新排序。监督训练任务使得这种方法比先前的过滤和重新排名方法更加高效。然而,我们的实验证明我们的编辑器在准确性和效率方面优于这种最先进的方法。

我们选择GPT-Neo-1.3B-finetuned作为基础模型,并在APPS-train数据集上进行微调,保持与CodeRanker相同的实验设置,以进行公平比较。我们的方法(“+ editor”)在准确性和效率方面明显优于CodeRanker(“+ ranker”)。特别是在APPS-test上,我们的方法可以将pass@1从0.14%提高到0.68%,而他们的方法只能从0.14%提高到0.3%。这意味着我们的方法可以在这个具有挑战性的数据集上解决19个问题。我们还在表9中提供了其他再现的基础模型的性能,其中我们的方法通常表现更好。

表9 与CodeRanker在APPS数据集上的通过率结果的详细信息。我们使用GPT-Neo-125M-finetuned、GPT-Neo-1.3B-finetuned和GPT-J-6B-finetuned作为基础模型。

更重要的是,现有的后处理方法依赖于从LLMs中采样许多输出。例如,CodeRanker对每个问题需要100个输出,然后使用其排名模型选择k个样本来评估pass@k指标。相比之下,我们的方法每个问题只需要k = {1, 5}个输出,然后利用这些输出通过编辑生成最终解决方案。我们的方法更为高效和有效,特别是在从大型语言模型获取输出成本高昂的情况下。因此,我们的方法具有更大的实际意义,并更适合在有限的样本预算下使用。

RQ3 剔除辅助注释的实验

在这个实验中,我们评估了在我们的方法中剔除辅助注释的影响。辅助注释是通过执行LLMs生成的代码并根据测试结果构建的,它们为编辑器模型提供了关于代码质量的信息。

我们采用了两个不同的设置进行比较:一个包含辅助注释的完整设置(Full),以及删除辅助注释的削减设置(No Comment)。我们使用GPT-J-6B-finetuned作为基础模型进行实验,使用APPS-dev数据集进行训练和评估。

表7

实验结果如表7所示。可以看出,在完整设置下,我们的编辑器显著提高了pass@1,达到了9.51%的改进。然而,在没有辅助注释的情况下,性能下降到了5.84%。这表明辅助注释在指导编辑器改进生成的代码方面发挥了关键作用。因此,这个实验结果强调了辅助注释在我们方法中的重要性,它们为编辑器提供了必要的上下文和指导信息,从而提高了代码生成的效果。

RQ4 编辑轮数的剔除实验

在我们的自编辑方法中,我们对LLMs的输出进行编辑,以生成最终的程序。这引发了一个问题:如果我们在第一次编辑之后对程序进行额外的编辑会发生什么?我们通过使用我们原始的编辑器添加了一个额外的编辑步骤来回答这个问题。具体而言,编辑后的程序在示例测试用例上执行以获得注释,然后再次通过编辑器模型进行细化。这个方法的结果在表7中呈现,标签为“+ edit round”的列表示两轮编辑的方法。

结果显示,两轮编辑会在APPS-dev上略微增加pass@1。然而,在APPS-test上,额外的编辑轮次会影响性能。我们猜测原因是第二次编辑轮次中训练和测试时间之间存在差距。编辑器被训练来编辑LLMs的输出,但在第二轮编辑中用于编辑自己的输出。在这种情况下,额外的编辑轮次对生成更好程序并不是很有帮助。

转述:王越

0 阅读:0

互联不一般哥

简介:感谢大家的关注