如何提升代碼搜索效果?GitHub團(tuán)隊(duì)打造代碼搜索領(lǐng)域的GLUE數(shù)據(jù)集
想提升代碼搜索效果?首先你得知道怎么才算提升。GitHub 團(tuán)隊(duì)創(chuàng)建 CodeSearchNet 語(yǔ)料庫(kù),旨在為代碼搜索領(lǐng)域提供基準(zhǔn)數(shù)據(jù)集,提升代碼搜索結(jié)果的質(zhì)量。
搜索代碼進(jìn)行重用、調(diào)用,或者借此查看別人處理問(wèn)題的方式,是軟件開(kāi)發(fā)者日常工作中最常見(jiàn)的任務(wù)之一。然而,代碼搜索引擎的效果通常不太好,和常規(guī)的 web 搜索引擎不同,它無(wú)法充分理解你的需求。GitHub 團(tuán)隊(duì)嘗試使用現(xiàn)代機(jī)器學(xué)習(xí)技術(shù)改善代碼搜索結(jié)果,但很快意識(shí)到一個(gè)問(wèn)題:他們無(wú)法衡量改善效果。自然語(yǔ)言處理領(lǐng)域有 GLUE 基準(zhǔn),而代碼搜索評(píng)估領(lǐng)域并沒(méi)有適合的標(biāo)準(zhǔn)數(shù)據(jù)集。
因此 GitHub 與 Weights & Biases 公司展開(kāi)合作,并于昨日推出 CodeSearchNet Challenge 評(píng)估環(huán)境和排行榜。與此同時(shí),GitHub 還發(fā)布了一個(gè)大型數(shù)據(jù)集,以幫助數(shù)據(jù)科學(xué)家構(gòu)建適合該任務(wù)的模型,并提供了多個(gè)代表當(dāng)前最優(yōu)水平的基線(xiàn)模型。該排行榜使用一個(gè) query 標(biāo)注數(shù)據(jù)集來(lái)評(píng)估代碼搜索工具的質(zhì)量。
- 論文地址:https://arxiv.org/abs/1909.09436
- 語(yǔ)料庫(kù)及基線(xiàn)模型地址:https://github.com/github/CodeSearchNet
- 挑戰(zhàn)賽地址:https://app.wandb.ai/github/codesearchnet/benchmark
CodeSearchNet 語(yǔ)料庫(kù)
使用專(zhuān)家標(biāo)注創(chuàng)建足以訓(xùn)練高容量模型的大型數(shù)據(jù)集成本高昂,不切實(shí)際,因此 GitHub 創(chuàng)建了一個(gè)質(zhì)量較低的代理數(shù)據(jù)集。GitHub 遵循文獻(xiàn) [5, 6, 9, 11] 中的做法,將開(kāi)源軟件中的函數(shù)與其對(duì)應(yīng)文檔中的自然語(yǔ)言進(jìn)行匹配。但是,這樣做需要執(zhí)行大量預(yù)處理步驟和啟發(fā)式方法。
通過(guò)對(duì)常見(jiàn)錯(cuò)誤案例進(jìn)行深入分析,GitHub 團(tuán)隊(duì)總結(jié)出一些通用法則和決策。
CodeSearchNet 語(yǔ)料庫(kù)收集過(guò)程
GitHub 團(tuán)隊(duì)從開(kāi)源 non-fork GitHub repo 中收集語(yǔ)料,使用 libraries.io 確認(rèn)所有項(xiàng)目均被至少一個(gè)其他項(xiàng)目使用,并按照「流行度」(popularity)對(duì)其進(jìn)行排序(流行度根據(jù) star 和 fork 數(shù)而定)。然后,刪除沒(méi)有 license 或者 license 未明確允許重新分發(fā)的項(xiàng)目。之后,GitHub 團(tuán)隊(duì)使用其通用解析器 TreeSitter 對(duì)所有 Go、Java、JavaScript、Python、PHP 和 Ruby 函數(shù)(或方法)執(zhí)行分詞操作,并使用啟發(fā)式正則表達(dá)式對(duì)函數(shù)對(duì)應(yīng)的文檔文本進(jìn)行分詞處理。
篩選
為了給 CodeSearchNet Challenge 生成訓(xùn)練數(shù)據(jù),GitHub 團(tuán)隊(duì)首先考慮了語(yǔ)料庫(kù)中具備相關(guān)文檔的函數(shù)。這就得到了一組 (c_i , d_i) 對(duì),其中 c_i 是函數(shù),d_i 是對(duì)應(yīng)的文檔。為了使數(shù)據(jù)更加適合代碼搜索任務(wù),GitHub 團(tuán)隊(duì)執(zhí)行了一系列預(yù)處理步驟:
文檔 d_i 被截?cái)啵瑑H保留第一個(gè)完整段落,以使文檔長(zhǎng)度匹配搜索 query,并刪除對(duì)函數(shù)參數(shù)和返回值的深入討論。
刪除 d_i 短于三個(gè) token 的對(duì),因?yàn)榇祟?lèi)注釋無(wú)法提供有效信息。
刪除 c_i 實(shí)現(xiàn)少于三行的對(duì),因?yàn)樗鼈兺ǔ0磳?shí)現(xiàn)的方法、getters、setters 等。
刪除名稱(chēng)中包含子字符串「test」的函數(shù)。類(lèi)似地,刪除構(gòu)造函數(shù)和標(biāo)準(zhǔn)擴(kuò)展方法,如 Python 中的 __str__、Java 中的 toString。
識(shí)別數(shù)據(jù)集中的(近似)重復(fù)函數(shù),僅保留其中一個(gè)副本,從而刪除數(shù)據(jù)集中的重復(fù)項(xiàng)。這就消除了出現(xiàn)多個(gè)版本自生成代碼和復(fù)制粘貼的情況。
篩選后的語(yǔ)料庫(kù)和數(shù)據(jù)抽取代碼詳見(jiàn):https://github.com/github/CodeSearchNet
數(shù)據(jù)集詳情
該數(shù)據(jù)集包含 200 萬(wàn)函數(shù)-文檔對(duì)、約 400 萬(wàn)不具備對(duì)應(yīng)文檔的函數(shù)(見(jiàn)下表 1)。GitHub 團(tuán)隊(duì)將該數(shù)據(jù)集按照 80-10-10 的比例劃分為訓(xùn)練集/驗(yàn)證集/測(cè)試集,建議用戶(hù)按照該比例使用此數(shù)據(jù)集。
表 1:數(shù)據(jù)集詳情
局限性
該數(shù)據(jù)集噪聲很大。首先,文檔與 query 存在本質(zhì)區(qū)別,它們使用的是不同的語(yǔ)言形式。文檔通常是代碼作者在寫(xiě)代碼的同時(shí)寫(xiě)成的,更傾向于使用同樣的詞匯,這與搜索 query 存在差異。其次,盡管 GitHub 團(tuán)隊(duì)在創(chuàng)建數(shù)據(jù)集的過(guò)程中執(zhí)行了數(shù)據(jù)清洗,但他們無(wú)法得知每個(gè)文檔 d_i 描述對(duì)應(yīng)代碼段 c_i 的精確程度。最后,一些文檔是用非英語(yǔ)文本寫(xiě)成的,而 CodeSearchNet Challenge 評(píng)估數(shù)據(jù)集主要關(guān)注的是英文 query。
CodeSearchNet 基線(xiàn)模型
基于 GitHub 之前在語(yǔ)義代碼搜索領(lǐng)域的努力,該團(tuán)隊(duì)發(fā)布了一組基線(xiàn)模型,這些模型利用現(xiàn)代技術(shù)學(xué)習(xí)序列(包括 BERT 類(lèi)的自注意力模型),幫助數(shù)據(jù)科學(xué)家開(kāi)啟代碼搜索。
和之前的工作一樣,GitHub 團(tuán)隊(duì)使用代碼和 query 的聯(lián)合嵌入來(lái)實(shí)現(xiàn)神經(jīng)搜索系統(tǒng)。該架構(gòu)對(duì)每個(gè)輸入(自然或編程)語(yǔ)言使用一個(gè)編碼器,并訓(xùn)練它們使得輸入映射至一個(gè)聯(lián)合向量空間。其訓(xùn)練目標(biāo)是將代碼及其對(duì)應(yīng)語(yǔ)言映射至鄰近的向量,這樣我們就可以嵌入 query 實(shí)現(xiàn)搜索,然后返回嵌入空間中「鄰近」的代碼段集合。
考慮 query 和代碼之間更多交互的較復(fù)雜模型當(dāng)然性能更好,但是為每個(gè) query 或代碼段生成單個(gè)向量可以實(shí)現(xiàn)更高效的索引和搜索。
為了學(xué)習(xí)這些嵌入函數(shù),GitHub 團(tuán)隊(duì)在架構(gòu)中加入了標(biāo)準(zhǔn)序列編碼器模型,如圖 3 所示。首先,根據(jù)輸入序列的語(yǔ)義對(duì)其執(zhí)行預(yù)處理:將代碼 token 中的標(biāo)識(shí)符分割為子 token(如變量 camelCase 變成了兩個(gè)子 token:camel 和 case),使用字節(jié)對(duì)編碼(byte-pair encoding,BPE)分割自然語(yǔ)言 token。
圖 3:模型架構(gòu)概覽
然后使用以下架構(gòu)之一處理 token 序列,以獲得(語(yǔ)境化的)token 嵌入。
- 神經(jīng)詞袋模型:每個(gè)(子)token 都被轉(zhuǎn)換為可學(xué)習(xí)嵌入(向量表示)。
- 雙向 RNN 模型:利用 GRU 單元總結(jié)輸入序列。
- 一維卷積神經(jīng)網(wǎng)絡(luò):用于處理輸入 token 序列。
- 自注意力模型:其多頭注意力用于計(jì)算序列中每個(gè) token 的表示。
之后,使用池化函數(shù)將這些 token 嵌入組合為一個(gè)序列嵌入,GitHub 團(tuán)隊(duì)已經(jīng)實(shí)現(xiàn)了 mean/max-pooling 和類(lèi)注意力的加權(quán)和機(jī)制。
下圖展示了基線(xiàn)模型的通用架構(gòu):
CodeSearchNet 挑戰(zhàn)賽
為了評(píng)估代碼搜索模型,GitHub 團(tuán)隊(duì)收集了一組代碼搜索 query,并讓程序員標(biāo)注 query 與可能結(jié)果的關(guān)聯(lián)程度。他們首先從必應(yīng)中收集了一些常見(jiàn)搜索 query,結(jié)合 StaQC 中的 query 一共獲得 99 個(gè)與代碼概念相關(guān)的 query(GitHub 團(tuán)隊(duì)刪除了 API 文檔查詢(xún)方面的問(wèn)題)。
圖 1:標(biāo)注者指導(dǎo)說(shuō)明
之后,GitHub 團(tuán)隊(duì)使用標(biāo)準(zhǔn) Elasticsearch 和基線(xiàn)模型,從 CodeSearchNet 語(yǔ)料庫(kù)中為每個(gè) query 獲得 10 個(gè)可能的結(jié)果。最后,GitHub 團(tuán)隊(duì)請(qǐng)程序員、數(shù)據(jù)科學(xué)家和機(jī)器學(xué)習(xí)研究者按照 [0, 3] 的標(biāo)準(zhǔn)標(biāo)注每個(gè)結(jié)果與 query 的關(guān)聯(lián)程度(0 表示「完全不相關(guān)」,3 表示「完全匹配」)。
未來(lái),GitHub 團(tuán)隊(duì)想在該評(píng)估數(shù)據(jù)集中納入更多語(yǔ)言、query 和標(biāo)注。接下來(lái)幾個(gè)月,他們將持續(xù)添加新的數(shù)據(jù),為下一個(gè)版本的 CodeSearchNet Challenge 制作擴(kuò)展版數(shù)據(jù)集。