国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

一種結(jié)構(gòu)信息增強(qiáng)的代碼修改自動(dòng)轉(zhuǎn)換方法*

2021-05-23 06:11:56曹英魁孫澤宇鄒艷珍
軟件學(xué)報(bào) 2021年4期
關(guān)鍵詞:編碼器示例代碼

曹英魁 ,孫澤宇 ,鄒艷珍 ,謝 冰

1(北京大學(xué) 信息科學(xué)技術(shù)學(xué)院,北京 100871)

2(高可信軟件技術(shù)教育部重點(diǎn)實(shí)驗(yàn)室(北京大學(xué)),北京 100871)

1 引 言

在開發(fā)過程中,開發(fā)人員常常面臨著相似的代碼修改任務(wù),而完成這些任務(wù)不僅需要耗費(fèi)開發(fā)人員大量的時(shí)間和精力,同時(shí),開發(fā)人員在完成重復(fù)的任務(wù)時(shí)又容易引入新的錯(cuò)誤[1,2].得益于不同形式的版本控制系統(tǒng),軟件項(xiàng)目的演化歷史得以完整地記錄下來.在這些演化歷史中,存在著豐富的代碼修改場(chǎng)景和修改方案[3-6].基于已有的相似代碼修改,研究人員開始關(guān)注于相似代碼自動(dòng)轉(zhuǎn)換方法的研究,即:給定一組示例代碼修改,通過對(duì)修改示例的修改方式進(jìn)行抽取和表示,并基于表示結(jié)果來實(shí)現(xiàn)對(duì)相似代碼的自動(dòng)修改.

在早期工作中,代碼修改的自動(dòng)轉(zhuǎn)換多是采用人工特征工程的方式[7-11],即人工地提出規(guī)則對(duì)修改示例中的修改方案的適配條件、修改過程和修改結(jié)果進(jìn)行限定和說明.然而,這些規(guī)則的提出往往基于研究人員對(duì)特定語言的專家知識(shí),并需要耗費(fèi)大量的時(shí)間精力來總結(jié)、提煉.近來,采用基于機(jī)器學(xué)習(xí)的代碼轉(zhuǎn)換方法[12,13]不斷涌現(xiàn)出來.常見的做法是采用端到端的翻譯模式,將待修改的代碼“翻譯”為修改后的代碼.然而,現(xiàn)有的工作尚未對(duì)修改示例的結(jié)構(gòu)信息充分加以使用.一方面,一些現(xiàn)有工作利用翻譯模型,直接將修改前的代碼“翻譯”為修改后的代碼.然而,在缺失修改示例信息的條件下,試圖訓(xùn)練一個(gè)全局的翻譯模型來處理代碼轉(zhuǎn)換任務(wù),無疑是十分困難的.另一方面,相比于自然語言,代碼內(nèi)的信息存在著更為顯著的長(zhǎng)程依賴.如圖1 所示,函數(shù)調(diào)用fis.close()中的變量名fis,與最初聲明的變量名為同一個(gè)變量.現(xiàn)有工作在采用時(shí)序模型對(duì)代碼信息建模時(shí),由于兩處代碼間隔很遠(yuǎn),很難捕獲兩處變量間的依賴關(guān)系.

Fig.1 Long dependency among variable names圖1 變量名間的長(zhǎng)程依賴

針對(duì)上述問題,本文提出了一種利用結(jié)構(gòu)信息增強(qiáng)代碼轉(zhuǎn)換的方法ExpTrans.一方面,給定修改示例,方法將修改前和修改后的代碼解析為抽象語法樹,并尋找其節(jié)點(diǎn)間的對(duì)應(yīng)關(guān)系.基于節(jié)點(diǎn)間的對(duì)應(yīng)關(guān)系,方法采用圖的形式來表示給定的示例代碼修改,以增強(qiáng)模型對(duì)代碼修改中的結(jié)構(gòu)信息的捕獲能力;另一方面,方法通過將圖卷積網(wǎng)絡(luò)和Transformer 架構(gòu)[14]有效地結(jié)合來增強(qiáng)模型對(duì)代碼間的依賴信息,尤其是長(zhǎng)程依賴關(guān)系的捕獲能力.

方法整體上遵循encoder-decoder 的架構(gòu)設(shè)計(jì).其中,編碼器分別對(duì)待修改代碼、修改示例代碼以及中間信息進(jìn)行建模;解碼器依據(jù)編碼器的編碼結(jié)果,預(yù)測(cè)轉(zhuǎn)換后的代碼.為了保證方法產(chǎn)生的代碼的可編譯性,方法在預(yù)測(cè)修改后的代碼時(shí),借鑒了Yin 和Sun 等人的工作[15,16],以預(yù)測(cè)指令序列的方式來產(chǎn)生代碼.所謂指令,形如α→β1β2…,其所代表的含義是在代碼的抽象語法樹中的類型為α的節(jié)點(diǎn)上,插入子節(jié)點(diǎn),類型分別為β1、β2….具體地,在產(chǎn)生代碼的過程中,方法將維持一棵表示中間結(jié)果的抽象語法樹,并基于預(yù)測(cè)的下一條指令,對(duì)當(dāng)前最左的待擴(kuò)展的節(jié)點(diǎn)進(jìn)行擴(kuò)充,直至所有的非葉子節(jié)點(diǎn)被擴(kuò)展完畢,所生成的抽象語法樹所對(duì)應(yīng)的代碼即為轉(zhuǎn)換后的代碼.

為了驗(yàn)證方法的有效性,本文在兩個(gè)數(shù)據(jù)集上進(jìn)行了對(duì)比實(shí)驗(yàn).第1 個(gè)數(shù)據(jù)集是Yin 等人對(duì)外開放的111 724個(gè)C#代碼修改[13].實(shí)驗(yàn)結(jié)果表明,對(duì)比Yin 的工作(基于深度學(xué)習(xí))[13],本文方法在準(zhǔn)確率上有11.8%~30.8%的提升.第2 個(gè)數(shù)據(jù)集是從GitHub 上收集整理的6 組Java 語言典型的相似代碼修改.在該數(shù)據(jù)集上,分別將本文方法、GenPat 方法[17](基于人工規(guī)則)和ARES 方法[10](基于人工規(guī)則)用于自動(dòng)修改這些代碼實(shí)例.實(shí)驗(yàn)結(jié)果表明,在每組數(shù)據(jù)中,均有實(shí)例被ExpTrans 正確修改,并且在一組數(shù)據(jù)上的實(shí)例全部被ExpTrans 正確修改.相比于GenPat 和ARES 方法的結(jié)果,ExpTrans 的結(jié)果在準(zhǔn)確率和正確修改的實(shí)例的數(shù)量上,均有大幅度提升.

