謝春麗,梁 瑤,王 霞
江蘇師范大學 計算機科學與技術(shù)學院,江蘇 徐州 221116
GitHub、Stack Overflow等平臺存在著大量的開源項目和源代碼片段,開發(fā)人員如果能有效地將功能相似的代碼引入到自己的項目中,可以大幅度提高軟件開發(fā)效率。那么,如何在海量源碼中快速而準確地找到滿足功能和性能需求的源碼成為軟件工程領(lǐng)域關(guān)注的主要任務(wù)。除此以外,代碼克隆檢測、故障定位、代碼注釋、代碼摘要生成、命名推薦等問題也是該領(lǐng)域的研究熱點。這幾個任務(wù)都有一個相同的關(guān)鍵問題,即需要對源代碼進行一定的預(yù)處理,從中抽取特征進行源代碼表征。如圖1所示,源代碼表征是解決各類問題的第一步,在此基礎(chǔ)上設(shè)計各類算法,從而完成代碼克隆檢測、故障定位、摘要生成或其他任務(wù)。源代碼的表征方式?jīng)Q定了對源代碼特征抽取的程度,進而影響后續(xù)任務(wù)所能檢測的精度。
圖1 基于深度學習的代碼表征框架Fig.1 Code representation framework based on deep learning
根據(jù)對源代碼信息的利用程度,代碼表征方法可以分為基于文本、詞匯、語法、語義四個層面:基于文本的表征方式不經(jīng)任何修飾直接把源代碼看作文本/字符;基于詞匯的表征方式首先對源代碼詞法分析,然后把源代碼符號序列化;基于語法的表征方式往往從源代碼的語法樹或者抽象語法樹抽取代碼語法信息;基于語義的表征方式則進一步從源代碼的控制流和數(shù)據(jù)流等信息中提取隱含的語義信息。這幾種不同層次的表征方式各有自己的特征與優(yōu)缺點,基于文本的表征最直接、最簡單,基于詞匯的表征相比基于文本的方式具有一定的抽象能力,基于語法的以及基于語義的表征抽象程度更高,但需要把源代碼轉(zhuǎn)換為樹型或者圖型結(jié)構(gòu)等額外的預(yù)處理技術(shù)。根據(jù)源代碼表征的技術(shù),可以分為三大類:傳統(tǒng)方法、基于機器學習的方法和基于深度學習的方法。傳統(tǒng)方法手工提取源碼中操作符、操作數(shù)、代碼行數(shù)、函數(shù)、數(shù)據(jù)類型、常量等特征[1],利用統(tǒng)計學原理表征源碼。由于,源碼和自然語言有諸多相似之處,隨后,Hindle等人提出了代碼的自然性假設(shè),為源代碼表征提供了新的思路,大量的機器學習算法,例如N-Gram,被運用到代碼表征中,但此時機器學習方法仍然需要從源代碼中手工提取代碼特征[2]。近年來,由于深度學習在自然語言中的出色表現(xiàn),逐漸有學者開始把循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network,RNN)、卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Networks,CNN)、圖神經(jīng)網(wǎng)絡(luò)(Graph Neural Networks,GNN)等模型引入到源代碼表征中,試圖挖掘隱藏在源代碼中的深層次復(fù)雜特征,進一步提高代碼表征的能力[3]。
目前尚無工作單純對源代碼表征方式的研究進展進行梳理和歸納的研究工作。鑒于此,本文擬針對當前源代碼的表征方式的研究進展,進行歸納和總結(jié),討論當前該領(lǐng)域存在的關(guān)鍵問題、解決思路與研究發(fā)展趨勢。
為分析源代碼表征技術(shù)的研究現(xiàn)狀,本文選用ACM、IEEEXplore、SpingerLink電子文獻數(shù)據(jù)庫、中國知網(wǎng)數(shù)據(jù)庫以及百度學術(shù)搜索對公開發(fā)布的期刊論文、會議論文進行搜索,檢索的關(guān)鍵字包括code representations、code similarity、code clone等,檢索范圍包括主題、關(guān)鍵詞、篇名,從中檢索在代碼表征方法中提出新模型、新方法的文獻,檢索時間從2019年開始。同時對中國計算機學會推薦的軟件工程/系統(tǒng)軟件/程序語言領(lǐng)域中的TSE、ASE、TOSEM等國際學術(shù)期刊和FSE/ESEC、ICSE、ASE、MSR、SANER等國際學術(shù)會議進行檢索,另外,有部分相關(guān)文獻可能出現(xiàn)在人工智能領(lǐng)域,所以對人工智能領(lǐng)域的頂級會議AAAI、NIPS、ICLR等也進行了相關(guān)檢索。并在閱讀文獻的過程中,把文獻中提供的經(jīng)典算法的參考文獻也作為研究對象。經(jīng)過檢索和篩選,去除一些綜述文獻,本文共對54篇文獻進行了研究,圖2顯示了這54篇文獻的期刊或會議出版情況。
圖2 相關(guān)論文發(fā)表情況Fig.2 Statistics of papers published
如圖3所示,對源代碼特征的抽取,根據(jù)粒度大小,可分為詞匯級、語句級、函數(shù)級。詞匯是表達源碼的最小粒度,但是很難表達詞和詞之間的關(guān)系,語句級介于詞匯級和函數(shù)級之間,能結(jié)合上下文表達源碼,函數(shù)級的抽象層次比前兩個要高,相對而言能更好地表達語義信息,但是復(fù)雜度提高,除此之外,還有文件級、項目級的表達,在此不再描述。
圖3 代碼表征分類Fig.3 Code representation classification
(1)詞匯級:源代碼本身就是詞匯序列,或者經(jīng)過詞法分析,轉(zhuǎn)換為標識符、關(guān)鍵字、數(shù)值、字符串、注釋、特殊符號和運算符等各類標識符(Token)組成的序列。采用Token表達源代碼可以去除空白、制表符、注釋等無關(guān)信息,同時還可以消除由變量命名不同所導(dǎo)致的不同語義[4]。著名的CCFinder、CP-Miner等克隆檢測工具都是基于Token級的,可以很好地檢測完全相同的代碼對以及參數(shù)化后的代碼對克隆問題[5-6]。Nguyen等人在代碼的Token序列上利用概率統(tǒng)計模型,預(yù)測下一個可能的Token,完成代碼詞匯級的補全[7]。Dam等人對代碼的Token構(gòu)建了端對端的長短期記憶神經(jīng)網(wǎng)絡(luò)(Long-Short Term Memory,LSTM)模型,用于建模軟件及其開發(fā)過程,解決代碼推薦和預(yù)測等任務(wù)[8]。
(2)語句級:代碼是由各種不同類型的語句構(gòu)成的,語句是代碼片段的基本構(gòu)成,代碼的執(zhí)行是按照語句逐條進行的。但是不同的程序語言一行代碼能夠表達的內(nèi)容是不一致,有可能是一個簡單的運算,也可能是一個算法。因此語句級的劃分上,主要采用抽象語法樹(Abstract Syntax Tree,AST)、數(shù)據(jù)流圖(Data Flow Graph,DFG)等不依賴具體語言的表達方式。Ben-Nun等人提出了Inst2Vec模型,首先把源代碼片段轉(zhuǎn)成一種中間語言,并進行預(yù)處理,去除了注釋等無關(guān)信息,將變量和常量用統(tǒng)一的符號替換,同時提出了一套生成語句上下文的方法,結(jié)合上下文關(guān)系將代碼語句映射到向量空間,使得具有相同上下文的代碼語句具有相似的含義[9],該方法充分考慮了語句的數(shù)據(jù)依賴關(guān)系和執(zhí)行特性。由于代碼的AST一般規(guī)模都比較大,代碼表征過程中很容易產(chǎn)生梯度消失問題,Zhang等人提出了一種針對語句級的方法,將一個代碼片段的AST分割成多個語句級的子樹,產(chǎn)生語句級向量,和完整的AST相比,語句級嵌入在樹的大小和語法特征抽取有一個較好的折中,有效緩解了梯度消失問題[10]。
(3)函數(shù)級:直接對函數(shù)進行表征的研究很少,大部分方法都是把函數(shù)分解為Token序列,對Token向量進行一定的加權(quán)組合來表達函數(shù)。Mou等人轉(zhuǎn)換成函數(shù)的AST,對AST中結(jié)點,采用自下向上的方法,通過孩子結(jié)點的加權(quán)組合學習父結(jié)點的表達,最頂層根結(jié)點的表征則是該函數(shù)的表征向量[11]。DeFreez等人提出了Fun2Vec方法,解決代碼的路徑爆炸問題,該方法采用隨機游走算法,隨機選擇部分執(zhí)行路徑,捕獲程序的層級結(jié)構(gòu),每條執(zhí)行路徑轉(zhuǎn)換為一個標簽序列,借助Word2Vec方法,把標簽映射為連續(xù)實值向量,并通過神經(jīng)網(wǎng)絡(luò)訓練函數(shù)的嵌入向量[12]。
如圖2所示,根據(jù)源代碼的抽象層次不同,代碼表征有文本級、結(jié)構(gòu)級、語義級、功能級四個層次。層次越高,抽象程度越高,能夠提取的信息就越多。
(1)文本級:自從自然語言模型出現(xiàn)之后,任意文本都可以轉(zhuǎn)為詞序列,并映射為詞向量,源代碼的詞匯級表征分為兩種,一種是文本形式的詞匯,即源代碼不經(jīng)過詞法解析直接分解為詞序列,利用Word2Vec把各個詞轉(zhuǎn)換成向量,向量的加權(quán)平均用來表達文本[13]。另一種常見形式是先對源碼詞法解析,得到一個詞匯序列,也叫Token序列,并過濾掉無用的字符、空格、注釋等。Token序列提高了代碼的抽象表示,該方法通常對具有較小更改的代碼段,例如代碼間格式、間距變化或重命名等的檢測能力更強,更具有魯棒性,廣泛用于源代碼相似性測量,并用于CCFinder、CP-Miner、SourceCC等克隆檢測工具中[5-6,14],這種方法的缺點在于沒有考慮代碼行的順序,忽略了代碼中的結(jié)構(gòu)信息。
(2)語法級:也稱為結(jié)構(gòu)級,如果對代碼只修改了變量名稱、數(shù)據(jù)類型和注釋信息,改變書寫代碼風格、增加或刪除一些無關(guān)緊要的語句,而不改變程序執(zhí)行結(jié)構(gòu),那么這兩段代碼從結(jié)構(gòu)上是一樣的,則表征的結(jié)果也是一樣的?;谡Z法樹的代碼表征是典型的結(jié)構(gòu)級表征,通過語法解析將源程序轉(zhuǎn)換為解析樹或AST,AST可以避免格式化和詞匯差異問題,而關(guān)注兩個程序之間的結(jié)構(gòu)信息。然而,基于樹的表征具有較高的計算復(fù)雜度,兩個具有n個結(jié)點的AST相似性比較的時間復(fù)雜度為O(n3)[15]。Baxter等人把基于樹的代碼表征應(yīng)用到克隆檢測任務(wù),把源代碼轉(zhuǎn)成帶注釋的解析樹,然后將子樹分組到桶中,僅在同一個桶中的子樹通過樹匹配算法相互比較[16]。Jiang等人把AST子樹中相關(guān)結(jié)點出現(xiàn)的次數(shù)作為特征值,抓取AST的結(jié)構(gòu)信息,映射為特征向量,采用局部敏感散列(Locality-Sensitive Hashing,LSH)來聚類相似的向量,增加了優(yōu)化機制降低時間復(fù)雜度[1]。為了更準確地抽取AST的結(jié)構(gòu)信息,Mou等人構(gòu)建基于樹型結(jié)構(gòu)的卷積神經(jīng)網(wǎng)絡(luò)模型,用孩子結(jié)點的加權(quán)組合學習父結(jié)點的表達,提高了語法信息的抽取能力[11]。
(3)語義級:代碼不僅具有靜態(tài)結(jié)構(gòu)信息,同時還是可執(zhí)行的,結(jié)構(gòu)相同的代碼,執(zhí)行路徑可能不同,代碼語義也不同?;趫D的方法是主要的語義級表征,通過程序的數(shù)據(jù)流圖、控制流圖(Control Flow Graph,CFG)、程序依賴圖(Program Dependence Graph,PDG)等方式來提供比簡單語法相似性更精確的信息。例如,程序依賴圖中結(jié)點代表表達式和語句,邊表示控制依賴關(guān)系和數(shù)據(jù)依賴關(guān)系,代碼相似問題就變成了同構(gòu)子圖的問題。Komondoor和Horwitz提出了一種基于程序依賴圖的方法,該方法能計算程序兩個版本之間的語法和語義相似,并使用程序切片技術(shù)找到同構(gòu)子圖[17]。其后也有研究者陸續(xù)提出基于圖的檢測方法[18],但子圖同構(gòu)問題的計算開銷非常大,可擴展性差,因此國內(nèi)外相關(guān)研究較少。隨著圖嵌入技術(shù)的出現(xiàn),語義級表征的問題逐漸成為新的研究熱點。Alon等人提出了Code2Vec模型,把AST中的路徑上下文作為神經(jīng)網(wǎng)絡(luò)模型的嵌入層,源代碼表征為路徑上下文向量表征的加權(quán)組合,該模型可以提高函數(shù)名推薦的精度[19]。
(4)功能級:有些代碼片段即使語法和語義不同,但是功能是相同的,DeFreez等人提出Func2Vec方法,其基本思想是首先抽取源碼的控制流,把控制流圖中的路徑上的結(jié)點加上標簽,這些標簽包含指令類標簽、結(jié)構(gòu)體類標簽、函數(shù)標簽、錯誤類標簽,然后采用隨機游走算法從眾多執(zhí)行路徑中選擇部分路徑,即得到一系列標簽序列,這些標簽序列作為訓練的語料庫,用Word2Vec訓練每個標簽的向量表征。采用隨機游走算法解決了路徑爆炸問題,并在路徑生成過程中捕獲了程序的層級結(jié)構(gòu)特征[20]。
通過對選定文獻的表征粒度、表征層次和應(yīng)用場景進行分析。其中不同表征粒度的分布如圖4(a)所示,從圖中可以看出目前已有文獻主要集中在詞匯粒度的表征,不管是傳統(tǒng)的方法,還是基于統(tǒng)計學原理的語言模型或是深度學習,詞匯是基本的研究對象。如圖4(b)所示,由于選擇的文獻絕大多數(shù)是近幾年的方法,所以多數(shù)以研究代碼語義為主,這也體現(xiàn)了新的研究趨勢。受自然語言、圖神經(jīng)網(wǎng)絡(luò)等技術(shù)的快速發(fā)展的影響,各種深度學習代碼表征模型紛紛出現(xiàn),這也引起了代碼表征的研究熱潮。圖4(c)表明了代碼表征通用技術(shù)已經(jīng)成為新的研究熱點,目前,普遍的方法是在大規(guī)模無監(jiān)督數(shù)據(jù)集上進行預(yù)訓練,當應(yīng)用到具體下游任務(wù)時,只需在預(yù)訓練好的模型上進行微調(diào),即可獲得較好的性能。
圖4 文獻分布圖Fig.4 Distribution of papers
2016年,Hindle在論文中假設(shè)代碼和自然語言一樣具有一定的統(tǒng)計規(guī)律[21],緊隨其后,2017年,Vincent等人從詞匯方面分析了源代碼和自然語言的不同特征,指出自然語言的詞匯表非常有限且更新速度較慢,而代碼的詞匯表更新速度太快,新詞匯層出不窮,且是動態(tài)增長的,給數(shù)據(jù)訓練帶來很大的困難。在實驗中,通過調(diào)整傳統(tǒng)N-Gram模型,產(chǎn)生的結(jié)果比RNN和LSTM的效果還要好[22]。2018年,Allamanis等人再次提出,程序應(yīng)該具有自然屬性,軟件語料和自然語言語料具有相似的統(tǒng)計特性,語料越大包含的模式更豐富,并將機器學習中的概率模型引入到代碼表征領(lǐng)域,以學習和研究代碼的特性,此后各種不同的編程語言模型紛紛被提出,并迅速應(yīng)用到Bug修復(fù)、代碼推薦、克隆檢測、注釋自動生成等不同任務(wù)上[23-24]。為解決代碼的那些不在詞表中詞的問題(Out of Vocabulary,OOV),2019年,Karampatsis等人提出了一個基于開放詞匯的神經(jīng)語言模型,該模型不限于固定的標識符詞匯表,采用Subword技術(shù)處理未知或罕見詞匯問題,取得了較好的效果[25]。
雖然源代碼具有自然性,但由于源代碼的詞匯可以任意創(chuàng)建,使得代碼語料庫非常復(fù)雜,語料庫的構(gòu)建直接影響神經(jīng)網(wǎng)絡(luò)模型的性能。2019年,Babii等人對如何更好地構(gòu)建源代碼語料庫進行了探討,從代碼的非英文單詞的處理、字面常量的處理、注釋的處理、空白符/空白行的處理、分詞和大小寫問題的處理以及SubWord分詞的處理六個決策方面全面探討了各種選擇對語料庫和模型性能的影響,列出了源代碼語料庫的重要特征,并在含有10 106個項目的大型語料庫中做了驗證,證明了決策選擇對快速訓練精確的神經(jīng)語言模型具有決定性的作用[26]。標識符是源代碼最基礎(chǔ)的組成元素,據(jù)調(diào)查,標識符約占據(jù)源代碼總量的70%,標識符包括變量名、函數(shù)名、類名、域等,其語義對源碼具有重要的作用。Wainakh等人通過500名開發(fā)者對標識符相關(guān)性、相似性和上下文相關(guān)性的調(diào)查問卷,構(gòu)建了標識符嵌入的基準,提供了一個標識符的IdBench[27]。隨后,Wang等人構(gòu)建了COSET作為程序語義嵌入的基準框架,COSET有多種不同類型的源代碼的數(shù)據(jù)集組成,由專家對程序?qū)傩赃M行手工標注,并對TreeLSTM,門控圖神經(jīng)網(wǎng)絡(luò)(Gated Graph Neural Network,GGNN),ASTPath神經(jīng)網(wǎng)絡(luò)等模型進行了初步研究,該框架對發(fā)現(xiàn)模型的優(yōu)勢、局限性很有作用[28]。
因此,隨著機器學習研究的最新進展,軟件維護從符號形式方法轉(zhuǎn)向數(shù)據(jù)驅(qū)動方法。在這種情況下,隱藏在源代碼標識符中的豐富語義為構(gòu)建代碼的語義表示提供了機會,Efstathiou等人為六種流行的編程語言預(yù)訓練了向量空間模型,提供了分布式代碼表示并指出自然語言和源代碼之間的差異[29]。
N-Gram模型是代碼表征最經(jīng)典的機器學習方法,它是一種基于概率的語言模型(Language Model,LM),根據(jù)單詞的順序序列,預(yù)測下一個輸出序列的概率,即這些單詞的聯(lián)合概率。N-Gram引入到程序分析領(lǐng)域后,被應(yīng)用到代碼推薦、代碼維護、代碼遷移、錯位定位以及函數(shù)名/類名推薦領(lǐng)域,獲得了很好的效果。Hindle等人構(gòu)建了N-Gram模型用來預(yù)測下一個Token,該模型包含前N-1個詞所能提供的全部信息,必須要相當規(guī)模的訓練文本來確定模型的參數(shù),當N很大時,模型的參數(shù)空間過大,容易發(fā)生數(shù)據(jù)稀疏問題,進而導(dǎo)致數(shù)據(jù)平滑問題[21]。
Nguyen等人為了提高N-Gram預(yù)測的性能,把數(shù)據(jù)類型、主題等信息加入到N-Gram模型中[30-33],Tu等人增加了緩存機制獲取本地文件中的Tokens[34-35]。Allamanis等人利用N-Gram預(yù)測下一個API調(diào)用[36-38]。N-Gram模型只適合處理較短的文本,Yang等人把Tokens根據(jù)詞匯和變量相關(guān)性分組,利用PCC優(yōu)化Token級別的N-Gram模型,使之可以處理語句級的長文本推薦[39]。Nguyen等人提出AUTOSC模型框架結(jié)合程序分析和自然語言的概率統(tǒng)計方法進行代碼補全,采用N-Gram模型在大型代碼語料庫上對源代碼的詞法進行訓練,訓練出能產(chǎn)生候選語句的語言模型。然后,使用程序分析從候選集中篩選合乎語法的抽象語句,并將其具體化為有效的候選語句并進行排序。實驗驗證AUTOSC的性能超過現(xiàn)存最好的方法[7]。Karnalim等人針對樹型結(jié)構(gòu)相似性檢測的計算量大的問題,提出一個啟發(fā)式規(guī)則把樹型結(jié)構(gòu)線性化為結(jié)點序列,采用N-Gram模型計算代碼相似度值[40]。如表1中所示,N-Gram技術(shù)主要應(yīng)用于代碼補全等生成式模型中。
傳統(tǒng)語言模型雖然容易訓練,但是缺乏上下文泛化能力且平滑技術(shù)復(fù)雜。隨著深度學習在自然語言和圖像識別領(lǐng)域的成功應(yīng)用,各種神經(jīng)語言模型被提出,并應(yīng)用到軟件工程領(lǐng)域。目前軟件工程領(lǐng)域的神經(jīng)語言模型主要有循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)、卷積神經(jīng)網(wǎng)絡(luò)(CNN)、seq2seq模型以及圖神經(jīng)網(wǎng)絡(luò)(GNN)。深度學習的一般過程為通常將代碼及類別標簽作為數(shù)據(jù)輸入,利用深度學習模型學習代碼中隱藏的深層次特征,把代碼從高維稀疏表示映射為低維空間連續(xù)向量,并利用這種向量表征,完成各類下游任務(wù)。一個基本的模型結(jié)構(gòu)如圖5所示,包含輸入層、模型層、輸出層和應(yīng)用層。輸入層的數(shù)據(jù)主要有組成代碼的詞匯序列、詞法分析的Token序列、語法分析的抽象語法樹以及上下文環(huán)境的控制流圖、數(shù)據(jù)流圖、程序依賴圖等。模型層可以是各類深度學習模型,包括循環(huán)神經(jīng)網(wǎng)絡(luò)、卷積神經(jīng)網(wǎng)絡(luò)、深度神經(jīng)網(wǎng)絡(luò)、編碼-解碼器等,輸出層得到代碼的向量表示,并通過連接一些激活函數(shù)、相似性計算函數(shù)、全連接層完成具體應(yīng)用,表1中列出了近幾年基于深度學習的代碼表征模型及其特征。
圖5 基于深度學習的代碼表征模型Fig.5 Code representation based on deep learning
表1 基于深度學習的源碼表征模型的現(xiàn)有工作Table 1 Existing studies of code representation framework based on deep learning
一般的神經(jīng)網(wǎng)絡(luò)是從輸入層到隱藏層再到輸出層的結(jié)構(gòu),層與層之間是全連接的,層內(nèi)結(jié)點是無連接的,在處理序列化數(shù)據(jù)時,顯得無能為力。循環(huán)神經(jīng)網(wǎng)絡(luò)將層內(nèi)結(jié)點通過時序連接起來,能夠?qū)π蛄袛?shù)據(jù)之間的依賴關(guān)系建模,被應(yīng)用在機器翻譯、語音識別等領(lǐng)域。RNN具有較好的處理序列數(shù)據(jù)的能力,其一般結(jié)構(gòu)如圖6所示,網(wǎng)絡(luò)接收輸入序列作為輸入,產(chǎn)生固定大小的向量作為序列的表示,其輸出可以作為其他網(wǎng)絡(luò)結(jié)構(gòu)的輸入,應(yīng)用于分類、預(yù)測、翻譯等問題。White等人第一個把RNN引入到代碼推薦系統(tǒng),結(jié)合循環(huán)神經(jīng)網(wǎng)絡(luò)和遞歸神經(jīng)網(wǎng)絡(luò),提取代碼的詞匯信息和結(jié)構(gòu)信息,賦予代碼表征以豐富的深層次信息,通過實驗驗證了RNN模型在代碼推薦、代碼克隆檢測任務(wù)中有著遠遠超過傳統(tǒng)N-Gram方法的性能[42,49]。Dam等人提出了DeepSoft模型,構(gòu)建了一個基于LSTM的端到端的軟件分析框架,學習軟件建模中的長期依賴性,在代碼推薦等任務(wù)取得了較好的效果[8]。
圖6 RNN模型結(jié)構(gòu)Fig.6 Structure of RNN model
Zhang等人提出了語句級別的向量嵌入,如圖7所示,首先將一個代碼片段的AST分割成多個小粒度的語句樹,編碼器遍歷語句樹,并對所有的語句樹執(zhí)行基于樹的嵌入,產(chǎn)生語句級的向量,把語句級向量序列送入雙向RNN模型,捕捉語句以及語句之間的序列依賴關(guān)系,進而生成代碼片段向量,有效捕獲了代碼的語法和語義信息[10]。Ben-Nun等人針對代碼本身具有的一些結(jié)構(gòu)化的特性,提出了Inst2vec模型,該模型首先把源代碼轉(zhuǎn)換成一種新的中間語言,基于這種中間語言,構(gòu)建基于上下文流動圖,送入RNN模型訓練出語句級的向量表征,該模型在代碼分類、性能預(yù)測等多種任務(wù)中得到了很好的效果[9]。
圖7 語句級RNN模型Fig.7 Structure of RNN model on sentence level
RNN模型的主要用途是處理和預(yù)測序列數(shù)據(jù),因此在代碼翻譯、代碼推薦等必須要考慮上下文相關(guān)信息的任務(wù)上,表現(xiàn)出較好的性能。但是當上下文序列很長的時候,容易有梯度消失時的問題。
卷積神經(jīng)網(wǎng)絡(luò)是一種特殊的前饋神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),為減少網(wǎng)絡(luò)中參數(shù)個數(shù),用卷積層來代替?zhèn)鹘y(tǒng)的全連接層,提高神經(jīng)網(wǎng)絡(luò)的訓練效率,卷積神經(jīng)網(wǎng)絡(luò)可以提取信息最多的數(shù)據(jù)特征,生成一個固定大小的向量表示結(jié)構(gòu)。
Mou等人[43]設(shè)計了一個樹形結(jié)構(gòu)的卷積層,AST中上層結(jié)點的詞向量由它的孩子結(jié)點表示。如圖8所示,對于每個非葉結(jié)點p以及它的孩子c1,c2,…,cn,具有表達式:
圖8 CNN模型結(jié)構(gòu)Fig.8 Structure of CNN model
其中,vec(p),vec(c1),vec(c2),…,vec(cn)是結(jié)點p以及孩子結(jié)點的向量表示,w i是結(jié)點c i的權(quán)重矩陣,b是偏置項。根結(jié)點的向量即為整個AST的表示,用來表示函數(shù)的特征,該方法在代碼分類和搜索任務(wù)中有較好的表現(xiàn)[46]。
CNN模型適用于樹、圖等具有局部空間相關(guān)性的數(shù)據(jù),主要用于代碼的AST結(jié)構(gòu)上。AST的葉子結(jié)點能夠表示Token級別的語義信息,CNN網(wǎng)絡(luò)可以提取代碼的語法結(jié)構(gòu)信息,因此,CNN模型能夠挖掘深層次的語法和語義信息,在代碼分類、代碼克隆、函數(shù)命名等任務(wù)有較好的性能表現(xiàn),但同樣在深層次模型中存在梯度消失現(xiàn)象。
由于LM模型的限制,Nguyen等人在前期工作的基礎(chǔ)上提出了深度神經(jīng)網(wǎng)絡(luò)模型Dnn4C,如圖9所示,其基本思想是設(shè)置三類映射通路,通過深度學習模型將三類信息實體映射到同一個隱空間,這三類信息包括源代碼的詞匯序列,對解析樹的非終端結(jié)點抽取并按照一定規(guī)則轉(zhuǎn)換成的語法符號序列,以及類型轉(zhuǎn)換序列,源代碼的表征即為三類信息隱藏向量的融合[45]。
圖9 DNN模型結(jié)構(gòu)Fig.9 Structure of DNN model
為解決子圖匹配計算量的問題,Zhao等人[46]提出一個新型的編碼方法,如圖10所示,對代碼中的變量特征、基本塊特征、變量和基本塊之間的關(guān)系特征進行編碼,將代碼的控制流和數(shù)據(jù)流編碼成一個壓縮的語義矩陣,矩陣的每個元素都是一個高維稀疏的二進制向量,代表了該元素的控制流和數(shù)據(jù)流信息?;谶@個語義矩陣,設(shè)計一個DNN模型把代碼相似性問題轉(zhuǎn)化為一個二元分類的問題,進而學習相似代碼的模式。與傳統(tǒng)的CFG和DFG相比,語義特征矩陣減少了尋找同構(gòu)子圖以檢測矩陣中相似模式的問題,并且在下游任務(wù)很容易使用。Wang等解析程序源代碼中的抽象語法樹,利用深度置信網(wǎng)絡(luò)從中抽取語義特征,利用這些特征進行軟件缺陷檢測[50]。
圖10 新型編碼模型Fig.10 New coding model
DNN模型就是多層神經(jīng)網(wǎng)絡(luò),在一維數(shù)據(jù)上具有較好的表現(xiàn),可通過各種組合形式,把不同類型數(shù)據(jù)融合一起,但實際應(yīng)用中采用較少的模型。
編碼-解碼模型是一個解決Seq2Seq問題的模型,就是根據(jù)一個輸入序列X,來生成另一個輸出序列Y,通過一個編碼和一個解碼過程來重構(gòu)輸入數(shù)據(jù),學習數(shù)據(jù)的隱層表示?;镜木幋a-解碼模型,結(jié)構(gòu)如圖11所示,所謂編碼,就是將輸入序列轉(zhuǎn)化成一個固定長度的向量;解碼,就是將之前生成的固定向量再轉(zhuǎn)化成輸出序列,其中編碼器和解碼器可以是任何一種深度神經(jīng)網(wǎng)絡(luò)模型,例如CNN/RNN/GRU/LSTM等等。Alon等人基于編碼-解碼器提出了Code2Seq模型,從源碼的AST中分別提取葉子結(jié)點和非葉子結(jié)點,AST的葉結(jié)點代表變量名和變量類型名,非葉結(jié)點代表程序的結(jié)構(gòu)集,比如循環(huán)、變量聲明、表達式等。編碼器為雙向LSTM,輸入為AST中任意一條路徑上的結(jié)點的嵌入向量的全連接,解碼器采用單向LSTM,初始狀態(tài)為各條路徑編碼的加權(quán)平均。該模型和基于Token的方法的區(qū)別是Encoder的輸入不是Token句子序列,而是由Encoder為AST中的每條路徑分別創(chuàng)建一個向量表示。Decoder過程處理這些AST路徑編碼[41]。
圖11 Encoder-Decoder模型結(jié)構(gòu)Fig.11 Structure of Encoder-Decoder model
編碼-解碼模型是一個通用的模型,Encoder、Decoder可以是任何類型的數(shù)據(jù),針對不同的任務(wù),可以構(gòu)建CNN、DNN、LSTM等任意深度學習模型。因為是一個端到端的學習算法,在代碼翻譯領(lǐng)域應(yīng)用較多。
源代碼分析的最大難點在于程序語言是一種高度結(jié)構(gòu)化的語言,現(xiàn)有方法大多只考慮了源代碼的淺層特征,如方法名、Token分詞,但忽略了抽象語法樹中的語義信息和控制流圖的結(jié)構(gòu)化特征。其次,盡管基于深度學習的方法在源代碼的表示上表現(xiàn)良好,但缺乏可解釋性,幾乎不可能理解源代碼的哪些特征對最終結(jié)果貢獻更多。為了解決上述兩個問題,Wan等人提出了一種用于語義源代碼檢索的新型多模態(tài)注意力機制網(wǎng)絡(luò),開發(fā)了一種全面的多模態(tài)表示,用于表示源代碼的非結(jié)構(gòu)化和結(jié)構(gòu)化特征,其中LSTM用于表示代碼的分詞,Tree-LSTM用于表示代碼AST,GGNN用于表示代碼的CFG。該模型在代碼檢索領(lǐng)域獲得了最好的性能[47]。
把代碼表達為文本序列或者Token序列,甚至解析為AST,即使和深度學習結(jié)合,也更多只是抓住了代碼淺層的文本結(jié)構(gòu)信息。錯失了代碼豐富的語義信息。為了彌補這一問題,Allamanis等人提出圖神經(jīng)網(wǎng)絡(luò)抓取代碼的語法和語義特征,圖神經(jīng)網(wǎng)絡(luò)通過消息傳遞迭代更新所有結(jié)點的隱藏狀態(tài),每個結(jié)點既接受相鄰結(jié)點的信息,又向相鄰結(jié)點發(fā)送信息。圖神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)如圖12所示,首先把源代碼解析為AST,在AST中通過增加數(shù)據(jù)流和類型層級兩種信息,將程序編碼成圖,圖由語法結(jié)點即語法中的非終結(jié)符和語法標記即終端結(jié)點組成,圖的邊代表Token之間的語法和語義關(guān)系,直接將這些語義輸入到機器學習模型中以減少對訓練數(shù)據(jù)量的要求。結(jié)點的初始狀態(tài)為Token的文本表示與它的類型的結(jié)合,通過多次迭代更新得到每個結(jié)點的最終狀態(tài)[24,51-52]。
圖12 GNN模型結(jié)構(gòu)Fig.12 Structure of GNN model
GNN網(wǎng)絡(luò)用于學習非歐式空間數(shù)據(jù)的信息,GNN既可以學習圖中Token結(jié)點信息,也可以學習邊的預(yù)測,同時還可以學習CFG、DFG、PDG等圖的整體信息,用來表示一個程序的語義特征。所以GNN使用非常廣泛,可以用于代碼分類、缺陷預(yù)測、代碼推薦或者代碼摘要生成等任務(wù)。但是GNN不適用于圖中結(jié)點不斷變化的動態(tài)圖,而且還有很多缺陷需要進一步研究,例如圖的不動點計算問題、邊信息傳播問題,都需要新的方法進一步來解決。
隨著神經(jīng)網(wǎng)絡(luò)、語言模型的不斷發(fā)展,深度學習在軟件系統(tǒng)中的應(yīng)用越來越受到廣泛關(guān)注,基于深度學習的代碼分析研究已經(jīng)成為軟件工程領(lǐng)域的新的研究熱點[53]。不過,從前面的分析,可以看出深度學習在代碼分析領(lǐng)域的應(yīng)用才剛剛起步,未來還需要在以下幾方面進一步努力。以下總結(jié)了三個可能的研究方向:
(1)現(xiàn)有語言模型的改進
自從驗證了自然語言的模型對代碼分析具有很好的效果,各種編程語言模型紛紛出現(xiàn),并廣泛應(yīng)用到代碼生成、代碼克隆檢測、Bug檢測等各個領(lǐng)域。這些方法把自然語言深度學習模型直接應(yīng)用到源碼中,學習代碼中詞、Token、語法樹中各類結(jié)點的向量表示。同時,Allamanis等人指出語言模型依賴代碼的文本自然特征,但是程序代碼和自然語言只有小部分詞匯是重復(fù)的,即使相同的詞,在自然語言和程序代碼中的含義也不相同,而且程序員可以隨意給變量命名,造成代碼的詞匯是開放性的,因此,不能直接把語言模型應(yīng)用到代碼上[23],必須挖掘程序代碼的其他特征。
(2)多模態(tài)深度學習框架
從前面的分析可以看出,借助自然語言模型對代碼詞匯、Token、AST、CFG/DFG等進行向量表征,雖然該方向取得了一定研究進展,但目前仍存在以下問題:首先,現(xiàn)有方法能夠解決簡單相似以及結(jié)構(gòu)相似問題,但對功能相似的研究還比較少,即使有些用同構(gòu)子圖的方法來研究,但鑒于代價太高,不能廣泛應(yīng)用。代碼不僅具有文本自然性,同時具有語法結(jié)構(gòu)信息,而且代碼是可執(zhí)行的,具有動態(tài)語義特征,和詞匯、語法和語義都有相關(guān)性,因此結(jié)合深度學習技術(shù),采用多模態(tài)代碼表征方式,構(gòu)建新的學習框架,從詞匯、語法、執(zhí)行路徑等多個方面學習代碼的語義信息,直接學習隱藏在源代碼中更加復(fù)雜的語義和語法特征,將會成為程序分析領(lǐng)域的一個重點研究方向。
(3)通用的深度學習框架
面向特定應(yīng)用領(lǐng)域的深度學習表征模型獲得了較好的性能,純粹的、通用的代碼表征是研究者致力解決的新問題。類似于自然語言的Word2Vec,Alon等人提出了Code2Vec方法,利用神經(jīng)網(wǎng)絡(luò)訓練代碼的語義向量[19],緊接著Kang等人把Code2Vec方法應(yīng)用到了注釋生成、作者識別、代碼克隆三種不同類型的任務(wù),評價了Code2Vec對各項任務(wù)的通用性,實驗結(jié)果表明非特定任務(wù)的訓練,其效果并不比一般方法好[54]。
目前常用的表征粒度中,標識符可以表達領(lǐng)域語義和開發(fā)者的理論基礎(chǔ),在特征定位和軟件模塊化有作用。抽象語法樹可以捕獲程序的語法結(jié)構(gòu)和模式。CFG和DFG可以表達程序的部分語義信息,這些不同的表征,其抽象程度不同,適合不同的具體任務(wù),Tufano等人提出集成學習方法,把不同粒度的表征向量通過加權(quán)平均組合為代碼的抽象表征[48]。Feng等人基于Transformer的神經(jīng)網(wǎng)絡(luò)構(gòu)建了CodeBERT模型,在大規(guī)模數(shù)據(jù)集上預(yù)訓練,致力學習源碼的通用表示方法,支持下游自然語言代碼搜索、代碼文檔生成等應(yīng)用[55]。但總體上來說,通用的代碼表征框架和模型還不夠成熟,未來還有待進一步更廣泛的研究。
隨著開源代碼的日益增多,將深度學習技術(shù)融入代碼表征,開展更深層次的代碼表征的研究,已經(jīng)成為軟件工程領(lǐng)域解決各類問題的新方法。通過從海量數(shù)據(jù)中學習代碼的語法、語義特征,用于解決各類下游任務(wù)。與傳統(tǒng)的基于機器學習的方法相比,基于深度學習的表征模型能夠自動學習代碼的結(jié)構(gòu)和抽象語義的隱藏特征,更有效地提高代碼表征的準確性。本文主要介紹和分析了基于深度學習的代碼表征的研究現(xiàn)狀和進展,并根據(jù)現(xiàn)有工作的局限性討論了今后可能的發(fā)展方向和趨勢。