源代码模型跨任务泛化的基准测试

互联不一般哥 2024-06-30 19:28:48

CrossCodeBench: Benchmarking Cross-Task Generalization of Source Code Models

Changan Niu, Chuanyi Li, Vincent Ng, Bin Luo

1State Key Laboratory for Novel Software Technology, Nanjing University, Nanjing

引用

Niu C, Li C, Ng V, et al. Crosscodebench: Benchmarking cross-task generalization of source code models[C]//2023 IEEE/ACM 45th International Conference on Software Engineering (ICSE). IEEE, 2023: 537-549.

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

仓库:https://github.com/NougatCA/CrossCodeBench/tree/main

摘要

在大规模源代码数据上预训练的模型能够获得令人满意的泛化能力,但对于资源有限或不可用的目标任务是不利的。因此,跨任务泛化具有很强的研究和应用价值,其目标是提高模型对以前未见过的任务的泛化能力。在本文中,我们提出了一个大规模的基准测试,其中包括216个现有的代码相关任务。然后,我们进行了一些初步的实验来证明模型的跨任务泛化能力可以通过上下文学习方法得到很大的提高,表明了在我们的基准上进行跨任务学习研究的良好前景。

1 引言

采用预先训练好的模型作为下游任务的骨干,而不是从头开始学习模型,已经成为软件智能领域的一种常见做法。然而,预训练模型到目标任务的迁移学习的有效性在很大程度上取决于微调数据的大小和质量。不幸的是,在实践中,我们经常需要将预先训练的模型应用于可用数据资源非常少的任务。在这种情况下,我们无法根据足够的目标任务数据对预训练的模型进行微调,以获得可应用于目标任务的微调模型。

在软件智能领域,还没有系统的工作来评估和探索代码模型的跨任务可推广性。因此,为了详细、全面地评估模型在代码相关任务上的跨任务能力,我们构建了一个名为CrossCodeBench的大规模基准测试。我们从收集尽可能多、尽可能多样化的代码相关任务开始,最终收集了28个类别、7种类型和18种编程语言的216个任务。然后,为了使我们的基准可用于多个跨任务学习设置(例如,少样本学习、从任务指令中学习),我们手动为216个任务中的每个任务标记大量的元信息,如任务描述、定义、正/负样本示例等。接下来,我们创建了10个对应于不同基准类型的训练/评估拆分。

给定分割好的数据,我们采用两种类型的预训练模型来进行实验:(1)直接在评估集上使用的现成模型,以及(2)在训练集上进一步微调的模型。通过使用所有或合适的跨任务学习方法,如少样本学习、从指令中学习等,将所有模型应用于所有分割数据。最后但并非最不重要的是,我们进行了大量的缩放实验,通过这些实验,我们发现(1)当每个任务的数据达到一定水平(例如,10000个例子)时,随着数据实例的增加,模型很难保持较高的性能提升速度,(2)更大的模型总是能带来更好的性能。我们希望我们提供的基准、实验结果和分析将有助于未来对SE文献中更强大的跨任务方法的研究。此外,由于我们的基准包含大量数据集(并且可以更新),不仅是我们的CrossCodeBench,我们还希望这样一个大规模的元数据集将有助于构建更多的基准。

2 技术介绍

2.1 收集数据集

我们通过阅读调查和基于论文关系递归地扩大了数据集的收集范围。此外,我们对许多广泛用于发布或收集数据集的网站进行了详尽的搜索,如GitHub、HuggingFace数据集、PapersWithCode数据集等。最后,我们总共收集了66个与代码相关的数据集。

由于一些数据集包含多个子集,或可以支持多个任务,并且它们不对应于相同的任务描述,我们将这些数据集划分为不同的任务。结果,我们最终划分出216项任务。此外,对于任务实例,我们直接使用数据集提供的文本序列形式的输入和输出,而无需任何进一步处理。由于我们为所有类型的任务提出了一个统一的基准,我们参考T5,以文本到文本的形式将所有任务的输入和输出形式化。特别是,对于分类任务,我们将所有标签转换为相应的任务相关文本。

2.2 元信息

在收集数据集和任务时,已经完成了一些基本信息,如URL、BibTeX和输入/输出语言。接下来,为了使我们的基准测试支持多种跨任务的上下文学习方法,即FSL、ZSL和LTI,我们需要将每个任务与一些相应的元信息配对。在本节中,我们首先介绍我们需要完成的元信息的模式,然后说明我们完成元信息所遵循的编码过程。

2.2.1 元信息模式

