閆 鑫,周 宇+,黃志球
1.南京航空航天大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,南京 210016
2.南京航空航天大學(xué) 高安全系統(tǒng)的軟件開發(fā)與驗(yàn)證技術(shù)工信部重點(diǎn)實(shí)驗(yàn)室,南京 210016
在當(dāng)前開源的軟件生態(tài)環(huán)境下,海量代碼及多種相關(guān)代碼描述信息可以公開、免費(fèi)地獲取,而且軟件數(shù)量及規(guī)模正在迅速擴(kuò)大,這也就為代碼復(fù)用奠定了基礎(chǔ)。Gabel 等人[1]對(duì)超過6 000 個(gè)開源項(xiàng)目的約4.2 億行代碼進(jìn)行了統(tǒng)計(jì)和研究,發(fā)現(xiàn)軟件通常缺少唯一性(uniqueness),即程序代碼經(jīng)常是重復(fù)的,這也為代碼復(fù)用提供了理論支撐。有效的代碼復(fù)用可以提高開發(fā)者的開發(fā)效率,降低開發(fā)成本。
在實(shí)際軟件開發(fā)過程中,代碼推薦是一種常用的實(shí)現(xiàn)代碼復(fù)用的方法。為了實(shí)現(xiàn)一個(gè)特有的代碼功能,開發(fā)者通常會(huì)通過給定一個(gè)用戶查詢,從代碼庫(kù)中檢索和復(fù)用已有的代碼。
當(dāng)前存在有很多的代碼推薦方法[2-3],這些方法通常是基于信息檢索技術(shù)來(lái)實(shí)現(xiàn)。McMillan 等人[4]提出了Portfolio。它通過關(guān)鍵字匹配和佩奇排名(Page-Rank)返回一個(gè)函數(shù)鏈。Lv 等人[5]提出了CodeHow,通過一個(gè)拓展的布爾模型把文本相似度和應(yīng)用編程接口匹配相結(jié)合,來(lái)實(shí)現(xiàn)代碼檢索和推薦。
基于信息檢索的代碼檢索和推薦方法存在的一個(gè)根本的問題在于自然語(yǔ)言查詢的高層級(jí)的意圖與代碼的低層級(jí)的實(shí)現(xiàn)細(xì)節(jié)不匹配[6]。它們兩者之間或許并沒有共同的詞匯、同義詞、近義詞和語(yǔ)言結(jié)構(gòu),只是存在一定的語(yǔ)義相關(guān)性,而基于信息檢索的方法并不能解決這個(gè)問題。
針對(duì)上述問題,本文提出了一種基于序列到序列模型的代碼片段推薦方法,該方法不再是直接計(jì)算自然語(yǔ)言查詢和代碼之間的相似度,而是首先訓(xùn)練一個(gè)自然語(yǔ)言查詢生成模型用于實(shí)現(xiàn)代碼到對(duì)應(yīng)的自然語(yǔ)言查詢的生成。然后,通過計(jì)算用戶輸入的自然語(yǔ)言查詢與代碼片段對(duì)應(yīng)的生成查詢之間的相似度,檢索并推薦相關(guān)度高的代碼片段給開發(fā)人員。自然語(yǔ)言查詢之間的相似度不再存在語(yǔ)義差的問題,這也就解決了自然語(yǔ)言查詢與代碼之間語(yǔ)義差的問題。
本文從StackOverflow 問答網(wǎng)站上提取數(shù)據(jù)并構(gòu)建語(yǔ)料庫(kù),確保數(shù)據(jù)是真實(shí)的自然語(yǔ)言查詢和相匹配的代碼片段。通過計(jì)算推薦結(jié)果的平均倒數(shù)排名(mean reciprocal rank,MRR)來(lái)驗(yàn)證方法的有效性。實(shí)驗(yàn)結(jié)果表明,基于序列到序列模型的代碼推薦方法DeepCR 能有效提高代碼推薦效果,優(yōu)于現(xiàn)有研究工作,能夠很好地完成代碼推薦任務(wù),為開發(fā)人員輸入的自然語(yǔ)言查詢提供精準(zhǔn)的代碼推薦。
本文工作的主要貢獻(xiàn)在于以下兩方面:
(1)利用程序靜態(tài)分析技術(shù),設(shè)計(jì)啟發(fā)式規(guī)則,對(duì)Stack Overflow 問答網(wǎng)站上積累的龐大數(shù)據(jù)進(jìn)行了清洗和篩選,構(gòu)建了一個(gè)干凈的代碼庫(kù),用于代碼片段推薦的研究工作;
(2)結(jié)合深度神經(jīng)網(wǎng)絡(luò)技術(shù)和程序靜態(tài)分析技術(shù),訓(xùn)練自然語(yǔ)言查詢生成模型,為代碼片段生成自然語(yǔ)言查詢,執(zhí)行生成的查詢與用戶輸入的查詢之間的相似度計(jì)算,進(jìn)而提出一種基于序列到序列模型的代碼片段推薦方法DeepCR,致力于解決自然語(yǔ)言查詢與代碼片段之間語(yǔ)義差的問題。
當(dāng)前,存在很多傳統(tǒng)的基于信息檢索的代碼推薦方法。Ohloh、Krugle 和Sando[7]都是代碼推薦引擎,可以根據(jù)自然語(yǔ)言查詢中出現(xiàn)的關(guān)鍵字或者正則表達(dá)式來(lái)推薦代碼片段。Linstead 等人[3]提出了Sourcerer。它是一個(gè)基于Lucene 的用于大規(guī)模代碼庫(kù)的代碼推薦工具。它通過把代碼的文本信息和結(jié)構(gòu)信息相結(jié)合來(lái)實(shí)現(xiàn)代碼推薦。SNIFF[8]則整合了代碼片段中包含的Java APIs 的注解信息,執(zhí)行與自然語(yǔ)言查詢的相似度計(jì)算過程,進(jìn)而實(shí)現(xiàn)代碼片段的推薦。這些傳統(tǒng)的信息檢索方法存在的一個(gè)基本問題是自然語(yǔ)言查詢的高層級(jí)的意圖與代碼的低層級(jí)的實(shí)現(xiàn)細(xì)節(jié)不匹配。
近年來(lái),研究者也提出了一些基于深度神經(jīng)網(wǎng)絡(luò)技術(shù)的代碼推薦方法。Gu 等人[9]提出了DeepCS,通過神經(jīng)網(wǎng)絡(luò)模型將代碼和自然語(yǔ)言描述映射到高維度向量中,使得代碼片段和其對(duì)應(yīng)的自然語(yǔ)言查詢具有相似的向量呈現(xiàn),通過計(jì)算兩者之間的余弦相似度來(lái)實(shí)現(xiàn)代碼推薦。Yao 等人[10]提出了CoaCor。它結(jié)合強(qiáng)化學(xué)習(xí)相關(guān)技術(shù)訓(xùn)練一個(gè)代碼注解模型,為代碼生成注解,并結(jié)合DeepCS 中所提出方法的基本框架訓(xùn)練代碼檢索模型,綜合計(jì)算兩者之間的相似度得分,進(jìn)而實(shí)現(xiàn)代碼推薦任務(wù)。
本章將對(duì)所提出的代碼推薦方法DeepCR 進(jìn)行詳細(xì)介紹。DeepCR 的整體架構(gòu)圖如圖1 所示。DeepCR 致力于根據(jù)開發(fā)人員輸入的自然語(yǔ)言查詢,推薦Java 相關(guān)的代碼片段。DeepCR 由三個(gè)核心模塊組成:數(shù)據(jù)準(zhǔn)備、自然語(yǔ)言查詢生成模型訓(xùn)練以及基于序列到序列模型的代碼片段推薦。
考慮到代碼推薦的應(yīng)用場(chǎng)景中開發(fā)人員輸入的均為自然語(yǔ)言查詢,且考慮到一些問答網(wǎng)站上的問題都是真實(shí)的自然語(yǔ)言查詢,為了保證所收集的數(shù)據(jù)的真實(shí)性和可靠性,本文選擇從Stack Overflow 上收集數(shù)據(jù)。數(shù)據(jù)準(zhǔn)備過程包括數(shù)據(jù)收集和清洗、代碼信息提取和代碼標(biāo)識(shí)符替換。
3.1.1 數(shù)據(jù)收集和清洗
Stack Overflow 問答網(wǎng)站上包含超過1 790 萬(wàn)個(gè)問題,考慮到本文所提出的方法DeepCR 是針對(duì)Java編程語(yǔ)言相關(guān)的代碼推薦,因此只將標(biāo)簽中包含Java的問題集合作為數(shù)據(jù)候選集,問題總量超過150 萬(wàn)。
只做到這一步,所提取的數(shù)據(jù)候選集的質(zhì)量依舊參差不齊。圖2 展示了一個(gè)來(lái)自于Stack Overflow問答網(wǎng)站的問題示例。如圖2 所示,每個(gè)用戶的自然語(yǔ)言查詢都對(duì)應(yīng)著一系列的標(biāo)簽,以及不定數(shù)量的答案,而每個(gè)答案的主體部分則可能包含著不定數(shù)量的代碼片段。
為了構(gòu)建一個(gè)干凈的數(shù)據(jù)集,需要針對(duì)每個(gè)自然語(yǔ)言查詢,提取和篩選出與自然語(yǔ)言查詢匹配的代碼片段,構(gòu)建高質(zhì)量數(shù)據(jù)集。因此,本文利用程序靜態(tài)分析技術(shù),尤其是抽象語(yǔ)法樹解析技術(shù),設(shè)計(jì)啟發(fā)式規(guī)則,對(duì)數(shù)據(jù)候選集進(jìn)行清洗和篩選,具體體現(xiàn)在以下幾方面。
Fig.1 Overall framework of DeepCR圖1 DeepCR 的整體架構(gòu)
Fig.2 Example of question from Stack Overflow圖2 一個(gè)來(lái)自于Stack Overflow 上的問題示例
(1)答案主體中不包含代碼片段。本文的目標(biāo)是做代碼片段推薦,如果與自然語(yǔ)言查詢相對(duì)應(yīng)的答案中沒有任何的代碼片段,顯而易見,此類答案不能作為數(shù)據(jù)集的一部分,則將其濾除。
(2)答案主體中的代碼片段不是Java 代碼。本文是針對(duì)Java 語(yǔ)言做代碼片段推薦,不涉及其他編程語(yǔ)言,如Python、C++、SQL,甚至是偽代碼等。因此,對(duì)不是由Java語(yǔ)言構(gòu)成的代碼片段進(jìn)行過濾。
(3)答案主體中的代碼片段粒度不符合要求。本文的代碼推薦的最大粒度是單個(gè)Java 方法,但答案主體中包含的代碼片段可能是一個(gè)完整的Java類、包含兩個(gè)或更多的Java 方法、構(gòu)造函數(shù)或者靜態(tài)代碼塊等。這些代碼片段雖然都是Java 代碼,但與本文的代碼推薦粒度不符,因此此類代碼片段也會(huì)被濾除。
(4)答案主體中的代碼片段包含不屬于JDK(Java development kit)的方法調(diào)用。當(dāng)前存在著海量的Jar包,把所有相關(guān)的Jar 包中包含的方法調(diào)用囊括到一個(gè)代碼推薦方法中務(wù)必會(huì)使推薦方法的準(zhǔn)確性和可靠性大大降低,而JDK 是Java 軟件開發(fā)必不可少的軟件開發(fā)工具包,且功能強(qiáng)大。因此,本文出于代碼推薦的準(zhǔn)確率和可靠性的考量,針對(duì)的是與JDK 相關(guān)的代碼推薦,對(duì)于包含不屬于JDK 的方法調(diào)用的代碼片段也會(huì)被濾除。
(5)自然語(yǔ)言查詢不是一個(gè)具體的編程任務(wù)。一個(gè)自然語(yǔ)言查詢,由于其開放性和自由性,所涉及的可能并不是一個(gè)具體的Java 編程任務(wù),而是詢問不同點(diǎn)、原因等。例如,“What is the difference between an int and an Integer in Java and C#?”“Why can’t I use a try block around my super() call?”。這類自然語(yǔ)言查詢均不涉及一個(gè)具體的編程任務(wù),均不可作為有效數(shù)據(jù)集的一部分而存在,為此人工對(duì)數(shù)據(jù)進(jìn)行了研究和總結(jié),設(shè)計(jì)了一些啟發(fā)式規(guī)則,進(jìn)而實(shí)現(xiàn)對(duì)此類冗余數(shù)據(jù)進(jìn)行過濾和清洗。
3.1.2 代碼信息提取
在此步驟中,結(jié)合抽象語(yǔ)法樹解析技術(shù),并調(diào)用Eclipse JDT提供的大量應(yīng)用編程接口(application programming interfaces,APIs)來(lái)實(shí)現(xiàn)代碼信息的提取。
對(duì)于每一個(gè)Java 方法,對(duì)其做抽象語(yǔ)法樹解析,提取方法名、常量、變量名、類型名、方法調(diào)用名以及內(nèi)部方法聲明信息。
形式上而言,給定一個(gè)Java 方法的抽象語(yǔ)法樹,采用深度優(yōu)先搜索對(duì)其做遍歷操作。具體地,通過解析抽象語(yǔ)法樹的MethodDeclaration 節(jié)點(diǎn)來(lái)獲取方法名和內(nèi)部方法聲明信息;通過解析NumberLiteral、StringLiteral 和CharacterLiteral 節(jié)點(diǎn)來(lái)獲取常量信息;通過解析SimpleType、PrimitiveType、QualifiedType 等節(jié)點(diǎn)來(lái)獲取類型名信息;通過解析MethodInvocation等節(jié)點(diǎn)來(lái)獲取方法調(diào)用名信息;通過解析SimpleName節(jié)點(diǎn)來(lái)獲取變量名信息。
圖3 給出了一個(gè)代碼片段示例,圖4 則給出了代碼片段對(duì)應(yīng)的抽象語(yǔ)法樹示例。出于空間的考慮,只展示代碼片段對(duì)應(yīng)的抽象語(yǔ)法樹到語(yǔ)句層級(jí)。
Fig.3 Example of code snippet圖3 一個(gè)代碼片段實(shí)例
通過調(diào)用相關(guān)API,抽象語(yǔ)法樹中的節(jié)點(diǎn)可以被精確定位,并且代碼信息可以輕松地獲取到,并將所提取的信息與對(duì)應(yīng)的代碼片段形成映射,一一對(duì)應(yīng),至此代碼信息提取工作完成。
3.1.3 代碼標(biāo)識(shí)符替換
由于標(biāo)識(shí)符命名的開放性,當(dāng)代碼片段的量級(jí)達(dá)到一定程度時(shí),則代碼片段的詞匯表的數(shù)量會(huì)很自然地超出一個(gè)合理的詞匯表的大小。因此,合理的詞匯表壓縮方法是極其有必要的。
為了合理地對(duì)詞匯表進(jìn)行壓縮,采用了一種標(biāo)識(shí)符替換方法來(lái)把代碼的詞匯表大小壓縮到一個(gè)合理的范圍內(nèi)。具體而言,利用從Java 方法中提取到的代碼信息將代碼片段中的一些標(biāo)識(shí)符替換為一些特定的詞匯。首先,計(jì)算所有代碼片段中每個(gè)詞匯的出現(xiàn)頻次,并選取出現(xiàn)頻次最高的30 000 個(gè)詞匯作為代碼片段的初始詞匯表;其次,根據(jù)初始詞匯表以及提取到的代碼信息對(duì)代碼片段執(zhí)行標(biāo)識(shí)符替換操作。標(biāo)識(shí)符替換操作可以被分為6 類,具體包括方法名替換、常量替換、變量名替換、類型名替換、方法調(diào)用名替換以及內(nèi)部方法聲明替換。相應(yīng)地,引入了一些特定的詞匯作為替代詞匯,標(biāo)識(shí)符替換類別和引入的特定詞匯如表1 所示,其中k是一個(gè)從0 開始計(jì)數(shù)的正整數(shù)。
Table 1 Categories and rules of identifier replacement表1 標(biāo)識(shí)符替換類別及替換規(guī)則
Fig.4 Example of abstract syntax tree for code snippet圖4 一個(gè)代碼片段的抽象語(yǔ)法樹示例
具體而言,考慮到對(duì)每一個(gè)Java 方法而言,只存在唯一的一個(gè)方法名,因此引入一個(gè)固定的詞匯METHODNAME 作為方法名的替代詞匯??紤]到去區(qū)分不同的字符串常量、字符常量和數(shù)字常量意義不大,因此,分別引入特定的詞匯STRINGLITERAL、CHARACTERLITERAL 和NUMBERLITERAL 來(lái) 作為字符串常量、字符常量和數(shù)字常量的替換詞匯。至于其他的特定詞匯都包含一個(gè)變量k,用來(lái)表示當(dāng)前Java方法中出現(xiàn)的特定類別的第k個(gè)標(biāo)識(shí)符。
舉例而言,圖5 展示了一個(gè)經(jīng)過標(biāo)識(shí)符替換操作的代碼片段示例。變量vari和alist由于出現(xiàn)頻次過低,不存在于初始詞匯表中,因此執(zhí)行變量名替換操作,即用SIMPLENAME_k的形式替換變量vari和alist,k為從1 開始計(jì)數(shù)的正整數(shù)。因此,vari和alist被分別替換為SIMPLENAME_1 和SIMPLENAME_2。
Fig.5 Example of identifier replacement圖5 一個(gè)標(biāo)識(shí)符替換示例
經(jīng)過標(biāo)識(shí)符替換操作后,超出初始詞匯表的標(biāo)識(shí)符均會(huì)被替換為表1 中引入的特定詞匯。進(jìn)而,所有代碼片段的詞匯表大小就會(huì)被壓縮到一個(gè)合理的范圍內(nèi)。最終,用于后續(xù)自然語(yǔ)言查詢生成模型訓(xùn)練的代碼片段的詞匯表以及數(shù)據(jù)集也會(huì)被確定,構(gòu)建一個(gè)<代碼,查詢>對(duì)的數(shù)據(jù)集。
在此步驟中,致力于訓(xùn)練一個(gè)基于序列到序列模型的自然語(yǔ)言查詢生成模型。序列到序列模型已經(jīng)被廣泛應(yīng)用于各種神經(jīng)網(wǎng)絡(luò)機(jī)器翻譯(neural machine translation,NMT)任務(wù)[11-12]中。
大體而言,序列到序列模型可以簡(jiǎn)單地分為兩個(gè)循環(huán)神經(jīng)網(wǎng)絡(luò)(recurrent neural network,RNN)結(jié)構(gòu),即一個(gè)編碼器、一個(gè)解碼器。編碼器負(fù)責(zé)把輸入序列通過非線性變化映射到一個(gè)固定維度向量中去,而解碼器則是把固定維度的向量映射到相應(yīng)的目標(biāo)序列。當(dāng)輸入序列較長(zhǎng)時(shí),這個(gè)中間向量難以存儲(chǔ)足夠的信息,就成為序列到序列模型的一個(gè)瓶頸。因此,引入注意力機(jī)制(attention mechanism)[13]對(duì)編碼器每個(gè)隱藏狀態(tài)做加權(quán)處理,允許解碼器隨時(shí)查閱輸入序列中的部分單詞或片段,因此不再需要在中間向量中存儲(chǔ)所有信息。本文選用長(zhǎng)短期記憶網(wǎng)絡(luò)(long short-term memory,LSTM)——循環(huán)神經(jīng)網(wǎng)絡(luò)的一種變體,作為序列到序列模型的基本神經(jīng)單元,模型示意圖如圖6 所示。
編碼器的輸入對(duì)應(yīng)于代碼片段的向量表示。以編碼器為例,輸入序列是(x1,x2,…,xm),其對(duì)應(yīng)的輸出隱藏狀態(tài)序列則為(h1,h2,…,hm),而當(dāng)前隱藏狀態(tài)ht的更新是根據(jù)當(dāng)前輸入xt以及前一隱藏狀態(tài)ht-1實(shí)現(xiàn),其對(duì)應(yīng)的計(jì)算公式如下:
解碼器則是給定上下文向量Ct以及先前預(yù)測(cè)的輸出序列(y1,y2,…,yt-1)來(lái)預(yù)測(cè)下一個(gè)輸出詞匯yt出現(xiàn)的概率,最終產(chǎn)生輸出序列(y1,y2,…,yn),計(jì)算公式如下:
其中,st是解碼器在t時(shí)刻的隱藏狀態(tài),公式如下:
Ct是根據(jù)編碼層的編碼器的隱藏狀態(tài)作加權(quán),計(jì)算公式如下:
其中,hk為編碼層輸出的隱藏狀態(tài),αtk為對(duì)應(yīng)的權(quán)重,其計(jì)算公式如下:
Fig.6 Seq2Seq model with attention mechanism圖6 結(jié)合注意力機(jī)制的序列到序列模型
DeepCR 通過以下幾個(gè)模塊實(shí)現(xiàn)方法層級(jí)的代碼推薦:
(1)自然語(yǔ)言查詢生成。自然語(yǔ)言查詢生成模型訓(xùn)練的目的即為對(duì)推薦代碼庫(kù)的代碼片段生成相匹配的自然語(yǔ)言查詢。由此,每個(gè)代碼片段也就存在了一個(gè)與之匹配的由模型生成的自然語(yǔ)言查詢。推薦代碼庫(kù)中的元素也就以<代碼片段,生成的查詢>對(duì)的形式存在。
(2)相似度計(jì)算。由于推薦代碼庫(kù)中每個(gè)代碼片段都有了與之對(duì)應(yīng)的生成的查詢,因此用戶查詢與代碼之間的相似度可以轉(zhuǎn)化為計(jì)算生成的查詢與用戶查詢之間的相似度。在計(jì)算相似度之前,結(jié)合較為流行的Stanford POS Tagger(詞性標(biāo)注器)[14]對(duì)生成的查詢和用戶查詢執(zhí)行小寫化、停止詞刪除和詞干提取等操作,對(duì)自然語(yǔ)言查詢執(zhí)行數(shù)據(jù)預(yù)處理操作。在此基礎(chǔ)上,本文采用TF-IDF(term frequencyinverse document frequency)[15]來(lái)進(jìn)行兩者之間相似度的計(jì)算。Lucene 是一套用于全文檢索和搜尋的開源程序庫(kù),由Apache軟件基金會(huì)支持和提供。Lucene提供了一個(gè)簡(jiǎn)單卻強(qiáng)大的API 庫(kù),能夠做全文索引和搜尋。在Java 開發(fā)環(huán)境里L(fēng)ucene 是一個(gè)成熟的免費(fèi)開放源代碼工具,使用Lucene 提供的TF-IDF 算法來(lái)進(jìn)行相似度計(jì)算。
(3)代碼片段推薦。通過上述步驟中相似度計(jì)算結(jié)果,確定相似度得分最高的生成的查詢的列表。由于代碼和生成的查詢之間存在映射關(guān)系,則從推薦代碼庫(kù)中檢索相對(duì)應(yīng)的代碼片段,并將其呈現(xiàn)給用戶,作為代碼片段推薦結(jié)果。
本文的實(shí)驗(yàn)平臺(tái)為Intel i7-6700 3.4 GHz處理器、GeForce GTX 1070 Ti 的GPU 以 及32.0 GB RAM 的臺(tái)式機(jī),搭載Ubuntu 18.04 LTS操作系統(tǒng),使用JDK1.8版本,并使用Eclipse 作為數(shù)據(jù)處理的集成開發(fā)環(huán)境,使用Python3,結(jié)合Tensorflow 框架作為自然語(yǔ)言查詢生成模型訓(xùn)練和預(yù)測(cè)的運(yùn)行環(huán)境。
針對(duì)自然語(yǔ)言查詢生成模型的訓(xùn)練,使用隨機(jī)梯度下降算法來(lái)訓(xùn)練和更新模型參數(shù),LSTM 隱藏狀態(tài)和詞嵌入的維度均設(shè)為512,學(xué)習(xí)速率初始值設(shè)為0.99,對(duì)應(yīng)的影響因子設(shè)為0.8。為避免過擬合,設(shè)置模型參數(shù)dropout的值為0.3。
4.1.1 數(shù)據(jù)準(zhǔn)備
從Stack Overflow 官方的數(shù)據(jù)倉(cāng)庫(kù)下載了原始數(shù)據(jù),解壓后數(shù)據(jù)集的大小超過70 GB,與Java 相關(guān)的問題總數(shù)超過150 萬(wàn)?;?.1 節(jié)所述的數(shù)據(jù)清洗操作,最終獲得了48 003 條有效數(shù)據(jù),每條數(shù)據(jù)中問題(即自然語(yǔ)言查詢)和答案(即代碼片段)一一對(duì)應(yīng)。接著按照9∶1 的比例將數(shù)據(jù)劃分為訓(xùn)練集和測(cè)試集,即訓(xùn)練集包含43 203 條,測(cè)試集包含4 800 條數(shù)據(jù)。按照Iyer 等人[16]的方法,從測(cè)試集中隨機(jī)抽取200 條數(shù)據(jù)用來(lái)評(píng)估代碼片段推薦方法的性能。
4.1.2 評(píng)估指標(biāo)
采用MRR(mean reciprocal rank)[17]和Hit@K[18]來(lái)對(duì)代碼推薦性能進(jìn)行評(píng)估。
對(duì)用來(lái)評(píng)估代碼推薦性能的數(shù)據(jù)集中的每一條數(shù)據(jù)<代碼片段C,查詢Q>對(duì),把C作為有效代碼片段,并從數(shù)據(jù)集中隨機(jī)抽取K個(gè)與Q不匹配的代碼片段作為無(wú)效代碼片段。進(jìn)而,給定查詢Q,計(jì)算代碼片段C在K+1 條候選集中的排序。按照文獻(xiàn)[10,16]中采用的方法將K設(shè)為49。代碼推薦性能則根據(jù)整個(gè)數(shù)據(jù)集D={<C1,Q1>,<C2,Q2>,…,<C|D|,Q|D|>} 上的MRR來(lái)進(jìn)行評(píng)估:
其中,Ranki表示對(duì)于查詢Qi,代碼片段Ci的排序結(jié)果。MRR的值越高,則代碼片段推薦性能越好。
還采用Hit@K來(lái)評(píng)估DeepCR 的代碼片段推薦性能。Hit@K(K=1,3,5,10)的計(jì)算公式如下:
其中,Id表示對(duì)于查詢Qi,代碼片段Ci是否在前K個(gè)推薦結(jié)果中,如果在,Id取值為1,否則為0。
多次重復(fù)執(zhí)行此實(shí)驗(yàn),來(lái)確保實(shí)驗(yàn)結(jié)果的真實(shí)性和可靠性。
4.2.1 基準(zhǔn)實(shí)驗(yàn)
為了檢測(cè)DeepCR 在代碼推薦性能上的有效性,把DeepCR 與傳統(tǒng)的信息檢索方法BM25[19]和TF-IDF進(jìn)行對(duì)比。BM25 和TF-IDF 是兩個(gè)比較具有代表性的計(jì)算文本相似度的算法??紤]到代碼和自然語(yǔ)言查詢之間的語(yǔ)義差,在用傳統(tǒng)的信息檢索方法計(jì)算代碼和自然語(yǔ)言查詢之間的相似度時(shí),對(duì)代碼中的標(biāo)識(shí)符做了駝峰式命名法分詞、小寫化、停止詞去除、詞干提取等操作。
4.2.2 實(shí)驗(yàn)結(jié)果
表2 呈現(xiàn)了所提及到的3 種不同的代碼推薦方法以MRR 為度量指標(biāo)重復(fù)執(zhí)行20 次的實(shí)驗(yàn)結(jié)果。DeepCR 嘗試訓(xùn)練自然語(yǔ)言查詢生成模型為代碼片段生成查詢,以此來(lái)消除代碼和查詢之間的語(yǔ)義差。從表2 可以得知,DeepCR 的代碼推薦性能優(yōu)于TFIDF 算法和BM25 算法。具體而言,DeepCR 的MRR得分相較于TF-IDF 算法提升了0.22,相較于BM25算法提升了0.18,由此可見DeepCR 代碼推薦方法的有效性。
Table 2 MRR of 3 methods for code recommendation表2 3 種不同代碼推薦方法的MRR 得分
表3 呈現(xiàn)了所提及的3 種不同的代碼推薦方法以Hit@K(K=1,3,5,10)為度量指標(biāo)重復(fù)執(zhí)行20 次的實(shí)驗(yàn)結(jié)果的平均值。從表3 可以得知,BM25 的代碼推薦性能略優(yōu)于TF-IDF算法,而DeepCR的Hit@K得分相較于TF-IDF 和BM25 算法均有明顯提升,DeepCR 在Hit@1 的得分達(dá)到了38.25%,相較于TF-IDF 和BM25,分別提升了23.17 個(gè)百分點(diǎn)和19.12 個(gè)百分點(diǎn),由此可見DeepCR 代碼片段推薦方法的有效性。
Table 3 Hit@K of 3 methods for code recommendation表3 3 種不同代碼推薦方法的Hit@K 得分 %
本文針對(duì)的是Java 相關(guān)的代碼片段推薦工作,而沒有拓展到其他編程語(yǔ)言,因此編程語(yǔ)言可能是一個(gè)對(duì)本文方法有效性產(chǎn)生威脅的一個(gè)因素。將來(lái)會(huì)結(jié)合不同編程語(yǔ)言各自的特點(diǎn),將本文的方法擴(kuò)展到其他的語(yǔ)言環(huán)境。
數(shù)據(jù)有效性。所提取的數(shù)據(jù)來(lái)源于Stack Overflow問答網(wǎng)站上的數(shù)據(jù)。由于問答網(wǎng)站對(duì)問題以及回答的開放性和自由性,所收集的數(shù)據(jù)集中存在大量低質(zhì)量的數(shù)據(jù),以至于對(duì)自然語(yǔ)言查詢生成模型的訓(xùn)練造成威脅。鑒于此,對(duì)收集的數(shù)據(jù)做了一系列清洗和篩選操作,以保證數(shù)據(jù)質(zhì)量的可靠性和有效性,盡可能減少存在于最終的數(shù)據(jù)集中的噪音。
本文提出了一種基于序列到序列模型的代碼片段推薦方法DeepCR。該方法結(jié)合程序靜態(tài)分析技術(shù),對(duì)代碼做抽象語(yǔ)法樹解析,提取代碼信息,構(gòu)建訓(xùn)練集;運(yùn)用序列到序列模型,訓(xùn)練自然語(yǔ)言查詢生成模型;根據(jù)訓(xùn)練好的查詢生成模型,對(duì)代碼庫(kù)的代碼生成對(duì)應(yīng)的查詢;通過計(jì)算用戶輸入的自然語(yǔ)言查詢與代碼對(duì)應(yīng)的生成查詢之間的相似度計(jì)算得分,檢索并推薦相關(guān)度高的代碼片段給開發(fā)人員。DeepCR 的MRR 和Hit@K得分均明顯優(yōu)于傳統(tǒng)的信息檢索方法的代碼推薦性能,能夠有效提高代碼推薦效果,為開發(fā)人員提供精準(zhǔn)的代碼片段推薦。