代码检索技术和工具在帮助软件开发人员在给定用户查询(例如,描述检索特定代码片段的功能的简短自然语言文本)的可用开源存储库中检索现有代码片段方面发挥了关键作用。尽管目前在提高代码检索效率方面做了大量的工作,但仍有两个主要问题阻碍了它们在回答复杂查询时从大规模存储库中准确地检索出可满足要求的代码片段。首先,现有的方法只考虑源代码的浅层特征,如方法名称和代码标记,而忽略了源代码的抽象语法树(AST)和控制流图(CFG)等结构化特征,这些特征包含了源代码丰富和定义良好的语义。第二,虽然基于深度学习的方法在源 代码的表示方面表现良好,但是它缺乏可解释性,使得很难解释检索结果,并且几乎不可能理解源代码的哪些特征对最终结果的贡献更大。
为了解决上述两个问题,本文提出了一种新的用于语义源代码检索的多模式注意力网络 MMAN。针对源代码的非结构化和结构化特征,提出了一种综合的多模态表示法,其中 LSTM 表示代码的顺序标记,树 LSTM 表示代码的 AST,GGNN 表示代码的 CFG。此外,采用多模态注意融合层,对源代码的每个模态的不同部分赋予权重,然后将其集成到一个单一的混合表示中。在一个大规模的真实数据集上的综合实验和分析表明,我们提出的模型能够准确地检索代码片段,并优于现有的方法。
随着 GitHub 和 StackOverflow 等大量源代码库的出现,它正逐渐成为程序员搜索具有相同功能 的现有代码并尽可能多地重用这些代码的一项关键软件开发活动。代码检索的目标是在给定用户规范的情况下,从可用的开源存储库中检索特定的代码片段(例如,描述代码片段功能的简短文本)。实现这样一个代码检索系统的关键挑战在于两个方面:(A)对源代码的深入语义理解;(B)度量跨模式的相似性(即输入自然语言和源代码)。
现状和局限性为了搜索自然语言查询的大量可用代码资源已经做了许多的努力,从关键字匹配到语义检索。Lu 等人用从 WordNet 获得的同义词扩展查询,然后执行方法签名的关键字匹配。Lv 等人用 api 扩展了查询,并考虑了文本相似性和潜在 api 对代码搜索的影响。Reiss 等人开发了一个代码检索系统 Sourceer,它通过概率主题模型学习源代码的语义表示。受计算机视觉和自然语言处理任务中深度学习成功的启发,深度学习被应用于更好地表示克隆检测和代码摘要等任务的源代码。
据我们所知,Gu 等人是第一个将深度学习网络应用于代码检索任务,它捕捉了语义源代码与中间语义空 间中自然语言查询之间的相关性。然而,这种方法仍然存在两个主要的局限性:(a)源代码的深层结构特征经常被忽略。方法捕获了浅显的源代码信息,包括方法名、代码标记和 API 序列,从而错过了捕获代码丰富结构语义的机会。(b) 缺乏可解释性。深层神经网络的最终结果往往很难解释,因为它的内部工作对输入数据和不同的应用程序总是透明的。
解析。上述这些限制促使我们设计一个模型,学习更全面的源代码表示以及解释能力。一方面,对于限制(a),除了代码的标记外,我们还从代码的多个视图中提取更多的特征,如抽象语法树(AST)和控制流图(CFG)。AST 和 CFG 是两种中间代码,一种表示程序的层次语法结构,另一种表示程序的计算和控制流。在本文中,我们认为从源代码的多个视图聚合互补信息可以丰富其表示形式。在本文中,我们将术语“视图”和“模态”互换使用。我们将从多视图/模式学习代码表示的方法称为多模式学习。为了解决(b)的限制,因为不同的模式反映了源代码的不同特性。因此,每种模态对最终代码表示的贡献可能不相等。对于给定的模态,它由多个元素(令牌、AST/CFG 中的节点)组成,通过表示学习将权重分配给不同的元素。因此,我们可以从最终表征中推断出哪个部分对最终结果的贡献更大,从而使解释成为可能。在本文中,我们设计了一个注意机制,将多模态特征整合到一个单一的混合代码表示中。
动机举例。为了更好地说明我们的想法,我们在图 1 中给出了一个例子。图 1(a)显示了一个简单的 C 代码示例,其目的是验证整数数组是否包含偶数。图 1(b)和(c)分别表示图 1(a)中相应的 AST 和过程间 CFG。从图 1(a)中,我们可以看到突出显示的三个单词 Verify,array,even 可以被不同的代码表示精确地捕捉到,例如纯文本(用于 check)、type augmented AST(用于 BinaryOperator)和 CFG(for while)。这些表示关注不同视图下代码的不同结构信息,例如 AST 上的每个节点代表一个标记,CFG 上的每个节点代表一个语句。这表明有必要考虑各种方式来更好地表示源代码。有必要从多个视图,特别是从结构化信息中表示代码,因为这两个视图上的标记和语句的顺序可能因代码表示的不同而不同。从图 1 中,我们还可以观察到代码片段和它的描述之间存在一种对齐关系。例如,关键字 Verify 应该与单词 check-in 代码紧密相连。这意味着,在代码检索中,我们可以推断出检索到的代码的哪一部分对输入的查询词贡献最大。这对模型的可解释性非常重要。
A 问题表述
首先,我们介绍一些基本的符号。假设我们有一组 N 个代码片段的 D,以及相应的描述,即 D={,,…,}。每个代码片段和描述都可以看作是一系列标记。xi=(xi1,xi2,...,xi|xi|)是源代码片段的序列,di=(di1,dd2,...,di|di|)是描述的序列,其中|·|表示序列的长度。正如我们前面所声明的,我们从三种模式(即令牌、AST 和 CFG)表示源代码)。我们将代码片段 xi 表示为 xi=,其中 xitok,xiast,xicfg 分别表示这三种模式的表示。
由于代码片段及其描述是异构的,因此本文的目标是训练一个模型来同时学习它们在中间语义空间中的表示。然后,在测试阶段,模型可以返回给定查询的每个候选代码片段的相似性向量。
B 多模式学习
多模式学习旨在建立能够处理和聚合来自多种模式的信息的模型。多模态学习的一个重要任务是多模态表征学习,它大致分为两类:联合学习和协调学习。联合表示将单峰信号组合到同一个表示空间,而协调表示则分别处理单峰信号,但对其施加一定的相似性约束,使其进入我们所说的中间语义空间。图 2 说明了联合表示和协调表示之间的区别和联系。
对于代码片段 x,我们提取了它的多种形式,例如 xtok,xast,xcfg。由于这些模式是同一代码的互补表示,我们可以应用联合表示,其公式化如下:x = f(xtok,xast,xcfg),其中,多模态表示 x 是使用依赖于单峰表示 xtok,xast,xcfg 的函数 f(例如,深度神经网络)计算的。
在考虑代码段 x 和描述 d 的同时,由于这两种模式来自 不同的来源,我们希望对它们应用协调表示,其定义如下:g1 (x)~g2 (d),其中,每个模态都有相应的投影函数(g1 和 g2),将其投影到具有相似约束/协调的中间语义空间中。这种协调的例子包括最小化余弦距离或最大化相关性。本文采用余弦相似函数。
C 注意力机制
注意力网络学习的功能提供了一个对输入或内部特征的加权,以引导网络其他部分可见的信息。从另一个角度来看,注意机制可以看作是在一个存储单元中进行软寻址的过程。源代码由 key 和 value 组成,可以看作是内存的内容。给定一个输入查询,注意力机制的目标是返回一个注意值。形式上,我们可以定义查询、键和值之间的注意值,如下所示,α(q, k) = softmax(g(q, k)),其中 q 是查询,k 是键,g 是注意评分函数,它度量查询和键之间的相似性。通常,g 有许多选项,如多层感知器、点积和缩放点积。我们把这种注意称为相互注意。然而,存在一个条件,即查询本身是键本身。在这种情况下,我们将其称为内部注意(也称为自我注意),它在模拟远程依赖关系的能力和计算和统计效率之间表现出更好的平衡。在获得注意分数后,最后的注意向量可以表示为内存中每个值的加权和。
图 3 显示了如何获得一个经过训练的模型的整个工作流程,它包括离线训练阶段和在线检索阶段。在训练阶段,我们准备了一个大规模的带注释代码的语料库<代码,描述>对。然后将带注释的对输入到我们提出的 MMAN 模型中进行训练。经过训练,我们可以得到一个经过训练的检索网络。然后,给定一个自然语言查询,相关的源代码片段就可以由经过训练的网络检索出来。
图 4 是我们提出的 MMAN 模型的网络体系结构的概述。我们将框架分为三个子模块。(a)多模式代码表示。此模块用于将源代码表示为隐藏空间。(b)多模态注意力融合。该注意力模块旨在为每个模态在不同的部分分配不同的权重,然后将关注向量融合成一个向量。(c)模型学习。该注意力模块通过一个排序损失函数学习公共空间中的注释描述表示和代码表示。
4 实验与分析
为了评估我们提出的方法,在本节中,我们进行实验来回答以下问题:
RQ1:
我们在评估数据集上评估 Mman,它由 1000 个描述组成。在这个自动评估中,我们认为每个描述都是一个输入查询,其相应的代码片段是基本事实。表 1 展示了这三种方法的总体性能,用成功率@k 和 MRR 来衡量。列 R@1、R@5 和 R@10 分别显示 k 分别为 1、5 和 10 时所有查询的平均成功率@k 的结果。列 MRR 显示了这三种方法的 MRR 值。从这个表中,我们可以得以下结论:(A) 在所有实验设置下,我们的 MMAN(Tok+AST+CFG)方法在这两个指标上都获得了较高的性能,这表明该方法具有更好的代码检索性能。对于 R@k,对 DeepCS 的改进分别为 26.18%、16.06%和 12.21%。对于 MRR,对 DeepCS 的改善率为 19.89%。(b)比较我们提议的模型 MAN(Tok AST CFG)-w.或 w/o 的两个变体的性能。我们可以观察到,我们设计的注意力机制确实有积极的作用。
RQ2:
为了验证多模态融合的有效性,我们对不同模态组合进行了实验,以验证多模态融合的有效性。表 2 给出了 MMAN 在各种源组合中有无注意力机制的性能。从这张表中我们可以看出,采用更多的模式将获得更好的效果,这表明这些模式之间存在互补而非冲突的关系。通过比较注意力机制时每种模态的表现,我们也发现我们设计的注意机制对这些模式的融合有积极的作用。
RQ3:
为了分析我们提出的模型的鲁棒性,我们研究了四个参数(即代码长度、代码 AST 节点号、代码 CFG 节点号和注释长度),它们可能对代码和注释表示产生影响。图 8a 显示了我们提出的基于不同的评估度量的方法的性能,这些度量具有不同的代码和注释长度。从图 8a(a)(b)(c)中,我们可以看到,在大多数情况下,我们提出的模型具有稳定的性能,尽管代码长度或节点数急剧增加。我们将这种影响归因于我们在模型中采用的混合表示。从图 8a(d)中,我们可以看到我们提出的模型在评论长度增加时的性能有所下降。这表明,增加评论的长度会增加评论理解的难度。进一步验证了多模态表示的整体鲁棒性。
RQ4:
为了深入了解多模态表示的优越性以及注意力是如何工作的,我们展示了由模型检索到的具有不同模态组合的案例(见图 9),并展示了每个模态的注意力可视化,以解释代码的哪一部分对最终结果的贡献更大(见图 10)。
图 9 显示了我们提出的具有不同模态组合的 MMAN 模型的第一个检索结果,以及查询“Print any message in axel structure”的 DeepCS。我们可以看到,与最先进的 DeepCS 模型(如图 9b 所示)相比,我们提出的模型能够准确地检索出真实的代码片段。在比较图 9a 与图 9c 和图 9d 时,我们也可以清楚地看到多模态方法在精确代码表示方面的优越性。
图 10 显示了分配给代码每一部分的不同模式的注意权重,以便解释哪部分代码对最终结果的贡献更大。从图 10a 可以看出,对令牌的关注确实可以提取出重要的部分,比如函数名 print_message。另一方面,从图 10b 中,我们可以看到对 AST 的关注在叶节点(例如 axel)以及一些操作节点(例如 BinaryOperator)上分配了更多的权重。此外,在图 10c 中,我们注意 CFG 上的注意为被调用的功能节点(例如 prinf 和 free)分配了更多的权重。