我们决定在以下字段中为每个任务匹配元信息:(a)类型:任务所属的任务类型。(b)描述:任务的简短描述,例如“将英语翻译成法语”、“总结”等。(c)定义:任务的详细说明,包括输入的描述,以及如何将输入映射到输出。(d)正/负样本:典型的正或负样本。有了这些字段,我们的基准可以支持所有当前的跨任务学习方法。对于FSL,我们使用任务描述和正样本;对于ZSL,我们只使用任务描述;对于LTI,我们使用任务定义和正/负样本。

2.2.2 总结

最后,我们得到如下7种任务类型。

分类:根据输入输出相应的标签。它进一步分为两个子类型,二分和多标签,分别对应于只有两个标签和两个以上标签的任务填空:预测给定输入中缺失的标记或序列。翻译:将用一种语言编写的代码片段翻译成另一种语言,保留语义和功能。生成:根据输入生成序列。三个子类型分别是(1)重写:修改给定代码序列的一部分并输出修改后的版本;(2)文本到代码:输入自然语言描述并输出对应的代码;(3)代码到文本:使用代码输入,根据需要输出所需的自然语言序列。总结:给定一段代码,输出该段代码的功能描述。类型预测:预测给定代码片段中所有标识符的类型,这是一种序列标记任务。问答:给定一段代码和一个自然语言问题,输出该问题的答案。

表1展示了CrossCodeBench的统计数据,表2则展示了最终任务类型的统计数据。该基准总共包括216个任务、28个任务类别和超过5400万个样本。注意,任务类别是任务类型的子集(例如Bug Fixing和Mutant Generation类别都是“重写”子类型),当且仅当任务类型只有一个任务类别时,它们是相等的,例如翻译类型只有一种类别,即代码翻译。

表1:CrossCodeBench的统计数据

表2:任务类型的统计数据

2.2.3 演示

在我们的基准测试中,每个任务由两个json文件组成,一个包含元信息,另一个包含数据实例。两个文件的第一部分具有相同的名称,即“task {task id} {dataset name} {task type}”,后面分别是”.meta.json” and ”.data.json”。为了更好地说明,图1显示了json文件的内容以及任务的元信息。

图1 任务元信息的一个例子

2.3 数据集划分

准备好数据后,我们需要创建不同的训练/评估集拆分,以评估模型在不同应用场景和困难下的跨任务学习能力。表3列出了我们创建和使用的10个训练/评估集。我们将它们分为两个维度:跨任务和训练任务。此外,我们在表四中列出了每个划分的名称、任务数量以及训练/评估划分的实例。接下来,我们将从跨类别、跨子类型和跨类型三个跨任务级别依次介绍这些拆分。

表3:任务划分及其统计数据

跨类别。为新定义的任务类别收集和标记大量数据可能非常耗费人力,甚至是不可能的。但是,我们可以准确地定义任务类别,并给出几个例子。在这一点上,我们可以考虑使用跨任务学习方法,在这种方法中,对大量现有任务的学习可以获得对这一新任务的泛化能力。因此,我们首先希望探索当前的跨任务学习方法是否以及在多大程度上会使模型实现跨任务类别能力。此外,由于任务类别是任务类型的子集,我们还想探索来自不同任务类型的更多数据是否有助于提高模型在一个任务类别上的跨任务性能。最后,我们为跨类别级别创建了4个划分,即Cat-Intra-CD、Cat-Intra-BF、Cat-Inter-CD、Cat-Inter-BF。

跨子类型。我们还想探索模型可以在多大程度上学习子类型级别的跨任务能力。因此,我们分别为它们选择了两个子类型,即多标签分类和重写生成。对于多标签分类子类型,我们想研究该模型是否可以通过只学习二分类子类型(和其他类型的任务)来推广到多标签任务。对于重写任务,我们希望探索模型在没有学习任何重写任务的情况下可以获得多少重写技能。与交叉类别中的设置一样,我们在训练集的不同范围下为两个选定的子类型中的每一个创建2个划分。因此,我们创建了4个划分,即Sub-Intra-ML、Sub-Intra-C2T、Sub-Inter-ML和Sub-Inter-C2。

跨类型。我们将整个任务类型中的所有任务用作评估集,将其他类型的任务用作训练集。首先,我们希望使用翻译类型作为评估集,并探索该模型是否可以通过学习其他类型的任务来学习不同语言之间的对应关系。其次,我们选择了一种任务最少的类型,即问答。这种划分最接近实际情况,即通过在资源充足的任务类型上学习,我们期望模型在没有资源的情况下对新任务类型具有更好的泛化能力。因此,我们在跨类型级别创建了2个拆分,即Type-Trans、Type-QA。

3 实验评估

3.1 实验设置

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

RQ1:现有模型在CrossCodeBench上的总体结果如何?

RQ2:训练数据的大小是否会影响模型的跨任务性能?

RQ3:模型参数的数量是否会影响模型的跨任务性能?

