Multi-Modal Attention Network Learning for Semantic Source Code Retrieval 解读

发布时间 2023-07-07 10:42:52作者: 故里oc

Multi-Modal Attention Network Learning for Semantic Source Code Retrieva

 

Multi-Modal Attention Network Learning for Semantic Source Code Retrieval,题目意思是用于语义源代码检索的多模态注意网络学习,2019年发表于ASE的

## 研究什么东西

Background:
研究代码检索技术,对于一个代码存储库进行方法级别的搜索,给定一个描述代码片段功能的短文,从代码存储库中检索特定的代码片段。
image

论文挑战和贡献

image

前人的做法

Gu等人[6]是第一个将深度学习网络应用于代码检索任务的人,它在中间语义空间中捕捉语义源代码与自然语言查询之间的相关性。然而,这种方法仍然受到两个主要限制:(a)源代码的深层结构特征经常被忽视。方法[6]捕获了浅层源代码信息,包括方法名、代码标记和API序列,从而错过了捕获代码的丰富结构语义的机会。

本文

本文提出了一种新的用于语义源代码检索的多模注意力网络MMAN。开发了一种用于表示源代码的非结构化和结构化特征的综合多模态表示,其中一个LSTM用于代码的顺序标记,一个树LSTM用于编码的AST,一个GGNN(门控图神经网络)用于编码的CFG。此外,应用多模态注意力融合层为源代码的每个模态的不同部分分配权重,然后将它们集成到单个混合表示中。在大规模真实世界数据集上的综合实验和分析表明,本文提出的模型可以准确地检索代码片段,并且优于最先进的方法。

如何破局
image

上述这些限制促使本文设计一个模型,该模型学习更全面的源代码表示,并具有可解释性。一方面,对于限制(a),除了代码的标记外,本文还从代码的多个视图中提取了更多的代码特征,如抽象语法树(AST)和控制流图(CFG)1

因此,每个模态对最终代码表示的贡献可能不相等。对于给定的模态,它由许多元素(标记、AST/CFG中的节点)组成,通过表示学习将权重分配给不同的元素。

在本文中,本文设计了一种注意力机制,将多模态特征集成到单个混合代码表示中。

纯文本(用于检查),键入augmented AST(用于BinaryOperator)和CFG(用于while)。

这些表示关注不同视图中代码的不同结构信息,例如,AST上的每个节点表示一个令牌,CFG上的每个结点表示一个语句。这表明了考虑各种模式以更好地表示源代码的必要性

有必要从多个视图角度表示代码,特别是从结构化信息表示代码,因为根据不同的代码表示,两个视图上的令牌和语句的顺序可能不同。例如,基于纯文本,图1(a)中“while”后面的标记是“()”,然后是“head”。不同的是,在AST上,“Compound”之后将有两个可能的令牌,即分支测试“if”、“BinaryOperator”,如图1(b)所示。类似地,在第6行最后一条语句中的标记“g”之后,将不会留下基于纯文本的标记。然而,基于CFG,下一个标记是基于CFG的循环函数开头的“while”。

不同的表示有不同的理解

为了解决上述两个问题,本文提出了一种新的语义源代码检索模型,称为多模注意力网络(MMAN)。本文不仅考虑了先前工作中研究的序列特征(即方法名称和标记),还考虑了结构特征(即从代码中提取的AST和CFG)。

本文探索了一种新的多模态神经网络来同时有效地捕捉这些多模态特征。特别地,我们使用LSTM[10]来表示代码片段的顺序标记, 使用Tree LSTM[11]网络来表示抽象语法树(AST),使用门控图神经网络(GGNN)[12]来表示CFG

为了克服可解释性问题,本文设计了一种注意力机制,为源代码的每个模态的不同部分分配不同的权重,并具有解释能力。总之,本文的主要贡献如下

本文提出了一种更全面的源代码多模态表示方法,其中一个LSTM用于源代码的顺序内容,一个Tree LSTM用于原代码的AST,一个GGNN用于源代码中的CFG。此外,还应用了多模态融合层来集成这三种表示。

基本概念

