Sonia Kim、Hongyu Li、Satish Chandra作者路 一鸣 思源编译

如何写最高端的代码?Facebook教你怎样用机器学习做最美的代码搜索工具

如何基于文本查询快速获取代码示例,对于工程师而言是一个很影响效率的事儿。Facebook 最近提出了新型代码搜索工具——神经代码搜索(NCS)和 UNIF,分别基于无监督和监督的方式提供快速高效的代码检索。

当工程师能够轻松获取代码示例,指导其完成特定编程任务时,他们的工作效率会显著提高。例如,对于「如何以编程方式关闭或隐藏安卓软键盘?」这类问题,工程师可以从 Stack Overflow 等常用网站上获取可用信息。但是当问题涉及专有代码或 API(或者用不常用编程语言写的代码)时,工程师需要不同的解决方案,因为在常用论坛上可能找不到这方面的答案。

为了解决这个需求,Facebook 开发了一个代码搜索工具——神经代码搜索(Neural Code Search,NCS),该工具使用自然语言处理(NLP)和信息检索(IR)技术直接处理源代码文本。该工具接收自然语言作为查询(query),并返回从代码库中直接检索到的相关代码段。这里的前提是能够获取大型代码库,从而更有可能搜索到与开发者提出的查询相关的代码段。

Facebook 介绍了两种可用模型:

  • NCS:结合自然语言处理信息检索技术的无监督模型;

  • UNIF:NCS 的扩展,当训练过程中有好的监督数据时,UNIF 使用监督神经网络模型来提升性能。

利用 Facebook AI 开源工具(包括 fastText、FAISS、PyTorch),NCS 和 UNIF 将自然语言查询和代码段表示为向量,然后训练一个网络,使语义相近的代码段和查询的向量表示在向量空间中彼此靠近。使用这些模型,我们能够从代码库中直接寻找代码段,从而高效解决工程师的问题。为了评估 NCS 和 UNIF,Facebook 使用了新创建的数据集(包含 Stack Overflow 上的公开查询和对应的代码段答案)。结果表明,这两个模型可以正确回答该数据集中的问题,如:

  • 如何关闭/隐藏安卓软键盘?

  • 如何在安卓中将位图转换为可画的?

  • 如何删除一整个文件夹及其内容?

  • 如何处理 back button?

NCS 的性能表明,相对简单的方法可以很好地处理源代码;UNIF 的性能表明,简单的监督学习方法可以在有可用标注数据时,提供显著的额外收益。当这些模型与其他 Facebook 构建系统(如 Aroma 和 Getafix)结合时,这个项目可以为工程师提供可扩展且不断增长的 ML 工具包,帮助他们更高效地写代码、管理代码。

NCS 如何使用嵌入向量

NCS 模型使用嵌入(连续向量表示)来捕捉程序语义(即代码段的意图)。当进行恰当计算时,这些嵌入能够将语义相近的实体在向量空间中拉近距离。

如下图示例所示,关于如何关闭/隐藏安卓软键盘有两个不同的方法。由于它们共享类似的语义,因此即使它们代码行不完全相同,它们在向量空间中的位置彼此接近。

上图表明语义相近的代码段在向量空间中距离较为接近。

Facebook 使用这个概念构建 NCS 模型。总体而言,在模型生成过程中,每个代码段以方法级粒度嵌入到向量空间中。一旦模型构建完成,给定的查询语句就会映射到同样的向量空间中,那么向量距离就可用于估计代码段与查询的相关程度。

下图展示了模型生成和搜索检索流程:

上图展示了 NCS 的整个模型生成和搜索检索过程。

模型生成

为了生成模型,NCS 必须抽取单词,构建词嵌入,然后构建文档嵌入。(这里「文档」指方法体(method body)。)

抽取单词

NCS 从源代码中抽取单词,并执行分词,生成词的线性序列。