本文的貢獻(xiàn)主要表現(xiàn)在:(1) 提出了一種基于圖的代碼修改表示方法.相比采用單詞序列的表示方式,圖結(jié)構(gòu)能夠更加準(zhǔn)確地表示代碼修改過程,增強(qiáng)了方法對(duì)代碼修改中結(jié)構(gòu)信息的捕獲能力.(2) 提出了一種基于Transformer 架構(gòu)的結(jié)構(gòu)信息增強(qiáng)的代碼修改自動(dòng)轉(zhuǎn)換方法.該方法采用特殊的copy 指令顯式地表示代碼間廣泛存在的變量聲明與使用的依賴關(guān)系,增加了模型對(duì)代碼中長(zhǎng)程依賴信息的捕獲能力.(3) 本文在兩個(gè)數(shù)據(jù)集上開展了對(duì)比實(shí)驗(yàn)并將數(shù)據(jù)全部公開(https://github.com/caoyingkui/ExpTrans).相比于現(xiàn)有的機(jī)器學(xué)習(xí)方法,本文方法ExpTrans 在準(zhǔn)確率上提升了11.8%~30.8%.對(duì)比基于人工規(guī)則的方法,在修改實(shí)例的準(zhǔn)確率和正確修改的實(shí)例數(shù)量上均有顯著提升.

本文第2 節(jié)介紹已有的相關(guān)工作.第3 節(jié)介紹基于預(yù)測(cè)指令序列的代碼產(chǎn)生方式,以及本文方法的整體框架.第4 節(jié)介紹本文方法的具體實(shí)現(xiàn)細(xì)節(jié).第5 節(jié)介紹為了驗(yàn)證方法有效性而開展的實(shí)驗(yàn)以及實(shí)驗(yàn)設(shè)置和實(shí)驗(yàn)結(jié)果.最后,第6 節(jié)對(duì)本文工作進(jìn)行總結(jié).

2 相關(guān)工作

如前所述,本文的相關(guān)工作分為兩類:一類是基于人工特征的方法,另一類是基于深度學(xué)習(xí)的代碼修改自動(dòng)轉(zhuǎn)換方法.

2.1 基于人工特征的方法

該類工作的主要思路是:基于人工提煉的規(guī)則,從給定的修改示例中,抽取一個(gè)代碼轉(zhuǎn)換“腳本”,以對(duì)示例中的修改條件、修改模式和修改過程進(jìn)行說明和約束,并可以按照腳本中所約定的方式自動(dòng)地適配到符合條件的代碼區(qū)域并完成代碼轉(zhuǎn)換.在現(xiàn)有的工作中,這種腳本呈現(xiàn)出代碼編輯序列[7]、模板[8]和特定領(lǐng)域語言(DSL)[11]等多種形式.

在Meng 等人提出的SYDIT 中,利用一組編輯操作序列來表示代碼轉(zhuǎn)換的過程[7].序列中的編輯操作共包含4 種類型:增加、刪除、修改和移動(dòng)一個(gè)AST 節(jié)點(diǎn),同時(shí)利用通配符等方式編輯操作中的類型、位置信息以進(jìn)行泛化.例如,!config.inValid()被表示為!v2.m5().按照順序依次執(zhí)行該序列中的操作,即可將代碼修改為修改后的代碼.LASE 是Meng 等人提出的另外一種代碼轉(zhuǎn)換方法,并依舊利用一組編輯操作序列來表示示例代碼中的代碼轉(zhuǎn)換[9].與SYDIT 不同,LASE 是從多個(gè)相似修改示例中抽取代碼轉(zhuǎn)換.

此外,還有一些現(xiàn)有工作采用模板的形式來表示修改示例中的修改模式.如Andersen 等人提出的方法spdiff[8],該方法從修改示例中抽取一個(gè)單詞替換補(bǔ)丁(term replacement patch)用于刻畫示例中的代碼修改方法,并將一組修改示例中最長(zhǎng)的公共子補(bǔ)丁作為該組示例代碼修改中的代碼轉(zhuǎn)換.針對(duì)LASE 在表示代碼修改模式時(shí),無法準(zhǔn)確地識(shí)別代碼語句移動(dòng)等問題,Dotzler 等人提出方法ARSE.ARSE 同樣是一種基于多示例的代碼轉(zhuǎn)換方法[10].然而,與LASE 不同,ARSE 以模板的方式來表示修改示例中的修改模式.Jiang 等人提出的方法GENPAT 是一種從單一修改示例中抽取代碼轉(zhuǎn)換的方法[17].在該方法中,代碼轉(zhuǎn)換最終以樹形結(jié)構(gòu)的形式加以表示.在該結(jié)構(gòu)中,每一個(gè)節(jié)點(diǎn)的信息包括節(jié)點(diǎn)ID 和一組屬性值.同時(shí),GENPAT 的表示結(jié)果中還包含一組操作,其中的每一個(gè)操作為一個(gè)元組〈id,id'〉,id 和id'分別代表了修改前、后代碼的AST 中的一個(gè)節(jié)點(diǎn)并存在對(duì)應(yīng)關(guān)系,即節(jié)點(diǎn)id 發(fā)生修改后變?yōu)楣?jié)點(diǎn)id'.

在Rolim 等人提出的REFAZER 中,定義了一種特殊的DSL(領(lǐng)域特定語言)[11].其主要功能是定義對(duì)代碼AST 的操作,以及對(duì)符合特定修改的AST 的條件及發(fā)生修改的位置和修改類型進(jìn)行限定.因此,該代碼生成任務(wù)的目標(biāo)為:一段基于該DSL 的代碼.該代碼的輸入為一段修改前的代碼,輸出為修改后的一段代碼.

2.2 基于深度學(xué)習(xí)的方法

該類工作的主要思路是:給定待修改的代碼,利用機(jī)器學(xué)習(xí)模型預(yù)測(cè)修改后的代碼.Tufano 等人提出了基于翻譯模型完成代碼轉(zhuǎn)化方法[12].具體地,給定一段待修改的代碼作為模型的輸入,并利用翻譯模型直接預(yù)測(cè)一個(gè)token 序列,作為轉(zhuǎn)換后的代碼.同時(shí),該方法也探索了翻譯模型適合何種類型的代碼轉(zhuǎn)化場(chǎng)景.例如,缺陷修改、代碼重構(gòu).代碼作為一種高度結(jié)構(gòu)化的文本,其功能和可編譯性嚴(yán)格依賴于代碼具體的token 序列和token 間的相互位置關(guān)系.然而,以預(yù)測(cè)token 序列方式進(jìn)行工作的翻譯模型,無法從方法層面上保證模型的輸出結(jié)果是可編譯的.為了克服這一問題,在Yin 等人提出的方法中,以預(yù)測(cè)指令序列的方式來生成代碼,從而能夠保證模型輸出代碼的語法正確性[15].基于此工作機(jī)制,Yin 等人提出一種基于學(xué)習(xí)的代碼轉(zhuǎn)換模型[13].該模型的輸入,除一段待修改的代碼外,同時(shí)輸入一個(gè)修改示例的修改前、后的代碼,即通過學(xué)習(xí)修改示例中的代碼轉(zhuǎn)換方式,來指導(dǎo)待修改的代碼轉(zhuǎn)換過程.

綜上,Yin 等人提出的方法是目前現(xiàn)有工作中與本文最為相關(guān)的方法.在Yin 等人的方法中,利用了LSTM 時(shí)序模型用于處理代碼的文本信息.然而,相比于自然語言,代碼存在更為顯著的長(zhǎng)程依賴關(guān)系,然而有研究表明,時(shí)序模型在處理信息長(zhǎng)程依賴時(shí)效果并不佳[14].因此,克服代碼的信息長(zhǎng)程依賴挑戰(zhàn),是提出本文方法的一個(gè)主要的動(dòng)機(jī).

3 方法框架

為了保證方法生成代碼的可編譯性,本文借鑒了Yin 等人的工作[15],采用以預(yù)測(cè)指令序列的方式來生成代碼,而非單詞序列.同時(shí),方法在代碼指令集合中,增設(shè)了特殊的copy 指令,以顯式地刻畫代碼中變量間的依賴關(guān)系.在介紹方法框架之前,本節(jié)首先詳細(xì)介紹方法中基于預(yù)測(cè)指令序列的代碼產(chǎn)生方式.

代碼指令.在本文中,所謂指令,指的是形如α→β1β2…的產(chǎn)生式.其中,α為非終結(jié)符,βi為終結(jié)符或非終結(jié)符.在代碼的抽象語法樹上,每個(gè)非葉子節(jié)點(diǎn)均對(duì)應(yīng)一條指令α→β1β2…,其中,α為該節(jié)點(diǎn)的語法類型,βi為其子節(jié)點(diǎn)的語法類型(或單詞).

指令集的獲取.給定一棵代碼抽象語法樹,假定其中某個(gè)節(jié)點(diǎn)v的語法類型為α,節(jié)點(diǎn)v共有n個(gè)子節(jié)點(diǎn),且其語法類型(或單詞)分別為β1,…,βn,則依據(jù)節(jié)點(diǎn)v,可獲取指令r:α→β1…βn.按照由上到下、從左至右的順序依次遍歷抽象語法樹的所有非葉子節(jié)點(diǎn),即可獲取相應(yīng)的代碼指令序列.同時(shí),通過遍歷已有數(shù)據(jù)集中代碼的抽象語法樹,即可獲取指令集{r1,…,rN}.

在代碼中,由于開發(fā)人員可以采用任何合法的變量名、字符串等,導(dǎo)致代碼中存在比自然語言更為顯著的OOV(out of vocabulary)問題.因此方法對(duì)出現(xiàn)在指令中的單詞進(jìn)行限制和預(yù)處理,從而避免所獲取的指令集的規(guī)模過于龐大,不利于方法進(jìn)行建模.具體地,在獲取指令之前,本文統(tǒng)計(jì)了在給定數(shù)據(jù)集中的代碼中出現(xiàn)的所有變量名、字符串、數(shù)字常量、字符常量及其出現(xiàn)的次數(shù),只有當(dāng)出現(xiàn)次數(shù)超過閾值的單詞,才會(huì)將其保留.當(dāng)代碼中某個(gè)單詞出現(xiàn)次數(shù)低于閾值時(shí),本文方法將采用形如type_id 的形式進(jìn)行替換,其中,type 表示該單詞對(duì)應(yīng)的類型,即變量名、字符串、數(shù)字常量或字符常量,id 為其編號(hào).通過這樣的處理,能夠保證所獲取的指令規(guī)模在有限的范圍內(nèi).

