自然語言語義程式碼搜尋之路
研究動機
目前,在 GitHub 上搜索程式碼還侷限於關鍵字搜尋。這建立在假設使用者瞭解句法,或者可以預測出他們要找的程式碼周圍的註釋中可能有什麼關鍵字的基礎上。我們的機器學習科學家一直在研究能夠對程式碼進行語義搜尋(https://en.wikipedia.org/wiki/Semantic_search)的方法。
如果讀者想要充分了解語義搜尋的概念,可以想一想下面的搜尋查詢「ping REST api and return results」
請注意,即使在搜尋查詢和文字之間並不存在共有的關鍵字(在程式碼和註釋中並沒有找到「Ping」、「REST」或「api」字樣),我們的語義搜尋演算法也能返回有意義的結果!通過語義搜尋增強關鍵字搜尋是意義深遠的。例如,這種能力可以加快新的軟體工程師上手軟體專案的過程,並且普遍提高程式碼被發現的可能性。
在本文中,我們想向讀者分享我們是如何利用深度學習技術在自然語言語義搜尋領域取得進步的。我們也分享了一個開源的示例,以及復現這些結果所需的程式碼和資料。
引言
目前,對實體的表示學習是 GitHub 上正在進行的機器學習研究的關鍵領域之一,這裡的實體包括程式碼倉庫、程式碼、問題單、檔案和使用者。我們通過學習與文字共享一個向量空間的程式碼的表徵,在促進語義搜尋方面取得了顯著的進步。我們不妨看一看下面的示意圖中的例子:
在上面的例子中,Text 2(藍色)是對程式碼的合理描述,而 Text1(紅色)則與程式碼完全無關。我們的目標是讓學習到的描述相同概念的文字、程式碼對的表徵比較接近。而不相關的文字、程式碼對的表徵的距離較遠。通過在相同的向量空間中表徵程式碼和文字,我們可以將使用者的搜尋查詢向量化,並查詢最接近的表示程式碼的向量。下面是我們目前用來完成這項任務的方法的四個步驟:
1. 學習程式碼的表徵
為了學習程式碼的表徵,我們訓練了一個序列到序列(Seq2Seq)模型,它能夠學著對程式碼進行總結。在 Python 環境下實現這一目的一種方法是提供(程式碼,文件字串)對,其中文件字串是模型試圖預測的目標變數。對我們來說,引入對特定領域(如基於樹結構的LSTM、門控圖網路以及有語法意識的分詞處理)的優化是一個熱點的研究領域。下圖展示了程式碼總結模型的工作過程。在這個例子中,有兩個 python 函式作為輸入,在這兩種情況下,模型都將合理的程式碼總結作為輸出:
應當指出,在上面的例子中,模型通過使用整段程式碼塊而不僅僅是函式名來生成對程式碼的總結。
構建程式碼總結器本身是一個非常激動人心的專案,然而,我們還可以利用這個模型的編碼器作為程式碼的通用特徵提取器。從這個模型中提取出編碼器後,我們可以對它進行調優,從而建立程式碼到自然語言的向量空間的對映。
我們可以客觀地使用 BLEU 得分來評估這個模型。目前,我們已經能夠使用 fairseq-py 程式碼庫(https://github.com/pytorch/fairseq)構建Seq2Seq模型,在一個 python 程式碼驗證集上獲得 13.5 的 BLEU 得分。
2. 學習短文字的表徵
除了學習程式碼的表徵以外,我們還需要為短文字(例如在 Python 文件字串中找到的句子)找到合適的表徵。最初,我們嘗試使用通用的句子編碼器,這是一個預訓練好的文字編碼器,可以從TensorFlow Hub(https://www.tensorflow.org/hub/modules/google/universal-sentence-encoder/1)上獲取。儘管目前嵌入技術的效能已經很好,我們發現學習針對於軟體開發的詞彙和語義的嵌入仍然是有益的。目前正在進行一個研究領域旨在評估用於訓練我們模型的不同特定領域的語料庫,其範圍涵蓋從 GitHub 問題單到第三方資料集的諸多領域。
為了學習短文字的表徵,我們利用 fast.ai 庫訓練了一個神經語言模型。這個庫讓我們可以很容易使用像 AWDLSTM這樣最先進的架構,以及類似帶隨機重啟的週期性學習率(cyclical learning rates with random restarts)這樣的技術。我們使用
《Universal Language Model Fine-tuning for TextClassification》中提出的級聯池化(concat pooling)技術通過總結隱藏狀態從該模型中抽取出對短文字的表徵。
這項工作一個最具挑戰性的方面是評估這些嵌入的質量。我們目前正在構建各種類似於《SentEval: evaluation toolkit for sentence embeddings》(https://github.com/facebookresearch/SentEval)中概述 (https://github.com/facebookresearch/SentEval) 的下游監督任務,它們將幫助我們客觀地評估這些嵌入的質量。與此同時,我們還通過手動檢驗相似短文字之間的相似程度來檢查我們的嵌入。下面的截圖展示了一些示例,我們在這裡根據使用者提供的短文字和向量化的文件字串間的相似程度進行搜尋。
3. 將程式碼表徵對映到具有相同向量空間的文字上
接下來,我們將把從程式碼總結模型(第 1 部分)中學到的程式碼表徵對映到文字的向量空間。我們通過對該模型的編碼器進行調優來實現這一點。這個模型的輸入仍然是程式碼塊,然而模型的目標變數現在變成了文件字串的向量化版本。這些文件字串使用上一節介紹的方法進行了向量化處理。
具體而言,我們使用餘弦近似損失(即預測值與真實標籤的餘弦距離平均值的相反數)進行多維迴歸,將編碼器的隱藏狀態帶入與文字相同的向量空間。
我們正在積極研究直接學習程式碼和自然語言的聯合向量空間的方法,我們的研究借鑑了《Efficient Natural Language Response Suggestion for Smart Reply》中介紹的一些思路。
4. 建立一個語義搜尋系統
最後,在成功地建立了一個可以將程式碼向量化到與文字相同的向量空間的模型之後,我們可以建立一個語義搜尋機制。最簡單的形式是,我們可以在一個數據庫中儲存所有程式碼的向量化版本,對向量化的搜尋查詢執行最近鄰查詢。
我們研究的另一個熱點領域是確定用語義結果增強現有關鍵字搜尋的最佳方法,以及如何引入上下文語境和相關性等額外的資訊。此外,我們正在積極探索如何評估搜尋結果的質量,使我們能夠在這個問題上快速迭代開發。我們未來將在博文中繼續討論這些話題。
總結
下面的示意圖總結了我們當前的語義搜尋工作流程中所有的步驟:
我們正在探索如何改進這種方法的幾乎每一個組成部分,包括資料準備、模型架構、評估過程以及整體的系統設計。本文所介紹的只是一個基礎的最小的示例。
開源示例
我們的開源端到端教程(https://towardsdatascience.com/semantic-code-search-3cd6d244a39c)包含了本文中概述的方法的詳細演示過程,以及你可以用來複現結果的程式碼和資料。(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/)上,語義程式碼搜尋的實時演示(https://blog.github.com/2018-09-18-introducing-experiments-an-ongoing-research-effort-from-github/)不允許使用者針對程式碼倉庫進行特定的搜尋。實際上,這種演示只是為了分享可能得到的效果,並且只搜尋一組受限的、靜態的 python 程式碼集。
此外,與所有的機器學習技術一樣,該方法的效果也受限於使用的訓練資料。例如,用於訓練這些模型的資料是(程式碼,文件字串)對。因此,與文件字串最相似的搜尋查詢成功的機率最大。另一方面,如果查詢與文件字串差別很大或者含有支撐資料很少的概念,該模型可能不會產生很好的搜尋結果。因此,這對於我們進行實時演示是一個挑戰。儘管如此,我們初步的結果表明,這是一個碩果累累的研究領域,我們很高興與你們分享。
語義程式碼搜尋還有更多的用例場景。例如,我們可以對本文介紹的想法進行擴充套件,允許使用者使用他們選擇的自然語言(法語、普通話、阿拉伯語等)同時對用許多不同的程式語言編寫的程式碼進行搜尋。
原文連結:https://githubengineering.com/towards-natural-language-semantic-code-search/