像人一样编辑源代码,谷歌大脑提出神经网络也可以学「编程」

利用机器学习学会如何自动写代码或修改代码一直非常诱人,它不仅能减少大量工程努力,同时也能构建更高级的智能体。在这篇论文中,谷歌大脑的研究者提出利用神经网络建模我们对源代码的编辑,也就是说将我们对代码的编辑视为一个序列,然后像语言模型那样学会如何「写代码」。
 
总体而言,这份研究在于理解人类编写代码的过程(例如 GitHub 的 commit),并使用深度神经网络模拟这个动态的编辑过程。只需要给定上一次的编辑信息,模型就能预测下一次代码编辑该改什么,从而继续修改与生成代码。前一段时间,OpenAI 的 GPT-2 能生成逼真的自然语言对话,也许采用相同的模式,这种动态代码编辑也能生成「逻辑合理」的源代码。
 
这篇论文的四位作者都来自于谷歌大脑,其中二作 David Bieber 还发布过一个可以自动生成命令行接口的库:Python Fire。
 
什么是代码建模
 
代码是一种机器语言,它和自然语言一样以一种序列的方式展现出来。以前机器之心曾介绍过多种代码生成的方式,其中研究最多的是如何根据 GUI 生成对应代码,例如 17 年提出的 Pix2Code,我们只需要输入界面图像就能生成对应的前端代码。
 
其次,2018 年 11 月,北大研究者首次成功用 CNN 自动生成代码,他们这篇 AAAI 2019 论文采用了 CNN 解码器,并在《炉石传说》数据集上进行了测试,效果超过了此前各类业内最佳模型。这类研究一般都会采用结构化的神经网络来建模代码结构,例如北大研究者的这篇论文就根据抽象语法树(AST)中的语法结构规则生成代码。
 
但是,这些方法都将代码生成看成是一种「一次到位」的静态过程。不过我们知道,代码的编写是动态的,随着新特性的实现、bug 的修复以及重构的进行,源代码库一直处在变化之中。其实在人类开发的过程中,开发者都需要访问已有的代码库或片段,考虑用一个或多个意图对其进行更改。
 
在本文中,谷歌大脑团队探讨的主要问题是如何观察一个过去的编辑序列并预测即将发生的编辑。这是一个重要的问题,因为构建更好的开发者工具的核心挑战是理解开发者行为背后的意图。
 
这也是一个有趣的研究挑战,因为仅根据编辑的内容(输入或删除的内容)无法理解编辑模式。我们需要根据变更与变更之前的状态之间的关系来理解编辑,而且,准确地对编辑序列建模需要学习过去编辑的表征,这一表征允许模型泛化模式并预测未来编辑。
 
如图 1 所示,图中有两个可能的编辑序列(History A 和 History B)。经过两次编辑后,两个序列都有了相同的编码状态(State 2),但 History A 正在给 foo 函数添加一个额外的参数,History B 正在从 foo 函数中移除第二个参数。
 
研究者希望通过观察初始状态(State 0)和编辑序列(Edits 1 & 2)让模型具备预测 Edit 3 的能力。在 History A 的情况下,要插入的特定值是不明确的,但是在该位置插入某个值这一事实应该具有相当高的置信度
图 1:两个编辑序列图示。History A 和 History B 共享相同的 State 2,但根据以往经验,History A 更有可能通过修改对 foo 函数的调用来获取一个额外的参数,History B 更有可能通过修改 foo 的定义来获取一个参数。
 
因此在谷歌大脑的这篇研究中,神经网络需要学习不同的编辑序列及编辑内容,从而在给定编辑序列的情况下,模型能知道后面该怎样继续「写代码」。这就和语言模型一样,给定一小段话,模型就能接着用自然语言「编下去」。
 
谷歌大脑的解决方案
 
编辑建模序列的主要挑战是如何开发良好的表征,既能捕捉所需的意图信息,又能优雅地缩放序列长度。研究者考虑了两种编辑表征:显式表征和隐式表征。
 
显式表征明确实例化序列中每一次编辑的状态结果,而隐式表征实例化完整的初始状态以及更紧凑的类 diff 表征中的后续编辑。在显式表征中,研究者将层级递归指针网络模型视为一个强大但计算昂贵的基线。
 
在隐式表征中,研究者考虑一个简单的序列到序列模型以及一个基于注意力的双向模型,后者拥有一个生成编辑位置的指针网络 head 和一个生成编辑内容的内容 head。这些模型显示了源自不同问题公式化之间的权衡,并为将来的编辑序列模型提供设计方案。
 
在精心设计的合成数据以及对 Python 源代码进行细粒度编辑的大规模数据集中,研究者根据模型观察一系列过去编辑的能力来评估模型的可扩展性和准确性,然后预测未来的编辑。
 
在制作合成数据集以评估模型的特定功能之后,研究者将注意力转移到真实数据。研究者根据开发实际代码的专业开发人员创建的谷歌代码库构建了一个大型编辑序列数据集。
 