此外,本文在指令集中額外增設(shè)了一類特殊的copy 指令.在代碼中,變量聲明和使用之間隱含著明確的依賴關(guān)系.本文利用增設(shè)的copy 指令,顯示地標(biāo)記變量聲明與使用間的這種依賴關(guān)系.如在圖1 中,語句“fis.close();”中的變量名fis 是由語句“FileInputStream fis=new FileInputStream(new File(dir));”聲明得來.因此,方法在解析fis.close();時(shí),采用copy 指令,以標(biāo)記該變量名fis由復(fù)制之前定義的fis而得來.采用copy 指令主要有兩方面的優(yōu)勢(shì):一方面,顯式地標(biāo)記出變量間依賴關(guān)系的方式,有利于增強(qiáng)后續(xù)模型對(duì)變量間的依賴關(guān)系的捕獲能力.另一方面,copy 指令的復(fù)制變量的范圍,是代碼已定義的變量名集合.因此,方法在后續(xù)的預(yù)測(cè)過程中,所面臨的預(yù)測(cè)空間即為代碼已定義的變量名集合.相比于整個(gè)單詞空間,copy 指令縮小了在預(yù)測(cè)變量名時(shí)的預(yù)測(cè)空間,從而能夠提升方法的準(zhǔn)確率.

預(yù)測(cè)指令序列的代碼產(chǎn)生方式.圖2 展示了基于指令序列[r1,…,r7]生成代碼“fis.close();”的過程.給定圖2(a)的指令序列,方法首先初始一個(gè)只有根節(jié)點(diǎn)的抽象語法樹,其類型為Statement.基于第1 條指令,即Statement→ExpressionStatement,方法為根節(jié)點(diǎn)插入一個(gè)類型為ExpressionStatement 的子節(jié)點(diǎn).如圖2(b)所示,當(dāng)完成前3 條指令后,當(dāng)前的抽象語法樹最下層將含有2 個(gè)待擴(kuò)展的節(jié)點(diǎn),分別是Expression 和SimpleName.由于“.”“(”和“)”已經(jīng)是終結(jié)符,因此在后續(xù)過程中,方法將不會(huì)對(duì)它們進(jìn)行擴(kuò)展.此時(shí),方法將利用下一條指令r4來擴(kuò)展最左邊的待擴(kuò)展的節(jié)點(diǎn)(非終結(jié)符),即左側(cè)的Expression 節(jié)點(diǎn).按照上述方式,最終將產(chǎn)生一棵完整的抽象語法樹.按照從左至右的方式,獲取所有的葉子節(jié)點(diǎn)內(nèi)容,即為所生成的代碼.

Fig.2 The rule sequences and generation of fis.close();圖2 語句fis.close();對(duì)應(yīng)的指令序列和生成過程

基于上述方式,本文將以預(yù)測(cè)指令序列的方式,實(shí)現(xiàn)代碼修改的自動(dòng)轉(zhuǎn)換.方法的輸入為x和xΔ→yΔ,其中,x為待修改的代碼,xΔ→yΔ表示一個(gè)修改示例(xΔ和yΔ分別為修改前、后的代碼).最終的輸出為表示代碼轉(zhuǎn)換后的結(jié)果.在生成代碼的過程中,方法將維持一棵表示中間結(jié)果的抽象語法樹.通過預(yù)測(cè)下一條指令,并基于該指令對(duì)當(dāng)前的抽象語法樹最左邊待擴(kuò)展的節(jié)點(diǎn)進(jìn)行擴(kuò)展,直至生成最終的代碼.本文將生成代碼的概率形式化地表示為

其中,r1,…,ri為產(chǎn)生的指令序列.

圖3 展示了本文方法ExpTrans 的整體框架.

Fig.3 The neural network of ExpTrans圖3 ExpTrans 的網(wǎng)絡(luò)結(jié)構(gòu)

方法整體上遵從encoder-decoder 的架構(gòu)模式,主要包含4 個(gè)模塊:代碼差異編碼器、代碼編碼器、AST 編碼器和解碼器.這4 個(gè)子模塊均采用了Transformer 的多塊(block)架構(gòu)[14],即每一個(gè)子模塊均由多個(gè)結(jié)構(gòu)相同的塊組成.例如,在代碼差異編碼器中,每一個(gè)塊均由相同的網(wǎng)絡(luò)結(jié)構(gòu)組成,包含Graph-conv 層、Self-attention 層、Char-gating 層和Rule-gating 層,并且按照前一塊的輸出為后一塊的輸入的方式進(jìn)行連接.在每一個(gè)塊的內(nèi)部結(jié)構(gòu)中,采用殘差連接的方式[18],將相鄰的網(wǎng)絡(luò)層(如Graph-conv 層與Self-attention 層之間)進(jìn)行連接.需要注意的是,圖3 只展示了每個(gè)模塊的一個(gè)塊.4 個(gè)模塊的主要功能如下.

代碼差異編碼器.方法利用該模塊,對(duì)輸入的修改示例xΔ→yΔ的信息進(jìn)行建模.

代碼編碼器.方法利用該模塊,對(duì)輸入的待修改代碼x的信息進(jìn)行建模.

AST 編碼器.在生成代碼時(shí),方法需要維持一棵表示中間狀態(tài)的抽象語法樹.方法利用該模塊對(duì)該抽象語法樹的信息進(jìn)行建模,以期在預(yù)測(cè)代碼指令時(shí),能夠提供語法樹的全局信息.

解碼器.在生成代碼時(shí),解碼器將依據(jù)已生成的抽象語法樹中待擴(kuò)展的節(jié)點(diǎn),預(yù)測(cè)一下指令.具體地,方法以待擴(kuò)展的節(jié)點(diǎn)信息作為查詢,輸入解碼器.基于輸入的查詢,解碼器采用注意力機(jī)制來結(jié)合代碼編碼器、代碼差異編碼器和AST 編碼器的建模信息.然后根據(jù)解碼器的輸出,方法結(jié)合softmax 和指針網(wǎng)絡(luò)[19]以預(yù)測(cè)下一條指令.

4 本文方法

給定待修改代碼x以及修改示例xΔ→yΔ,方法的目標(biāo)是實(shí)現(xiàn)代碼轉(zhuǎn)換其中,為轉(zhuǎn)換后的代碼.本節(jié)將介紹ExpTrans 中不同模塊的具體實(shí)現(xiàn).

4.1 代碼差異編碼器

方法利用代碼差異編碼器,對(duì)修改示例xΔ→yΔ的修改方式、代碼結(jié)構(gòu)信息進(jìn)行建模.基于xΔ→yΔ,分別將修改前、后代碼xΔ和yΔ表示為抽象語法樹的形式.按照由上到下、從左至右的順序,獲取兩棵語法樹所對(duì)應(yīng)的節(jié)點(diǎn)序列然后將兩個(gè)序列合并成同一個(gè)序列并統(tǒng)一地記為其中,L為方法預(yù)設(shè)的最大長(zhǎng)度,前L(ori)個(gè)節(jié)點(diǎn)為修改前的節(jié)點(diǎn)序列,隨后L(mod)個(gè)節(jié)點(diǎn)為修改后的節(jié)點(diǎn).當(dāng)修改前、后的節(jié)點(diǎn)序列長(zhǎng)度小于L時(shí),方法利用特殊的占位符號(hào)〈EMPTY〉進(jìn)行擴(kuò)充.代碼差異編碼器將修改示例xΔ→yΔ建模為節(jié)點(diǎn)序列[v1,…,vL],代碼差異編碼器的輸出為其中,為節(jié)點(diǎn)vi的表示向量,H表示空間緯度(在方法中設(shè)定為128).

4.1.1 代碼差異的圖表示

修改示例xΔ→yΔ,以修改前、后代碼分離的形式存在,采用單獨(dú)對(duì)xΔ和yΔ進(jìn)行建模的方式,將損失修改前、后代碼間的關(guān)聯(lián)關(guān)系及結(jié)構(gòu)信息.為了更加精確地表示xΔ→yΔ中所蘊(yùn)含的代碼修改方式,本文將xΔ→yΔ表示為一個(gè)統(tǒng)一的圖G=〈V,E〉,其中,節(jié)點(diǎn)集合V包含xΔ和yΔ所對(duì)應(yīng)的抽象語法樹節(jié)點(diǎn)的集合,E為節(jié)點(diǎn)間的連邊集合.