设本文有一组 D,由 N 个代码片段组成,并具有相应的描述

每个代码片段和描述都可以看作是一系列标记

对于每个代码片段组成一个X组合,如下
image
联合学习协调学习是多模态表示学习中的两种策略,用于将不同模态(例如图像、文本、音频等)的信号融合或处理,以获得一个共享的表示空间。

  1. 联合学习(Joint Learning):联合学习的目标是将不同模态的信号组合到同一表示空间中。在联合学习中,不同模态的特征被同时传入模型,并共同学习一个综合的多模态表示。通过联合学习,模型可以同时考虑不同模态之间的关联性和互补性,从而更好地捕捉多模态数据的整体特征。

例如,考虑一个图像和文本的联合学习任务,本文希望将图像和文本的信息融合到一个共享的表示空间中。可以使用深度神经网络模型,将图像输入一个卷积神经网络(CNN)进行图像特征提取,将文本输入一个循环神经网络(RNN)进行文本特征提取,然后将它们的特征向量连接或求平均,最终得到一个联合的多模态表示

  1. 协调学习(Coordinated Learning):协调学习的目标是单独处理每个模态的信号,但对它们施加某些相似性约束,将它们映射到一个中间的语义空间。在协调学习中,每个模态的特征经过单独的处理,然后通过某种方式约束它们的表示,使它们在语义上保持一致。

以图像和文本为例,协调学习可以通过一个共享的中间语义空间来实现。首先,使用一个图像编码器将图像转换为图像特征向量,使用一个文本编码器将文本转换为文本特征向量。然后,使用一个协调机制(例如,对抗性训练、距离度量等),通过最小化图像和文本特征之间的差异,将它们约束到一个共享的中间语义空间中。这样,图像和文本的表示可以在中间语义空间中更好地对齐,使得它们在语义上更加一致。

总的来说,联合学习将不同模态的信号组合到同一表示空间中,而协调学习单独处理每个模态的信号,并约束它们在一个中间语义空间中保持一致。这两种策略都旨在实现多模态数据的有效融合和表示学习,以便更好地理解和处理多模态信息。

下面的先把单峰的 token ast cfg算好

然后再融合成一个向量,最后再传入到一个函数(深度学习网络)中获得x

下面的这个d是要搜索的关键字描述,和坐标系进行匹配,将其投影到具有相似性约束/协调的中间语义空间中。这种协调的示例包括最小化余弦距离或最大化相关性。本文采用余弦相似度函数
image

工作流程

图3展示了如何获得经过训练的模型的整体工作流程,其中包括离线训练阶段和在线检索阶段。在训练阶段,本文准备了一个大规模的带注释的<code,description>对的语料库。然后将带注释的对输入到本文提出的 MMAN 模型中进行训练。

经过训练,本文可以得到一个经过训练的检索网络。然后,给定自然语言查询,经过训练的网络可以检索相关的源代码片段。
image
具体各个组件如下

图 4 是本文提出的 MMAN 模型的网络架构的概述。本文将框架分为三个子模块。 (a) 多模式代码表示(参见

秒。 IV-B)。该模块用于将源代码表示到隐藏空间中。 (b) 多模态注意力融合(参见第 2 节)

IV-C)。该注意模块旨在为每种模态的不同部分分配不同的权重,然后将注意向量融合为单个向量。 (c) 模型学习(参见第 2 节)

我有)。该注意力模块旨在通过排名损失函数学习公共空间中的评论描述表示和代码表示。本文将在以下部分详细阐述该框架中的每个组件

image
与以前仅利用顺序标记来表示代码的方法不同,本文还考虑了源代码的结构信息,在本节中,本文提出了一种用于代码表示的混合嵌入方法。本文使用 LSTM 来表示代码的 token,使用 Tree-LSTM 来表示代码的 AST,使用 GGNN 来表示代码的 CFG。

Tree-LSTM 自下而上递归地计算 N 的嵌入。假设左子节点和右子节点分别保持 LSTM 状态 (hL; cL) 和 (hR; cR)。然后 N 的 LSTM 状态 (h; c) 计算为
Tree-LSTM 和LSTM的递推公式不一样
image
cfg用one hot去编码了

