徐少峰,潘文韜,熊 赟,朱揚(yáng)勇
(復(fù)旦大學(xué) 計(jì)算機(jī)科學(xué)技術(shù)學(xué)院 上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室,上海 201203)
代碼注釋是軟件開發(fā)過程中的重要組成部分,軟件工程師可以通過閱讀代碼注釋來回顧歷史代碼構(gòu)建思路或了解其他開源項(xiàng)目的實(shí)現(xiàn)細(xì)節(jié),在學(xué)習(xí)一門新的開發(fā)語言時(shí)也能夠通過閱讀注釋加快學(xué)習(xí)過程。然而,為每行代碼撰寫其對(duì)應(yīng)的注釋既繁瑣又耗時(shí),導(dǎo)致程序員的開發(fā)效率較低。
在早期的注釋生成任務(wù)中,研究人員主要探索各種基于規(guī)則的模型。文獻(xiàn)[1-2]研究了基于Java編程語言的自動(dòng)注釋生成,利用手工編寫代碼和注釋之間的映射規(guī)則,并通過填寫預(yù)定義的句子模板為Java方法生成注釋。但是,基于規(guī)則的方法缺乏可移植性和靈活性,當(dāng)源代碼中出現(xiàn)從未見過的新規(guī)則或使用其他編程語言開發(fā)新項(xiàng)目時(shí),必須手動(dòng)更新規(guī)則表和句子模板。
隨后,有研究人員開始探究基于數(shù)據(jù)的注釋生成方法。文獻(xiàn)[3]從在線問答網(wǎng)站的回答中爬取代碼和對(duì)應(yīng)的自然語言描述,并在生成注釋時(shí)通過匹配相似的代碼段輸出代碼注釋,但是該模型的泛化能力較差。文獻(xiàn)[4]使用改編的基于自動(dòng)文本摘要[5]的文本檢索(TR)技術(shù)為源代碼創(chuàng)建摘要。
近年來,研究者們?cè)谧⑨屔扇蝿?wù)中引入深度神經(jīng)網(wǎng)絡(luò)。在偽代碼生成任務(wù)中,文獻(xiàn)[6]結(jié)合了基于規(guī)則的方法和基于數(shù)據(jù)的方法,其能夠自動(dòng)更新規(guī)則表,并通過N-Gram語言模型生成偽代碼。然而,N-Gram語言模型很難捕捉長句子的特征,從而降低了注釋生成的性能。
為代碼自動(dòng)生成注釋本質(zhì)上是一種翻譯任務(wù),即將源代碼翻譯成自然語言描述。目前,代碼及其對(duì)應(yīng)的注釋數(shù)據(jù)越來越多[7],已有一些研究者將成熟的翻譯模型(編碼器解碼器模型)應(yīng)用于注釋生成任務(wù)中[8-9]。編碼器解碼器模型Seq2seq將源代碼看作源語言,利用循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network,RNN)[10]進(jìn)行編碼和解碼,這種方式僅能獲取源代碼的序列信息,而忽略了代碼中最重要的結(jié)構(gòu)信息[11]。編程語言具有嚴(yán)格的語法規(guī)范,受語法規(guī)范約束編寫的代碼可以轉(zhuǎn)換成相應(yīng)的抽象語法樹,抽象語法樹包含了豐富的語義信息,能夠提高注釋生成的效果。
根據(jù)代碼能夠獲取對(duì)應(yīng)的抽象語法樹,但是現(xiàn)有的編碼器解碼器翻譯模型不支持融入抽象語法樹信息。本文構(gòu)建一種基于結(jié)構(gòu)感知的雙編碼器解碼器模型,同時(shí)對(duì)代碼序列信息和抽象語法樹結(jié)構(gòu)信息進(jìn)行編碼,將兩部分信息融合后再解碼以生成注釋。
圖1展示了一個(gè)Python語句自動(dòng)生成注釋的過程。傳統(tǒng)的翻譯模型只使用序列信息,生成的注釋(圖1中的注釋1)缺少關(guān)鍵要素,不夠精確。本文綜合利用源代碼序列信息和能夠反映代碼結(jié)構(gòu)信息的抽象語法樹來提升注釋生成效果(圖1中的注釋2),從而準(zhǔn)確生成方法名和參數(shù)個(gè)數(shù)。
圖1 基于結(jié)構(gòu)感知的代碼注釋自動(dòng)生成過程
為了融入代碼結(jié)構(gòu)信息,受經(jīng)典編碼器解碼器翻譯模型的啟發(fā),本文提出一種基于結(jié)構(gòu)感知的雙編碼器解碼器模型。雙編碼器包含一個(gè)序列編碼器(用于編碼代碼序列信息)和一個(gè)樹編碼器(用于編碼抽象語法樹信息)。將雙編碼器生成的表示信息相融合,傳入解碼器進(jìn)行解碼,模型即生成相應(yīng)的自然語言注釋。本文基于結(jié)構(gòu)感知的雙編碼器解碼器模型架構(gòu)如圖2所示。
圖2 基于結(jié)構(gòu)感知的雙編碼器解碼器模型
2.1.1 序列編碼器
圖3 序列編碼器結(jié)構(gòu)
2.1.2 樹編碼器
(1)
(2)
(3)
(4)
(5)
(6)
(7)
圖4 樹編碼器結(jié)構(gòu)
至此,本文解碼器就同時(shí)融合了代碼的序列信息和結(jié)構(gòu)信息,從而生成了自然語言注釋。本文解碼器結(jié)構(gòu)如圖5所示。
圖5 解碼器結(jié)構(gòu)
3.1.1 數(shù)據(jù)描述
為有效評(píng)估模型,本文選用來自文獻(xiàn)[6]的高質(zhì)量數(shù)據(jù)集,該數(shù)據(jù)集包含了18 805對(duì)Python源代碼和對(duì)應(yīng)的自然語言注釋。源代碼來源于Django項(xiàng)目(一個(gè)開源的Python網(wǎng)絡(luò)應(yīng)用框架),注釋則由Python專家進(jìn)行標(biāo)注。將數(shù)據(jù)集劃分為訓(xùn)練集和測試集,訓(xùn)練集包含16 000對(duì)源代碼和注釋,用于訓(xùn)練雙編碼器解碼器模型,測試集包含剩下的2 805對(duì)源代碼和注釋,用于評(píng)估模型的性能及注釋生成效果。
3.1.2 數(shù)據(jù)預(yù)處理
由于本文模型融合了代碼抽象語法樹信息,而原數(shù)據(jù)集只有代碼序列,因此需要對(duì)數(shù)據(jù)進(jìn)行相應(yīng)的預(yù)處理。使用Python解釋器自帶的語法分析器AST Parser,將數(shù)據(jù)集中的每行代碼轉(zhuǎn)化成對(duì)應(yīng)的抽象語法樹。除Python語言外,本文模型還可以應(yīng)用在Java和C++等其他編程語言中,只需同樣進(jìn)行相應(yīng)的預(yù)處理即可。為了實(shí)驗(yàn)過程的一致性,本文對(duì)數(shù)據(jù)集中的所有代碼進(jìn)行如下的預(yù)處理操作:
1)使用Python解釋器內(nèi)置的ast模塊提取每一行源代碼的抽象語法樹。
2)對(duì)于每一棵抽象語法樹,抽取節(jié)點(diǎn)的符號(hào)并放入符號(hào)數(shù)組。
3)先為抽象語法樹的所有節(jié)點(diǎn)建立索引,再按照順序?qū)⒎?hào)數(shù)組中每個(gè)節(jié)點(diǎn)的父親節(jié)點(diǎn)的索引放入索引數(shù)組,根節(jié)點(diǎn)的父親節(jié)點(diǎn)的索引為0。
最后,將預(yù)處理后新生成的符號(hào)數(shù)組和索引數(shù)組添加到原有的數(shù)據(jù)集中。其中,原有的源代碼序列作為序列編碼器的輸入,符號(hào)數(shù)組和索引數(shù)組作為樹編碼器的輸入。
3.2.1 對(duì)比算法
本文實(shí)驗(yàn)選取以下5種算法進(jìn)行對(duì)比與分析:
1)PBMT[6,19-20],是一個(gè)統(tǒng)計(jì)機(jī)器翻譯框架,其通過構(gòu)建源語言和目標(biāo)語言之間的詞組對(duì)應(yīng)表來提升翻譯效果。ODA等人將PBMT框架應(yīng)用于偽代碼生成任務(wù)中[6]。
2)Seq2seq w/attention[9,21-22],Seq2seq模型是經(jīng)典的編碼器解碼器模型,常用于機(jī)器翻譯任務(wù),其使用編碼器編碼代碼序列信息,再由解碼器生成對(duì)應(yīng)的自然語言注釋。其中,注意力(attention)機(jī)制將解碼器的隱層狀態(tài)與編碼器的輸出狀態(tài)進(jìn)行對(duì)齊,可以提升注釋生成效果。
3)雙編碼器解碼器模型,即本文模型,其綜合考慮代碼序列信息和抽象語法樹信息。
4)序列編碼器解碼器,是雙編碼器模型的弱化版本,它僅保留CNN序列編碼器,用于學(xué)習(xí)代碼的序列特征。
5)樹編碼器解碼器,是雙編碼器模型的另一個(gè)弱化版本,它僅保留樹編碼器,用于學(xué)習(xí)代碼的抽象語法樹特征。
對(duì)于Seq2seq模型和雙編碼器解碼器模型中的解碼器,本文使用單層LSTM網(wǎng)絡(luò)。通過調(diào)整詞向量的維度、編碼器表示向量的維度以及解碼器隱層節(jié)點(diǎn)的維度來獲取各方法的最優(yōu)結(jié)果。其中,訓(xùn)練輪數(shù)設(shè)定為30,所有實(shí)驗(yàn)都在一臺(tái)配備了GTX1080顯卡的Linux機(jī)器上運(yùn)行。
3.2.2 評(píng)價(jià)指標(biāo)
本文采用BLEU得分[23]作為實(shí)驗(yàn)的評(píng)價(jià)指標(biāo)。BLEU得分通常應(yīng)用于機(jī)器翻譯任務(wù),其將生成的句子與若干真實(shí)的句子進(jìn)行對(duì)比,從而評(píng)估翻譯質(zhì)量。BLEU得分的取值是一個(gè)0~1范圍內(nèi)的實(shí)數(shù),值的大小表示生成句子和真實(shí)句子的相似程度,0表示完全不相似,1表示完全相似。在本文實(shí)驗(yàn)中,將BLEU得分乘以100以便于分析。
3.3.1 模型性能對(duì)比
對(duì)于PBMT模型,由于文獻(xiàn)[6]沒有列出BLEU-1、BLEU-2、BLEU-3三個(gè)指標(biāo)的結(jié)果,因此本文只比較BLEU- 4得分。從表1可以看出,本文基于結(jié)構(gòu)感知的雙編碼器解碼器模型的BLEU得分最高,其相較于PBMT有約1.5倍的性能提升,并且比經(jīng)典的編碼器解碼器Seq2seq模型表現(xiàn)更好。該結(jié)果表明,融合了代碼結(jié)構(gòu)信息的編碼器解碼器模型能夠提高注釋生成效果。
表1 各模型的BLEU得分對(duì)比
3.3.2 編碼器對(duì)比
為了評(píng)估雙編碼器中序列編碼器和樹編碼器對(duì)模型的影響,表2對(duì)不同編碼器情況下所生成注釋的性能進(jìn)行對(duì)比。從表2可以看出,雙編碼器模型的BLEU得分最高,體現(xiàn)出序列信息和結(jié)構(gòu)信息融合的優(yōu)勢(shì)。僅使用序列編碼器或樹編碼器,模型性能比較接近,但是樹編碼器稍優(yōu)于序列編碼器,原因在于樹編碼器能夠捕捉抽象語法樹中包含的更高層次的代碼語義信息。
表2 不同編碼器下的BLEU得分對(duì)比
3.3.3 隱層節(jié)點(diǎn)維度對(duì)比
在本次實(shí)驗(yàn)中,設(shè)置詞向量的維度、雙編碼器的表示向量維度以及解碼器隱層節(jié)點(diǎn)的維度。為方便調(diào)試,將3個(gè)維度設(shè)為相同的值,進(jìn)行4次實(shí)驗(yàn),結(jié)果如表3所示。從表3可以看出,當(dāng)維度為512時(shí),雙編碼器解碼器模型的性能表現(xiàn)最好,這說明更多的維度能夠更好地表達(dá)特征,從而提升模型性能。
表3 不同隱層節(jié)點(diǎn)維度下的模型BLEU得分比較
本文提供如圖6所示的4個(gè)測試集示例,進(jìn)一步說明注釋生成的準(zhǔn)確性和可讀性。圖6中的示例包括賦值語句、條件語句和循環(huán)語句,也包含了函數(shù)調(diào)用。
圖6 雙編碼器解碼器模型注釋生成示例
示例1是一個(gè)普通的賦值語句,本文模型生成的注釋描述了變量help在賦值后所代表的含義。本文同時(shí)描述了變量的類型(string),而該信息存在于抽象語法樹中,這表明融合抽象語法樹信息能夠提高注釋生成的準(zhǔn)確度。示例2是一個(gè)條件判斷語句。在代碼中,“if can_fail”是“if can_fail is true”的縮寫,本文模型利用樹編碼器從抽象語法樹中提取出完整的布爾判斷,從而在注釋中對(duì)布爾判斷進(jìn)行自然語言描述。示例3是一個(gè)函數(shù)調(diào)用語句。本文模型按照順序,首先生成函數(shù)調(diào)用部分注釋,再生成賦值部分注釋。在函數(shù)調(diào)用部分,生成了關(guān)鍵字“function”,包括函數(shù)名、參數(shù)個(gè)數(shù)和參數(shù)名,在賦值部分,描述了將函數(shù)返回結(jié)果賦值給變量“value”。示例4融合了循環(huán)語句和函數(shù)調(diào)用語句。類似于示例3,首先生成函數(shù)調(diào)用部分注釋,再循環(huán)調(diào)用函數(shù)返回結(jié)果。
本文提出一種基于結(jié)構(gòu)感知的雙編碼器解碼器模型,其將序列編碼器和樹編碼器相結(jié)合并融入抽象語法樹信息,從而將源代碼轉(zhuǎn)換為自然語言注釋。在真實(shí)數(shù)據(jù)集上的實(shí)驗(yàn)結(jié)果表明,相對(duì)于PBMT、Seq2seq模型,該模型注釋生成結(jié)果的BLEU得分較高,且具有更高的實(shí)用性和可讀性。本文模型可以通過特定的語法解析器擴(kuò)展到其他編程語言,下一步將結(jié)合序列特征并引入注意力機(jī)制,以提高模型注釋翻譯的準(zhǔn)確性。