為了建立xΔ和yΔ節(jié)點(diǎn)間的連邊關(guān)系,本文利用工具GumTree[20]來獲取代碼修改前、后節(jié)點(diǎn)的對(duì)應(yīng)關(guān)系.GumTree 是一種基于代碼抽象語法樹的代碼差異抽取工具.在工作時(shí),GumTree 首先獲取修改前、后的代碼xΔ和yΔ所對(duì)應(yīng)的抽象語法樹treex和treey.然后按照自底向上的順序,對(duì)比treex和treey節(jié)點(diǎn)間的類型信息和文本信息,并據(jù)此計(jì)算節(jié)點(diǎn)的相似度,從而獲取treex和treey節(jié)點(diǎn)間的最佳匹配關(guān)系,并據(jù)此能夠準(zhǔn)確地推導(dǎo)出代碼的修改過程.

在本文中,當(dāng)節(jié)點(diǎn)vj與節(jié)點(diǎn)vi間存在連邊,且由vj指向vi時(shí),我們稱節(jié)點(diǎn)vj為節(jié)點(diǎn)vi的父節(jié)點(diǎn).進(jìn)一步地,vj的父節(jié)點(diǎn)為vi的祖父節(jié)點(diǎn).結(jié)合工具GumTree 的結(jié)果,當(dāng)節(jié)點(diǎn)vi和vj滿足下列3 種關(guān)系之一時(shí),則建立一條由vj指向vi的連邊,并將該邊加入集合E中.

(1) 節(jié)點(diǎn)vi和vj均為treex上的節(jié)點(diǎn),且vj為vi的父節(jié)點(diǎn).

(2) 節(jié)點(diǎn)vi和vj均為treey上的節(jié)點(diǎn),且vj為vi的父節(jié)點(diǎn).

(3) 節(jié)點(diǎn)vi為treey上的節(jié)點(diǎn),vj為treex上的節(jié)點(diǎn),并且在GumTree 的結(jié)果中,vi和vj存在對(duì)應(yīng)關(guān)系.

基于集合E,可以構(gòu)造節(jié)點(diǎn)間的鄰接矩陣M,當(dāng)vj為vi的父節(jié)點(diǎn)時(shí),M[i][j]=1;否則,M[i][j]=0.例如,現(xiàn)將代碼“fis.close();”修改為“inputFile.close();”,圖4 給出了采用圖結(jié)構(gòu)表示本處代碼修改的過程和結(jié)果.如圖4(a)所示,首先將修改前、后的代碼表示為抽象語法樹的形式(這里,省略了‘.’‘(’等符號(hào)以方便展示).基于獲取的抽象語法樹,利用GumTree 來獲取修改前、后代碼抽象語法樹節(jié)點(diǎn)間的對(duì)應(yīng)關(guān)系.在圖4(a)中,節(jié)點(diǎn)ni與in'表示兩個(gè)節(jié)點(diǎn)存在對(duì)應(yīng)關(guān)系.最終,該處代碼修改的圖結(jié)構(gòu)被表示為圖4(b)所示的鄰接矩陣.

Fig.4 The illustration of construction of code change adjacent matrix圖4 代碼差異鄰接矩陣建立過程示意圖

4.1.2 編碼信息的抽取

在計(jì)算Y(diff)之前,方法首先獲取每個(gè)節(jié)點(diǎn)不同方面的初始信息.

節(jié)點(diǎn)的單詞信息.在代碼的抽象語法樹中,每個(gè)非葉子節(jié)點(diǎn)均有相應(yīng)的語法類型,每個(gè)葉子節(jié)點(diǎn)則對(duì)應(yīng)代碼中的一個(gè)單詞.基于圖G的節(jié)點(diǎn)序列V,可以得到單詞序列[w1,…,wL].當(dāng)vi為非葉子節(jié)點(diǎn)時(shí),wi為節(jié)點(diǎn)對(duì)應(yīng)的語法類型;當(dāng)vi為葉子節(jié)點(diǎn)時(shí),wi為節(jié)點(diǎn)對(duì)應(yīng)的單詞.通過embedding 方式,為每個(gè)單詞wi初始一個(gè)表示向量節(jié)點(diǎn)序列[v1,…,vL]對(duì)應(yīng)的單詞信息為

節(jié)點(diǎn)單詞字符信息.有些語義相似的單詞存在相同的字符序列.例如,同一詞根派生的不同類型的單詞.而上述將單詞作為整體進(jìn)行信息編碼的方式,將丟棄單詞的字符信息.為了在節(jié)點(diǎn)表示向量中引入單詞的字符信息,本文借鑒了Sun 等人對(duì)單詞的字符信息進(jìn)行編碼的方法[16].具體地,基于獲取的單詞序列,將每個(gè)單詞wi切分為字符序列[ci,1,…,ci,L′],其中,L′為模型預(yù)設(shè)的單詞最大長(zhǎng)度.類似地,利用embedding 方式為每個(gè)字符ci,j初始一個(gè)表示向量并利用全連接層,按照公式(2)的方式,計(jì)算單詞wi的字符表示向量

節(jié)點(diǎn)指令信息.在抽象語法樹中,每個(gè)非葉子節(jié)點(diǎn)vi均對(duì)應(yīng)一條指令其中,α(i)為節(jié)點(diǎn)vi對(duì)應(yīng)的語法類型,所有β(i)均為節(jié)點(diǎn)vi的子節(jié)點(diǎn)的語法類型或單詞.采用類似公式(2)的方式,方法為每條指令ri:計(jì)算表示向量此外,葉子節(jié)點(diǎn)所對(duì)應(yīng)的指令,統(tǒng)一采用特殊指令〈EMPTY_RULE〉進(jìn)行替代.據(jù)此,節(jié)點(diǎn)序列[v1,…,vL]對(duì)應(yīng)的節(jié)點(diǎn)指令信息為

位置信息.在Transformer 的架構(gòu)中,方法將時(shí)序數(shù)據(jù)(例如代碼的節(jié)點(diǎn)序列的表示向量)打包成一個(gè)向量矩陣,以使得模型能夠并行訓(xùn)練.但這樣的處理方式會(huì)丟失數(shù)據(jù)的位置(時(shí)序)信息.因此,需要額外添加數(shù)據(jù)的位置信息.在本文中,采用Dehghani 的工作做法[21],利用下列公式人為地構(gòu)造每個(gè)節(jié)點(diǎn)的位置信息:

4.1.3 代碼差異編碼器的網(wǎng)絡(luò)結(jié)構(gòu)

Graph-conv 層.在將xΔ→yΔ表示為節(jié)點(diǎn)序列后,模型將無法捕獲節(jié)點(diǎn)間的結(jié)構(gòu)信息.因此本文方法利用一層圖卷積層來實(shí)現(xiàn)在每個(gè)節(jié)點(diǎn)的表示向量中引入代碼的結(jié)構(gòu)信息.

基于構(gòu)造的圖G=〈V,E〉和鄰接矩陣M,假定當(dāng)前節(jié)點(diǎn)的表示向量矩陣F=[f1;…;fL],方法將按照公式(5)計(jì)算每個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)的表示向量:

在卷積時(shí),方法預(yù)設(shè)了卷積窗口K,并按照如下方式進(jìn)行卷積:

其中,W(conv)為卷積參數(shù),f為Rule 激活函數(shù).

該層的輸入為節(jié)點(diǎn)的表示向量和節(jié)點(diǎn)的位置信息之和,即I=[xb,1+pb,1;…;xb,L+pb,L],該層的輸出為

其中,當(dāng)b=1 時(shí)(即為第1 個(gè)塊),xb,i表示對(duì)應(yīng)節(jié)點(diǎn)的單詞信息(即);而對(duì)于其他塊,xb,i則為前一塊的輸出.

Self-attention 層.該層采用了Transformer 中的self-attention 注意力機(jī)制[14],以捕獲代碼間的長(zhǎng)程依賴關(guān)系.

在Transformer 中,注意力機(jī)制被表述為將查詢Q、鍵值對(duì)K和V映射到輸出的過程,其中,Q、K和V均為向量矩陣.輸出為對(duì)V中分量加權(quán)求和的結(jié)果.而V中每個(gè)value 相應(yīng)的權(quán)重將依據(jù)Q與相應(yīng)的關(guān)鍵字K的匹配程度給出,其加權(quán)求和的結(jié)果為

其中,dk為縮放因子.同時(shí),Transformer 采用multi-head attention 機(jī)制,使得模型能夠從不同的表示空間的角度來注意到不同位置的信息,即:

特別地,當(dāng)Q、K和V相同時(shí),即形如Multihead(Q,Q,Q)的形式,則被稱為self-attention.該層實(shí)現(xiàn)了selfattention 注意力機(jī)制,該層的輸出為