RQ4:使用不同范围的数据是否会影响模型的跨任务性能?

对比方法。我们推导出7种跨任务学习方法,它们将用作下面介绍的基准模型的输入。

捷径方法。对于CrossCodeBench,关键是模型是否真的可以通过使用特定的跨任务学习方法来获得跨任务能力,而不仅仅表现得像是通过一些简单的捷径获得了这种能力。因此,我们为每次划分提出了两种这样的捷径,(1)Copy Ex-output:随机复制四个正样本之一的输出;(2)Copy Ins-input:复制当前样本的输入。我们使用这两种快捷方法来评估CrossCodeBench的可行性。

现成的模型是那些没有进行微调,直接在评估集上评估的模型。我们首先希望选择一个“少样本学习”的模型,我们选择了Tk-Instruct,这是一个基于T5的模型,已经被训练为遵循通用语言的任务指令。Tk-Instruct被证明比少样本学习模型(如GPT-3)和其他任务指令学习模型(例如指令GPT)具有更好的跨任务性能。我们在没有任何微调的情况下使用Tk-Instruct模型,并在不同的学习方法下直接在评估集上对它们进行评估。

我们还评估了在训练集上微调的预训练模型。首先,我们选择了两个最近广泛使用的预训练源代码模型,PLBART和CodeT5。特别是,PLBART是一个基于BART的序列到序列模型,并在大量Java和Python函数以及相关的NL文本上进行了预训练。CodeT5是一个基于T5的模型,在包含8种编程语言和自然语言的大型语料库上进行预训练。我们确保PLBART和CodeT5都没有对任何拆分中的任何评估任务进行监督培训。我们还确保他们在预训练中使用的数据集与所有划分的评估集之间没有重叠。因此,我们获得了三个微调模型,两个预训练的源代码模型PLBART和CodeT5,还有一个指令学习模型Tk-Instruct,为了平衡效率和有效性,我们使用所有三种型号的官方“大型”版本。

我们通过对所有评估集上的所有任务样本(用于评估的样本除外)上的CodeT5-large进行监督微调来估计每个划分的性能上限。这种方法遵循经典的“先预训练后微调”范式,即我们在微调中使用所有评估任务,而不应用任何跨任务学习方法。由于这种方法允许模型看到目标任务的数据并执行有监督的微调,因此从理论上讲,这种方法是跨任务方法在相应划分上的上限。

最后,我们给出了2种捷径方法,2种现成模型,3种微调模型和1种监督模型。所有模型检查点都是从HuggingFace Hub上发布的官方模型加载的。

评估指标。由于所有任务的输出都是文本形式的,因此在我们的实验中使用了特定于文本的度量。具体来说,对于输出短且有限的分类任务,我们采用精确匹配(EM)来测量模型生成与黄金标签完全相同字符串的样本的比例。对于其他任务,它们的输出是一个较长的序列,因此我们使用BLEU和ROUGE-L。两者都是广泛采用的字符串重叠度量,用于测量模型生成的文本序列与黄金序列之间的相似性。对于所有指标,我们都以百分比形式报告分数。

RQ1 总体结果

表4展示了总体测试结果。首先,我们可以看到,遵循经典的“预训练后微调”范式的监督微调CodeT5比任何其他方法都具有非常显著的优势。由于监督CodeT5对目标任务执行监督训练,因此可以将其视为源代码的大型语言模型在相应评估集上的性能的上限。这表明,基于预先训练的源代码模型的跨任务学习仍有很大的理论改进空间。

表4:总体基准测试结果

我们发现,除了Cat Intra-CD和Sub-Intra-ML之外,最佳微调模型在所有划分的所有指标上都优于捷径方法。这说明跨任务学习方法允许模型获得真正的跨任务能力,而不仅仅是一些简单的捷径方法。在快捷方法获胜的两个划分上,我们发现了两点,一是两个分割的训练集的范围都是“Intra”,二是只有这两个分割中的评估集才包含分类任务。关于第一点,我们通过改变训练集中任务的范围来创建“内部”和“内部”的划分,前者将训练集中的任务限制为与验证任务相同的类型,而后者具有训练集中的所有其他任务。在与这两个“Intra”相对应的两个“Inter”上,微调方法反而优于快捷方法。因此,我们可以得出结论,拥有更多其他类型任务的数据可以提高特定类型任务的跨任务性能。关于第二点,我们认为主要原因是这些任务的输出是短的和固定的,并且我们的实验没有限制这些条件,只依赖于在任务定义和示例的输出中给出一些“软”提示。

