tfix: learning to fix coding errors with a text-to-text transformer
berkay berabi1 2, jingxuan he1, veselin raychev1 2, martin vechev1
引用
berabi, berkay , et al. "tfix: learning to fix coding errors with a text-to-text transformer." international conference on machine learning pmlr, 2021.
论文:https://files.sri.inf.ethz.ch/website/papers/icml21-tfix.pdf
仓库:https://github.com/eth-sri/tfix?tab=readme-ov-file
摘要
本文介绍了一个名为tfix的新型学习工具,它利用transformer模型,先在自然语言上进行预训练,再在github提交中获得的大规模高质量数据集上微调,生成代码修复。tfix不局限于某种编程语言或缺陷类别,通过同时在静态分析工具报告的52种不同错误类型上进行微调,提高了精度。在javascript程序的大规模数据集上评估显示,tfix有效性显著:在约67%的情况下能够生成修复错误的代码,并明显优于现有的基于学习的方法。
1 引言
现代代码库的高度复杂性和庞大规模导致了大量的编码错误,严重阻碍了程序员的工作效率。虽然强大的静态分析工具可以检测错误,但是仍然需要大量的人工工作来检查报告并试图正确地修复这些错误。这种情况表明,我们非常需要一种能够自动修复现实世界中的编码错误的工具。一个理想的工具应该覆盖大范围的错误,以使大多数开发人员受益,并具有较高的修复精度。然而,这很难实现,因为不同类型的编码错误,例如可变误用和整数类型错误,有不同的根本原因。为了正确解决这些问题,该工具必须能够捕获一系列不同的程序行为。事实上,现有的基于学习的模型要么只修复特定种类的错误,要么在处理更多样但仍然有限的一组错误时变得非常不准确。
为了应对这一挑战,我们提出了一个新的基于学习的工具,称为tfix,它可以精确地产生对最流行的javascript静态分析器eslint覆盖的大范围错误的修复。tfix将修复编码错误的问题公式化为文本到文本的预测任务。也就是说,如果将错误编码为文本,tfix会预测一个新的文本来表示修复错误的代码。这种公式化在三个方面有利于tfix:(a)它允许tfix以相同的文本格式捕获各种错误类型,(b)将tfix从创建复杂的代码表示中解放出来,(c)使tfix能够利用一个强大的模型t5,也叫文本到文本转换转换器,它已经被证明可以概括文本到文本格式的各种自然语言问题,因此非常适合我们的任务。
tfix利用了两种不常用的知识转移。首先,我们的t5模型在自然语言上进行预训练,然后在代码修复的编程任务上进行微调。这使得自然语言和编程语言之间的能够进行知识转移。此外,不同于现有的为每个单独的错误类型训练模型的工作,我们一起微调各种错误类型,这使得错误类型之间的知识转移成为可能,并可以被视为一种多任务学习的形式(修复每种错误类型是一项单独的任务)。这两个特征是tfix学习程序语义的深刻理解,从而达到修复编码错误的高精度的关键因素。
为了微调t5这个具有数百万个参数的大型模型,我们提取了100k个对齐的编码错误对的大规模数据集以及来自550万github提交的修复。我们还调用静态分析器,并通过贪婪二分匹配和差异算法的组合,使得数据集的质量明显高于通过注入人工错误或应用树变换规则产出的数据集。tfix的设计评估并不针对特定的编程语言或错误类型,因为它将代码表示为纯文本。我们在javascript程序的大规模数据集上对tfix进行了广泛的评估,以修复eslint检测到的52种错误类型。结果表明,tfix生成的代码可以修复67%的错误。此外,我们还将tfix与sequencer,coconut,以及hoppity等用于代码错误修复的最先进机器学习工具进行了对比,tfix明显优于这些工具。我们的案例研究表明,tfix可以为复杂的编码错误生成正确的修复。
2 技术介绍
tfix的工作流程如图1所示。tfix首先将输入程序发送到eslint,eslint识别程序中的一组错误。对于图2中的例子,eslint检测缺陷并产生以下信息:错误行、错误类型、错误消息。tfix修复的方式是首先提取由错误行和两个相邻行组成的错误上下文。接下来,tfix将关于错误的所有信息表示为一段文本:
然后tfix使用这个文本来请求一个称为t5的文本到文本的转换器,生成表示修复错误代码的新文本,如图3所示。输出文本与错误上下文相同,只是插入的if语句调用了hasownproperty,以绿色显示。实际上,在原始程序中的错误上下文被生成的代码替换后,错误就被修复了。
图1:tfix的工作流程
图2:有错误的实例代码片段
图3:tfix修复错误后的输出
2.1 应用外部错误检测器
tfix的输入是一组由代码分析工具发现的编码错误,我们称之为检测器。在我们的例子中,检测器将输入程序解析为抽象语法树(ast),然后执行程序分析技术来检测和报告编码错误,而检测不同的错误需要各种程序分析。举个简单的例子,寻找未使用的变量需要
计算ast中变量的范围。tfix是模块化的,因为它不依赖于检测器复杂的内部逻辑,并且可以直接受益于缺陷发现的进步。tfix的输入是检测器生成的错误报告。每个报告由一个错误类型、一条错误消息和一个位置组成。
更正式地说,给定一个具有n行的程序,检测器识别出p中包含m个错误的列表。代码分析器检测器通常支持由集合t表示的多种错误类型。每个错误类型用一个元组( ,l,t,m)表示。其中是检测器报告的引入错误的程序p中的第k行。是错误上下文,由和窗口大小为w的周围行组成。是错误类别,m是错误消息。tfix的输入是错误集,tfix会处理在中的每个错误。
在我们的工作中,我们使用javascript静态分析器eslint作为检测器,涵盖了各种错误。由于其受欢迎程度,我们专注于eslint:它被主要公司采用,并有1600万次每周下载。eslint允许配置返回的错误类型集合。我们使用其默认配置,该配置报告编码错误和最佳实践,但不报告格式问题。eslint内部包含一个功能,用于修复与代码中的空白区域相关的有限的风格问题。这些空白格式问题在默认的eslint配置中被禁用,并且不包含在tfix中。其他语言也有eslint之类的工具,比如python的pylint,在将tfix扩展到其他语言时可以使用。
2.2 使用t5生成代码修复
tfix的目标是为检测到的错误生成一个修复程序。我们将此任务公式化为文本到文本的预测,即给定文本形式的代码错误,tfix生成表示新代码的文本,该新代码已修复特定错误。更正式地说,给定一个错误,tfix通过填充以下模板将其表示为一段文本:
其中“fix”和“:”是原始字符串,⎵是一个空格。然后,tfix请求文本到文本编码器-解码器模型,该模型将输出为文本。被用于替换代码中的l,以便修复错误e。注意通过多次从模型中采样,直到修复通过检测器的检查,可以进一步改善tfix。我们把它作为未来的工作。
我们使用文本到文本转换转换器(t5)作为我们的文本到文本转换的模型,因为我们的公式符合其设计。t5是一个通用模型,它将不同的任务统一为文本到文本的格式,并在nlp任务上进行了预训练。我们将在3.3节讨论如何针对为代码错误生成修复的编程任务对t5进行微调。
为了处理大词汇量和词汇标记不足(oov)的问题,我们的工作对输入和输出的代码采用字节对编码(bpe),这和t5模型的思想一致。bpe算法由单个字符初始化,通过迭代合并最频繁出现的子词来构建关于子词标记的词汇表。bpe算法非常适合文本到文本的任务,并且可以比复制机制和和基于通用命名风格的名称拆分更好地概括训练时未见过的新单词。我们注意到我们的公式允许tfix比现有的工作捕获更多的错误。生成新的代码行来替换原有代码行,并不足以修复许多错误。这是因为正确的修复通常涉及修改该行的上下文。hoppity饱受经典的词汇量有限的困扰。当词汇表中不存在所需的节点值时,hoppity既不能表达也不能生成所需的定位。
2.3 微调t5以合成代码修复
我们假设一个微调数据集,由d对错误及其对应的由开发人员给出的组成。微调的目标是最小化交叉熵损失:
注意,我们在等式中的损失函数对中的所有错误类型求和,即我们一起微调所有错误类型,这也可以被视为一种多任务学习的形式,它显著扩大了数据集并利用了错误类型之间的知识转移,这项技术大大提高了tfix的准确性。未来的工作项目可以一起微调多种语言,以获得多语言代码修复模型,并受益于多语言之间的知识转移。
2.4 获取微调数据集
为了获得数据集d,我们分析了来自github的大量提交,其中p和p’是提交之前和之后的程序的两个版本。获取高质量数据集的一个挑战是将错误修复提交与通过完全删除代码或其他手段来移除错误的许多不相关的提交分开,并提取与错误修复提交中的错误和修复相对应的代码部分。algorithm 1给出了tfix的数据提取和清洗流程。
该算法从空集d开始,在github提交c上迭代,并运行检测器以在每次提交时从文件对p和p’中获取和。然后,它检查中的错误数是否大于中的错误数。如果大于,很可能是p’修复了p中的一些错误。因此,考虑提交中包含错误修复。我们注意到,我们发现用于确定错误修正提交的这一标准在实践中比先前基于提交消息中的关键字或树编辑的数量的方法要精确得多,因为它在决策过程中利用了错误检测器。
然后,tfix调用findfixederrors函数,通过比较和来确定一组已修复的错误,我们在这里使用了二分匹配算法。对于中每个已被修复的错误e,tfix调用computefix函数来提取p’中的l’,并获取要添加到数据集d中的样本(e,l’)。最后,clean函数移除包含未对齐错误修复的高噪声样本。
findfixederrors函数首先调用和之间的贪婪二分匹配过程,以识别一组在提交后仍未修复的错误。为了计算二分匹配,我们迭代所有错误对和,如果,,并且和处的代码行之间的归一化levenstein编辑距离很小,我们使用阈值0.3,我们就接受(e,e’)作为匹配。直觉上来说,这意味着e和e’很可能是同一个错误,所以e在提交后仍未被修复。迭代结束后,ε中所有匹配的错误形成集合。最后,被修复错误的集合可以通过得到。
为了使用computefix函数计算l’,我们利用了myers的差异算法来计算一系列的编辑操作,每次编辑操作插入或删除程序中的一段文本,这些操作可以将p转化为p’。我们在进行编辑操作的同时跟踪在程序中是如何变换的。在编辑操作结束后,我们在的最终位置定位。最后,l’是通过取的上下文得到的。注意,可能包含新添加的行,并且由于固定的窗口大小而变得无法与对齐。对于这些情况,我们在和附近应用基于代码行的启发式搜索方法。因此,和可能会延伸几行。
为了获得t5模型的高质量数据集,我们还进行了清洗步骤,从数据集d中移除潜在的噪声样本。我们只保存从p到p’所需的编辑操作少于6次的样本。注意,由于每次编辑操作可以改变一整行代码,d中的样本仍然非常复杂。原因在于,在过滤后的样本中,错误虽然已经被提交修复,但是不能保证是无噪声的训练样本。
3 实验评估
3.1 实验设置
研究问题。在本文中,我们研究以下研究问题:
rq1:tfix修复编码错误的准确性如何?
rq2:与最先进的方法对比时,tfix的表现如何?
评估数据集。我们根据星数,对来自排名前50万的github公共存储库的550万次提交运行了algorithm 1,并提取了eslint检测到的52种错误类型的修复数据集。我们发现某些错误类型的样本太多,这会使我们的数据集严重偏斜。因此,我们对这些错误类型执行了随机下采样。我们的最终数据集由104804个样本组成。详细统计数据如表1所示。
表1:每种错误类型的样本数量与精确匹配准确度
为了分割训练集和测试集,我们为每种错误类型随机选择10%的样本作为测试集,剩余部分进一步划分为90%用于微调,10%用于验证。为了测量数据集的质量,我们手动检查了从整个数据集中随机抽取的100个样本,并检查它们是否确实是错误的修复。我们发现其中96个是真正的错误修复,错误率位于区间[90.1,98.9],置信水平为95%,这意味着我们的数据构建过程产生了一个具有少量噪声的高质量数据集。
此外,我们还测试了tfix在推广到修复复杂或有噪声错误的数据上的表现。为此,我们组装了另一个名为随机测试的测试集,该测试集由github提交中的所有可修复错误组成,但不包含用于微调和验证的错误。
我们努力消除数据集中的重复项。对于github存储库,我们删除具有相同抽象语法树的重复文件,并在提交级别上进一步应用了重复数据消除过程:对于每个文件,我们检测并丢弃了与其他提交中的更改完全重复的提交。例如,对于提交a、b和c,其中c同时执行了a和b的更改,我们将删除c,即使它可能包含其他文件中的更改。我们还确保在最终数据集中没有相同的样本。
对比方法。为了研究我们设计选择的有效性,我们研究了三个t5基线:无预训练大型t5:我们初始化了一个没有自然语言预训练的t5大型模型,并用我们的数据集从头开始训练它。每种错误类型大型t5:我们分别对每种错误类型进行了微调和测试。基础t5和小型t5:我们微调了两个较小的t5型号,基础t5和小型t5分别有2.2亿和6000万个参数。其他重要条件(例如,数据集、优化器等)对这些模型保持不变。
评估指标。我们提出了两个度量tfix准确性的指标。当且仅当修复与提交中完成的人工修复完全匹配时,精确匹配指标才认为预测是正确的。请注意,该指标提供了tfix精度的下限,因为错误可以通过多种方式修复,并且tfix可以提出不同于人工修复的正确修复。如果错误被去除并且在错误代码被预测替换之后没有引入新的错误,则错误删除指标将该预测计数为正确。我们通过对每个错误类型的修复准确率求平均值来计算平均准确率。精确匹配是一个比错误删除更严格的指标,因为它不仅要求删除错误,而且要求修复与实际提交中的修复相同。
rq1 修复准确性
结果。我们在表2中给出了准确度结果。对于高质量数据集,tfix在精确匹配方面实现了49.3%的准确度,在消除错误方面实现了67.8%的准确度。对于随机测试集,tfix在52.5%的情况下移除了错误。接下来,我们提供每种模型变体的tfix消融研究。
表2:t5模型在高质量数据集和随机测试集上的准确率
消融研究。t5-no-pre-train的精度落后于预训练模型。这个实验有趣的结果是,在精确匹配上6.7%的结果明显更差。这表明,自然语言预训练是tfix生成正确修复程序的关键,但也表明,仅依靠代码提交可能会导致模型学习如何以人类无法做到的方式移除错误。
我们比较tfix和t5在每个错误类型的表现,以量化将所有错误类别一起微调的影响。从表2可以看出,在所有指标上,tfix的准确率都显著高于t5(>13%)。表1显示,对于几乎所有的错误类型,tfix对比t5-large-per-type在每种错误类型上表现都显著提高。这些结果证实了将所有错误类型微调在一起的重要性,以及不同类型的错误之间存在知识转移。将所有错误类型在一起微调可以增加包含目标语句的样本的大小,帮助模型更好地学习这些语句的语义,从而获得更高的准确性。
我们将tfix与t5-base和t5-small进行比较,以研究模型大小的影响。对于高质量数据集的精确匹配,tfix的准确度比t5-base高0.8%,比t5-small高10.1%。因此,从t5-small到t5-base,模型大小是一个重要因素,但从t5-base到t5-large,模型大小变得微不足道。就误差消除指标而言,三个模型中没有一个明显优于其他模型。我们选择t5-large作为tfix的模型,主要是因为t5-large在精确匹配方面实现了最高的精度,这意味着它最擅长生成类人修复。
rq2 对比最先进方法
结果。结果如表3所示,tfix显著优于sequencer和coconut。sequencer的性能比tfix差得多,因为lstm编码器-解码器模型很小,只能覆盖有限错误集的行为。coconut的性能甚至比sequencer还要差。coconut的原始论文学习了系综,并使用1k的波束大小为每个错误生成20k个修复。只要20k个修复中的任何一个通过了错误检查,该错误就被视为已修复。我们认为,为了使coconut有效,如此大的候选集比模型的可学习性更重要。
表3:与最先进方法的准确率对比
我们将tfix与hoppity进行了比较,结果表明tfix在数据集质量、表现力和准确性方面显著优于hoppity。我们首先测量了hoppity数据集的噪声水平,我们手动检查了从hoppity的onediff数据集中随机抽取的100个样本。我们选择onediff数据集,因为它是hoppity使用的主要数据集。我们发现,34个样本不是错误修复,包括版本更改、名称更改、字符串更改等。onediff数据集的真正错误修复率位于区间[55.8,75.2],置信水平为95%,明显低于我们的数据集。
我们还检查了我们的数据集是否考虑了比hoppity更复杂的错误修复,即是否需要更多的编辑操作次数来表示我们数据集中的样本。为了实现这一点,我们将我们的数据集转换为hoppity的格式。在转换过程中,我们在原始数据集中保留了相关信息:我们通过提取包含错误上下文的最小ast子树来保持错误定位,并添加错误类型作为提取子树的根。平均而言,需要大于8次编辑来表示我们的错误修复,而hoppity的数据集最多只包含3次编辑。此外,10504个测试样本中有1300个使用unk进行了编辑。tfix为其中393个输出了正确的修复,而hoppity输出了61个正确的修复。我们还注意到,在hoppity中使用bpe算法需要对其学习框架进行重大更改。
我们用他们的论文和代码中提供的超参数在转换后的数据集上训练和测试了hoppity。hoppity的精确匹配准确率仅为7.9%,显著低于tfix的49.3%。这并不奇怪,因为即使对于简单得多的onediff数据集,hoppity也只有14.2%的top-1准确率。如果我们考虑让hoppity生成完整的修复程序,其准确性将更低。此外,由于正确编辑序列的搜索空间较大,hoppity的准确率随着所需图形编辑次数的增加而显著下降:对于1到10次编辑,其准确率分别为47.2%、26.1%、7.6%、9.7%、15.6%、4.8%、10.6%、0.7%、1.4%、0.3%。对于大于10次编辑错误,其准确率为0%。tfix因为使用文本到文本转换,因此没有这样的限制。
转述:叶恒杰
尊龙游戏旗舰厅官网的版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。