Char-gating 層.在該層之前,節(jié)點(diǎn)序列被表示為向量矩陣Y(self),為了在節(jié)點(diǎn)vi的表示向量中引入字符信息方法在該層中采用門機(jī)制,使得節(jié)點(diǎn)的表示向量矩陣更新為Y(self)和Y(char)的加權(quán)和.

門(gating)機(jī)制的目的在于對(duì)不同表示空間的表示結(jié)果進(jìn)行綜合,形成最終的表示向量.給定表示向量f1和f2,門機(jī)制的目標(biāo)是將f1和f2進(jìn)行加權(quán)求和,獲取綜合f1和f2后的表示向量f(gate).其中,f1和f2的權(quán)重按照下列公式進(jìn)行計(jì)算:

為了保證Y(self)依舊為加權(quán)后的結(jié)果的信息主體,方法首先基于Y(self)通過線性變換的方式獲取控制向量C(self).然后按照下列公式來計(jì)算Y(self)和Y(char)的加權(quán)結(jié)果作為該層的輸出:

Rule-gating 層.類似地,該層同樣采用門機(jī)制,使得在節(jié)點(diǎn)的表示向量中融合指令字符信息Y(rule),該層的輸出為

其中,C(char)為通過Y(char-gate)線性變換得來的控制向量.

最終,代碼差異編碼器的輸出為Y(diff).

4.2 代碼編碼器

代碼編碼器的作用是為了實(shí)現(xiàn)對(duì)待修改代碼x的信息建模.如圖3 所示,代碼編碼器和差異編碼器的網(wǎng)絡(luò)結(jié)構(gòu)是一致的,只是在獲取該層的輸入方式上有所不同.

給定待修改代碼x,通過將其解析為抽象語法樹treex,并按照由上到下、從左至右的方式,獲取節(jié)點(diǎn)序列V(x)=基于treex,構(gòu)造圖G(x)=〈V,E〉,其中,V=V(x),E為treex中節(jié)點(diǎn)的連邊集合.此外,方法同樣為G(x)構(gòu)造其節(jié)點(diǎn)間的鄰接矩陣M(x).

代碼編碼器采用與代碼差異編碼器中相同的數(shù)據(jù)預(yù)處理方式來獲取節(jié)點(diǎn)序列V(x)對(duì)應(yīng)的單詞信息、節(jié)點(diǎn)單詞字符信息、節(jié)點(diǎn)指令信息和位置信息.然后,信息依次經(jīng)過Graph-conv 層、Self-attention 層、Char-gating層和Rule-gating 層,并記代碼編碼器的最終輸出為Y(x).

4.3 AST編碼器

本文方法在產(chǎn)生代碼的過程中,需要維持一個(gè)由已產(chǎn)生指令序列生成的抽象語法樹.方法以當(dāng)前抽象語法樹的最左非葉子節(jié)點(diǎn)(待擴(kuò)展節(jié)點(diǎn))為查詢,預(yù)測(cè)下一條指令,并利用預(yù)測(cè)的指令對(duì)該節(jié)點(diǎn)進(jìn)行擴(kuò)充.因此,需要對(duì)產(chǎn)生代碼過程中的抽象語法樹的信息進(jìn)行編碼,以在預(yù)測(cè)下一條指令時(shí),能夠?yàn)槟P吞峁┏橄笳Z法樹的全局視圖.在AST 編碼器中,方法利用已產(chǎn)生的指令序列[r1,…,rp]來表示已產(chǎn)生的抽象語法樹.AST 編碼器的目標(biāo)則是:將指令序列最終表示為其中,為指令ri的表示向量,P為方法預(yù)設(shè)的最大指令序列長(zhǎng)度.

4.3.1 編碼信息的提取

指令的初始向量.利用embedding 方式,為每條指令r初始一條表示向量因此給定指令序列[r1,…,rR],通過查表的方式,獲取指令序列的初始表示向量矩陣

指令的符號(hào)信息.在形如α→β1β2…的指令表示形式中,符號(hào)α和βi對(duì)于表達(dá)指令的語義信息十分重要.例如,當(dāng)待擴(kuò)展的節(jié)點(diǎn)的類型為Statement 時(shí),則下一條預(yù)測(cè)的指令的非終結(jié)符α必須同樣為Statement,以保證代碼的合法性.然而,上述將指令作為整體進(jìn)行編碼的方式,將忽略指令涉及的符號(hào)信息.為了在指令的表示向量中引入指令符號(hào)序列的信息,本文采用了Sun 的工作方式[16].具體地,給定指令ri:α→β1β2…,α及所有的βj均為標(biāo)準(zhǔn)單詞集中的一個(gè)單詞.通過查表的方式,可以獲取每個(gè)單詞對(duì)應(yīng)的表示向量α(w)或類似公式(2),利用全連接層,以α(w)和所有作為輸入,并將輸出記為最后,再次利用全連接層實(shí)現(xiàn)如下的編碼方式:

其中,W(rule)為網(wǎng)絡(luò)參數(shù).指令序列[r1,...,rp]對(duì)應(yīng)的符號(hào)信息為

位置信息.本文方法同樣利用公式(3)和公式(4)來計(jì)算指令序列的位置信息其中,為第b個(gè)塊中的第i條指令的位置信息.

4.3.2 AST 編碼器的網(wǎng)絡(luò)結(jié)構(gòu)

Self-attention 層.方法利用一層Self-attention 層來捕獲指令間的依賴關(guān)系.該層結(jié)構(gòu)和代碼差異編碼器中的Self-attention 層的結(jié)構(gòu)是一致的.該層輸入為指令表示向量與位置信息之和,即該層的輸出為

當(dāng)b=1 時(shí)(即為第1 塊),rb,i表示指令ri的初始向量(即);而在其他塊中,rb,i為前一塊的輸出.

Rule-gating 層.如上文所述,指令的符號(hào)信息對(duì)于表示指令的語義十分重要,因此,本文方法同樣利用門機(jī)制來實(shí)現(xiàn)對(duì)指令符號(hào)信息的獲取.與之前的gating 層相似,首先基于R(self)獲取控制變量矩陣C(r-self),然后按照下列公式進(jìn)行計(jì)算:

Code-attention 層.代碼轉(zhuǎn)換需要依據(jù)待修改代碼x進(jìn)行轉(zhuǎn)換,因此在后續(xù)的代碼指令預(yù)測(cè)過程中,模型需要獲取待修改代碼的信息.本文方法利用該層來實(shí)現(xiàn)在指令序列的編碼結(jié)果中,引入待修改代碼x的信息(Y(x)).該層的輸出為

Diff-attention 層.針對(duì)代碼x進(jìn)行修改時(shí),所修改的方式依賴于給定修改示例xΔ→yΔ.因此,在后續(xù)的代碼指令預(yù)測(cè)過程中,模型需要修改示例的差異信息.本文方法利用該層來實(shí)現(xiàn)在指令序列的編碼結(jié)果中,引入xΔ→yΔ的信息(Y(diff)).該層的輸出為

Graph-conv 層.由已生成的代碼指令可以構(gòu)造出局部的抽象語法樹,而預(yù)測(cè)的指令均會(huì)對(duì)應(yīng)特定的節(jié)點(diǎn),因此,指令(節(jié)點(diǎn))間存在有意義的結(jié)構(gòu)關(guān)系.然而,以指令序列的形式表示已生成的抽象語法樹,將會(huì)遺失指令間的結(jié)構(gòu)信息.因此,本文方法采用一層圖卷積層,利用代碼的抽象語法樹的結(jié)構(gòu)信息來增強(qiáng)指令的編碼信息.

依據(jù)指令對(duì)應(yīng)節(jié)點(diǎn)的鄰接關(guān)系,可以獲得一個(gè)指令間的鄰接矩陣Mp*p.當(dāng)M[i][j]=1 時(shí),表示指令rj(對(duì)應(yīng)的節(jié)點(diǎn))為指令ri(對(duì)應(yīng)的節(jié)點(diǎn))的父節(jié)點(diǎn).基于此,該層的輸出為

最終AST 編碼器的輸出為R(ast).

4.4 解碼器