开发人员每保存一份文件,就会创建一份静态代码状态,由此生成比典型 Git commit 等更细的粒度。研究者对不同模型观察以往编辑和预测未来编辑的能力进行可扩展性和准确性评估。
 
结果显示,双向注意力模型特别能够在真实数据中实现高准确率、精准置信度和较好的可扩展性,这使得研究者对开发者工具的前景保持乐观,而这些工具在开发人员对大型和真实代码库进行编辑时学习提取他们的意图。
 
总之,本文将研究从编辑序列中学习和预测编辑的问题,是对模型空间的初步探索,此外还展示了从开发者编辑源代码过程中学习的适用性。
 
论文:NEURAL NETWORKS FOR MODELING SOURCE CODE EDITS



摘要:编程语言正成为机器学习中一个充满挑战与趣味的领域。其核心任务是构建源代码的生成模型,这一任务近年来已受到大量关注。
 
但是,就目前所知,以前的生成模型总是生成静态代码片段。在本文中,我们将源代码视作动态目标,要解决的问题是建模软件工程师对源代码文件进行的编辑。
 
解决上述问题需要从先前的编辑中提取意图,然后利用它来生成后续编辑。我们开发了几个神经网络,并利用合成数据来测试它们学习编辑模式的能力,而这需要很强的泛化能力。然后我们收集并在谷歌源代码的大型数据集上训练了我们的模型,该源代码包含来自数千 Python 开发者的数百万次细粒度编辑。
 
从建模的角度来看,我们的主要结论是注意力和指针网络组件的新组合提供了最好的整体性能和可扩展性。而从应用角度来看,我们的结果表明,开发工具以学习预测未来的编辑是可行的。
 
模型方法
 
第一个要思考的问题是我们该如何表征编辑代码的序列数据。我们定义了两种数据格式,它们各有各的权衡。下图 2(a) 所示为显式格式,它将编辑序列以二维网格的形式表示为序列的序列,横纵坐标分别为两种序列。这种格式的预测任务会根据前面 t-1 行的编辑序列预测第 t 个时间步的编辑位置与内容

图 2:编辑序列的显式和隐式表征,它们都将「BACA」转换为「BABBCACC」,其中<S>和<E>分别为起始和终止符。
 
如上图 2(b) 所示,隐式格式将初始状态表示为一个 Token 序列,并将其作为(position, content)对进行编辑。
 
根据两种数据表征格式,现在我们有基线显式模型和基线隐式模型。其中基线显式模型是一个两阶段的 LSTM 网络,有点类似于层级 RNN 模型。
 
在最简单的基线显式模型中,第一阶段的 LSTM 会并行地编码每一个状态序列,并产生隐藏状态;而第二阶段的 LSTM 会将前面的隐藏状态和已观察到的编辑序列作为输入,并产生解码的隐藏状态和输出状态。下图 3(a) 有一个更形象的展示。
 
而基线隐式模型最自然的方式就是使用 Seq2Seq 框架,将初始状态序列输入到编码器中,并期待解码器输出(position, content)对。

图 3:(a):基线显式模型;(b, c)隐式注意力模型。
 
除了基线模型,更强大的是隐式注意力模型,该模型能对隐式表征进行操作,并能更好地捕捉到编辑内容和编辑上下文之间的关系序列。隐式注意力模型的编码器和解码器在上图 3(b, c) 中展示,其中编码器会将初始状态和所有已知编辑编码为隐藏向量,解码器会将其解码为每一个编辑的位置、以及给定位置的编辑内容。该模型的细节见原论文的附录,它参考了 Transformer 的位置编码与 Multi-head Attention(MHA)。
 
实验
 
实验的目标是了解模型的能力和限制。两个主要的因素是,模型能够多准确地识别编辑序列中的模式?它对大型数据的适应效果如何?
 
表 1 中报告了能够产生最佳开发性能的步骤和超参数设置的测试性能。显式模型和改进的隐式模型几乎可以解决所有任务,甚至是那些涉及元字符和具有较长替换序列的任务。
 

表 1:具有最佳开发性能的步骤和超参数设置在合成数据集上的测试准确率。准确率在.5% 以内的结果用粗体显示。POMP:Position-Oracle Match-Pattern;E:显式基线模型;IR:隐式基线模型;IA:改进的隐式模型。
 
如图 4 所示,显式模型始终比隐式模型要昂贵,而且随着数据的增加,这个差距也在拉大。长度为 100 的插入序列比真实数据集中的序列小十倍,但在运行时方面已经存在一个数量级的差异。

图 4:(a)-(c):具有不同插入数量(10,50,100)的 n 元问题的训练过程中处理序列所需的时间。注意,每个图的 y 轴刻度不同。(d):把预测限制到模型置信度最高时,模型在真实数据集上的 Token 级别准确率。
 
来源  | 机器之心 

上一篇:理解神经网络:从神经元到RNN、CNN、深度学习
下一篇:我在谷歌大脑工作的 18 个月中,是怎样研究强化学习的?