DeepFix: Fixing Common C Language Errors by Deep Learning
自动修复编程错误的问题是软件工程领域一个非常活跃的研究主题。这个问题难度很大,因为修复单个错误也许就需要分析整个程序。在实际操作中,许多错误都是因为程序员对编程语言不熟练或没有注意细节所导致的。
作者将这些错误称为常见编程错误(common programming errors)。它们类似于自然语言中的语法错误。编译器可以检测这样的错误,但它们得到的错误信息往往是不准确的。在这项成果中,作者提出了一种端到端的解决方案DeepFix,其可以无需依赖任何外部工具来定位或修复,就可以修复一个程序中多个这样的错误。
在一个由学生为93个编程任务编写的6971个错误C语言程序的集合中,DeepFix可以完整修复其中1881(27%)个,并且可以部分修复其中1338(19%)个。
细节与模型
DeepFix的核心是一个多层的序列到序列神经网络,带有注意力机制(attention),其被训练用于预测错误的程序位置以及所需的正确写法。
程序的表示
每个程序都有大量的关键字、函数名、库名。对不同的程序,使用一个公共的字典来存储这些字段,每个程序的表示都包含这个公共字典。接着定义一个固定大小的名称池,然后通过将程序中的每个不同标识符(变量或函数名称)随机映射到池中的唯一名称,为每个程序构建单独的编码映射。需要选择一个足够大的池来为数据集中的任何程序创建上述映射。这种转换不会改变程序的语义,并且是可逆的。而文字的确切值对该学习任务而言无关紧要,因此,作者根据其类型将文字映射到特殊标记,例如,将所有整数文字映射到NUM,将所有字符串文字映射到STR。使用特殊标记来表示标记序列的结束。
作者希望将输入的代码段 $X$ 经过网络模型得到输出 $Y$,$Y$ 表示修复后的代码。然而一个代码可能有几百个token,输出如果也具有相同规模的话很难保证正确率,故作者将行号也编码进程序的表示中。程序的第 $L$ 行语句 $S$ 被表示为 $(l,s)$,总行数为 $k$ 的完整的程序 $P$ 则可以表示为 $(l_1,s_1),\cdots,(l_k,s_k),<eos>$ 。这样就可以训练网络来修复单行中的错误了。
神经网络模型
作者使用的是Vinyals et al. 2015的模型。编码器与译码器由 $N$ 个GRU网络堆叠而成,输出采用softmax进行变换。损失函数使用的是交叉熵。
迭代修复程序
整个程序按照下图的步骤来进行迭代修复程序代码。通过神经网络模型生成新代码。在Oracle中,将代码更新并进行编译,如果产生了比原来少的错误则认为更新是有效的,将新生成的代码以及错误的个数送入网络继续训练。当确定没有错误或者达到迭代上限的时候就停止修复程序。
提出的修复策略有几个优点:
- 将完整的程序代码送入神经网络。识别和修复编程错误通常需要能够来推断长依赖性。该网络结构能够选择性参与程序代码的部分或者全部内容,进而推断代码的结构和语法约束,最终预测错误的位置和所需的修复。
- 在输入和输出中包含行号进而减少了粒度,因此降低了预测任务的复杂性。
- DeepFix可以迭代地修复程序中的多个错误。
- Oracle用于跟踪进度并防止无益或任意更改。
- DeepFix的修复策略具有普遍性。例如,如果要尝试修复逻辑错误,可以使用带有测试套件的测试引擎作为oracle。如果程序通过更多测试用例,则表明可以接受修复。
数据集
数据集来自于一个大学的在线编程教育平台Prutor 。Prutor是一个入门编程教学的软件系统。它包括一个界面,学生可以实践编程问题,并在解决问题的同时获得有关其解决方案的即时反馈。
下载地址:Dataset。数据是db格式的,可以使用SQLite打开。表单的字段如下表所示。
键名称 | 描述 |
---|---|
user_id | 用户ID |
problem_id | 问题ID |
code | 输入代码 |
error | 编译错误消息内容 |
errorcount | 错误个数 |