然后用三个数据用对应的网络或者模型训练一次后,还需要用注意力机制,注意力机制每个权重是不一样的,每个数据模型都有各自的权值

这一部分看论文的公式
image
最后再

多模态融合。然后,本文通过相应的注意力分数将多模态表示集成为单个表示。本文首先将它们连接起来,然后将它们输入到单层线性网络中,该网络可以公式化如下
image
串联了三个

然后关于描述,刚开始描述是从代码里面拿进去训练的,后面描述就是一个输入

用LSTM生成了一个向量

如下图

image
w是一个词向量

最后的一个输出h (des)就是输入描述的向量了

如果代码片段和描述具有相似的语义,那么它们的嵌入向量应该彼此接近。换句话说,给定任意代码片段 x 和任意描述 d,如果 d 是 x 的正确描述,本文希望它预测高相似性,否则预测低相似性
image
最后判断相似度

其中 x 和 q 分别是代码和查询的向量。

相似度越高,代码与给定查询的相关性越强。

RQ1。与一些最先进的方法相比,本文提出的方法是否提高了代码检索的性能?

• RQ2。源代码的每种模式(例如源代码的顺序标记、AST、CFG)对最终检索性能的有效性和贡献是什么,它们的组合又如何?

• RQ3。当改变代码长度、代码长度、代码 AST 节点数量、代码 CFG 节点数量和注释长度时,本文提出的模型的性能如何?

• RQ4。本文提出的注意力机制的性能如何?注意力可视化的可解释性是什么?

提取数据

难点

方法名很短的,就20行,提取的信息,不多

image

图 7 显示了测试数据上代码和注释的长度分布。从图 7a 中,本文可以发现大多数代码片段的长度都在 20 到 40 之间。[52] 中的引用“函数几乎不应该是 20 行长”也观察到了这一点。从图 7b 中,本文可以注意到几乎所有评论的长度都小于 10。

这也证实了捕获短文本与其相应代码片段之间的相关性的挑战。

最后收集记得数据如下

<method name, tokens, AST, CFG, description>

关于每个元素的提取

方法名 对于每个 C 方法,本文提取其名称,并根据驼峰大小写将名称解析为标记序列,如果它包含 _,则通过 _ 对其进行标记化。

真正代码 Token Extraction. To collect tokens from a C method, we
tokenize the code by f. , " ; ) ( ! (space)g. After
we tokenize function body, function name, we limit their max
length as 100 and 50 respectively.

先分割,然后统计token ,然后总共长度限制住 函数体 函数名 100 50

对于AST树 作者也做了限制

具体怎么限制的如下gpt4说的

I'll explain the method with an example, using a simple C code snippet and the corresponding AST. Let's consider the following C code:

c

Copy

int main() {
    int a = 3;
    int b = 4;
    int c = a + b;
    return c;
}

Here's a representation of the AST tree with the non-terminal nodes labeled:

Copy

1. FUNCTION_DEF
   ├── 2. DECL
   └── 3. COMPOUND_STMT
       ├── 4. DECL
       ├── 5. DECL
       ├── 6. DECL
       │    └── 7. BINARY_OP
       │         ├── 8. REFERENCE
       │         └── 9. REFERENCE
       └──10. RETURN_STMT
            └──11. REFERENCE

Now we will apply the two-step transformation mentioned in the paper to convert this AST to a binary tree:

Step 1 (Split nodes with more than 2 children):

We will traverse the tree in a top-down manner and split the nodes with more than 2 children. In our example, node 3 (COMPOUND_STMT) has 4 children.

After splitting node 3, we get the following binary tree:

Copy

1. FUNCTION_DEF
   ├── 2. DECL
   └── 3. COMPOUND_STMT
       ├── 4. DECL
       └──12. NEW_NODE
           ├── 5. DECL
           └──13. NEW_NODE
               ├── 6. DECL
               │    └── 7. BINARY_OP
               │         ├── 8. REFERENCE
               │         └── 9. REFERENCE
               └──10. RETURN_STMT
                    └──11. REFERENCE

