ICML 2020 | 斯坦福 AI Lab:代码出错?AI帮你自动修复!

导语:利用程序反馈图和自监督学习,AI 根据出错消息自动修复代码。

作者:Michihiro Yasunaga
编译:McGL

用于程序修复的机器学习

在编写程序时,无论是对于初学者(想象一下你上的编程入门课程)还是对于专业开发人员(例如,这个来自谷歌的程序员编译错误案例研究:https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42184.pdf),大量时间都用于调试或修复源代码错误。自动化程序修复可以极大地提高编程和学习编程的生产效率。在我们最近发表在 ICML 2020上的工作《Graph-based, Self-Supervised Program Repair from Diagnostic Feedback》中,我们研究了如何使用机器学习来自动修复程序。

问题设置

程序员以增量方式编写程序: 编写代码,编译或执行,如果有任何错误,根据接收到的反馈修复程序。我们能用机器学习来建模和解决这个问题吗?

假设我们有一个出错的 C++ 程序(图左),其中第5行中的 char 实际上应该是 string。当我们编译它时,我们得到一个错误(图右上角) ,它说“第9行请求 a 的大小,而 a 的类型是 char”。从这条消息中,程序员可以注意到错误与变量 a 的类型有关,跟踪 a 在源代码中的使用方式或声明方式,定位到第5行,然后编辑该行以修复错误。因此,我们希望我们的机器学习模型解决的具体任务是,给定出错的代码(图左)和错误消息(图右上) ,定位错误行(第5行)并生成修复版本(“string tmp, a, b;”)(图右下)。

挑战: 这项任务提出了两个主要挑战。首先,在建模方面,我们需要连接并联合推理两种模式,程序和错误消息: 例如,跟踪导致错误的变量,正如我们在上面的例子中看到的。其次,在训练数据方面,我们需要一个高效的数据源,为修正有问题的程序提供监督; 不幸的是,现有的带有 < 有问题的代码,修复的代码 > 对的标签数据集很小,很难获得,而且不能扩大规模。在这项工作中,我们介绍了这两个挑战的很有前景的解决方案: 1)利用程序反馈图(program-feedback graph)建模程序修复,2)引入了一种使用未标记程序的自监督训练方案。

建模方法: 程序反馈图

我们如何才能有效地连接这两个模式(程序和错误消息) ,并执行修复所需的推理?为了实现这一点,我们引入了一个程序反馈图,一个联合图表示,连接跨程序和错误消息的符号。例如,示例中的编译器消息提到 a、 size 和 char,因此我们将这些符号连接到它们在源代码中出现的位置,以获取语义对应关系。这样,我们在一个共享的语义空间而不是分开处理这两种模式。然后我们使用图注意力(graph attention)对这个空间中的符号进行推理。

具体来说,对于模型结构,我们用了 NLP 中常用的编解码(encoder-decoder)框架,它对输入序列(在我们的例子中是程序和错误信息; 见下图底部)进行编码,然后解码输出(在我们的例子中是定位到的行数和修复的版本; 见图顶部) ,我们在结构的中间层(图中间)加入了一个图注意力模块应用到程序反馈图上。

训练方法: 自监督学习

我们的第二个技术是自监督学习(self-supervised learning)。有标签的程序修复数据集很小,但网上有大量无标签的程序可用。例如,GitHub 拥有超过3000万个公共仓库。使用这大量免费可用的代码来改进学习程序修复,将显著提高系统的可伸缩性和可靠性。我们的想法是这样的: 我们首先从诸如 GitHub 和 codeforce.com (图左) 这样的在线资源中收集未标记的工作程序。然后我们设计随机程序损坏过程(例如删除/插入/替换 token)来损坏未标记的程序(图中)。这样损坏的程序给我们带来了错误(图右)。通过这种方式,我们可以创建许多新的程序修复例子,例如,<有问题的代码,错误消息,修复的代码> 。我们可以利用这些额外的数据对程序修复模型进行预训练,然后用有标记的目标数据集进行微调。