解碼器以當(dāng)前待擴(kuò)展的非葉子節(jié)點(diǎn)為輸入,即Q(d)=[q1;…;qR],其中,qi為每個(gè)待擴(kuò)展節(jié)點(diǎn)的表示向量.由于解碼器仍然遵循多塊的設(shè)計(jì),因此在第1 塊中,qi為節(jié)點(diǎn)對(duì)應(yīng)的語法類型或單詞信息;而在其他塊中,qi則為上一個(gè)塊的輸出.解碼器的目標(biāo)是生成查詢矩陣其中,為第i個(gè)節(jié)點(diǎn)相應(yīng)的查詢向量,方法將依據(jù)預(yù)測(cè)第i條指令.解碼器首先利用Self-attention 層來獲取待擴(kuò)展節(jié)點(diǎn)間的依賴關(guān)系.然后,數(shù)據(jù)流將依次經(jīng)過AST-attention 層、Code-attention 層和Diff-attention 層,分別用于獲取抽象語法樹、待修改代碼和修改示例的信息.具體地:

Self-attention 層.在上述查詢矩陣中,每個(gè)查詢分量qi均代表抽象語法樹中的一個(gè)待擴(kuò)展的節(jié)點(diǎn)的信息.在代碼的抽象語法樹中,不同節(jié)點(diǎn)彼此間存在不同程度的依賴關(guān)系,因此,我們利用一層Self-attention 層來捕獲查詢間(即節(jié)點(diǎn)間)的依賴關(guān)系,該層的輸出為

Ast-attention 層.本文方法利用該層實(shí)現(xiàn)利用已產(chǎn)生的抽象語法樹的信息(即Y(ast))增強(qiáng)查詢信息.在該層中,Q=D(self),K=Y(ast),V=Y(ast),輸出為

Code-attention 層.本文方法利用該層實(shí)現(xiàn)待修改代碼x的信息(即Y(x))增強(qiáng)查詢信息.在該層中,Q=D(ast),K=Y(x),V=Y(xt).輸出為

Diff-attention 層.本文方法利用該層實(shí)現(xiàn)利用修改示例xΔ→yΔ的信息(即Y(diff))增強(qiáng)查詢信息.在該層中,Q=D(x),K=Y(diff),V=Y(diff),輸出為

最終,解碼器的輸出為D(query).

4.5 指令預(yù)測(cè)

在預(yù)測(cè)下一條指令時(shí),模型的預(yù)測(cè)范圍將來自于兩種類型的指令.首先,在我們處理數(shù)據(jù)時(shí),獲取了標(biāo)準(zhǔn)數(shù)據(jù)指令集R={r1,r2,…,rN},該指令集能夠滿足正常的代碼生成需要.然而,在代碼中變量名定義和使用存在依賴關(guān)系.為了捕獲這種依賴關(guān)系,方法增設(shè)了指令copy(n),表示復(fù)制已產(chǎn)生指令中第n條指令中的變量名.在預(yù)測(cè)第i條指令時(shí),除了計(jì)算指令rj的概率p(rj)以外,方法還將依據(jù)指針網(wǎng)絡(luò)計(jì)算p(copy(t))的概率.因此,最終的第i條指令預(yù)測(cè)結(jié)果需要從指令rj和copy(t)中進(jìn)行選擇.本文將采用下列公式所示的門機(jī)制來對(duì)兩種類型的指令進(jìn)行篩選:

其中,g表示當(dāng)前預(yù)測(cè)指令屬于R的概率,亦即指令為copy 的概率為1-g.最終預(yù)測(cè)的指令為op=argmaxopp(op).

其中,本文將按照公式(27),計(jì)算下一條指令為rj的概率p(rj):

在預(yù)測(cè)第i條指令時(shí),copy 指令所復(fù)制的變量名來自于前i-1 條指令中聲明的變量,因此,本文方法將按照下列公式來計(jì)算p(copy(t)):

其中,1≤t≤i–1,為已產(chǎn)生的第t條指令的表示向量,W(query)和W(rule)為網(wǎng)絡(luò)參數(shù).

5 方法驗(yàn)證

本文開展了兩個(gè)實(shí)驗(yàn),分別將本文方法與現(xiàn)有的基于深度學(xué)習(xí)的方法以及基于人工規(guī)則的方法進(jìn)行對(duì)比,以驗(yàn)證方法的有效性.

5.1 實(shí)驗(yàn)1

該實(shí)驗(yàn)通過與現(xiàn)有的基于深度學(xué)習(xí)的方法進(jìn)行對(duì)比,以驗(yàn)證本文方法的有效性.

(1) 數(shù)據(jù)集.該實(shí)驗(yàn)在Yin 等人的數(shù)據(jù)集[13]上來驗(yàn)證本文方法的有效性.在Yin 等人的工作中,他們從Github上收集了54 個(gè)C#開源項(xiàng)目,然后從這些軟件項(xiàng)目的提交歷史中抽取并篩選出111 724 處C#代碼修改,其中,91372/10176/10176 條數(shù)據(jù)分別用于訓(xùn)練/驗(yàn)證/測(cè)試.在該數(shù)據(jù)集中,數(shù)據(jù)示例均可表示為〈xi,yi〉的形式,其中,xi為修改前的代碼,yi為修改后的代碼.依據(jù)實(shí)例〈xi,yi〉,將其轉(zhuǎn)換為數(shù)據(jù)〈xi,xi→yi,yi〉的形式,并按照模型輸入要求進(jìn)行預(yù)處理.具體地,首先將xi和yi解析為抽象語法樹的形式,并按照由上至下、從左到右的順序,獲取節(jié)點(diǎn)序列此外,依據(jù)yi對(duì)應(yīng)的抽象語法樹,獲取指令序列[r1,…,rR].其中,修改前的代碼節(jié)點(diǎn)序列作為代碼編碼器的輸入,長(zhǎng)度為L(zhǎng)(ori);將修改前、后代碼的節(jié)點(diǎn)序列進(jìn)行拼接,作為代碼差異編碼器的輸入,長(zhǎng)度為L(zhǎng)(ori)+L(mod);指令序列作為模型預(yù)測(cè)目標(biāo),長(zhǎng)度為P.表1 列出了該數(shù)據(jù)集中數(shù)據(jù)的4 種長(zhǎng)度的分布情況.如表中第2 行所示,92.2%的實(shí)例的長(zhǎng)度L(ori)小于100,7.7%的實(shí)例的長(zhǎng)度L(ori)處于101~200 之間,0.1%的實(shí)例的長(zhǎng)度L(ori)處于201~300 之間,最大長(zhǎng)度為239.此外,本文對(duì)預(yù)處理后的數(shù)據(jù)中所包含的單詞進(jìn)行統(tǒng)計(jì),共發(fā)現(xiàn)2 931 個(gè)獨(dú)有單詞,其中最大字符長(zhǎng)度為70,且80.3%的單詞的字符長(zhǎng)度少于20 個(gè).

(2) 對(duì)比方法.Yin 等人的工作是目前與本文工作最為相關(guān)的最新工作[13].在該工作中,作者通過表示示例代碼中的修改方式,并用于指導(dǎo)代碼轉(zhuǎn)換.在該工作中,作者還嘗試?yán)貌煌姆绞絹肀硎敬a和修改示例,并驗(yàn)證不同模型的組合的方法效果.

(3) 參數(shù)設(shè)置.為了獲取較優(yōu)的Transformer 架構(gòu)的塊數(shù)設(shè)置,方法分別將塊數(shù)設(shè)為4、6 和8,且實(shí)驗(yàn)結(jié)果表明,當(dāng)塊數(shù)為6 時(shí),模型效果最佳(具體結(jié)果見表3).因此,方法最終將代碼差異編碼器、代碼編碼器、AST 編碼器和解碼器的塊數(shù)(即N1/N2/N3/N4)設(shè)為6.此外,如表1 所示,由于所有的代碼長(zhǎng)度都小于300,且超過98%的代碼差異長(zhǎng)度小于300,因此代碼差異編碼器和代碼編碼器的最大輸入長(zhǎng)度L被設(shè)定為300.同時(shí),所有數(shù)據(jù)實(shí)例的指令長(zhǎng)度均小于200,且最大長(zhǎng)度為185,因此,模型允許最大的指令長(zhǎng)度P被設(shè)定為200.此外,還有超過80%的單詞的字符≤20,且最大長(zhǎng)度為70,因此,方法將模型允許單詞的最大長(zhǎng)度L'設(shè)定為20,意在保證該值能夠覆蓋到大多數(shù)單詞的同時(shí),避免引入過多的占位符而影響模型效果.

(4) 實(shí)驗(yàn)過程.在與Yin 等人的方法進(jìn)行對(duì)比時(shí),本文沿用了他們的實(shí)驗(yàn)設(shè)置,并利用其數(shù)據(jù)集的訓(xùn)練集和驗(yàn)證集來訓(xùn)練本文方法.然后,利用測(cè)試集來測(cè)試本文方法,即方法是否能夠?qū)⑿薷那暗拇axi正確地轉(zhuǎn)換為yi.

