在本文中,作者分享了如何利用深度学习技术推动自然语言语义搜索的发展。此外,他们还提供了一个开源示例,以及实现这些功能所需的代码和数据。
目前,在GitHub上进行代码搜索主要依赖于关键词搜索。这假设用户已经了解代码的语法或能够预测到代码周围注释中可能出现的关键词。我们的机器学习专家一直在研究如何实现代码的语义搜索(https://en.wikipedia.org/wiki/Semantic_search)。
举个例子,考虑这样一个搜索查询:“ping REST api 并返回结果”。即使搜索查询和文本之间没有共同的关键词(代码和注释中没有出现“Ping”、“REST”或“api”),我们的语义搜索算法仍然能返回有意义的结果。通过增强关键词搜索的语义功能,可以显著提升新软件工程师快速上手软件项目的能力,并提高代码被发现的可能性。
在本文中,我们介绍了如何利用深度学习技术提升自然语言语义搜索的能力,并分享了一个开源示例以及相应的代码和数据。
当前,实体的表示学习是GitHub上正在进行的重要机器学习研究领域之一。实体包括代码仓库、代码、成绩单、文件和用户。通过学习与文本共享同一向量空间的代码表示,我们在语义搜索方面取得了显著进展。以下是一个示例:
在示例中,Text 2(蓝色)是对代码的合理描述,而Text 1(红色)则与代码无关。我们的目标是让学习到的文本和代码对的表示尽可能接近,而不相关的文本和代码对的表示距离较远。通过在同一个向量空间中表示代码和文本,我们可以将用户的搜索查询向量化,并查找最接近代码表示的向量。以下是实现这一目标的四个步骤:
为了学习代码的表示,我们训练了一个序列到序列(Seq2Seq)模型,使其学会对代码进行总结。在Python环境下,一种方法是提供(代码,文档字符串)对,其中文档字符串是模型试图预测的目标变量。我们还专注于特定领域的优化,如基于树结构的LSTM、门控图网络以及语法感知的分词处理。下图展示了代码总结模型的工作过程:
在示例中,两个Python函数作为输入,模型对代码进行了合理的总结。
我们构建代码总结器本身是一个令人兴奋的项目,但我们可以进一步利用这个模型的编码器作为通用的代码特征提取器。通过调优编码器,我们可以建立代码到自然语言的向量空间映射。
我们可以使用BLEU得分来评估这个模型。目前,我们已经能够使用fairseq-py代码库(https://github.com/pytorch/fairseq)构建Seq2Seq模型,在Python代码验证集上获得13.5的BLEU得分。
除了学习代码的表示外,我们还需要为短文本(例如Python文档字符串中的句子)找到合适的表示。我们尝试使用通用的句子编码器,这是一个预训练的文本编码器,可以从TensorFlow Hub(https://www.tensorflow.org/hub/modules/google/universal-sentence-encoder/1)获取。尽管现有的嵌入技术已经很成熟,我们发现针对软件开发的词汇和语义进行特定领域的嵌入学习仍然很有价值。我们正在研究不同特定领域的语料库,范围从GitHub成绩单到第三方数据集。
为了学习短文本的表示,我们使用fast.ai库训练了一个神经语言模型。该库让我们可以轻松使用先进的架构,如AWD LSTM,以及类似于带有随机重启的周期性学习率的技术。我们使用《Universal Language Model Fine-tuning for Text Classification》中提出的级联池化技术从模型中提取短文本的表示。
评价这些嵌入的质量是一个挑战。我们正在构建一些下游任务,如《SentEval: evaluation toolkit for sentence embeddings》,以帮助我们客观评估这些嵌入的质量。同时,我们还通过手动检查相似短文本之间的相似程度来验证嵌入的质量。以下是一些示例,展示了根据用户提供的短文本和向量化文档字符串之间的相似度进行搜索。
接下来,我们将学习到的代码表示映射到文本的向量空间。我们通过调优模型的编码器来实现这一点。该模型的输入仍然是代码块,但目标变量现在变成了文档字符串的向量化版本。这些文档字符串使用上一节介绍的方法进行向量化处理。
具体来说,我们使用余弦近似损失(即预测值与真实标签的余弦距离的相反数)进行多维回归,将编码器的隐藏状态带入与文本相同的向量空间。
我们还在研究如何直接学习代码和自然语言的联合向量空间,借鉴了《Efficient Natural Language Response Suggestion for Smart Reply》中的一些思路。
最后,在成功创建了一个可以将代码向量化到与文本相同向量空间的模型后,我们可以创建一个语义搜索机制。最简单的方法是在数据库中存储所有代码的向量化版本,并对向量化搜索查询执行最近邻查找。
我们研究的另一个热点是确定如何最佳地增强现有关键词搜索的语义结果,以及如何引入上下文和相关性等额外信息。此外,我们正在积极探讨如何评估搜索结果的质量,以便能够在这个问题上快速迭代开发。我们未来将在博客中继续讨论这些话题。
以下图表总结了我们当前语义搜索工作流程的所有步骤:
我们正在探索如何改进这一方法的几乎所有组成部分,包括数据准备、模型架构、评估过程以及整个系统设计。本文介绍的内容只是一个基础的最小示例。
我们的开源端到端教程(https://towardsdatascience.com/semantic-code-search-3cd6d244a39c)详细介绍了本文中提到的方法,并提供了复现结果所需的代码和数据。
该开源示例(进行了一些修改)也被用作kubeflow项目的教程,详情请参阅:https://github.com/kubeflow/examples/tree/master/code_search
我们认为,相对于通常使用的“如何…”这类查询,语义代码搜索将最有益于针对特定实体(如代码仓库、组织或用户)的代码搜索。在我们最近发布的实验网站上,语义代码搜索的实时演示(https://blog.github.com/2018-09-18-introducing-experiments-an-ongoing-research-effort-from-github/)不允许用户针对代码仓库进行特定搜索。实际上,这种演示只是为了展示可能的结果,并且只搜索一组受限的、静态的Python代码集。
此外,与所有机器学习技术一样,该方法的效果也受限于使用的训练数据。例如,用于训练这些模型的数据是(代码,文档字符串)对。因此,与文档字符串最相似的搜索查询成功的几率最大。另一方面,如果查询与文档字符串差异较大或者包含很少支撑数据的概念,该模型可能不会产生良好的搜索结果。因此,这对于我们进行实时演示是一个挑战。尽管如此,我们初步的结果表明,这是一个充满潜力的研究领域,我们很高兴与大家分享。
语义代码搜索还有更多的应用场景。例如,我们可以扩展本文介绍的想法,允许用户使用他们选择的自然语言(如法语、普通话、阿拉伯语等)同时对用多种编程语言编写的代码进行搜索。