使用我们的程序修复模型!

我们在两个基准任务上应用和评估我们的修复模型(我们称之为 DrRepair) :

  • 批改学生写的 C 语言程式(DeepFix 数据集)
  • 修正 C++ 程序合成的输出(SPoC 数据集)

应用于 DeepFix (改正学生程序)

在 DeepFix 中,任务是纠正学生在编程入门课程中编写的 C 程序,以便他们能够编译。输入程序可能有多行有错误,因此我们迭代地应用修复模型,一次处理一个错误。例如,下图显示了 DeepFix 中的一个示例程序,其中有一个编译器错误,提示“ i 是未声明的”。通过应用修复模型 DrRepair,在第5行插入 i 的声明来修复此错误。在这个修复之后,我们注意到还有另一个错误,它说“大括号之前预期有分号”。我们可以再次应用修复模型。这一次,模型插入一个分号在第12行,现在修复的程序编译成功了!这种方法是迭代求精的思想: 我们可以持续运行修复模型并逐步修复错误。

使用错误消息、程序反馈图和自监督预训练的效果如何? 在 DeepFix 上研究的现有修复系统没有使用编译器错误消息——它们的目的是直接将出错的代码转换为修复好的代码。为了看到使用错误消息的效果,我们尝试从系统中移除所有技术: 使用编译器消息、程序反馈图和预训练。这个版本的模型(下图中的“our: no compiler”)在 DeepFix 上可以达到34% 的修复准确率,与现有系统相当。现在我们将编译器消息添加到输入中。我们发现,这个模型实现了更好的性能和泛化(62.5% 的准确率,图中的“ours: base”)。这表明,通过加入错误信息,模型学习了正确的归纳偏差,从而根据反馈修复代码。接下来,我们增加了程序反馈图和自监督的预训练。我们发现两者都有了进一步的改进(“ours: base+graph”和“ ours: base+graph+pretrain”) ,我们的最终系统可以修复 DeepFix 中68.2% 的故障程序!

应用程序到 SPoC (自然语言到代码)

程序合成,特别是能够将自然语言描述(如英语)翻译成代码(如 Python,C++)的系统,是非常有用的,因为它们可以帮助更多的人使用编程语言。在 SPoC (Pseudocode-to-Code)中,任务是从伪代码(一种程序的自然语言描述)中合成 C++ 实现。然而,现有合成器(应用于 SPoC 的机器翻译模型)遇到的一个挑战是,它们倾向于输出不一致的代码,这些代码不能编译 —— 例如,在下图中,变量 i 在合成代码中被声明了两次。我们发现,我们可以将我们的程序修复模型应用于这个无效的代码,并将其修复成正确的代码,从而帮助程序合成任务。在 SPoC 的评估中,使用我们的修复模型将最终的合成成功率从现有系统的34% 提高到了37.6% 。

总结

在这项工作中,我们研究了如何利用机器学习从出错消息中修复程序,并得出了三个关键的见解:

  1. 出错信息为程序修复学习提供了关键信号。
  2. 程序反馈图(代码和出错信息的联合表示)帮助修复推理的建模(例如跟踪导致错误的变量)。
  3. 自监督学习允许我们将可自由获取的、未标记的程序(例如 GitHub 代码)转化为程序修复的有用训练样本。

该工作还提供了一个“从反馈中学习”的总体框架,该框架有多种应用: 基于评论的文档编辑、交互式对话中的用户学习等。

我们的论文全文(ICML 2020): https://arxiv.org/pdf/2005.10636.pdf

GitHub 上的源代码/数据: https://github.com/michiyasunaga/DrRepair

关于这项工作的演示 PPT: https://cs.stanford.edu/~myasu/files/DrRepair_slides.pdf

来源: https://ai.stanford.edu/blog/DrRepair/

posted @ 2020-11-11 12:03  McGL  阅读(306)  评论(0编辑  收藏  举报