史志成,周 宇,3+
1.南京航空航天大學 計算機科學與技術學院,南京210016
2.南京航空航天大學 高安全系統的軟件開發與驗證技術工信部重點實驗室,南京210016
3.南京大學 軟件新技術國家重點實驗室,南京210023
在“大代碼”背景的驅動下,如何高效地提取代碼特征并對其進行編碼以快速地處理海量數據的需求已日益迫切。如今,深度學習在自然語言處理的很多領域已經取得了突破性的進展,人工智能也逐漸從“感知智能”邁向“認知智能”,機器不僅能夠感知客觀世界所釋放的信息,更能夠對這些信息像人一樣進行理解和分析。Hindle等人[1]已經論證了程序語言和自然語言類似,具備眾多可供分析的統計屬性,代碼語言可以和自然語言一樣能夠被機器理解和分析。因此,許多學者簡單地將代碼作為自然語言來處理。例如,代碼被表示為一個字符串序列應用在克隆檢測[2-3]、漏洞定位[4]以及代碼作者分類(code authorship classification)[5]的任務當中。盡管代碼和自然語言有很多共性的特征,都是由一系列單詞組成且都能表示成語法樹的形式,然而代碼有許多自己專有的特性,代碼具有更強的邏輯結構,自定義的標志符,且標志符之間存在長距離依賴等。簡單地將代碼視作自然語言進行處理難免會造成信息丟失。為了使模型更適合處理代碼語言,部分學者借助軟件工程的領域知識,制定了一些啟發式規則來靜態地提取代碼特征,例如在應用程序接口(application programming interface,API)推薦領域,Zhou 等人[6]就將用戶查詢后得到的反饋信息作為構建API 向量的一維特征來提高查詢效果,然而這些靜態提取代碼特征的方式有以下三個弊端:
(1)完全依賴研究人員的先驗知識來提取特征,所提取的特征數目有限。
(2)當代碼庫過于龐大和復雜,特征規則的制定也會相應變得復雜,難以適用于對海量且結構復雜的代碼數據進行處理。
(3)規則的制定往往是面向特定任務的,可遷移性差。
因此,近幾年許多研究使用深度學習模型來自動提取代碼的特征[7-10],以擺脫對人工提取特征的依賴。這些模型很多借助代碼的抽象語法樹(abstract syntax tree,AST)來提取特征,通過對AST 內部節點之間的依賴關系進行提取,來獲得代碼的結構信息,而不是簡單地根據代碼的標志符序列提取語義信息。然而這些方法有一個明顯的弊端,就是都將樹的二維結構轉化成一維的序列結構進行預處理,如Alon 等人[10]將AST 處理成一個路徑集合,Hu 等人[8]以及White等人[9]直接使用先序遍歷的方式獲得AST節點的展開序列作為模型的輸入,這些降維的處理方式一定程度上會造成節點之間某些依賴關系的丟失。
因此,為了更充分地提取代碼的結構信息,部分學者提出了一些不降維而直接處理AST的樹形深度學習模型。如Wan等人[11]使用Tree-LSTM(tree-structured long short-term memory networks)進行注釋生成,Mou等人[12]使用TBCNN(tree-based convolutional neural network)模型進行代碼分類。然而這些樹形深度學習模型仍然有兩個局限性:(1)與自然語言的長文本類似,在AST規模十分龐大的情境下,很容易在模型的訓練過程中出現梯度消失的情況[13-15],因此無論是Wan等人使用Tree-LSTM或者Mou等人使用的TBCNN都會很容易丟失那些存在長期依賴關系節點之間的部分信息。(2)Wan 等人使用的Tree-LSTM 要將AST先轉化為二叉樹再進行編碼,該預處理操作破壞了AST 原始的結構且轉化后樹的深度將會大幅增加,加劇梯度消失問題。
本文結合TBCNN以及Zhang等人[16]提出的ASTNN(AST-based neural network)模型構建了一個基于卷積和循環神經網絡的自動代碼特征提取模型(convolutional and recurrent neural network,CVRNN)。模型的輸入是代碼的AST,為了提高AST 初始詞向量的質量,還針對AST 設計了一個專門的詞向量預訓練模型,預訓練詞向量不僅能提升后面實驗部分代碼分類以及相似代碼搜索的實驗結果,還能在模型的訓練過程中加速模型的收斂。借鑒ASTNN 切割AST的思想,模型并未直接對整棵AST進行處理,而是根據“if”“while”“for”以及“function”這4 個代表程序控制塊的AST 節點將AST 切割成一系列子樹,再利用TBCNN 模型對這些子樹進行卷積運算。子樹的規模遠小于原先的AST,因此可以有效地解決節點長期依賴導致的梯度消失問題。之后,采用雙向循環神經網絡[17],內部神經元采用LSTM[18],來提取代碼塊之間的序列信息,將每個時間步生成的代碼向量存放到一個向量矩陣中,最終采用最大池化得到代碼向量。為了驗證CVRNN 模型生成的代碼向量的質量,還在CVRNN模型基礎之上設計了兩個具體的應用模型,代碼分類以及相似代碼搜索模型。實驗結果表明,CVRNN模型不僅能夠高效地自動提取代碼的特征,且所提取的特征具有很強的遷移能力,不僅適用于代碼分類任務,其分類訓練后得到的編碼器在相似代碼搜索任務上也有很好的效果。
本文的主要貢獻是:
(1)提出了一個基于AST的詞向量訓練模型。
(2)提出了一個基于卷積和循環神經網絡的自動代碼特征提取的深度學習模型。
(3)構建了一個高效的用于搜索相似代碼的系統,且該項目所使用的數據集以及代碼已在github上開源(https://github.com/zhichengshi/CVRNN)。
這部分主要分為兩個子模型進行介紹:
(1)基于樹的詞向量訓練模型。
(2)基于卷積和循環神經網絡的特征提取模型。
詞嵌入技術是將自然語言文本中的單詞嵌入到低維空間的一種向量表示方法[19]。近幾年,許多學者也將該方法成功應用在處理代碼的任務中,如API推薦[20-21]、漏洞檢測[22]等。然而這些詞嵌入技術簡單地將代碼視為序列化的文本進行處理,往往采用類似Word2vec[23]滑動窗口的方式建立特定單詞的上下文情境,并以此為依據來生成相應的詞向量,如Zhang等人[16]就通過先序遍歷的方式獲得AST的節點序列,然后使用Word2vec 生成詞向量,而代碼元素之間的依賴關系其實是一種二維的樹形結構,應用這種方法獲取詞的情境難免會導致代碼的部分結構信息難以編碼進最終生成的詞向量中。Mou 等人[12]在對AST中節點進行編碼時只將當前節點的孩子節點作為情境,這種情境信息的提取方式仍然不完備。因此本文借鑒Pérez等人[24]構造樹中節點上下文情境的方法,結合當前節點的祖先節點、兄弟節點以及子孫節點作為情境,在提取祖先節點以及子孫節點作為情境的過程中分別設置一個向上以及向下探測的參數來控制提取情境的深度,并設計了一個詞向量訓練模型根據提取的情境來生成AST 節點的詞向量。為了提高相似代碼搜索的準確率以及提高模型的訓練速度,將不考慮AST 葉子節點的信息即代碼中的標志符信息,因此該模型更側重于對代碼結構信息的提取。相似代碼搜索任務使用的數據集來源于leetcode 編程網站,代碼分類任務所使用的數據集是Mou 等人[12]提供的104 數據集,該數據集共包含104個類別的代碼,每個類別下包含500個樣本。在分類訓練的過程中并沒有使用AST葉子節點的信息,原因有二:(1)由于代碼中的標志符是程序員自定義的,而據觀察104數據集中的詞匯與leetcode上的詞匯并不重疊,使用葉子節點反而會引入噪音。(2)leetcode 上的代碼所使用的變量名僅僅起一個命名的作用并沒有語義表示的功能,更多的是采用a、b、temp 等不攜帶語義信息的單詞作為變量名。AST的生成工具使用的是srcml(https://www.srcml.org/),該工具不檢查代碼的正確性,因此即使編譯不能通過的代碼也能生成AST,提高了模型的容錯性。在代碼分類以及相似代碼搜索的任務中所使用的代碼都是C/C++編寫的,而srcml 用來表示這兩種語言所定義的節點只有60 個,加入葉子節點后詞匯表的大小將擴充至上萬,因此不使用葉子節點將大大提高模型的收斂速度。
圖1展示了整個詞向量的訓練過程。選取節點b作為當前節點,向上探測尋找祖先節點的深度設置為1,向下探測子孫節點的深度設置為2,因此得到祖先節點情境信息a,兄弟節點情境信息c,以及子孫節點情境信息(d,e,f,g,h),將這三類情境節點合并得到(a,c,d,e,f,g,h)作為節點b的情境信息。在模型的訓練過程中,首先使用正態分布對詞匯表矩陣進行初始化,得到矩陣D∈Rn×f,其中n表示詞匯表的大小,f表示詞向量的維度。模型的輸入分為兩部分,情境節點以及當前節點在詞匯表中的索引,通過查找詞匯表可以得到情境節點以及當前節點的詞向量表示,分別對應圖1中的context embedding以及current vector,之后context embedding 經過兩層全連接神經網絡得到變換后的情境向量context vector,使用交叉熵作為損失函數,Adam作為優化器進行梯度下降。為了使生成的context vector 能夠與current vector進行交叉熵運算,輸出層輸出向量的維度與詞匯表中向量的維度相同。整個計算過程的數學公式如下:

其中,v∈R1×f表示情境向量,h表示經過隱層后得到的輸出向量,W1∈Rf×k是隱層的權值矩陣,用來將維度是f的向量映射成k維向量,b∈R1×k是隱層網絡的偏置項。W2∈Rk×v是輸出層的權值矩陣,y′∈R1×v是輸出層的輸出向量,y∈R1×v是當前節點的詞向量,表示向量y′在第i個維度上的值,yi同理。
1.2.1 AST切割
為了防止AST 規模過大,帶來梯度消失問題。并未直接將AST 作為模型的輸入,而是先將AST 進行切割,得到AST的子樹序列輸入到模型之中,詳細步驟如下。
首先,定義一個用于存儲AST 分裂節點的集合S={if,while,for,function,unit}。其中“if”用來提取條件子樹,“while”“for”用于提取循環子樹,“function”用來提取方法體子樹,“unit”是未切割前AST的根節點,AST 剩余的節點都在“unit”子樹上,如下展示了具體的切割算法:
算法1split AST


Fig.1 Node embedding training model圖1 詞向量訓練模型

該算法以AST的根節點作為輸入。首先用根節點初始化一個數組T,然后通過深度優先遍歷獲得節點序列N,對N中的節點進行遍歷,若節點屬于集合{function,if,while,for},該節點連同其子節點將會被切割取出存儲到T中。值得注意的是,當出現嵌套的情況,例如“if”子樹中還包含“for”子樹,“for”子樹將會從“if”子樹上提前切割下來,因為深度優先遍歷將先訪問“for”再訪問“if”節點,兩個獨立的子樹根節點將按序存儲到T中。
1.2.2 卷積以及循環神經網絡模型
在介紹CVRNN 模型之前,先詳細地闡述一下Mou 等人[12]提出的基于樹的卷積神經網絡TBCNN。圖2 展示了該模型執行的具體流程。首先應用詞嵌入技術將AST中的節點轉化成向量表示,不同的是,該方法只使用了被表示節點的孩子節點作為情境,沒有考慮到兄弟節點以及祖先節點的信息,具體的詞嵌入公式如下:

其中,p表示雙親節點,ci表示p的第i個孩子節點,Wi是ci的權值矩陣,(孫子節點的數目除以孩子節點的數目)是系數,b是偏置項。然后通過設置一個固定深度的滑動窗口遍歷整個AST,再引入最大池化得到一個形狀與原先一樣的AST,接著使用動態池化[25]以及兩層全連接神經網絡得到最終的代碼向量。圖3 展示了CVRNN 的模型架構,以下是該模型的算法執行邏輯:

Fig.2 TBCNN model圖2 TBCNN模型
首先根據1.1 節得到的詞向量表將AST 中的節點表示為向量,再使用TBCNN模型對AST進行卷積得到編碼后的向量,然后使用雙向循環神經網絡并以LSTM 作為神經元對得到的向量序列進行編碼以提取代碼的序列信息。標準的循環神經網絡(recurrent neural network,RNN)是單向的,其編碼受限于過去的信息,采用雙向RNN 則能夠同時使用過去和未來兩個方向的信息。在后面的實驗中,將會對這兩種不同的策略進行對比。給定一棵代碼的AST,假設該AST 被切割成n棵子樹,這些子樹經過TBCNN編碼后將得到一個向量序列TV∈Rn×k,k表示向量的維度,n表示子樹的數目。在某個時間步t,LSTM的計算公式如下:

其中,σ是sigmoid 激活函數,ct表示新狀態,ht∈R1×m表示輸出向量,由當前時間步兩個方向的向量拼接而成的向量,Wi、Wf、Wo是權值矩陣,bt、bf、bo是偏置項。it表示輸入門,決定et中哪一部分將被保留添加到ct中,ft表示遺忘門,決定et的哪一部分將被遺忘,ot是輸出門用來計算輸出向量。整個雙向RNN的計算可以形式化如下:

Fig.3 CVRNN model圖3 CVRNN模型

至此,每棵子樹將會被轉化成一個向量b∈R1×2m。這些向量將會被存放到一個矩陣BV∈Rn×2m中,考慮到不同的子樹的重要程度可能不同,例如“if”代碼塊中的語句要多于“for”代碼塊中的語句,直觀上該“if”子樹所攜帶的信息將高于“for”子樹,采用均值池化則兩個代碼塊所賦予的權值相同,因此該模型使用最大池化得到代碼向量r∈R1×2m,而沒有選擇均值池化。
算法2CVRNN

這部分主要介紹兩個基于CVRNN 的具體的應用模型,代碼分類模型以及相似代碼搜索模型,而相似代碼搜索模型所使用的編碼器是經過代碼分類任務訓練之后得到的CVRNN。
代碼分類所使用的數據集是Mou 等人[12]構建的104 數據集。原始數據集共包含52 000 條數據。訓練數據是隨機從中抽取的51 000 條數據,剩余的1 000 條作為驗證數據,來監控訓練過程中是否發生過擬合。
代碼分類模型如圖4 所示,經過CVRNN 的編碼,任意代碼段都能轉換成一個固定長度的向量表示r,為了使模型適配104 代碼分類任務,在該模型的基礎之上再添加了一層全連接網絡,將r映射成維度是104的向量r′。使用one-hot方法生成每個代碼的標記向量l,l的維度104,若代碼屬于第5類,則l位置5 上的值為1,其余位置為0。選取交叉熵作為損失函數,使用Adam作為優化器。

Fig.4 Code classification圖4 代碼分類
相似代碼搜索使用的數據集來源于leetcode,沒有在實際生產環境中去尋找功能相似的代碼的原因是這種人為的篩選方式具有很強的主觀性,功能劃分的粒度很難把握。比如同樣是一個排序代碼,一個使用快速排序實現,另一個使用歸并排序實現,很難判定兩個是否是同一個類別。而使用leetcode 編程網站上同一個題目的不同題解作為判斷功能是否相似是一個很客觀的標準。為了保證功能絕對一致,每個題解都輸入到在線的IDE(integrated development environment)中進行了測試,該網站會提供幾百到上千個測試用例,所篩選得到的題解能通過全部的測試用例。選擇其中一個題解用作查詢,另一個用作匹配對象放入數據庫中,該數據庫對應圖5中的正樣本。在真實環境下,數據庫中會包含更多的代碼,因此,為了使相似代碼搜索任務更貼近真實環境,該實驗對代碼的搜索空間進行了擴充。使用了104 數據集中的52 000 條數據作為負樣本加入到待搜索的數據庫中。
圖5左邊是代碼向量的生成過程,應用訓練好的CVRNN模型對數據庫中的代碼進行編碼,最后將代碼向量存放到一個數據庫中。圖5 右邊是代碼查詢的過程,模型的輸入是一個用作查詢的題解,經過CVRNN 編碼后得到代碼向量。借助Facebook 所提供的faiss[26]工具將數據庫中的代碼向量基于與查詢向量之間的歐式距離進行排序,選擇前10 個搜索結果作為結果統計樣本。因為在搜索時,數據庫中的代碼都已經是向量的形式,所以搜索時間很快,時間可以忽略不計,在普通筆記本上的搜索時間小于2 ms。

Fig.5 Similar code search圖5 相似代碼搜索
本章將基于4 個研究問題對CVRNN 模型的性能進行評估。
(1)CVRNN模型在分類任務上的效果如何?
(2)CVRNN生成的代碼向量是否滿足相似度越高,彼此之間的幾何距離就越短的性質?
(3)CVRNN在相似代碼搜索任務上的實驗結果如何?
(4)CVRNN預訓練詞向量以及雙向RNN模塊對模型產生怎樣的影響?
TBCNN是第一個用于處理104代碼分類任務的深度學習模型。前文已經詳細介紹過TBCNN 模型的執行流程。因為TBCNN模型內嵌在CVRNN模型之中,為了對比的公平性,在實驗過程中,TBCNN與CVRNN 中使用的TBCNN 在參數配置上完全相同,卷積層的數目、卷積維度都相同。在相似代碼搜索任務中,TBCNN 模型生成的代碼向量對應圖2 中倒數第二個全連接神經網絡的輸出向量,維度是400。
ASTNN模型是當下在104分類任務上取得最好結果的模型。該模型與CVRNN 模型的主要區別就是對AST序列進行編碼的方法不同。該模型主要采用如下公式對AST中的節點進行自下而上的編碼:

其中,h表示當前節點更新后的狀態,Wn是權值矩陣,是Wn的轉置矩陣,vn表示當前節點的初始化詞向量,hi表示當前節點第i個孩子節點的狀態,C表示當前節點孩子節點的數目,bn是偏置項。然后對AST中的所有節點使用最大池化獲得AST的向量表示。接著將得到的AST 向量序列輸入到雙向RNN 中,再經過最大池化得到整棵AST 的向量表示。因為CVRNN也使用雙向RNN來提取代碼的序列信息,為了對比的公平性,ASTNN、CVRNN均使用LSTM作為神經元,且隱層單元都設置為200,因此兩個時間方向拼接之后得到的代碼向量維度也都是400。
為了對比公平,CVRNN、TBCNN、ASTNN 共享同一張預訓練詞向量表。除了深度學習模型,CVRNN模型還與3個業界常用的相似代碼檢測工具進行了對比,分別是moss(measure of software similarity)(https://theory.stanford.edu/~aiken/moss/)[27]、jplag(https://jplag.ipd.kit.edu/)以及sim(https://dickgrune.com/Programs/similarity_tester/)。moss是斯坦福開發的利用文件指紋技術來確定程序相似性的系統。jplag 以程序覆蓋率作為代碼相似的度量指標,在覆蓋的過程中,代碼被分為多個相似的片段,通過計算平均覆蓋率和最大覆蓋率作為最終代碼相似度的值。sim 主要根據代碼所使用的詞匯來計算代碼之間的相似度,該工具廣泛應用于檢查大型軟件項目中的重復代碼。此外數據還被部署在一個開源的代碼搜索引擎search code(https://github.com/boyter/searchcode-server)上進行測試,該搜索引擎也是以代碼作為查詢語句在數據庫中搜索相似的代碼,然而所有反饋結果都是0,因此沒有在后面的實驗結果統計表中列出。
在代碼分類任務中,僅選取準確率作為度量指標。在相似代碼搜索中,應用以下3個在搜索領域廣泛使用的度量指標來衡量搜索結果:
(1)top(k)

式中,Q表示查詢樣本,|Q|表示查詢樣本的數目,若前k個候選樣本中有一個與查詢匹配,則認為該查詢命中結果,rel(k)表示命中的查詢樣本的數目。
(2)MRR(mean reciprocal rank)

式中,FRanki表示第i個樣本第一個命中的位置,Q以及|Q|的定義同上。
(3)NDCG(normalized discounted cumulative gain)

式中,n表示為限定反饋數目所設置的閾值,默認為10。relij表示第j個候選樣本與查詢樣本i的相關程度,在相似代碼搜索任務中,若候選樣本與查詢樣本匹配則值為1,反之為0。Q以及|Q|的定義同上。IDCG(idea discounted cumulative gain)表示DCG的最優計算結果,假設n為5,前5個搜索結果與查詢樣本的相關程度分別為(0,0,1,0,0),則使用(1,0,0,0,0)計算IDCG的值,(0,0,1,0,0)計算DCG的值,以此得到NDCG的值。
研究問題1CVRNN 模型在分類任務上的效果如何?
圖6 展示了經過每一輪訓練之后,3 個深度學習模型在驗證集上的分類準確率。可以看出CVRNN模型相對于其他兩個模型收斂速度更快,經過1輪訓練之后,CVRNN模型分類的精度就達到了76.5%,而ASTNN 以及TBCNN 則分別是45.2%和65.0%(對應圖6 縱軸的截距)。經過30 輪訓練之后,CVRNN 模型的分類精度達到94.4%,而ASTNN以及TBCNN則分別是90.7%和80.9%。在這30輪的訓練過程中,可以看出,CVRNN的每一輪的驗證精度均高于另外兩個深度學習模型。因此,CVRNN在代碼分類任務上對比這兩個深度學習模型有明顯的優勢。

Fig.6 Valid accuracy of three deep learning models in each epoch圖6 3個深度學習模型每訓練一輪的驗證精度
研究問題2 生成的代碼向量是否滿足相似度越高,彼此之間的幾何距離就越短的性質?
圖7 為leetcode上100 個問答對生成的代碼向量使用TSNE(t-distributed stochastic neighbor embedding)方法降維打印后的圖片,可以看出很多問答對生成的代碼向量之間的距離很近,因此也回答了研究問題2,使用CVRNN模型可以將功能相似的代碼向量聚集在一起,相似度越高,彼此之間的幾何距離就越短。

Fig.7 leetcode code vector圖7 leetcode代碼向量
研究問題3 CVRNN 在相似代碼搜索任務上的實驗結果如何?
表1 展示了各個模型在相似代碼搜索上的實驗精度,3個深度學習均采用第30輪訓練結束之后的編碼器。在使用jplag 模型進行測試時,本文統計了該模型由平均相似度以及最大相似度分別得到的實驗結果。實驗結果表明:深度學習模型的實驗效果相對于3 個傳統的相似度檢測工具有明顯的優勢,CVRNN 在各項度量指標上均高于其他任何一個模型。盡管在分類精度上TBCNN 要劣于ASTNN,ASTNN 要高出9.8 個百分點,然而除了Top1 二者的精度相等之外,TBCNN 其他幾項的精度均高于ASTNN。這說明,盡管可以借助代碼分類任務訓練編碼器,然而模型在兩個任務上的性能并不是正相關的,即在代碼分類任務上精度越高并不代表相似代碼搜索的效果越好,然而從表1 以及圖6 可以看出,CVRNN模型在兩個任務上的效果都是最優的。

Table 1 Similar code search accuracy表1 相似代碼搜索準確率 %
研究問題4CVRNN 預訓練詞向量以及雙向RNN模塊對模型產生怎樣的影響?
定義如下4個模型:
模型1(without biRNN)將雙向RNN直接從模型移除。
模型2(RNN)將雙向RNN替換成雙層單向RNN。
模型3(random)使用正態分布隨機初始化詞向量。
模型4(CVRNN)默認配置,使用雙向RNN以及預訓練的詞向量。
模型1直接將雙向RNN移除,表示整個CVRNN模型將不提取代碼的序列信息,而模型2將雙向RNN替換成雙層單向RNN則表示削弱模型提取序列信息的能力。表2 展示了各個模型在代碼分類上取得的精度,可以看出模型1在失去提取代碼序列信息模塊之后,分類精度直接從94.4%降到了73.8%,對比模型2的92.5%有很大的差距。表3展示了各個模型在相似代碼搜索任務上的結果,可以發現,模型1 雖然在失去提取序列信息的模塊之后在代碼分類任務上要明顯遜色于模型2,在相似代碼搜索各項度量指標上的值卻都高于模型2,該結果也與問題3 得到的結論相符合,模型在代碼分類以及相似代碼搜索上的性能并不是成正相關的。從表2以及表3可以看出,模型4 在兩個任務上的性能都要高于模型1 和模型2。圖8展示了模型3以及模型4在訓練過程中每一輪在驗證集上的分類精度。可以看出使用預訓練詞向量的模型4將收斂得更快,經過第一輪訓練之后,模型3的分類精度為72.00%,而模型4精度能達到76.50%。并且在每一個訓練輪次之中,使用預訓練向量的模型都有更高的精度,使用預訓練詞向量的模型4最終達到94.4%的分類精度,而隨機初始化詞向量的模型最終的分類精度是91.4%。從表3 也可以看出,預訓練詞向量對提升相似代碼搜索的性能也有貢獻。

Table 2 CVRNN ablation research in code classification表2 CVRNN代碼分類消融實驗 %

Table 3 CVRNN ablation research in similar code search表3 CVRNN相似代碼搜索消融實驗 %

Fig.8 Valid accuracy comparison between random initializing node embedding and pre-training embedding in each epoch圖8 隨機初始化詞向量以及使用預訓練詞向量每一輪驗證精度對比
如何對代碼進行表示一直是軟件工程領域研究的一個重要的問題。
傳統的信息檢索以及機器學習方法主要將代碼作為純文本進行處理。輸入到模型之中的是一個被規范化處理好的標志符序列[2],SourcererCC[3]在此基礎上增強了對代碼語義的挖掘,使用一種反向索引(inverted index)技術對標志符的序列信息進行挖掘。與SourcererCC不同的是,Deckard[28]模型選擇增強對代碼結構信息的挖掘,在輸入的數據中加入了代碼的語法結構信息。
不少研究人員借助AST所提供的代碼元素之間的依賴關系來增強對代碼信息的提取。例如:Zhou等人[7]借助AST 來擴充方法體內部元素的情境信息以增強代碼段的語義,提高注釋生成的效果。Yan等人[29]應用類似方法進行代碼推薦工作。除了借助AST 擴充代碼元素語義之外,更多的模型選擇將AST整體或切分后得到的子結構序列輸入到模型之中。例如:Hu 等人[8]就通過添加括號的方式來限定AST 中節點的作用域,將AST 轉化成一個節點序列來生成代碼對應的注釋;Alon 等人[10]借助函數體的AST 路徑來生成相應的函數名;White 等人[9]則分別根據代碼的標志符序列以及AST節點序列得到代碼的語義向量和結構向量,根據這兩個向量對代碼克隆檢測展開研究。
然而這些借助AST的方法都是將樹形結構轉化為一維的序列結構,破壞了元素之間的依賴關系。因此,部分學者提出了不降維而直接處理AST 的樹形深度學習模型。與White 的方法類似,Wan 等人[11]也將代碼分為兩部分作為輸入,使用RNN 對代碼的標志符序列進行編碼得到語義向量;不同的是,Wan等人使用Tree-LSTM模型[30]去處理AST,而并沒有簡單地通過將AST轉化成節點序列的方式得到代碼的結構向量。Wei 等人[31]同樣使用Tree-LSTM 對克隆檢測展開研究。Mou 等人[12]提出了一種基于樹的卷積神經網絡模型TBCNN,該模型直接在AST上進行卷積計算,然后使用動態池化技術將不同規格的AST 壓縮成代碼向量,該向量能夠很好地提取代碼中的結構信息,在104 類代碼分類任務上能夠達到94%的精度。為了解決上述樹神經網絡的問題,一種方法就是獲得代碼的控制流圖以及數據依賴圖,靜態地建立節點與節點之間的聯系,將代碼表示成一種圖的數據結構,最終使用圖嵌入技術[32]對代碼進行表示。例如Allamanis等人[33]就通過相同的函數名以及變量名來靜態地建立這些函數以及變量之間存在的依賴關系,Tufano等人[34]則直接構建程序的控制流圖來補充節點之間的控制關系。然而程序中元素的依賴關系往往要借助編譯后代碼的中間表示或者字節碼才能得到[35]。在現實環境中,很多代碼不能夠被編譯,因此這些方法所適用的范圍將會受到很大的限制,而且代碼圖結構的定義以及對于圖特征的提取也十分依賴專家的領域知識,模型往往很復雜,設計代價較高。
對比這些工作,本文的工作集中于借助AST 對代碼的結構以及序列信息進行提取。此外本文提出了一種新的獲得代碼向量的方法,該方法不使用完整的訓練好的代碼分類模型,僅截取模型中的一部分來生成代碼向量,得到的代碼向量可滿足功能越相似,幾何距離就越短的性質。
本文提出了一個基于卷積以及循環神經網絡的自動代碼特征提取模型CVRNN。通過對已經標記類別的代碼訓練編碼器,編碼器將自動地學會如何提取代碼特征,對比當下兩個前沿的代碼分類模型有顯著的優勢,在此過程中嵌入的預訓練詞向量不僅能提高分類的精度,更能夠加快整個模型的擬合速度。在相似代碼搜索任務上,CVRNN無論是對比近幾年提出的前沿的深度學習模型還是廣泛應用于業界的代碼相似度檢測工具都有顯著的優勢。