很明显,微调模型具有显著的性能优势。这是因为在我们的训练集进行微调可以帮助模型理解跨任务学习方法的输入表示(对于PLBART和CodeT5),或者更熟悉软件智能领域中的任务(对于Tk-Instruct)。尽管Tk-Instruct在其原始训练集中包含了几个与代码相关的任务,如“代码到文本”,但我们认为这些任务在大量的NLP任务中被稀释,使得现成的Tk-Instruct无法在代码相关任务中获得良好的能力。

我们还注意到,在大多数任务中,CodeT5能够比Tk-Instruct获得更好的结果。这表明,特定领域的预训练模型通常比跨领域模型更好。此外,我们还注意到,Tk-Instruct在生成自然语言的任务中获胜,如代码到文本和问答。这并不难理解,因为在大量的自然语言语料库上进行训练有助于生成更流畅、更合理的自然语言文本。

在所有跨任务学习方法中,LTI在所有任务和指标产生了最好的结果。具体来说,在大多数情况下,只使用两个正样本比使用两个正样本和两个负样本更有效。通过比较FSL、ZSL和LTI的输入表示,我们可以看到,与FSL和ZSL相比,LTI将简短的任务描述扩展为详细的任务定义,这使模型能够更全面地理解任务,包括输入/输出格式、输出的搜索空间等,从而学习任务的解决方案。此外,LSI进一步引入了负样本,但在大多数情况下,额外的负样本似乎对模型的性能没有积极贡献。

RQ2 数据缩放

我们还探讨了在给定相同任务的情况下,训练数据的大小是否会影响模型的跨任务性能。因此,在Type-Trans上,我们改变了用于微调CodeT5的每个任务的实例数,评估结果如图2所示。

图2 模型性能随样本数量变化趋势

可以看出,当训练集中的任务数量不变时,在一定范围内增加每个任务的最大样本数可以显著提高模型的跨任务性能。然而,当每个任务的最大样本数从10000增加到20000时,BLEU显示出显著下降,因此对于BLEU度量,该模型在10000时实现了最佳性能。另一方面,对于ROUGE-L,尽管平均值一直在增加,但如果考虑到数据错误的范围,当从5000增加到10000时,模型可能会开始显示性能下降。因此,总的来说,模型可以在每个任务最多10000个实例的情况下实现其最佳性能,如果继续增加,则会导致与模型性能增益不匹配的时间和空间成本。

RQ3 模型缩放

我们还通过从不同大小的检查点初始化CodeT5和Tk-Instruct来研究模型缩放的影响,结果如图3所示。

图3 模型性能随参数数量变化趋势

我们发现,在我们实验的模型大小范围内,不断增加模型大小可以提高模型的跨任务性能,并且与参数大小大致呈对数线性关系。结合图2中的结果,我们可以看到,一定规模的模型对数据量有固定的需求。例如,CodeT5-large会在每个任务最多10k个样本数的情况下达到性能瓶颈。相反,当数据规模达到一定大小时,更大的模型总是会带来更好的性能(请注意,数据量应该足够大,以避免过拟合)。

RQ4 范围缩放

最后,我们研究了一个实际应用中的问题,即,当我们提出一个新的任务类型并且获得相应的数据的成本非常高或不可能。在这一点上,考虑使用跨任务学习是一种可行的方法。因此,存在一个问题,即如何选择训练集的范围对于模型的跨任务能力很重要。因此,我们选择一个目标任务类别,缺陷修复,然后改变不同范围的训练集,以研究模型在哪个范围内可以实现缺陷修复任务类别的最佳跨任务性能。

选择缺陷修复作为目标任务类别后,我们从三个维度改变训练集数据的范围:(1)In-Sub-Type:训练集是否包含与目标任务相同任务子类型的任务数据;(2)In-Type:训练集是否有与目标任务相同任务类型的数据;(3)Out-Type:训练集中是否存在与目标任务不属于同一任务类型的数据,即除生成类型之外的其他6种类型。我们研究了在这三个维度的各种组合下训练集中的数据,并在表5中给出了结果。

表5:不同范围模型的跨任务性能

比较表5中的实验2和4,或3和5,我们了解到子类型中的数据起着重要作用。但只有当其他任务数据可用时,这样的结论才成立。如果我们将实验1与5和6进行比较,可以发现仅使用子类型内的数据所获得的性能不如使用子类型外的数据所达到的性能。

此外,将实验2和实验6对比,我们可以发现仅使用In-Type数据的性能比仅使用Out-Type数据的差。我们认为原因是类型外数据的数量大于类型内数据的数量,这使得模型能够利用更多的上下文信息学习更多的跨任务能力。因此,我们可以得出结论,数据量仍然发挥着重要作用。它对模型跨任务性能的贡献比相同类型任务的数据更重要。

转述:叶恒杰

0 阅读:0

互联不一般哥

简介:感谢大家的关注