林鍇,陶傳奇,2,3,4+,黃志球,2,4
1.南京航空航天大學(xué) 計算機科學(xué)與技術(shù)學(xué)院,南京211106
2.南京航空航天大學(xué) 高安全系統(tǒng)的軟件開發(fā)與驗證技術(shù)工信部重點實驗室,南京211106
3.南京大學(xué) 計算機軟件新技術(shù)國家重點實驗室,南京210023
4.軟件新技術(shù)與產(chǎn)業(yè)化協(xié)同創(chuàng)新中心,南京210093
異常是指程序運行過程中出現(xiàn)的不可預(yù)期的錯誤。不適當(dāng)?shù)漠惓L幚矸椒赡軙o意中增加程序故障的風(fēng)險,例如系統(tǒng)崩潰或者內(nèi)存泄漏,因為異常在軟件系統(tǒng)中很難被測試到。因此,有效的異常處理對于軟件開發(fā)來說至關(guān)重要。Sawadpong 等人[1]的研究表明,異常處理產(chǎn)生的缺陷數(shù)量大約是總體缺陷數(shù)量的三倍。
目前大多數(shù)的編程語言,例如Java、C++、Python,都內(nèi)置了異常處理機制來指導(dǎo)開發(fā)人員考慮程序中的異常路徑,防止程序崩潰的發(fā)生。然而,如今的軟件開發(fā)對API(application programming interface)庫的依賴程度很高。一個API 庫中包含了大量的異常類型以及異常處理的規(guī)則,例如Android 框架中就包含了超過260 個異常類型[2]。另外,熱門的API 庫的升級頻率很高并且版本間改變較大。因此,每個方法所產(chǎn)生的異常類型及其處理方式,是軟件開發(fā)過程中的重要活動。研究表明即使是經(jīng)驗豐富的開發(fā)者也經(jīng)常會編寫出錯誤的異常處理代碼[3-4]。
異常處理代碼推薦是指將try 語句塊中捕捉的異常代碼作為上下文信息,并針對上下文信息推薦合適的異常處理代碼。推薦的異常處理代碼可以是具體的語句塊也可以是API 調(diào)用序列。現(xiàn)有的異常處理相關(guān)的推薦方法大多是利用啟發(fā)式的規(guī)則或者統(tǒng)計學(xué)習(xí)的方法進行推薦。CAR-miner[5]通過挖掘try語句中的方法調(diào)用與catch 語句塊中異常處理代碼之間的關(guān)聯(lián)規(guī)則為上下文代碼推薦強相關(guān)的異常處理代碼。FuzzyCatch[6]則是利用模糊邏輯理論分析每個API 調(diào)用可能產(chǎn)生的各種異常的概率,并根據(jù)異常發(fā)生概率的高低來檢測代碼可能存在的缺陷。除此之外,F(xiàn)uzzyCatch 還會為存在缺陷的代碼推薦具體的異常處理API。以上方法大多從詞法層面挖掘代碼中的特征信息,忽略了語義信息。隨著深度學(xué)習(xí)的發(fā)展,軟件開發(fā)也逐漸走向智能化。一些研究將長短期記憶網(wǎng)絡(luò)(long short-term memory,LSTM)應(yīng)用于源代碼中[7],LSTM 處理過長輸入序列時,存在梯度爆炸以及梯度消失的問題[8]。自注意力網(wǎng)絡(luò)[9]很好地解決了這一問題。目前,自注意力網(wǎng)絡(luò)已廣泛應(yīng)用于自然語言處理領(lǐng)域。本文將自注意力網(wǎng)絡(luò)應(yīng)用于源代碼中,提出了一種基于自注意力網(wǎng)絡(luò)的異常處理代碼推薦方法,即DeepEHCR。該方法不同于現(xiàn)有方法的地方在于它利用深度學(xué)習(xí)技術(shù)挖掘異常代碼中各API 調(diào)用之間的深度特征信息進行異常處理的推薦。首先,DeepEHCR 利用API 調(diào)用樹結(jié)構(gòu)對上下文代碼進行建模,并采用基于結(jié)構(gòu)遍歷算法將樹轉(zhuǎn)化為序列的同時充分保留樹中的結(jié)構(gòu)信息。其次,借鑒Li 等人[10]的方法,將異常處理的策略分為三類:處理異常、記錄日志以及重新拋出。DeepEHCR 利用自注意力網(wǎng)絡(luò)學(xué)習(xí)API 調(diào)用樹中的特征信息,并根據(jù)這些信息推薦異常處理的策略。最后,針對處理異常這一策略太過抽象、缺乏針對性的問題,DeepEHCR利用Transformer 模型進一步推薦具體的異常處理API 序列。為了驗證DeepEHCR 模型的有效性,分別對其異常處理策略推薦功能以及異常處理API 序列推薦功能進行大規(guī)模實驗。本文從GitHub 中收集了1 524 個Android 項目,其中包含了55 763 個含有異常處理的代碼塊。這些代碼按照9∶1 的比例劃分為訓(xùn)練集與測試集。實驗結(jié)果表明DeepEHCR 在異常處理策略推薦方面,Accuracy、Precision、Recall 以及F1-score 的值分別達到了89.78%、89.98%、89.34%以及89.59%。在API 序列推薦方面,Hit@1/3/5 的值分別達到了57.83%、69.73%、74.79%。最后,本文還將DeepEHCR 的整體性能和當(dāng)前現(xiàn)有工作進行對比,DeepEHCR 在修復(fù)真實的異常漏洞方面明顯地優(yōu)于CARMiner[5]以及FuzzyCatch[6]。
本文的主要貢獻包括:
(1)提出了一種新型的API 調(diào)用模型,該API 調(diào)用模型根據(jù)代碼中的嵌套關(guān)系構(gòu)建API 調(diào)用樹以充分保留代碼的結(jié)構(gòu)信息。
(2)提出了一種基于自注意力網(wǎng)絡(luò)的智能化異常處理代碼推薦方法,該方法通過挖掘上下文代碼中的深層次特征信息來推薦異常處理策略以及異常處理代碼。
程序異常在軟件工程領(lǐng)域被廣泛地關(guān)注。許多研究人員對異常處理的代碼與軟件的魯棒性的關(guān)系進行了研究[11-13]。Osman 等人[12]研究了異常代碼的不同修改方式會對軟件系統(tǒng)的魯棒性產(chǎn)生怎樣的影響。Marinescu[13]驗證了使用到異常的類要比未使用異常的類更復(fù)雜,并且未正確處理異常的類要比正確處理異常的類出現(xiàn)漏洞的可能性高很多。
異常處理的復(fù)雜性導(dǎo)致開發(fā)者們在異常處理過程中產(chǎn)生了大量漏洞[10]。因此,近些年異常處理[5-6,14-16]引起了研究人員的較大關(guān)注。他們通過分析異常處理代碼來幫助開發(fā)者們理解程序中的異常鏈以及編寫異常處理的方式。
Li等人[10]將異常處理的策略共分為了四類:處理異常、記錄日志、拋出和包裹并重新拋出。他們通過異常代碼來推薦相關(guān)的異常處理策略。由于Li等人[10]是將整個方法塊作為上下文代碼,而本文默認(rèn)捕獲異常并且將try 語句塊內(nèi)代碼作為上下文信息,Deep-EHCR 沒有考慮拋出這一種情況。Zhong 等人[14]提出了一種方法Mono,用于修復(fù)異常相關(guān)的漏洞。它首先挖掘異常和修復(fù)方式之間的關(guān)聯(lián),然后建立每種異常的修復(fù)模型。CARMiner[5]利用代碼搜索引擎從現(xiàn)有開源項目中收集相關(guān)的代碼示例以增加數(shù)據(jù)量,并且結(jié)合一種新的挖掘算法,以序列關(guān)聯(lián)的形式檢測異常處理規(guī)則。DeepEHCR不同于CARMiner的地方在于它不僅考慮了上下文代碼中的API 調(diào)用信息,而且捕捉了其中的結(jié)構(gòu)信息。DeepEHCR 通過API 調(diào)用樹挖掘異常代碼中API 調(diào)用的潛在關(guān)聯(lián)來推薦相關(guān)的異常處理代碼。FuzzyCatch[6]利用模糊邏輯的算法預(yù)測代碼中可能會發(fā)生的運行時異常,并且推薦相關(guān)的異常處理代碼來修復(fù)異常。Fuzzy-Catch 通過分析每個API 調(diào)用產(chǎn)生異常的概率以及每種API 調(diào)用產(chǎn)生異常后對應(yīng)的異常處理API 調(diào)用概率來預(yù)測異常以及推薦異常處理的API。它將上下文中的API 調(diào)用相互獨立開進行分析,而DeepEHCR通過API 調(diào)用樹結(jié)構(gòu)充分考慮了上下文中各API 之間的潛在關(guān)聯(lián)??紤]到異常處理中可能存在多個API 調(diào)用,DeepEHCR 推薦的異常處理代碼為API 序列而不僅僅是單個API。
還有一些研究分析了異常對于程序全局的影響。Robillard 等人[17]針對當(dāng)前Java 的異常處理機制,認(rèn)為異常是全局問題并且很難提前定位異常源。Cacho等人[18]針對異常處理提出了一種面向切面的模型。該模型能夠顯式描述異??刂屏鞯娜忠晥D。DeepEHCR不同于這些方法的地方在于它是從局部出發(fā),以try語句塊作為上下文信息來推薦異常處理代碼。
本章主要對異常處理代碼推薦模型DeepEHCR進行介紹。圖1 展示了DeepEHCR 的整體結(jié)構(gòu)。該模型可以分為三個核心階段:上下文代碼表示、異常處理策略推薦以及異常處理的API 序列推薦。在上下文代碼表示階段,DeepEHCR 首先提取異常位置的上下文代碼,即try 語句塊內(nèi)的代碼。然后將上下文代碼解析成抽象語法樹(abstract syntax tree,AST),并進一步從AST 中提取API 調(diào)用樹。之后利用基于結(jié)構(gòu)的遍歷算法(structure-based traversal,SBT)[19]遍歷API 調(diào)用樹。將遍歷得到的序列與異常類型進行拼接,得到最終的上下文代碼表示形式。在異常處理策略推薦階段,異常處理共分為三種策略:處理異常、記錄日志以及重新拋出。本文采用一種基于自注意力網(wǎng)絡(luò)[9,20]的分類模型來將上下文代碼映射到具體的異常處理策略中。在異常處理API 序列推薦階段,DeepEHCR 會針對處理異常這一策略,利用Transformer 模型[17]推薦具體的API 序列,這些API 序列可以輔助開發(fā)人員編寫正確的異常處理代碼。記錄日志策略和重新拋出策略不需要做進一步API 序列推薦,因為這兩種策略的具體代碼形式相對簡單并且固定,不需要做出具體的代碼推薦。除此之外,記錄日志的方式有很多種,但是它們實現(xiàn)的功能是一樣的,用戶可以根據(jù)策略選擇自己特定的記錄日志方法來記錄日志。
圖1 DeepEHCR 整體框架圖Fig.1 Overall architecture of DeepEHCR
2.1.1 API調(diào)用樹
在做異常處理代碼推薦前,需要對上下文代碼進行特征表示。目前關(guān)于代碼特征表示的研究大多是通過語法分析將代碼轉(zhuǎn)化成AST 的形式[7,21]。但是異常處理的代碼通常會和try 語句塊中特定的API 調(diào)用有關(guān),因此本文采用API 調(diào)用關(guān)系來表示上下文代碼。代碼塊是一種嵌套結(jié)構(gòu),它通過if、for 等控制流語句將代碼中的語句分為不同的層次。圖2 中的代碼示例包含了三個層次:方法體中的代碼塊為第一層;方法體中的for 循環(huán)語句為第二層;for 循環(huán)中的while 語句為第三層。根據(jù)這種層次關(guān)系,本文提出了一種新型的API 調(diào)用樹結(jié)構(gòu)。API 調(diào)用樹相比API調(diào)用序列的優(yōu)勢在于它充分考慮了代碼中的結(jié)構(gòu)特征,因此可以發(fā)掘出更多潛在的特征信息。
圖2 代碼示例Fig.2 Code example
API 調(diào)用樹由控制流節(jié)點和API 調(diào)用節(jié)點組成。本文將控制流節(jié)點分為while、do-while、for、foreach、switch、if六類。它們在代碼中的對應(yīng)關(guān)系如圖3。
圖3 控制流與API調(diào)用節(jié)點對應(yīng)關(guān)系Fig.3 Correspondences between control flow and API call nodes
While 節(jié)點表示一個while 語句,它包含兩個子節(jié)點:Expression 節(jié)點和Block 節(jié)點。其中Expression節(jié)點表示while 語句中的條件表達式,它的子節(jié)點為條件表達式中的API 調(diào)用序列;Block 節(jié)點表示循環(huán)代碼塊,它的子節(jié)點為循環(huán)代碼塊中的API 調(diào)用或者控制流語句對應(yīng)的節(jié)點。Do-while 節(jié)點、For 節(jié)點以及Foreach 節(jié)點和While 節(jié)點類似,都包含Expression和Block 兩個子節(jié)點。Switch 節(jié)點的子節(jié)點個數(shù)由其對應(yīng)的語句塊中case 語句的個數(shù)決定,每一個case語句對應(yīng)一個Case子節(jié)點。If節(jié)點表示if-else語句塊。它包含三個子節(jié)點,其中Expression 節(jié)點表示條件表達式;Block 節(jié)點表示條件表達式為真時執(zhí)行的語句塊;Else節(jié)點為表示條件表達式為假時執(zhí)行的語句塊。
根據(jù)以上API 調(diào)用樹的定義規(guī)則,圖2 中的代碼塊可以表示成圖4 所示的層次結(jié)構(gòu)。
圖4 API調(diào)用樹結(jié)構(gòu)Fig.4 Structure of API call tree
本文利用JDT 工具(http://www.eclipse.org/jdt/)來對代碼進行解析。JDT 為Eclipse 內(nèi)置工具,用于編譯代碼并生成對應(yīng)的AST。根據(jù)代碼的AST 模型,構(gòu)建上下文代碼對應(yīng)的API調(diào)用樹。
相對于AST 表示方法,API 調(diào)用樹能夠大量地減少樹中節(jié)點的個數(shù),還可以過濾掉大量無用的節(jié)點。這些無用的節(jié)點不僅會增加推薦模型的復(fù)雜度,而且會對模型的訓(xùn)練產(chǎn)生干擾。
2.1.2 API調(diào)用樹遍歷
為了將API 調(diào)用樹輸入到自注意力網(wǎng)絡(luò)中,需要通過遍歷API 調(diào)用樹的方式將API 調(diào)用樹轉(zhuǎn)化為序列形式??梢赃x擇先序遍歷這樣的經(jīng)典樹遍歷算法來做序列化處理,但是這樣得到的序列無法重構(gòu)API調(diào)用樹,因此損失了其中的部分結(jié)構(gòu)信息。本文采用Hu 等人[19]提出的SBT 算法,以保證在遍歷API 調(diào)用樹的過程中保留樹中的完整結(jié)構(gòu)信息。圖5 為SBT 的一個簡單示例,它使用一對括號以及根節(jié)點來標(biāo)識一棵樹,然后遞歸地處理子樹并將處理結(jié)果放入括號內(nèi)。
圖5 SBT 的簡單示例Fig.5 Simple example of SBT
最后,本文將上下文代碼對應(yīng)的異常類型與API調(diào)用樹對應(yīng)的序列進行拼接,并通過分隔符進行區(qū)分。拼接后的詞素序列被用來表示上下文代碼。
2.2.1 異常處理策略分類
異常處理策略指的是根據(jù)特定的編程上下文代碼應(yīng)當(dāng)采取的異常處理方式。Li 等人[10]將異常處理模式分為了四類:拋出異常(THROW)、處理異常(HANDLE)、記錄日志或者忽略(LOG&IGNORE)以及捕捉并重新拋出(WRAP&RETHROW)。由于DeepEHCR 是在捕捉了異常代碼塊后執(zhí)行的推薦,本文不考慮第一種情況,即拋出異常。除此之外,本文認(rèn)為代碼中捕捉了異常但不對異常做任何處理的方式是不合理的,因此對記錄日志或者忽略這一類別進行了優(yōu)化,去除了忽略不做處理的情況。為了提高數(shù)據(jù)質(zhì)量,本文也將訓(xùn)練集中這樣的樣本剔除。最終本文將異常處理的策略分成表1 所示的三類。
表1 異常處理策略Table 1 Exception handling strategies
2.2.2 異常處理策略推薦模型構(gòu)建
本小節(jié)將詳細(xì)介紹異常處理策略推薦模型,該模型利用自注意力網(wǎng)絡(luò)從上一階段詞素序列中提取深層特征信息,對異常代碼進行分類,最后將最優(yōu)的異常處理策略推薦給開發(fā)者。自注意力網(wǎng)絡(luò)[9]是為了解決傳統(tǒng)循環(huán)神經(jīng)網(wǎng)絡(luò)(recurrent neural network,RNN)對于過長序列輸入導(dǎo)致梯度爆炸以及梯度消失的問題。它可以很好地處理這種長依賴的情況[8]。除此之外,自注意力網(wǎng)絡(luò)可以采用并行計算的方式來提升訓(xùn)練的速度。
首先需要對輸入序列進行處理,將其轉(zhuǎn)化為對應(yīng)的詞嵌入向量。詞嵌入技術(shù)是將自然語言輸入中的單詞嵌入到低維向量空間的方法[22]。近幾年,該方法也被應(yīng)用到源代碼處理的任務(wù)中,例如代碼推薦[23-24]、注釋生成[25-26]等。一般的做法是采用word2vec[27]詞嵌入方法預(yù)先將每個詞素轉(zhuǎn)化為向量,但是本文的輸入序列是從樹中遍歷得到,相鄰的詞素之間關(guān)系不大,因此將詞嵌入向量作為參數(shù)形式與自注意力網(wǎng)絡(luò)一同訓(xùn)練得到。由于自注意力網(wǎng)絡(luò)中沒有考慮詞素與詞素之間的位置關(guān)系,本文還加入了一個位置嵌入算法計算每個詞素對應(yīng)的位置向量。最后將詞嵌入向量與位置向量相加得到詞素序列對應(yīng)的輸入向量形式X=(x1,x2,…,xn)。
下面將對自注意力網(wǎng)絡(luò)[9]進行詳細(xì)介紹。每層自注意力網(wǎng)絡(luò)都是由一個多頭自注意力子層以及一個前饋網(wǎng)絡(luò)子層構(gòu)成。多頭自注意力子層的結(jié)構(gòu)如圖6 所示。
圖6 多頭自注意力子層Fig.6 Multi-head self-attention sub-layer
多頭自注意力子層使用多個自注意力函數(shù)來學(xué)習(xí)不同位置的不同子空間下的特征信息。由于每頭只專注學(xué)習(xí)所在子空間的特征,效率和學(xué)習(xí)的效果都要比單頭自注意力高很多。多頭自注意力子層的計算公式如下:
其中,Q、K和V分別由輸入矩陣X經(jīng)過不同的線性變換XWQ、XWK和XWV得到,WQ、WK和WV為權(quán)重矩陣,dk為K的維度,KT為K的轉(zhuǎn)置;矩陣將Q、K、V映射到不同的子空間中;Concat(h1,h2,…,hh)函數(shù)表示將參數(shù)中的多個矩陣按照第二維度進行拼接,例如hi∈,則Concat(h1,h2,…,hh)∈。
多頭自注意力子層的輸出會作為前饋網(wǎng)絡(luò)子層的輸入。前饋網(wǎng)絡(luò)子層可以是全連接神經(jīng)網(wǎng)絡(luò)也可以是卷積神經(jīng)網(wǎng)絡(luò)。本文采用全連接網(wǎng)絡(luò)作為前饋網(wǎng)絡(luò)子層。它由兩個線性變換以及一個ReLU 激活函數(shù)組成。
其中,W1、W2為權(quán)值矩陣,b1、b2為偏置項。
為了完成異常處理策略分類任務(wù),本文將自注意力網(wǎng)絡(luò)最后一層的輸出轉(zhuǎn)化為一維的向量,再經(jīng)過線性網(wǎng)絡(luò)以及Softmax 層輸出分類結(jié)果。
其中,view函數(shù)用來重組矩陣,按照第一維的順序?qū)⑵滢D(zhuǎn)化為一維向量。W為權(quán)值矩陣,b為偏置項。
2.2.3 模型訓(xùn)練
本文需要對訓(xùn)練數(shù)據(jù)集進行處理。對于每個訓(xùn)練樣本Code,根據(jù)Code中的異常代碼(try 語句塊內(nèi)的代碼)以及異常類型構(gòu)建上下文信息的序列表示token_seq。異常處理代碼對應(yīng)的異常處理策略strategy則需要進行人工標(biāo)記。為了保證標(biāo)記的準(zhǔn)確性,邀請了三名擁有多年Java 開發(fā)經(jīng)驗的學(xué)生來完成。除此之外,為了保證一定的客觀性,本文還預(yù)先定義了一些標(biāo)準(zhǔn)。例如,如果異常處理代碼中只包含log相關(guān)函數(shù),則將其策略定義為記錄日志。最終訓(xùn)練數(shù)據(jù)Code被表示成二元組形式
如果推薦的異常處理策略是記錄日志或者重新拋出異常,用戶可以輕松地根據(jù)策略來編寫相對應(yīng)的異常處理代碼。但是,如果推薦的異常處理策略是處理異常,情況不會像前兩種那樣簡單,因為用戶只知道需要處理異常,并不知道具體的處理方法。為了更加全面地輔助用戶編寫具體處理異常的代碼,本文還設(shè)計了一種API 序列推薦模型。在上一步得到了具體的策略后,針對處理異常這一策略進一步推薦具體的處理異常的API 序列,用戶可以根據(jù)API序列來構(gòu)建處理異常的代碼。
2.3.1 異常處理的API序列推薦模型構(gòu)建
異常處理的API 序列推薦模型使用的是Transformer模型[9]。該模型被廣泛應(yīng)用到自然語言處理等領(lǐng)域[28]。Transformer 模型主要由編碼器和解碼器兩部分組成。編碼器負(fù)責(zé)對輸入序列進行編碼,并將其映射到固定的維度空間中;解碼器負(fù)責(zé)將編碼后的向量映射為目標(biāo)序列。Transformer 模型的示意圖如圖7 所示。
圖7 Transformer模型Fig.7 Transformer model
2.3.2 模型訓(xùn)練
本文首先需要對訓(xùn)練集進行預(yù)處理。對于每一個訓(xùn)練數(shù)據(jù)Code,分離出異常代碼、異常類型以及異常處理代碼,構(gòu)成三元組形式
Transformer 模型首先將詞素序列token_seq作為輸入,利用編碼器對輸入序列進行編碼。解碼器部分首先會生成特殊API
本文選擇從GitHub(https://www.github.com/)中下載原始代碼數(shù)據(jù)。為了保證代碼的質(zhì)量,利用代碼的獲贊數(shù)作為選擇的標(biāo)準(zhǔn),因為代碼的獲贊數(shù)反映了代碼的關(guān)注程度以及被其他編程人員認(rèn)可的程度。一個代碼的獲贊數(shù)越高,它的質(zhì)量往往也越高。本文只選擇獲贊數(shù)大于100 的Android 項目代碼。如表2 所示,本文最終選取了1 524 個Android 項目,其中包含了55 763 個含有異常處理的代碼塊。另外,本文還對每種策略對應(yīng)的代碼塊數(shù)以及所占的比例進行了統(tǒng)計。其中處理異常策略為19 542個,占總代碼塊數(shù)的35.0%;記錄日志策略為22 443個,占總代碼塊數(shù)的40.2%;重新拋出策略為13 778個,占總代碼塊數(shù)的24.8%。
表2 實驗數(shù)據(jù)介紹Table 2 Introduction of experiment data
為了訓(xùn)練和評估異常處理策略分類模型,對全部的55 763 個代碼塊按9∶1 的比例隨機劃分為訓(xùn)練集和測試集,其中訓(xùn)練集包含50 187 個數(shù)據(jù),測試集包含5 576 個數(shù)據(jù)。由于異常處理API 序列生成模型針對的是處理異常這一策略,本文只選用處理異常策略的19 542 個代碼塊,同樣采用9∶1 的比例進行劃分。最后得到17 588 個訓(xùn)練數(shù)據(jù)以及1 954 個測試數(shù)據(jù)。
本文的實驗硬件環(huán)境為Intel i7-8700 3.2 GHz 處理器、GTX 1070 Ti的GPU以及32 GB內(nèi)存的工作站。DeepEHCR 模型選用Python 語言以及Pytorch(https://pytorch.org/)深度學(xué)習(xí)框架實現(xiàn)。為了訓(xùn)練模型,將訓(xùn)練數(shù)據(jù)隨機打亂并設(shè)置批次大小為64。本文選擇詞素出現(xiàn)的閾值為2,也就是說在訓(xùn)練集中出現(xiàn)次數(shù)大于等于2 的詞素會放入到詞匯表中。序列最大長度設(shè)置為100,對長度大于100 的序列進行截斷,對長度小于100 的序列采用填充的方式補齊。對于網(wǎng)絡(luò)模型的參數(shù),自注意力網(wǎng)絡(luò)的層數(shù)設(shè)置為3,自注意力共有4 個頭,嵌入向量的維度設(shè)置為256,前饋網(wǎng)絡(luò)的維度設(shè)置為了1 024。在模型中增加了Dropout機制使模型具有更好的泛化能力,Dropout被設(shè)置為0.1。表3 展示了詳細(xì)的參數(shù)設(shè)置。
表3 參數(shù)介紹Table 3 Parameter introduction
對于異常處理策略推薦,采用分類任務(wù)公認(rèn)的四個指標(biāo),即準(zhǔn)確率(Accuracy)、精確率(Precision)、召回率(Recall)以及F1 值(F1-score)來評估模型的有效性。
準(zhǔn)確率是指在整個測試數(shù)據(jù)中,模型分類正確的數(shù)量占總數(shù)量的百分比。
其中,Ntotal表示測試數(shù)據(jù)的總數(shù)量,Ncorrect表示測試數(shù)據(jù)中被分類正確的數(shù)量。
精確率和召回率作為評估二分類問題的性能指標(biāo),也可以擴展到多分類問題中。在二分類問題中,精確率是指預(yù)測為正的樣本中實際也為正的樣本占被預(yù)測為正的樣本的比例。
其中,TP表示正樣本中預(yù)測為正的樣本個數(shù),F(xiàn)P表示負(fù)樣本中預(yù)測為正的樣本個數(shù)。
在n分類問題中,每次對一個分類計算,將這一類作為正樣本,其余類作為負(fù)樣本,執(zhí)行一次二分類的精確率計算,最后對n次精確率計算的結(jié)果求加權(quán)平均值得到多分類情況下的精確率。
其中,Ntotal表示測試樣本的總數(shù)量;Label表示所有類別的集合;Nl表示測試樣本中類別為l的樣本個數(shù);Precision2(l)表示類別l為正樣本時的二分類精確率。
召回率在二分類問題中表示正樣本中預(yù)測為正的樣本占所有正樣本的比例。
其中,TP表示正樣本中預(yù)測為正的樣本個數(shù),F(xiàn)N表示正樣本中預(yù)測為負(fù)的樣本個數(shù)。
在n分類問題中,每次對一個分類計算,將這一類作為正樣本,其余類作為負(fù)樣本,執(zhí)行一次二分類的召回率計算,最后對n次召回率計算的結(jié)果求加權(quán)平均值得到多分類情況下的召回率。
其中,Ntotal表示測試樣本的總數(shù)量;Label表示所有類別的集合;Nl表示測試樣本中類別為l的樣本個數(shù);Recall2(l)表示類別l為正樣本時的二分類召回率。
F1 值兼顧了分類模型的精確率和召回率,它可以看作模型精確率和召回率的一種調(diào)和平均值。
對于異常處理的API序列推薦,采用Hit@K(K=1,3,5)來評估模型的性能。
Hit@K(K=1,3,5)指的是推薦的K個結(jié)果中存在正確結(jié)果的樣本占總測試樣本的百分比。
其中,D表示測試數(shù)據(jù)集;rankd表示正確結(jié)果在推薦列表中的排名;I(·)函數(shù)為二值函數(shù),當(dāng)條件為真時值為1,否則值為0。
3.4.1 基準(zhǔn)實驗
為了驗證DeepEHCR 在異常處理策略推薦以及異常處理的API 序列推薦上的性能,本文構(gòu)建了基于LSTM 的異常處理推薦算法。LSTM 是一種循環(huán)神經(jīng)網(wǎng)絡(luò)計算法,常用于自然語言處理當(dāng)中。LSTM 通過引入輸入門、遺忘門以及輸出門來達到門控的目的,從而充分捕捉序列中的關(guān)聯(lián)信息。除此之外,為了驗證API 調(diào)用樹提取的結(jié)構(gòu)特征對于模型性能帶來的優(yōu)勢,本文還構(gòu)建了一個基于簡單API 序列的自注意力網(wǎng)絡(luò)模型來進行對比。該模型沒有使用API調(diào)用樹信息,而是順序地從代碼塊中提取API 序列來表示。
最后,為了驗證DeepEHCR 整體在異常代碼處理上的優(yōu)勢,本文選擇了FuzzyCatch[6]和CarMiner[5]兩個當(dāng)前最新的異常代碼修復(fù)工具。FuzzyCatch[6]通過模糊邏輯算法預(yù)測代碼中可能存在的運行時異常,并推薦相應(yīng)的異常處理API。它不同于DeepEHCR的地方在于它只推薦一個API 作為異常處理代碼。CarMiner[5]通過關(guān)聯(lián)關(guān)系挖掘技術(shù)挖掘try 語句塊中代碼與catch 語句塊中代碼的關(guān)聯(lián)規(guī)則,從而發(fā)現(xiàn)代碼中可能存在的關(guān)于異常的漏洞。為了體現(xiàn)實驗的客觀性,本文選擇了FuzzyCatch[6]中使用的關(guān)于異常漏洞修復(fù)的437 個存在異常的真實代碼作為測試數(shù)據(jù)。對每一個存在異常代碼,DeepEHCR 為其推薦相關(guān)的異常處理策略或者異常處理的API 序列,如果推薦的結(jié)果正確,這表示該異常修復(fù)成功。
3.4.2 異常處理策略推薦性能評估
表4 展示了上文提及的3 種不同異常處理策略推薦模型在準(zhǔn)確率、精確率、召回率以及F1 值上的實驗結(jié)果。LSTM 表示基于LSTM 的異常處理推薦算法,Self-Attention 表示未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)。DeepEHCR 在4 個指標(biāo)上的值都接近90%,說明DeepEHCR 在異常處理策略推薦上的整體性能高,平均每進行10 次異常處理策略的推薦只會出現(xiàn)1 次錯誤。準(zhǔn)確率方面,DeepEHCR 要比LSTM 和未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)分別高15.85 個百分點和4.52個百分點;在F1 值方面,DeepEHCR 相較于LSTM 和未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)分別提升了17.6 個百分點以及4.54 個百分點。DeepEHCR 在異常處理策略推薦方面的性能要優(yōu)于其他方法的原因在于,它不僅充分考慮了上下文信息中的結(jié)構(gòu),而且利用自注意力網(wǎng)絡(luò)來挖掘API 調(diào)用之間的深層次的依賴關(guān)系。
表4 異常處理策略推薦實驗結(jié)果Table 4 Exception handling strategy recommendation result 單位:%
表5 展示了3 種模型在時間性能上的對比結(jié)果。在推薦時間方面,3 個模型在5 576 個測試樣本上完成推薦的總時間差不多,其中DeepEHCR 平均完成一次推薦的時間只需0.038 s。說明DeepEHCR在提升準(zhǔn)確率的同時并沒有增加耗時。
表5 3 種異常處理策略推薦方法的時間性能Table 5 Time performance of 3 exception handling strategy recommendation methods 單位:s
3.4.3 異常處理的API序列推薦性能評估
表6 展示了3 種異常處理的API 序列推薦方法在Hit@K(K=1,3,5)上的實驗結(jié)果。從表中的實驗結(jié)果可知,自注意力網(wǎng)絡(luò)的性能要優(yōu)于LSTM。在充分考慮上下文中的結(jié)構(gòu)信息后,DeepEHCR在Hit@K上的得分相較于LSTM 以及未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)又有了明顯的提升。具體而言,DeepEHCR在Hit@1 上的得分達到了57.83%,相較于LSTM 和自注意力網(wǎng)絡(luò),分別提升了10.77 個百分點以及3.56個百分點。DeepEHCR 的性能優(yōu)于LSTM 的原因在于API 調(diào)用樹SBT 后的序列要比正常序列長一倍以上,LSTM 很難捕捉這種長序列中的依賴關(guān)系,而自注意力網(wǎng)絡(luò)通過多頭自注意力的計算避免了長依賴消失的問題。綜上所述,DeepEHCR在異常處理的API序列推薦任務(wù)上對比其余兩種方法有明顯的優(yōu)勢。
表6 3 種推薦方法在Hit@K 上的得分Table 6 Hit@K of 3 recommendation methods 單位:%
表7 展示了3 個模型在生成API 序列上的時間耗費。在推薦時間方面,3 個模型在1 954 個測試樣本上完成推薦的總時間分別為271.1 s、301.5 s 以及314.2 s。平均每完成一次推薦花費的時間分別為0.139 s、0.154 s 以及0.160 s,雖然DeepEHCR 最慢,但是這種差距幾乎可以忽略不計。
表7 3 種API序列推薦方法的時間性能Table 7 Time performance of 3 API sequence recommendation methods 單位:s
3.4.4 DeepEHCR 整體性能評估
本小節(jié)從DeepEHCR 整體出發(fā),分析其面對真實異常代碼的情況下推薦的性能。表8 展示了DeepEHCR 和其他兩個對比方法在修復(fù)真實的異常漏洞方面的實驗結(jié)果。在所有439 個存在異常漏洞的代碼中,DeepEHCR 成功推薦了320 個異常處理代碼用于修復(fù)異常漏洞,修復(fù)的準(zhǔn)確率達到了72.8%,而CarMiner 和FuzzyCatch 分別成功修復(fù)了196 個(44.6%)異常漏洞以及287 個(65.4%)異常漏洞。實驗表明DeepEHCR 在整體異常處理代碼推薦的性能方面明顯好于其余兩個對比方法。
表8 異常漏洞修復(fù)結(jié)果Table 8 Results in fixing exception bugs
針對API 庫的更新速度快、學(xué)習(xí)正確異常處理成本高的問題,本文提出了一種基于自注意力網(wǎng)絡(luò)的智能化異常處理代碼推薦方法DeepEHCR。該方法構(gòu)建API 調(diào)用樹來表示上下文信息,并利用自注意力網(wǎng)絡(luò)推薦相應(yīng)的異常處理策略。針對處理異常這一具體策略,利用Transformer 模型進一步推薦處理異常相關(guān)的API 序列。實驗表明DeepEHCR 能夠有效提取上下文信息并做出正確的異常處理代碼推薦。
在未來的工作中,會優(yōu)化推薦的異常處理代碼形式,在異常處理的代碼中增加結(jié)構(gòu)信息。除此之外,為了更全面地分析模型的優(yōu)勢,會考慮構(gòu)建不同編程語言的推薦模型。