(5) 評(píng)估標(biāo)準(zhǔn).本文主要采用準(zhǔn)確率來量化方法的效果.給定一條數(shù)據(jù)〈x,xΔ→yΔ,y〉,其中,x為修改前的代碼,xΔ→yΔ為修改示例,y為修改后的代碼.若方法的預(yù)測(cè)結(jié)果與y完全相同,則稱x被正確地修改.本文將方法的準(zhǔn)確率定義為

(6) 實(shí)驗(yàn)結(jié)果.表2 展示了與Yin 等人方法對(duì)比后的實(shí)驗(yàn)結(jié)果.從實(shí)驗(yàn)結(jié)果來看,較之Yin 等人提出的不同的模型,本文方法在準(zhǔn)確率上提升了11.8%~30.8%.在Yin 等人的工作方法中,他們采用了兩個(gè)不同的子模塊分別用于編碼待修改代碼的信息和修改示例的信息.同時(shí),他們還嘗試了兩種不同思路以對(duì)信息進(jìn)行建模,即基于單詞序列的方式和基于圖結(jié)構(gòu)的方式,并探索了信息建模方式的不同組合的實(shí)際效果.僅從Yin 等人的工作結(jié)果來看,基于單詞序列模型(如Seq2Seq-Seq)的結(jié)果要比基于圖結(jié)構(gòu)模型(如Graph2Tree-Graph)的結(jié)果更優(yōu).導(dǎo)致這種結(jié)果的原因在于實(shí)驗(yàn)數(shù)據(jù)的特殊性.由于模型輸入的修改示例包含了待修改代碼的修改結(jié)果信息,反而更有利于基于單詞序列的模型預(yù)測(cè)修改結(jié)果.

反觀本文方法ExpTrans 的結(jié)果,若單獨(dú)與Yin 工作中的基于圖結(jié)構(gòu)的模型進(jìn)行對(duì)比,ExpTrans 在準(zhǔn)確率上提升了23.4%,這表明,ExpTrans 采用圖的形式來表示修改示例,并結(jié)合卷積神經(jīng)網(wǎng)絡(luò)的方式,能夠有效增強(qiáng)模型對(duì)代碼的結(jié)構(gòu)信息的捕獲能力,從而使得方法的準(zhǔn)確率有了大幅度的提升.與Yin 等人基于單詞序列的方法進(jìn)行對(duì)比,ExpTrans 同樣具有至少11.8%的提升.這些實(shí)驗(yàn)結(jié)果表明,本文方法是有效的.

此外,在模型ExpTrans 中,Transformer 架構(gòu)的塊數(shù)、copy 指令和模型的不同模塊對(duì)代碼轉(zhuǎn)換效果均有一定程度的影響.因此,基于ExpTrans,本文通過修改模型的架構(gòu)或參數(shù)設(shè)置,產(chǎn)生了不同的變體,以此探究上述因素在ExpTrans 轉(zhuǎn)換代碼過程中的有效性.表3 列出這些變體的具體設(shè)置及實(shí)驗(yàn)結(jié)果.

首先,為了尋求模型中Transformer 架構(gòu)層數(shù)較優(yōu)的設(shè)置,本文借鑒了Vaswani 等人的工作[14],先后將模型的層數(shù)設(shè)置為4、6 和8,具體的實(shí)驗(yàn)結(jié)果如表3 中(A)行所示.從結(jié)果可以看出,當(dāng)層數(shù)設(shè)置為4 時(shí),方法的準(zhǔn)確率為67.37%,較之層數(shù)為6 時(shí),下降4.08%.當(dāng)層數(shù)設(shè)置為8 時(shí),準(zhǔn)確率為64.37%.鑒于上述結(jié)果,最終將ExpTrans中Transformer 的層數(shù)設(shè)置為6 層.

同時(shí),為了驗(yàn)證copy 指令的有效性,本文再次將ExpTrans 應(yīng)用于Yin 等人提供的數(shù)據(jù)集上.所不同的是,在處理該數(shù)據(jù)集時(shí),取消了copy 指令.具體的實(shí)驗(yàn)結(jié)果如表3 中(B)行所示.如結(jié)果顯示,當(dāng)指令集中不含copy 指令時(shí),模型的準(zhǔn)確下降為60.12%,該結(jié)果表明,增設(shè)的copy 指令能夠有效地提升模型對(duì)代碼間長(zhǎng)程依賴的捕獲能力,并提升方法的準(zhǔn)確性.

最后,本文通過分別刪除模型中的代碼差異編碼器以及代碼差異編碼器和代碼編碼器中的圖卷積層,來驗(yàn)證它們的必要性.具體結(jié)果如表3 中(C)行所示.當(dāng)刪除代碼差異編碼器時(shí),方法的準(zhǔn)確率驟降為6.36%.該實(shí)驗(yàn)結(jié)果充分說明,在代碼自動(dòng)轉(zhuǎn)換時(shí),給定一個(gè)或一組實(shí)例修改的必要性,同時(shí)也說明了ExpTrans 中的代碼差異編碼器的有效性.另外,當(dāng)分別刪除代碼差異編碼器和代碼編碼器中圖卷積層后,模型的準(zhǔn)確率分別下降為68.01%和65.69%.在取消圖卷積層的情況下,模型實(shí)際處理輸入數(shù)據(jù)的效果等同于處理線性的單詞序列,失去了代碼自身的結(jié)構(gòu)信息.這些結(jié)果表明,本文方法采用圖卷積的方式能夠有效地捕獲代碼的結(jié)構(gòu)信息,并增強(qiáng)模型的轉(zhuǎn)換能力,提升模型效果.

Table 1 Data length distribution in the first experiment表1 實(shí)驗(yàn)1 中數(shù)據(jù)長(zhǎng)度分布

Table 2 The comprative results with the work of Yin,et al表2 與Yin 等人方法的對(duì)比實(shí)驗(yàn)結(jié)果

Table 3 Performances of different variations on ExpTrans表3 ExpTrans 的不同變體及實(shí)驗(yàn)結(jié)果

5.2 實(shí)驗(yàn)2

該實(shí)驗(yàn)通過與現(xiàn)有的基于人工規(guī)則的方法進(jìn)行對(duì)比,來驗(yàn)證本文方法的有效性.

1) 數(shù)據(jù)集.由于所對(duì)比的基于人工規(guī)則的方法是針對(duì)Java 語言的,因此我們又收集了Java 語言的代碼修改數(shù)據(jù)集.在收集該數(shù)據(jù)之前,我們查閱了Java 語言在不同版本上新增的功能,選出其中5 種并總結(jié)了可能導(dǎo)致的修改模式.表4 列出了所選出的Java 功能和相應(yīng)的修改模式.基于每種功能,構(gòu)造相應(yīng)的查詢語句,并在Github上搜索相關(guān)的commit.依據(jù)搜索結(jié)果,人工地為每個(gè)查詢篩選出10 個(gè)具有相應(yīng)修改模式的代碼修改,即為相似代碼修改.

2) 對(duì)比方法.在本實(shí)驗(yàn)中,對(duì)比了兩種基于人工特征的代碼轉(zhuǎn)換方法GenPat 和ARES.

(1) GenPat[17].該方法是一種基于單示例的代碼轉(zhuǎn)換方法,其通過將給定的單個(gè)修改示例解析為抽象語法樹,對(duì)抽象語法樹上的特定節(jié)點(diǎn)的屬性進(jìn)行了泛化或限制,并標(biāo)注抽象語法樹的節(jié)點(diǎn)的變化過程.在代碼轉(zhuǎn)換時(shí),GenPat 將根據(jù)抽取的抽象語法樹進(jìn)行代碼匹配,并利用標(biāo)注的節(jié)點(diǎn)的變化過程對(duì)代碼進(jìn)行轉(zhuǎn)換.

(2) ARES[10].該方法是一種基于多示例的代碼轉(zhuǎn)換方法,其利用模板來表示修改示例中的修改模式.在模板中保留了修改示例中的共有部分,而采用通配符的形式對(duì)不同的部分進(jìn)行泛化.在代碼轉(zhuǎn)換時(shí),ARES 將依據(jù)抽取的模版對(duì)代碼進(jìn)行匹配,并利用模版所刻畫的修改結(jié)果模式,對(duì)匹配成功的代碼進(jìn)行修改.

Table 4 The corresponding query sentences and change patterns of different Java features表4 不同Java 功能對(duì)應(yīng)的查詢語句和修改模式