为了生成能表示方法体的向量,Facebook 将源代码看作文本,从以下句法类中抽取单词:方法名称、方法调用、枚举值、字符串文本和注释。然后基于标准英语规范(如空格、标点)和代码相关标点(如下划线命名法和驼峰命名法)执行分词。例如上图中,对于方法体(或方法名)「pxToDp」,源代码可作为以下单词的集合:「converts pixel in dp px to dp get resources get display metrics」。

对于代码库中的每个方法体,我们都可以用这种方法对源代码执行分词,并为每个词学习一个嵌入。之后,从每个方法体中抽取的单词列表类似一个自然语言文档。

构建词嵌入

Facebook 使用 fastText 为词汇语料库中的所有单词构建词嵌入fastText 使用一个两层神经网络计算向量表示,该网络可以在大型语料库上以无监督方式训练。具体来讲,Facebook 使用 skip-gram 模型,利用目标 token 的嵌入来预测固定窗口大小中的上下文 token 的嵌入。如上例所示,给出 token「dp」的嵌入、窗口大小为 2,skip-gram 模型学习预测 token「pixel,」、「in,」、「px,」、「to.」的嵌入。其目标是学习嵌入矩阵,其中 |V_c| 表示语料库大小,d 表示词嵌入的维度,T 的第 k 行表示 V_c 中第 k 个单词的嵌入。

在该矩阵中,如果两个向量表示对应的单词经常出现在相似语境,则这两个向量表示距离较近。Facebook 使用该命题的逆命题帮助定义语义关系:向量距离接近的单词应该语义相关性较高。这在自然语言处理中叫作「分布假设」(distributional hypothesis),源文本同样适用于这一概念。

构建文档嵌入向量

下一步是利用方法体中的单词将其总体意图表达出来。为此,研究人员计算了方法体中所有词语的词嵌入向量的加权平均值。这被称为是文档嵌入。

公式中,d 表示方法体的词语集合,v_w 是词 w 的词嵌入,使用 fastText 处理。C 是包含所有文档的语料,u 是归一化函数。

研究人员使用词频-逆文档频率(TF-IDF),为给定文档中的给定词语分配权重。这一步骤旨在强调文档中最为重要的词语,即如果一个词在文档中出现频率很高,则它的权重很高,但如果它在语料库中的多个文档中都有出现,则该词汇会受到惩罚。

在这一步之后,研究人员给语料库中每个方法体的文档向量表示一个索引数字。模型生成就完成了。

搜索检索

