0.Abstract
代码注释的生成是一个快速发展的研究课题,应用于文档自动生成、程序理解和软件维护。传统的技术依赖于启发式方法和由人类专家手动构建的模板。最近,基于神经网络进行生成的数据驱动方法已经大大超过了基于模板的系统。但几乎所有这些技术都几乎完全依赖于具有良好内部文档的程序;如果没有明确的标识符名称,模型就无法创建好的摘要。在本文中,我们提出了一个神经网络模型,该模型将代码中的单词与AST中的代码结构相结合。
与以前的方法不同,我们的模型将每个数据源作为单独的输入进行处理,这使得模型能够独立于代码中的文本来学习代码结构。这一过程有助于我们的方法在许多情况下提供连贯的摘要,即使在没有提供任何内部文档的情况下也是如此。我们用210万Java方法创建的数据集来评估我们的技术,最终发现我们的模型对软件工程相关文献中的两种baseLine技术和NLP文献中的一种基线技术有所改进。
1.Introduction
1.1 正文背景部分的个人翻译
源代码的注释是对该部分源代码的简要自然语言描述,其最常见的目标之一是程序中的子程序;例如,在自动格式化文档(例如JavaDocs[2])中广泛使用的Java方法的一句话描述。这些描述很有用,因为它们有助于程序员理解子程序在程序中所扮演的角色。通过经验研究一再表明,理解子程序的角色是理解程序整体行为的关键一步。
长期以来,软件工程研究的方向就是自动生成摘要。但是在2002年前都没有取得比较成功的突破,并没有为自动化处理文档提供作用。
对生成代码的自然语言描述的研究被称为“源代码摘要”[9],主要集中在生成子程序摘要上:几年来,基于内容选择和句子模板的研究取得了重大进展[1],[10]-[14],甚至是一些特殊的解决方案,如模仿人类眼球运动[15]。然而,正如在许多研究领域以及Allamanis等人[16]最近的一项调查所记录的那样,这些技术在很大程度上已经让位于基于大数据输入的人工智能。
基于人工智能的代码摘要的绝大多数工作的灵感来源于自然语言处理研究社区的神经网络翻译模型(NMT)。NMT系统将一种自然语言转换为另一种。它通常被认为是序列到序列(seq2seq)学习,其中例如英语句子是一个序列,并被转换为表示例如法语句子的等效目标序列。在软件工程研究中,机器翻译可以被视为源代码摘要进行类比:子程序主体中的单词和标记是一个序列,而所需的自然语言摘要是目标序列。
NMT在代码摘要中的应用在各种应用中都显示出了强大的优势,但几乎所有源代码摘要技术的致命弱点是依赖于程序员以标识符名称或注释的形式编写高质量的内部文档。为了生成有意义的摘要,必须在子程序的主体中观察到有意义的单词。如果是在一般应用场景下的NMT,确实影响不大,但由于自然语言输入句子肯定具有与输出目标句子相关的单词。但在软件中,代码中的单词实际上与代码的行为无关。子程序的行为是由定义控制流、数据流等的编程语言关键字和标记的结构决定的。正如Hellendorn等人[23]在FSE’17上指出的那样,代码和自然语言之间的这些差异是提高软件工程中几种人工智能应用程序性能的障碍,这引起了一些争议。
1.2 背景个人理解
从上述来看,这里讲述了子程序对整体程序理解上的一个作用,也提到了当前使用NMT存在的隐患,因为代码中的单词不一定与相关的注释存在着关联性,反而是与控制流、数据流等的编程语言关键字和标记的结构决定的,所以不能单纯从代码中的单词上入手。
1.3 本文工作概述的翻译
在本文中,我们提出了一个用于总结子程序的神经模型。我们的模型结合了关于源代码的两种类型的信息:1)将代码视为文本的单词表示,以及2)抽象语法树(AST)表示。
与早期方法相比,我们的模型的一个区别因素是我们分别处理这两种表示。先前的技术通过用AST信息对单词表示进行了注释[22],虽然有一定的可行性,但最终注释的表示通过标准seq2seq模型作为单个序列发送。相反,我们的模型接受两个输入,一个用于单词表示,另一个用于AST。其优点是我们能够以不同的方式对待每一个输入,这增加了我们方法的灵活性,正如我们将在本文中展示的那样
本质上,我们提出的神经模型包括两个单向门控递归单元(GRU)层:一个处理源代码中的单词,另一个处理AST。我们修改了Hu等人[22]的SBT AST平坦化程序来表示AST。然后,我们使用注意机制将输出摘要句中的单词与码字表示中的单词联系起来,并使用单独的注意机制将摘要词与AST的部分联系起来。
我们将每个注意力机制的向量连接起来,创建一个上下文向量。最后,我们根据上下文向量一次预测一个单词的摘要,遵循seq2seq模型中的典型情况。
1.4 本文工作概述的个人想法
本质上还是seq2seq,但是在输入之前分别对两个种类的输入通过GRU进行处理,再通过attention机制将处理后的两个种类的输入进行拼接,最终通过seq2seq生成代码的注释。
1.5 本文验证部分概述的翻译
我们分两个阶段评估我们的技术。首先,我们从Sourcerer存储库[24]中收集了超过5100万个Java方法,并对它们进行预处理,以形成一个由大约210万个方法组成的数据集,其中包含适当的JavaDoc摘要注释。我们将数据集划分为训练/验证/测试集,并进行一组测试,将模型的结果与三个竞争基线进行比较。我们称之为标准实验,因为它符合SE和NLP场地的常见做法。
其次,为了评估我们的模型在源代码中没有单词的情况下的局限性,我们对每种Java方法只使用AST重复标准实验——在这项研究中,我们假设没有可用的代码单词,比如模糊代码、写得不好的代码,或者只有字节码的情况(可以从中提取AST,但码字可能在编译期间被移除)。这个“无代码词”实验模拟了SE域特有的情况,正如我们将要展示的那样,它远比程序员提供有用关键字的NMT标准应用程序困难,我们称之为挑战实验。
简而言之,我们的结果可以分为三个部分:1)在标准实验中,我们的模型和竞争性NLP基线提供了比较,提供了20.9 BLEU的最先进性能(比最近的基线提高了8%)。2) 在挑战实验中,我们的模型实现了9.5 BLEU,而任何基线的BLEU都为0。这是源代码摘要向前迈出的重要一步,因为它不需要任何有意义的码字。
1.6 本文验证部分概述的个人理解
该部分证明了即使在代码写得不够清楚或质量较低的情况下,也依然能进行良好的代码注释生成。
2、PROBLEM AND OVERVIEW
我们针对子例程的源代码摘要问题——自动生成子例程的自然语言描述。具体来说,我们针对Java方法的摘要,目的是创建类似于JavaDocs中使用的方法摘要。虽然我们将本文中的实验范围限制在一个大型Java数据集,但原则上,本文中描述的技术适用于任何具有子程序的编程语言,可以从中计算AST,并可以从中提取文本,例如标识符名称。我们对目标问题的范围与许多关于代码摘要的论文中的问题定义一致。
这个问题的解决方案将有许多实际应用。主要的实际应用程序是自动生成文档,帮助程序员更快地编写文档,并理解尚未文档化的代码。在我们在Sourcerer数据集中发现的5100万个Java方法中,只有大约10%有任何类型的方法摘要,只有大约4%包含符合我们在第五节中定义的基本质量过滤器的摘要。在我们看来,超过4%的“应该”被很好地记录下来,而自动摘要生成器将有助于提高可以记录的代码量。(不是很明白这一段的含义)
但更普遍地说,我们这篇论文的目标也是为正在进行的关于如何表示源代码以使用人工智能解决软件工程问题的学术辩论做出贡献。如前所述,基于神经的技术甚至适用于软件工程数据,这一点有合理的怀疑[23];最近在AAAI’18[26]举办的一次研讨会重点讨论了这场辩论。鉴于人工智能用于解决SE问题的历史悠久[27],我们对本文的真诚希望是深入了解构建SE数据神经模型的方法,即使是对于代码摘要特定任务之外的研究人员来说也是如此。我们已经做出了重大努力来保持我们的数据和技术的公开性和可复制性(见第X节),以尽可能地帮助这些其他研究人员。
本文概述如下。在下一节中,我们将介绍背景和相关技术。然后,我们介绍了我们提出的神经模型。然后我们描述了我们是如何获得和处理我们使用的Java数据集的。我们在同一组Java方法上进行了标准实验和挑战实验。最后,我们在示例和讨论上花费了大量篇幅。我们认为,深入研究该模型有效和无效的例子,将为未来改进或调整该模型提供关键见解。
3、BACKGROUND AND RELATED WORK
本节介绍了我们工作背后的支持技术,以及源代码摘要中的相关工作。
A. Source Code Summarization
源代码摘要中的相关工作可以大致分为人工智能/数据驱动或启发式/模板驱动两个方面。
1)Data-driven
在数据驱动技术中,Hu等人[22]最近的工作与本文的关系最为密切。这项工作建议使用AST对源代码中的单词进行注释,然后使用注释后的表示作为seq2seq神经模型的输入。该模型本身是一个现成的编码器-解码器;主要的进步是AST注释的表示,称为基于结构的遍历(SBT)。SBT本质上是一种用于使AST变平并确保代码中的单词与其AST节点类型相关联的技术。
使用sbt的方法遍历,其目的是将单词“request”、“remove”和“id”与它们出现的上下文相关联。在本例中,这是MethodInvocation节点。SBT表示形成了一个重要的baseline,用于我们在后面的实验中进行比较。普通读者会注意到,该论文中显示SBT获得了38 BLEU的显著性能,但我们注意到,[22]中的这与我们的实验结果无法直接比较。原因是在[22]的工作中,数据集是按函数划分的,因此训练、验证和测试集包含整个数据集中函数的随机选择。相比之下,我们按项目划分。在[22]中,来自同一项目的函数可以在训练集和测试集中。在我们的实验中,项目中的所有方法要么是训练、验证,要么是测试。
此外,我们还执行了其他预处理,如自动生成的代码删除(见第五节),以避免在训练集和测试集中出现相同方法的情况。因此,我们预计在实验中,所有方法的标称性能分数都会比[22]低得多。
生成子例程摘要的其他相关AI/数据方法包括1)Hu等人[28]专注于从API调用序列创建摘要的工作,以及2)Iyer等人[19]的CODE-NN,其类似于[22],创建代码的自定义单词表示,然后将其提供给现成的seq2seq模型。
除了子程序摘要任务之外还有相关工作,Jiang等人[20]和Loyola等人[29]生成代码更改的描述(即提交消息);Allamanis等人[18]从子程序的主体中预测子程序的名称;Oda等人[17]通过调整统计机器翻译,从源代码中创建伪代码;Yin等人[21]、Movshovitz等人[30]和Allamanis等人[31]针对短代码片段的注释,这项任务由公共数据集推动[32];Gu等人[33]已经证明了使用神经模型进行源代码搜索,这是另一项越来越受欢迎的任务,并得到了公共数据集的推动[34]。值得注意的是,Bahdanau等人[35]最初描述的注意力编码器-解码器seq2seq模型是许多论文的核心,因为它甚至为许多软件工程任务提供了强大的基线性能。
2)Heuristic/Template-based
Haiduc等人[11],[36]经常被引用为创建代码文本摘要的第一次尝试,事实上也是第一次引入“源代码摘要”一词的人。这些早期的方法通过使用TF/IDF等指标计算前n个关键字来创建提取摘要。此后不久,Sridhara等人[12],[37]的工作调整了SWUM[10](一种在代码中查找词性的技术),以使用模板为源代码创建简短的摘要短语。McBurney等人[1]的另一个基于模板的解决方案也使用了SWUM,但除了方法上下文之外,还总结了子例程的上下文(定义为调用或被方法调用的函数)。
Rodeghero等人[15]对启发式和模板解决方案的内容提取进行了进一步改进,通过修改启发式来模仿人类程序员如何用眼睛阅读代码。与其他与自然语言生成相关的研究领域一样[38],数据驱动技术在很大程度上取代了基于模板的技术,因为它具有更高的灵活性,并减少了创建模板的人力。
B. Neural Machine Translation
大多数神经机器翻译(NMT)系统的主力是注意力编码器-解码器架构[40]。这种体系结构起源于Bahdanau等人[35]的工作,并由大量高度优先的来源[41]-[45]进行了详细解释。在本节中,我们只涵盖在高水平上理解我们的方法所需的概念。在编码器-解码器架构中,至少有两个递归神经网络(RNN)。第一种称为编码器,将任意长度的序列转换为指定长度的单个矢量表示。第二种称为解码器,它将编码器给出的矢量表示转换为另一个任意长度的序列。输入到编码器的序列是一种语言,例如英语,而来自解码器的序列是另一种语言(例如法语)。
该网络接收1)整个输入序列,2)到目前为止的输出单词序列,3)正确的下一个单词,具体流程如下所示:
在推理过程中,给训练过的模型一个输入序列,用于预测输出句子中的第一个单词。然后输入的句子和第一个预测一起再次发送到模型。解码器输出一个句子中第二个单词的预测,依此类推,直到解码器预测一个句子结束标记。
这种策略的问题是编码器需要在每个输出步骤中创建适合预测的向量表示。实际上,对于特定的输出,输入句子中的某些单词会比其他单词更重要。例如,’ on ‘表示’ sur '。这就是“注意力”编码器-解码器网络[35]的动机。从本质上讲,所发生的不是输入句子的单一向量表示,而是在编码器和解码器之间放置了一个注意力机制。该注意机制在每个时间步接收编码器的状态-在上面的例子中,句子中的四个位置中的每个位置都有四个向量。从本质上讲,注意力机制从编码器中选择使用哪个向量,因此不同的解码器预测从输入序列的不同位置接收输入。我们的工作建立在注意力编码器-解码器策略的关键方面,我们将在下一节中描述。
4、OUR PROPOSED MODEL
A. Model Overview
我们的模型本质上是一个注意力编码器-解码器系统,除了有两个编码器:一个用于代码/文本数据,另一个用于AST数据。本着尽可能保持简单的精神,我们为编码器使用了相同大小的嵌入层和循环层。我们将每个编码器的注意机制输出连接在一起,如下所示:
组合不同数据源(指将代码/文本数据与AST数据拼接)的先例主要来自于图像标题[46]-49。本文的目的之一是演示类似的概念如何有利于代码摘要,而通常的seq2seq应用程序用于SE数据,其中所有信息都放在一个序列中。
我们还希望在为每个数据类型创建独特的处理技术方面为未来的几个工作领域进行开辟——以不同的方式处理软件的文本和结构有着悠久的传统。
B. Model Details
为了提高可再现性和清晰度,我们将模型解释为Keras实际实现的演练。下面从models/ast_attendgru_xtra.py中的第29行开始;所有代码可从我们的在线附录(第X节)下载。
首先是三个输入层,分别对应于代码/文本序列、注释序列和平面化AST序列。我们选择序列长度作为模型大小和数据集覆盖率之间的平衡。代码/文本和AST的序列大小为100,注释的序列大小为13,每个序列至少覆盖了训练集的80%。较短的序列用零填充,较长的序列被截断。
个人的一些想法: 这里的标准设定我觉得可以参考,感觉也是直接说明了为什么将序列大小设定为上述的值,我觉得这个是可以学习的。
我们从一个相当常见的编码结构开始,包括为每个编码输入类型(代码/文本和AST)嵌入层。嵌入将输出(batch_size, txtvocabsize, embdims)的形状。这意味着对于每个批处理,序列中的每个单词都由一个长度为embdims的向量进行表示。例如,(200,100,100)意味着对于批处理中的200个示例中的每个示例,有100个单词,每个单词由100个长度的嵌入向量表示。我们发现两个独立的嵌入比一个统一的嵌入空间有更好的性能,所以下面代码分为了两个embedding进行嵌入。
接下来是一个带有rnndims单元的GRU层(我们发现256个单元可以提供良好的结果而不会使模型过大)作为AST编码。我们使用CuDNNGRU来提高训练速度,而不是预测性能。return_state标志是必要的,这样我们才能得到AST编码器的最终隐藏状态。return_sequences标志是必要的,因为我们需要每个单元格的状态,而不仅仅是最终状态。我们需要每个单元格的状态,用于以后的注意力机制。
代码/文本编码器的操作方式与AST编码器几乎相同,只是我们以AST-GRU的最终状态对代码/文本GRU进行输入。这样进行输入的效果与直接将AST与源代码进行拼接作为输入的效果大致相同,除了以下几点:1)我们保留单独的嵌入空间,2)我们允许注意力以不同的方式而不是跨输入类型地集中在每个输入上,3)我们确保一个输入不会被另一个输入类型的过长序列截断,4)我们“敞开大门”进行进一步处理,例如通过卷积层,这将有利于一种输入类型,但不利于另一种输入。正如我们在评估中所表明的那样,这是未来工作的一个重要方面。
个人的一些想法: 第4点不是很明白,感觉是以不同的卷积层处理不同的数据,这样能够让效果更好。
解码器正如许多关于NMT的论文中所描述的那样:一个专用的嵌入空间,后面是一个递归层。我们以代码/文本RNN的最终状态启动解码器。
下一步是代码/文本注意机制,其设计与Luong等人[40]所描述的类似。首先,我们取decoder和代码/文本encoder输出的点积。decout的输出形状是例如(batch_size,13,256),txtout是(batch_size,100,256)。
decout的轴2有256长。txtout的轴2也是256长。因此,通过计算两者中沿轴2的点积,我们得到了形状张量(batch_size.13,100)。
以批次中的例子为例,我们得到(13,256)和txtout(256,100)的decout,其计算过程如下:
其中,a是向量v1和v3的点积,b是v1和v4的点积,以此类推。
结果如下:解码器序列中原本13个位置中的每一个256长度的向量现在由100长度的向量进行表示。100长度向量中的每个值反映解码器序列中的元素和编码器序列中的元件之间的相似性。(点积可以代表相似性)
上面的例子中的b反映了解码器序列中的相似元素1与代码/文本编码器序列中的元素2是如何相似的(因为解码器序列中的相似元素1与代码/文本编码器序列中的元素2点积得到的是元素b)。13个输入位置中每一个的100长度矢量反映了给定输入位置与输出中的位置相似的程度,换而言之,也是注意的程度。
然后,我们将softmax应用于13x100向量中。其效果是夸大“最相似”的东西,从而“更多地关注”更相似的输入向量——网络在训练过程中学习,使它们更相似。注意,这里的点积不是标准化的,所以它不一定等同于余弦相似性。
接下来,我们利用注意力向量来创建代码/文本输入的上下文向量。为此,我们通过注意力向量来缩放编码器向量。
这就是我们如何“关注”特定产出的特定输入领域。上面的代码行采用形状为(batch_size,13,100)的txt_attn,并计算具有txtout(batch_size,100,256)的点积。回想一下,编码器具有txtvocabsize;100个元素,因为它需要100个单词的序列。这个张量的轴1表示“对于输入序列的每个元素”。
一些想法: 先通过点积计算出txt注意力的重视程度,然后再通过与txt_out的点积得到上下文context。
我们对AST编码执行与对代码/文本编码相同的注意操作,得到一个ast_context。
但是,我们仍然需要将代码/文本和AST上下文与解码器序列信息相结合。这一点很重要,因为我们一次发送一个单词,如前一节所述。除了编码器序列中的单词外,该模型还可以查看句子中的前一个单词,没有同时预测整个输出序列的负担。从技术上讲,我们这里有两个带形状(batch_size,13256)的上下文矩阵和一个带形状的decout(batch_size,132256)。默认轴为-1,这意味着形状的最后一部分(在本例中为256)。这创建了一个形状张量(batch_size,13768):13个输入元素中的每个元素都有一个768长度的向量,而不是三个256长度的向量。
我们即将预测下一个单词。TimeDistributed层为上下文矩阵中的每个向量提供一个密集层,结果能够让解码器序列中的每个元素都有一个rnndims长度向量。
然而,我们试图输出一个单词,即序列中的下一个单词。最终,我们需要一个长度为comsvocabsize的单个输出向量。因此,我们首先将(13,256)矩阵展平为单个(3328)向量,然后使用长度为comsvocabsize的密集输出层,并应用softmax。
结果是一个具有代码/文本、AST和注释序列输入的模型,以及注释序列中预测的下一个单词作为输出。
一些想法: comsvocabsize,txt_out得看一下代码
V. CORPUS PREPARATION
我们从Lopes等人[24]提供的Sourcerer存储库中准备了一个大型Java方法语料库。该存储库包含来自50000多个项目的5100多万个Java方法。我们曾考虑从GitHub新下载一些数据用来更新存储库,但我们发现Sourcerer数据集非常全面,导致与无法消除的新项目有大量重叠(由于名称更改、代码克隆等)。这种重叠可能会导致我们的实验出现重大的有效性问题(例如,如果测试样本无意中被放置在训练集中)。我们决定只使用Sourcerer项目。
为了使存储库成为NMT应用的合适数据集,需要进行大量准备,我们认为这一准备是本文对研究领域的重要贡献(与NMT研究领域不同,用于代码摘要的精心策划的数据集相对较少)。
下载完成后,我们使用McMillan等人[51]提供的工具包提取Java方法,并将其组织到SQL数据库中。然后,我们筛选前面有JavaDoc注释的方法(由/**表示)。我们只使用了作为JavaDocs的注释,因为有一种假设,即注释中的第一句话将是方法行为的总结[2]。然后,我们通过查找第一个句号来提取第一个句子,如果没有句号,则查找第一个换行符。接下来,我们使用langdetect库来删除非英语的注释。在这些步骤之后,仍保留了约400万种方法。
一个潜在的问题是自动生成的代码。自动生成的代码是一个问题,因为自动生成器创建的代码和注释往往非常相似。如果训练和测试集中有几乎相同的代码,模型将很容易地学习这些案例,这可能会同时降低“真实”示例的性能,同时错误地夸大性能指标(如BLEU),因为这些指标会奖励模型正确识别重复案例。
令人高兴的是,解决方案相当简单:我们从包含Shimonaka等人[52]建议的“generated by”等短语的文件中删除任何方法。这个过滤器非常激进,因为它将数据集大小减少到了大约2百万个方法,并且在手动检查中,我们没有发现自动生成代码的情况。事实上,大多数经过过滤的方法都是完全重复的(在大约2米的去除方法中,大约有10万个独特的例子)。但是,由于对自动生成代码的注释通常仍然有意义,我们将10万个唯一示例中的每一个添加回数据集中,并确保它们只在训练集中(因此我们没有试图针对自动生成的注释进行测试)。结果是一个包含约210万种方法的数据集。
然后,我们将数据集按项目划分为训练集、验证集和测试集。我们所说的“按项目”是指我们将项目随机分为三组:90%的项目用于训练,5%用于验证,5%用于测试。然后,项目中的所有方法都进入分配给其项目的组中。副作用是,由于项目有不同数量的方法,91%的方法在训练中,4.8%在验证中,4.2%在测试中。但这种微小的变化对于维持一个现实的情况是必要的。如第三节所述,我们认为,不按项目拆分和不删除自动生成的代码是绝大多数以前的NMT应用程序在代码摘要中犯的错误,并人为地夸大了报告的分数。
为了获得AST,我们首先使用srcml[53]来提取每个方法的XML表示。然后,我们构建了一个工具,将XML表示转换为扁平SBT表示,以生成Hu等人[22]描述的SBT格式的输出。最后,我们创建了我们自己的SBT修改,其中所有的代码结构保持不变,但我们将代码中的所有单词(官方Java API类名除外)替换为一个特殊的令牌。我们将此SBT-AO仅用于SBT AST。我们使用这种修改来模拟只能提取AST的情况。
从这个Java方法的语料库中,我们创建了两个数据集:
-
标准数据集,包含每个Java方法的三个元素:1)该方法的预处理Java源代码,2)预处理注释,以及3)Java代码的SBT-AO表示。
-
挑战数据集,包含每个方法的两个元素:1)预处理的注释,以及2)Java代码的SBTAO表示。
从技术上讲,我们还有第三个数据集,其中包含默认的SBT表示(带有码字)和预处理的注释,然而,标准数据集和挑战数据集是我们在本文中的重点,旨在比较内部文档可用时的情况,以及只有AST的更困难的情况,所以在这里只使用在验证中。
VI. EVALUATION
本节介绍了我们的评估,将我们的方法与标准和挑战数据集的基线进行比较。
A. Research Questions
我们的研究目标是在我们通过这些研究问题探讨的两种情况下,确定我们的方法和竞争基线方法之间的性能差异,RQ如下:
- RQ1:假设内部文档,在“标准”情况下,我们的方法和竞争方法之间的性能差异是什么?
- RQ2:如果只有AST,我们的方法在“挑战”情况下的性能如何?
引言和背景部分主要介绍了这些RQ的基本原理。从本质上讲,NMT在代码摘要问题上的现有应用几乎完全依赖于程序员编写有意义的内部文档,如标识符名称。正如我们将要展示的那样,这一假设使seq2seq NMT模型的问题变得“容易”,因为许多方法都有与摘要评论非常相似的内部文档(Tan等人[54]和Louis等人[55]也观察到了这一现象)。我们询问RQ1是为了研究我们的方法在这种假设下的性能。
相比之下,我们询问RQ2是因为内部文档的假设通常是无效的。很多时候,只有字节码可用,或者程序员忽略了编写好的内部文档,或者代码甚至被故意混淆了。
在这些情况下,通常仍然可以为一个方法提取AST,即使它不包含有意义的单词。原则上,程序的结构是理解它所必需的全部,因为最终这就是定义程序行为的原因。在实践中,很难将结构直接连接到摘要中描述的高级概念。我们试图用我们的方法量化基线性能水平(据我们所知,在这种情况下,没有公布的方法起作用)。
B. Baselines
为了回答RQ1(标准实验),我们将我们的方法与三个基线进行了比较。
第一个基线(我们称之为attendgru)是一个通用的注意力编码器-解码器模型,代表了NLP研究领域的一种强大的现成方法的应用。请注意,NLP文献中描述了各种各样的NMT系统,但绝大多数系统的核心是attention encoder-decoder模型(见第三节)。为了保持“苹果对苹果”的比较,基线与我们方法中的“代码/文本”encoder和decoder相同。
从本质上讲,该基线与我们提出的方法相同,除了没有与AST编码器和相关联的级联。虽然我们可以从NLP文献中选择任何数量的方法,但很难说哪种方法对代码摘要最有效,我们需要确保差异最小,以最大限度地提高结果的有效性。例如,如果我们在编码器中使用了一个带有LSTM而不是GRU的架构,我们将无法知道我们的方法和基线之间的差异是由于我们添加的AST信息,还是由于使用LSTM而非GRU。
第二个基线是Hu等人[22]提出的SBT方法。这种方法是在ICPC’18上提出的。该论文使用了基于LSTM的编码器-解码器架构,该架构基于构建seq2seq NMT系统的流行指南,但使用了代码的SBT表示,而不是仅使用源代码。对于我们的基线,我们使用了他们的SBT表示,但使用了与NLP基线相同的基于GRU的编码器-解码器,也确保了“苹果对苹果”的比较。由于模型体系结构是相同的,我们可以安全地将性能差异归因于输入格式(例如,SBT与仅代码)。
第三个基线是codenn,由Iyer等人[19]提出。考虑到方法的复杂性,我们使用了它们的公共可用实现。最初的论文只描述了SQL和C#的应用程序,但我们注意到它们的C#解析器提取了Java中也可用的常见代码特性。我们对C#解析器做了一些小的修改,使其在Java中具有同等的功能。
在我们的实验中,我们把我们的方法称为ast-attengru,在使用贪婪搜索算法对所有方法进行推理,而不是波束搜索,以最大限度地减少实验变量的数量和计算成本。
C. Methodology
我们回答这两个RQ的方法是相同的,并遵循了NMT文献中确立的最佳实践(见第三节):对于RQ1,我们使用标准数据集的训练集对我们的方法和每个基线进行训练,总共训练了10个epoch。然后,对于每种方法,我们在每个历元之后根据验证集计算模型的性能指标。(在所有情况下,验证性能在五到六个epoch后开始下降。)接下来,我们选择验证性能最高的epoch后的模型,并根据测试集计算该模型的性能指标。这些测试结果就是我们在本文中报告的结果。我们回答RQ2的方法不同之处在于,我们使用挑战数据集进行了训练和测试。
我们报告了性能指标BLEU[56],也符合NMT的标准实践。BLEU是预测摘要和参考摘要之间文本相似性的度量。我们报告了除BLEU1至BLEU4外的综合BLEU评分。从技术上讲,我们在实现中使用了nltk.translate.bleuscore[57]完成了这部分的验证。
D. Threats to Validity
对该评估有效性的主要威胁包括:
- 1)数据集。为了最大限度地提高结果的可推广性,我们使用了一个包含数百万Java方法的非常大的数据集,但仍然有可能使用不同的数据集获得不同的结果
- 2)我们没有进行交叉验证。我们试图通过使用随机样本来分割训练/验证/测试集来减轻这种风险,不同的分割可能会导致不同的性能。但由于非常高的训练计算成本(每个epoch4小时以上),这种风险在NMT实验中很常见。
VII. EVALUATION RESULTS
本节讨论我们的评估结果和观察结果。在回答了我们的研究问题后,我们探索了一些例子,以深入了解网络的功能及其工作原理。请注意,在本文的最后,我们使用这些观测结果来构建一个集合方法。
A. RQ1: Standard Experiment
我们在标准实验中发现,ast attergru和attergru在BLEU分数方面获得了大致相等的性能。就BLEU得分而言,ast attegru和attegru的表现大致相同:19.6 BLEU和19.4 BLEU。SBT较低,约为14BLEU,而codenn约为10BLEU。图1包括一个表格,其中包含每个结果的完整BLEU结果(以及在线附录中的额外数据)。
对于SBT,结果与我们基于论文[22]的预期相冲突,在论文[22]中,SBT的表现优于attendgru等标准seq2seq模型。我们看到了两种可能的解释:首先,尽管我们的seq2seq基线实现代表了一种标准方法,但与Hu等人[22]的论文存在一些架构差异,例如不同的嵌入向量大小。
虽然我们没有观察到这些架构差异对结果发生了重大变化,但根据数据集的不同,每个架构所产生的结果不一定相同。其次,正如我们在第三节和第五节中所注意到的,之前的研究没有按项目划分,因此同一项目中的方法将在训练和测试集中。[22]中报告的非常高的BLEU分数可以用具有非常相似结构的重载方法来解释——SBT将检测测试集中具有与训练集中相同项目中的重载方法非常相似AST的函数。
所有方法对codenn的改进都符合之前实验的预期。codenn方法旨在作为一种用于代码搜索和摘要的通用技术,并且是将NMT应用于代码摘要问题的相对早期的尝试。此外,它是为C#和SQL数据集设计的;我们将其改编为Java,如前一节所述。
标准实验的一个关键观察结果是,ast attergru和attergru提供了正交预测——有一组方法中一种表现更好,而另一组方法则表现更高。虽然ast attergru略微领先于attergru,但我们并不认为0.2 BLEU的差异本身就是一个重大改进。通常,我们会期望一种方法在大多数例子中以一定的优势优于不同的方法(即非正交性能),这确实是我们在比较ast attergru和SBT时观察到的,如下图所示(大约60k种方法中ast atterglu表现更好,而SBT为20k):
但我们观察到,在91k测试集中,有两组大约33k的方法,其中一种或另一种方法具有更高的性能(上图,右)。换句话说,在两种方法之间存在差异的预测中,ast attergru在一半的情况下给出了更好的预测(就BLEU分数而言),而attergru也在一半情况下表现更好。正交性能使这两种方法成为集成预测的良好候选者,我们将在第C小节和第VIII节中进一步解释。
B. RQ2: Challenge Experiment
在挑战实验中,我们获得了大约9.5分的BLEU。请注意,标准实验和挑战实验之间的唯一区别是,我们只使用AST进行训练和测试,其形式是将SBT-AO表示提供给ast-Attentergru。从技术上讲,还有其他配置会产生相同的结果,例如使用SBT-AO作为attergru的输入,而不是源代码。这些配置中的任何一种都将满足我们的目标,即在只有AST可用的情况下为场景建立性能。
这里证明了在没有源代码的情况下,论文中所提到的结构依然可用。
C. Explanation and Example
仅仅报告BLEU分数就留下了一个悬而未决的问题,即分数在实践中意味着什么。考虑标准实验和挑战实验中的这两个例子(方法ID与我们可下载的数据集一致,以获得再现性)。我们选择以下示例是为了便于说明,也有助于解释。虽然相对较短,但我们认为这些方法为模型的运行提供了有用的见解。
示例1是当attegru失败时,ast attegru成功的情况之一。为了理解原因,请记住,在我们的模型中,就像大多数NMT系统一样,系统一次一个词地预测一个句子。对于每个单词,模型接收有关该方法的信息(代码/文本加上使用它的模型的AST),以及迄今为止预测的每个单词。
为了预测token,ast attegru将接收代码/文本、ast和短语“sets the”。相比之下,attegru只接收代码/文字和“sets the”。为了预测第一个单词“sets”,attergru只知道它是句子的开头(由句子的开头标记表示),以及代码/文本输入。为了帮助进行预测,attergru配备了在训练过程中学习的注意力层,以关注输入的某些部分。该层如图2(a)所示。
请注意,在位置(14,1)处有高度激活(亮黄色),表示对代码/文本输入中的位置14的高度关注:这是单词返回。实际情况是,在训练过程中,模型看到了许多getter方法的例子,这些方法只有几行,并以返回结束。
在许多情况下,模型可能依赖于非常明确的方法名称,例如getPlayerScore(方法ID 38221679)。
attergru在这些情况下表现非常好,因为这种情况很像自然语言——它学会将输入词汇中的单词与目标词汇中的词对齐,以及它们在句子中的位置。然而,在例如示例1的情况下,方法名称没有明确说明方法应该做什么(名称tokenUrl显然不是setter),attergru很难选择正确的单词,即使像示例1中那样,它正确地标识了操作的主题(“the url of the token”)。
在这些情况下,AST是有益的。ast attedgru的代码/文本激活层主要关注句子的开头标记(注意图2(b)中的第0列),由于“”是每个句子的开头,这可能起到“不确定”信号的作用,但该模型也有AST输入。
图3显示了AST attedgru在试图预测第一个单词时的AST注意力层。有四个感兴趣的区域有助于阐明模型如何处理模型的结构,在图中表示为A到D,并对AST输入中的相应区域进行颜色编码。
首先,区域A是方法签名在参数之前的部分。回想一下,我们的AST表示只是结构,所以几乎所有方法都将以相同的方式开始。因此,正如预期的那样,A区的注意力在很大程度上是无形的。热图在B区显示了更多的定义。这是参数列表,模型可能已经了解到,带有参数列表的短方法往往是设置者。该模型在位置C和D处非常活跃,这两个位置是expr-stmt-AST节点的开始和结束。训练集中一个非常常见的情况是,带有参数和赋值的短方法是setter。
模型已经学会了这一点,并选择“集合”作为第一个单词。经过实验说明,所有具有AST输入的模型都正确地选择了“sets”。
SBT发现该方法是"sets",但无法确定set的内容是什么。
我们将这种行为归因于SBT表示将代码/文本和结构信息混合到单个输入中,这给模型在同一向量空间中学习正交类型的信息带来了挑战。虽然本文中没有充分探索的空间,但我们注意到,即使是在挑战实验中的ast attegru也将该方法正确地描述为设置属性的值,当无法确定哪个属性时生成unknown token。
事实上,ast-attedgru在挑战实验中有33%正确预测了摘要的第一个单词(通常是动词),而在标准实验中这一比例为52%。
对于这种方法,所有方法都表现良好,但原因不同。attergru将方法名称与动词“断开连接”联系起来。SBT更多地依赖于后来的功能,例如对notifyDisconnect()的调用。最有趣的是,ast attegru在挑战实验中表现最好。
在探索这一结果的过程中,我们发现了一些具有类似AST的方法(ID 146827、22838818、28418561、5785101)。所有这些都在try块中有几行,后面是一个短的catch块,在try中有2-3个方法调用和null或false赋值。这些方法的摘要包括“关闭与gps设备的通信”、“停止计时器”和“断开与当前客户端的连接”——所有这些方法都处理关闭和清理行为。模型可能在训练中学会了这一点,并在摘要生成中选择了类似的单词。
在回答RQ1时,我们发现attendgru和ast attendgru在不同的方法集上表现更好。虽然我们不太愿意过度解释单个示例,但本节中的示例与数据集中的许多其他示例一致(我们在在线附录中为感兴趣的读者提供了一个随机采样示例的脚本,称为rand-samples preds.py)。这些例子也与现成的NMT系统(attegru)在总结从方法签名中清晰可见的情况下表现相当好的解释一致,在这些情况下,AST可能是多余的。但是,在代码/文本输入中的单词不够或不清楚的情况下,该模型受益于AST。
VIII. ENSEMBLE DECODING AND FUTURE WORK
作者希望对ast-attengru与attengru进行组合,组合本身很简单:我们计算每个模型(在我们的评估中使用的相同训练模型)的输出向量的元素平均值。训练和测试过程不变,只是在预测过程中,我们使用组合输出向量的最大值,而不是一个模型中的一个输出向量。
这与OpenNMT[59]实现的集成解码过程相同,是多源NMT[60]文献中描述的几种选项中最常见的一种。
由于我们正在组合输出向量,因此在预测每个单词的过程中,模型会“协同工作”——这不仅仅是为整个句子选择一个或另一个模型。其想法是,一个模型可以在输出向量中为两个或多个单词分配类似的权重,以防其表现不佳。另一个在这种情况下表现更好的模型可能会给一个单词赋予更多的权重。在我们的系统中,希望当代码/文本单词清晰时,attedgru会做出更多贡献,但当它们不清楚时,ast-attedgru会做出更大贡献。
集成解码过程将性能提高到20.9 BLEU,从ast-attendgru的19.6和attendgru的19.4。这不仅仅是一个完整的BLEU点改进,这对于一个相对简单的程序来说是非常重要的。这一结果为我们未来的工作指明了方向,包括更先进的集成解码(例如预测何时使用一个或另一个模型)、网络优化(例如:丢弃、参数调整),并且至关重要的是,对每种类型的输入使用不同的数据处理技术。
IX. CONCLUSION
作者已经提出了一个用于生成子程序的自然语言描述的神经模型。我们实现了我们的模型,并在Java方法的大型数据集上对其进行了评估。我们证明,就BLEU得分而言,我们的模型ast attergru优于SE文献中的基线,略领先于NLP文献中的现成方法。
除此之外,作者还证明了这一点,我们的方法和现成的NLP方法的集成优于所有其他测试配置。我们提供了一个演练示例,以深入了解模型是如何工作的——我们得出的结论是,默认的NMT系统在提供了良好的内部文档的情况下工作得很好,但在没有提供的情况下则不太好,并且ast attergru在这些情况下起到了帮助作用。最后也展示了ast attergru如何在没有内部文档的情况下产生连贯的预测。