3) 實(shí)驗(yàn)過程.在該實(shí)驗(yàn)中,本文按照搜集的6 組相似代碼修改分別進(jìn)行實(shí)驗(yàn).具體地,給定一組相似修改集合P={p1,…,p10},其中,代碼修改pi=〈xi,yi〉,xi為修改前的代碼,yi為修改后的代碼.為了訓(xùn)練本文方法,我們基于集合P,進(jìn)行了如下方式的構(gòu)造:

其中,1≤i,j≤10,pi,j所代表的含義是利用pj的修改模式對(duì)代碼xi進(jìn)行修改,并且按照如下方式將P'分為:

訓(xùn)練集:{pi,j},1≤i≤10,1≤j≤8;

驗(yàn)證集:{pi,j},1≤i≤10,j=9;

測(cè)試集:{pi,j},1≤i≤10,j=10.

在實(shí)驗(yàn)時(shí),分別利用3 種方法對(duì)測(cè)試集中的實(shí)例進(jìn)行修改.由于ARES 是一種基于多示例的代碼轉(zhuǎn)換方法,因此,我們將P作為一組相似代碼變更提供給ARES,以供ARES 抽取所需的代碼修改模式.

參數(shù)設(shè)置.在該實(shí)驗(yàn)中,本文方法中的模型參數(shù)沿用了實(shí)驗(yàn)1 中的參數(shù)設(shè)置.

實(shí)驗(yàn)結(jié)果.我們將方法的輸出結(jié)果分為4 種.

○ 表示方法無法從給定的修改示例中抽取出統(tǒng)一的修改模式,導(dǎo)致方法無輸出.

表5 中展示了方法在6 組數(shù)據(jù)上的對(duì)比實(shí)驗(yàn)結(jié)果.結(jié)果表明,ExpTrans 要明顯優(yōu)于其他兩種對(duì)比方法.ExpTrans 在所有組別上,均有正確的修改示例,尤其是在第2 組數(shù)據(jù)上,ExpTrans 能夠?qū)⑷康拇a實(shí)例修改成功.我們?nèi)斯z查了ExpTrans 修改錯(cuò)誤的實(shí)例,發(fā)現(xiàn)這些錯(cuò)誤實(shí)例主要發(fā)生在預(yù)測(cè)同一個(gè)函數(shù)的多個(gè)參數(shù)時(shí).其可能的原因是,ExpTrans 在預(yù)測(cè)函數(shù)的參數(shù)時(shí),缺乏參數(shù)的序列位置信息,從而導(dǎo)致錯(cuò)誤的預(yù)測(cè)結(jié)果.例如,一個(gè)預(yù)期結(jié)果為byteBufferReadCheck(in,buf,11);,而ExpTrans 的預(yù)測(cè)結(jié)果為byteBufferReadCheck(in,11,11);,其中,11為錯(cuò)誤預(yù)測(cè)的參數(shù).

進(jìn)一步地,我們檢查了GenPat 和ARES 的輸出結(jié)果,以探究導(dǎo)致兩種方法結(jié)果不理想的原因.

方法GenPat 失效的原因在于:(1) 方法依賴于待修改的代碼與修改示例代碼的結(jié)構(gòu)和語法類型的相似性.例如,“return new Double(0.0);”→“return new Double.valueOf(0.0);”中的修改方式,由于語法類型不同,導(dǎo)致無法用于修改代碼“val=new Double(0.0);”.而在本實(shí)驗(yàn)所獲取的數(shù)據(jù)中,無法保證相似的代碼修改具有相似的結(jié)構(gòu)和語法特征.因此,導(dǎo)致GenPat 從示例中抽取的修改模式無法適配于待修改的代碼(類型).(2) GenPat 需要利用代碼中變量的定義信息.然而,由于獲取代碼修改時(shí),只抽取了發(fā)生修改的代碼片段,因而無法保證抽取的代碼修改中同時(shí)包含所有涉及的變量的定義.由于GenPat 無法獲取足夠的信息,因此在將抽取的模式匹配待修改的代碼時(shí),產(chǎn)生錯(cuò)誤匹配,從而導(dǎo)致錯(cuò)誤修改(類型?).

Table 5 The comprative results with GenPat and ARES表5 ExpTrans 方法與GenPat 和ARES 方法的對(duì)比實(shí)驗(yàn)結(jié)果

由于ARES 方法是一種基于多示例的代碼轉(zhuǎn)換方法,因此,當(dāng)給定一組相似代碼修改示例時(shí),ARES 需要對(duì)修改示例中的不同部分進(jìn)行泛化,以使其表示的修改模式能夠擬合給定的修改示例.然而,當(dāng)給定的修改示例的修改語義相似但結(jié)構(gòu)不同時(shí),容易導(dǎo)致方法無法抽取出統(tǒng)一的修改模式.例如,在實(shí)驗(yàn)中我們發(fā)現(xiàn),ARES 無法從組別2、4、6 的數(shù)據(jù)中抽取統(tǒng)一的修改模式,因此也無法完成對(duì)相應(yīng)代碼的修改(類型○).此外,還有一些錯(cuò)誤實(shí)例是因?yàn)锳RES 在抽取的模式中,保留了修改示例所特有的變量名.因此,在將抽取的模式適配到待修改的代碼時(shí),所修改的結(jié)果中引入了這些變量名,導(dǎo)致修改失敗(類型?).

5.3 討 論

方法適用范圍.在本文方法中,我們針對(duì)輸入的待修改代碼x和修改示例xΔ→yΔ,預(yù)設(shè)了最大長(zhǎng)度.當(dāng)代碼長(zhǎng)度超過預(yù)設(shè)長(zhǎng)度時(shí),超過預(yù)設(shè)長(zhǎng)度的代碼內(nèi)容將被截取.這在一定程度上影響了方法能夠使用的代碼修改場(chǎng)景.但在一些頻繁發(fā)生的相似修改任務(wù)中,例如API 版本遷移,所修改的代碼往往是局部的、簡(jiǎn)短的,因此,方法預(yù)設(shè)最大的長(zhǎng)度并不會(huì)嚴(yán)重制約本文方法的實(shí)用性.盡管如此,在未來的工作中,我們?nèi)孕鑷L試提出不同的網(wǎng)絡(luò)模型以降低修改代碼長(zhǎng)度對(duì)方法的影響.

數(shù)據(jù)規(guī)模.在收集Java 數(shù)據(jù)時(shí),依賴于人工對(duì)搜索結(jié)果的篩選,這限制了本文收集數(shù)據(jù)的規(guī)模和效率,也一定程度地制約了挖掘方法更大的潛力.在未來的工作中,我們將嘗試?yán)米詣?dòng)化的方式來大規(guī)模地收集數(shù)據(jù),盡量在降低人力代價(jià)的情況下,提升方法的效果.

6 總結(jié)

在本文中,我們提出了一種基于深度學(xué)習(xí)的代碼轉(zhuǎn)換方法.通過采用圖形式來表示修改示例,并結(jié)合卷積網(wǎng)絡(luò)和Transformer架構(gòu),增加了方法捕獲代碼結(jié)構(gòu)信息的能力.實(shí)驗(yàn)結(jié)果表明,我們的方法比現(xiàn)有的基于深度學(xué)習(xí)和基于人工規(guī)則的方法,其效果有著較為明顯的提升.針對(duì)可能影響方法有效性的因素,我們將在未來的工作中,通過提出優(yōu)化模型和自動(dòng)化的數(shù)據(jù)收集方法,來降低這些因素對(duì)方法的影響.

猜你喜歡
編碼器示例代碼
大還是小
2019年高考上海卷作文示例
常見單位符號(hào)大小寫混淆示例
山東冶金(2019年5期)2019-11-16 09:09:22
“全等三角形”錯(cuò)解示例
創(chuàng)世代碼
創(chuàng)世代碼
創(chuàng)世代碼
創(chuàng)世代碼
基于FPGA的同步機(jī)軸角編碼器
基于PRBS檢測(cè)的8B/IOB編碼器設(shè)計(jì)
凤庆县| 桃江县| 印江| 尚义县| 塘沽区| 鹤山市| 敖汉旗| 郁南县| 昔阳县| 剑阁县| 时尚| 尤溪县| 惠东县| 锦屏县| 潞西市| 曲阳县| 吴江市| 莒南县| 天长市| 永春县| 田林县| 上犹县| 黑山县| 三江| 隆化县| 青神县| 涞水县| 广宁县| 绍兴县| 庆阳市| 隆安县| 广河县| 肥城市| 二连浩特市| 勃利县| 伊宁市| 灌云县| 周口市| 长沙县| 南阳市| 福安市|