Step 2 (Combine nodes with 1 child with its child):

We will now combine nodes with only 1 child with their child:

Copy

1. FUNCTION_DEF
   ├── 2. DECL
   └── 3. COMPOUND_STMT
       ├── 4. DECL
       └──12. NEW_NODE
           ├── 5. DECL
           └──13. NEW_NODE
               ├── 6. DECL
               │    └── 7. BINARY_OP
               │         ├── 8. REFERENCE
               │         └── 9. REFERENCE
               └──10. RETURN_STMT
                    └──11. REFERENCE

In this example, no nodes have exactly 1 child, so the tree remains the same.

Now we have a binary tree representation of the original AST, and this is the final result of the transformation.

对于CFG做了一些限制 两个操作

CFG 提取。为了构建代码的 CFG,本文首先通过一个名为 SVF 的开源工具将 C 函数解析为 CFG [53] (https://github.com/SVF-tools/SVF),该工具已广泛用于价值流分析[54],[55]。然后本文删除具有相同语句或没有语句的节点。对于具有相同语句的节点,本文首先保留在 SVF 的输出中出现的节点并删除其子节点,并将其子节点的子节点链接到它们。对于没有声明语句的节点(比如说空节点),本文删除它们并将它们的子节点链接到它们的父节点。本文将 CFG 节点的最大大小设置为 512。

评估指标

对于自动评估,本文采用两个常见的指标来衡量代码检索的有效性,即 SuccessRate @k 和平均倒数排名(MRR),这两个指标都已广泛应用于信息检索领域。为了衡量搜索结果的相关性,本文使用排名 k 的成功率。 SuccessRate @k 衡量排名前 k 的结果中可能存在多个正确结果的查询的百分比 [6],[56],计算如下:

image
其中 Q 是一组查询,δ(·) 是一个函数,如果输入为 true,则返回 1,否则返回 0。 SuccessRate @k 很重要,因为更好的代码搜索引擎应该允许开发人员通过检查更少的返回结果来发现所需的代码。 SuccessRate 值越高,代码搜索性能越好

还有MRR的指标
image
其中 jQj 是查询集的大小。 MRR值越高,代码检索性能越好。

具体实现

对于每个批次,代码都会使用特殊标记 填充到最大长度。本文数据集中的所有标记都转换为小写。

总的比较
image
比较本文提出的模型 MMAN (Tok+AST+CFG)-w 的两个变体的性能。或者w/o.Att.,本文可以观察到本文设计的注意力机制确实具有积极的作用。

下面是作者对于各个模态的实验

image

本文可以观察到,合并更多的模式将获得更好的性能,这表明这些模式之间存在互补而不是冲突的关系。比较有或没有注意力的每种模态的性能,本文还发现本文设计的注意力机制对于将这些模态融合在一起具有积极的作用

分析代码的长度是否会对模型有影响

为了分析本文提出的模型的鲁棒性,本文研究了四个可能对代码和注释表示产生影响的参数(即代码长度、代码 AST 节点数量、代码 CFG 节点数量和注释长度)。图 8a 显示了本文提出的方法的性能,该方法基于具有不同代码和注释长度的不同评估指标。

从图8a(a)(b)(c)中,本文可以看到,在大多数情况下,即使代码长度或节点数量急剧增加,本文提出的模型也具有稳定的性能。

本文通过将其归因于本文在模型中采用的混合表示来考虑这种影响。

image

比如说查一个 “Print any message in the axel structure 的方法名,用可视化来看注意力机制对哪部分加权比较大

image

图 10 可视化了分配给代码每个部分的注意力权重。不同的模式,以便解释代码的哪一部分对最终结果贡献更大。

从图10a中,本文可以看到,对token的注意力确实可以提取出函数名print_message等重要部分。

另一方面,从图 10b 中,本文可以看到 AST 上的注意力在叶节点(例如 axel)以及一些操作节点(例如 BinaryOperator)上分配了更多权重。
观察到 CFG 上的注意力在调用的函数节点(例如 prinf 和 free)上分配了更多权重。 CFG 可以描述源代码片段的控制流,这一事实可以说明这一点