搜索查询可以用自然语言表达,例如「关闭/隐藏软键盘」或「如何建立一个没有标题的对话框」。研究人员使用同样的方式对查询和源代码执行分词,且使用同样的 fastText 词嵌入矩阵 T。研究人员简单地计算了词向量表示的平均值,建立一个查询语句的文档嵌入,词表外的词被舍弃。研究使用标准的相似度搜索算法 FAISS,用于寻找和查询的余弦相似度最接近的文档向量,并返回 top n 个结果。(此外还有一些训练后排序,详情参见:https://dl.acm.org/citation.cfm?id=3211353

两个方法体和查询被映射在相同的向量空间中,且位置较为接近。这说明查询和这两个方法体在语义上相似,且相关。

实验结果

研究人员使用 Stack Overflow 问题测试 NCS 的性能。使用标题作为查询,以及答案中的一个代码段作为理想的代码回答。给定一个查询,研究人员评价模型能否在 top 1、5 或 10 个回答上从 GitHub 仓库集合中抽取正确的代码段。研究人员还提供了平均排序倒数(MRR)分数,用于衡量 NCS 能否在第 n 个结果返回正确的答案。

在 287 个问题中,NCS 能在 top 10 个结果内正确回答 175 个问题,大约是整体数据集的 60%。研究人员同时对比了 NCS 和其他传统信息检索算法的表现,如 BM25。如下图所示,NCS 的性能优于 BM25。

下面是 NCS 回答很好的一个问题示例:「从 app 中打开安卓市场」,NCS 返回的第一个答案如下所示:

private void showMarketAppIn() {  try {    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + BuildConfig.APPLICATION_ID)));  } catch (ActivityNotFoundException e) {    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id="                + BuildConfig.APPLICATION_ID)));  }}

UNIF:监督式方法

NCS 的关键在于词嵌入。由于 NCS 是无监督模型,它有很多优势。首先,它可以直接从搜索语料中学习,训练很快、很简单。NCS 假设查询中的词和源代码中抽取的词有着相同的域,因为查询和代码段被映射在相同的向量空间中。然而,事实不一定总是这样。

例如,「Get free space on internal memory」里,没有一个词出现在以下代码段中。研究人员想实现的是将查询词「free space」映射到下列代码中的单词「available」。

File path = Environment.getDataDirectory();StatFs stat = new StatFs(path.getPath());long blockSize = stat.getBlockSize();long availableBlocks = stat.getAvailableBlocks();return Formatter.formatFileSize(this, availableBlocks * blockSize);

基于包含 14005 个 Stack Overflow 帖子的数据集,Facebook 研究人员分析了查询词和源代码中词语的重叠情况。他们发现,查询中有 13,972 个唯一字,其中只有不到一半(6072 个词)在源代码中出现了。这说明,如果一个查询包含源代码没有的词,则 NCS 模型无法有效地检索正确的方法。这一结果促使研究人员进一步探索监督学习模型,以将查询词映射到源代码中。

Facebook 研究人员决定尝试 UNIF。这是一个监督模型,基于 NCS 进行了微小的扩展,用于连接自然语言和源代码。在这个模型中,研究人员使用监督学习训练词嵌入矩阵 T,生成两个嵌入矩阵 T_c 和 T_q,分别对应代码 token 和查询 token。此外,研究人员还用基于注意力的权重机制替换了代码 token 嵌入向量的 TF-IDF 权重机制。

UNIF 模型工作原理

研究人员在与训练 NCS 同样的 (c, q) 数据点集合中训练 UNIF,c、q 分别表示代码 token 和查询 token。模型架构表示如下:

T_q ∈ R^|V_q|×d 和 T_c ∈ R^|Vc |×d 是两个嵌入矩阵,分别映射每个自然语言描述单词和代码 token 到向量,向量长度为 d(V_q 是自然语言查询语料,V_c 是代码语料)。这两个矩阵使用同样的初始权重 T 初始化,并分别在训练中进行修改(对应 fastText)。

为了将离散的代码 token 向量结合为一个文档向量,研究人员使用注意力机制计算加权平均值。注意力权重 a_c ∈ R^d,是一个 d 维向量,从训练中学得,并代替了原有的 TF-IDF 权重。给定一个代码词袋嵌入向量 {e_1, ....., e_n},每个 e_i 的注意力权重 a_i 如下:

文档向量通过和注意力权重相乘后的词嵌入向量相加得到:

为了创建查询文档向量 e_q,研究人员求得每个查询词嵌入向量的平均,这和 NCS 的方法相同。在训练过程中,模型从标准的反向传播中学习参数 T_q、T_c、a_c。

UNIF 网络结构图示。

检索工作和 NCS 一致。给定一个查询,利用上述方法将其表示为文档向量,然后使用 FAISS 寻找和查询的余弦相似度最相近的文档向量。(原则上,UNIF 可以从后处理排序中获益,正如 NCS 那样。)

UNIF 和 NCS 的效果对比

研究人员对比了 NCS 和 UNIF 在 Stack Overflow 评测数据集上的表现。评价方法是模型能否用 top1、top5、top10 结果正确地解答此查询,评价使用 MRR 分数。下表说明,相比 NCS,UNIF 显著提高了回答问题的数量。

如此惊人的搜索性能说明,如果提供合适的训练语料,监督模型可以完成这项任务。例如,搜索查询是「如何退出应用并展示主菜单?」,NCS 会返回:

public void showHomeScreenDialog(View view) {  Intent nextScreen = new Intent(getApplicationContext(), HomeScreenActivity.class);  startActivity(nextScreen);}

而 UNIF 提供了更相关的代码:

public void clickExit(MenuItem item) {  Intent intent=new Intent(Intent.ACTION_MAIN);  intent.addCategory(Intent.CATEGORY_HOME);  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  metr.stop();  startActivity(intent);  finish();}

另一个例子是提问:「如何获得 ActionBar 的高度?」,NCS 模型会返回:

public int getActionBarHeight() {  return mActionBarHeight;  }

UNIF 返回相关的代码:

public static int getActionBarHeightPixel(Context context) {  TypedValue tv = new TypedValue();  if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {    return TypedValue.complexToDimensionPixelSize(tv.data,              context.getResources().getDisplayMetrics());  } else if (context.getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {    return TypedValue.complexToDimensionPixelSize(tv.data,              context.getResources().getDisplayMetrics());  } else {    return 0;  }} 

更多关于 UNIF 的性能情况,可以参考论文:https://arxiv.org/abs/1905.03813


原文链接:https://ai.facebook.com/blog/neural-code-search-ml-based-code-search-using-natural-language-queries/


工程信息检索自然语言处理代码Facebook
2
相关数据
信息检索技术

信息检索(IR)是基于用于查询检索信息的任务。流行的信息检索模型包括布尔模型、向量空间模型、概率模型和语言模型。信息检索最典型和最常见的应用是搜索引擎。

词嵌入技术

词嵌入是自然语言处理(NLP)中语言模型与表征学习技术的统称。概念上而言,它是指把一个维数为所有词的数量的高维空间嵌入到一个维数低得多的连续向量空间中,每个单词或词组被映射为实数域上的向量。

注意力机制技术

我们可以粗略地把神经注意机制类比成一个可以专注于输入内容的某一子集(或特征)的神经网络. 注意力机制最早是由 DeepMind 为图像分类提出的,这让「神经网络在执行预测任务时可以更多关注输入中的相关部分,更少关注不相关的部分」。当解码器生成一个用于构成目标句子的词时,源句子中仅有少部分是相关的;因此,可以应用一个基于内容的注意力机制来根据源句子动态地生成一个(加权的)语境向量(context vector), 然后网络会根据这个语境向量而不是某个固定长度的向量来预测词。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

映射技术

映射指的是具有某种特殊结构的函数,或泛指类函数思想的范畴论中的态射。 逻辑和图论中也有一些不太常规的用法。其数学定义为:两个非空集合A与B间存在着对应关系f,而且对于A中的每一个元素x,B中总有有唯一的一个元素y与它对应,就这种对应为从A到B的映射,记作f:A→B。其中,y称为元素x在映射f下的象,记作:y=f(x)。x称为y关于映射f的原象*。*集合A中所有元素的象的集合称为映射f的值域,记作f(A)。同样的,在机器学习中,映射就是输入与输出之间的对应关系。

监督学习技术

监督式学习(Supervised learning),是机器学习中的一个方法,可以由标记好的训练集中学到或建立一个模式(函数 / learning model),并依此模式推测新的实例。训练集是由一系列的训练范例组成,每个训练范例则由输入对象(通常是向量)和预期输出所组成。函数的输出可以是一个连续的值(称为回归分析),或是预测一个分类标签(称作分类)。

语料库技术

语料库一词在语言学上意指大量的文本,通常经过整理,具有既定格式与标记;事实上,语料库英文 "text corpus" 的涵意即为"body of text"。

fastText技术

Facebook开发的文本处理工具,是一个用于高效学习单词表示和句子分类的库。

自然语言处理技术

自然语言处理(英语:natural language processing,缩写作 NLP)是人工智能和语言学领域的分支学科。此领域探讨如何处理及运用自然语言;自然语言认知则是指让电脑“懂”人类的语言。自然语言生成系统把计算机数据转化为自然语言。自然语言理解系统把自然语言转化为计算机程序更易于处理的形式。

生成模型技术

在概率统计理论中, 生成模型是指能够随机生成观测数据的模型,尤其是在给定某些隐含参数的条件下。 它给观测值和标注数据序列指定一个联合概率分布。 在机器学习中,生成模型可以用来直接对数据建模(例如根据某个变量的概率密度函数进行数据采样),也可以用来建立变量间的条件概率分布。

暂无评论
暂无评论~