邊奕心,李禹齊,張子恒,趙 松,尹啟天,李文淵
(哈爾濱師范大學(xué) 計算機(jī)科學(xué)與信息工程學(xué)院,哈爾濱 150025)
代碼異味(Code Smell)又被稱為代碼壞味道,是由Martin Fowler于1999年提出的,表示存在于程序中的結(jié)構(gòu)不良的代碼段[1].代碼異味對軟件的可理解性和可維護(hù)性產(chǎn)生諸多負(fù)面影響.這種由Martin Fowler提出的異味也被稱為面向?qū)ο蟠a異味[2].隨著新技術(shù)的不斷涌現(xiàn)和代碼異味研究的持續(xù)發(fā)展,代碼異味不再局限于表示面向?qū)ο蟠a中的潛在問題,已經(jīng)拓展到眾多領(lǐng)域[3],如安卓/IOS應(yīng)用程序[4,5]、測試[6]及SQL[7]等代碼異味.隨著科學(xué)技術(shù)的迅猛發(fā)展,Android應(yīng)用程序由于其開放性和易用性,被廣泛應(yīng)用到人們的日常生活中.研究表明,代碼異味不僅僅存在于傳統(tǒng)的桌面應(yīng)用程序中,也存在于Android應(yīng)用程序中[2].與傳統(tǒng)桌面應(yīng)用程序相比,Android應(yīng)用程序沒有主函數(shù),程序執(zhí)行入口是由Activity所提供的;Android應(yīng)用程序沒有J2SE、API、Swing和JavaFX,而且Android應(yīng)用程序上的GUI是通過XML來聲明的等.以上差異導(dǎo)致Android應(yīng)用程序中不僅存在傳統(tǒng)面向?qū)ο蟠a異味,還存在著Android特有的代碼異味(Android-specific smells).2014年Reimann[4]等人提出了Android特有代碼異味并給出了詳細(xì)的異味列表.這種異味的存在對Android應(yīng)用程序產(chǎn)生了諸多負(fù)面影響,比如能耗、安全性、穩(wěn)定性、內(nèi)存和啟用時間等,極大降低了用戶體驗,不利于Android應(yīng)用程序的維護(hù)和演化[8].Manman等人的研究表明[9],可以使用傳統(tǒng)的桌面應(yīng)用程序中代碼異味檢測工具檢測Android應(yīng)用程序中的面向?qū)ο蟠a異味.但是,研究表明[10],由于Android應(yīng)用程序與傳統(tǒng)面向?qū)ο髴?yīng)用程序的結(jié)構(gòu)不同,因此,傳統(tǒng)檢測工具不能檢測Android特有代碼異味.
Android特有代碼異味共包括30種味道,本文針對其中一種異味,緩慢循環(huán),展開深入研究,研究其自動化檢測方法.緩慢循環(huán)是Android應(yīng)用程序中的傳統(tǒng)for循環(huán)語句.在Android應(yīng)用程序中,for-each循環(huán)語句比傳統(tǒng)for循環(huán)語句的處理速度快,因此,傳統(tǒng)for循環(huán)語句被認(rèn)為是代碼異味[4].目前檢測Android特有代碼異味效果最好的工具是DAAP[10],該工具采用傳統(tǒng)檢測工具普遍使用的程序靜態(tài)分析技術(shù),定義啟發(fā)式檢測規(guī)則,通過遍歷抽象語法樹,實現(xiàn)異味的檢測.
近幾年,深度學(xué)習(xí)在自然語言處理和圖像分類等領(lǐng)域成績卓著.軟件工程領(lǐng)域的很多學(xué)者嘗試使用不同的深度學(xué)習(xí)方法或者機(jī)器學(xué)習(xí)技術(shù)解決代碼異味問題,取得了良好的效果[8-13].然而,已有研究都關(guān)注面向?qū)ο蟮拇a異味研究,還沒有使用深度學(xué)習(xí)方法檢測Android特有代碼異味的相關(guān)研究.因此,本文使用深度學(xué)習(xí)方法檢測緩慢循環(huán)這種Android特有異味.此外,為了獲取模型所需的大量標(biāo)簽數(shù)據(jù),本文提出一種基于Android開源項目構(gòu)造正負(fù)樣本的方法,并實現(xiàn)工具ASSD,實現(xiàn)樣本的自動生成和標(biāo)注.
Android特有代碼異味,也被稱為安卓代碼壞味[3],是Android應(yīng)用程序中存在設(shè)計缺陷的代碼段[2].2014年Reimann等人[4]提出“質(zhì)量味道”的概念,用以表示Android應(yīng)用程序中影響軟件質(zhì)量的代碼結(jié)構(gòu),并具體列出了30種質(zhì)量味道.這些質(zhì)量味道被后續(xù)的研究稱為Android特有代碼異味[2,3,8,14].這種異味對Android應(yīng)用程序產(chǎn)生了諸多負(fù)面影響,比如能耗、安全性、穩(wěn)定性、內(nèi)存和啟用時間等,不利于Android應(yīng)用程序的維護(hù)和演化[15].根據(jù)Manman等人的研究[9],傳統(tǒng)檢測工具不能檢測Android特有代碼異味,因此,自2014年之后,學(xué)者們提出不同的方法檢測Android特有代碼異味.Palomba等人[2]提出將代碼度量與簡單文本比較相結(jié)合的方法檢測15種Android代碼異味,并實現(xiàn)了工具aDoctor.盡管aDoctor是免費的軟件,但其源代碼并不公開,使用者不能在其基礎(chǔ)上根據(jù)需要進(jìn)行功能擴(kuò)展.Iannone等人[17]將aDoctor進(jìn)展擴(kuò)展,對5種與能量相關(guān)的Android特有代碼異味進(jìn)行重構(gòu).為了檢測更多種類的Android代碼異味,Rasool等人[7]定義啟發(fā)式檢測規(guī)則,結(jié)合代碼度量對25種異味進(jìn)行檢測,并實現(xiàn)工具DAAP.工具DAAP在檢測異味種類與準(zhǔn)確度方面都優(yōu)于aDoctor,并公開源代碼,便于后續(xù)研究者的使用和功能擴(kuò)展.
緩慢循環(huán)(Slow Loop,簡稱SL)是Reimann等人[3]提出的30種Android特有代碼異味的一種,同時也是存在于程序中數(shù)量較多的一種味道(僅次于忽略成員的方法味道)[8].SL是Android應(yīng)用程序中的傳統(tǒng)for循環(huán)語句.由于這種循環(huán)語句處理數(shù)據(jù)的速度比for-each循環(huán)語句慢,所以,在Android應(yīng)用程序中,被認(rèn)為是異味.SL異味的重構(gòu)方式是通過迭代器模式來實現(xiàn)循環(huán)可以將其轉(zhuǎn)變?yōu)閒or-each循環(huán).
目前主要采用傳統(tǒng)的靜態(tài)程序分析方法檢測SL異味.Nucci等人[2]認(rèn)為傳統(tǒng)的for循環(huán)處理數(shù)據(jù)速度較慢,因此Android開發(fā)者應(yīng)該盡量使用增強版本的循環(huán)來提高應(yīng)用程序的效率,使用aDoctor檢測器將使用傳統(tǒng)for循環(huán)的所有方法識別為SL異味[2].Rasool等人[10]根據(jù)代碼異味的不同特征與定義選擇不同的度量,以預(yù)設(shè)閾值的方式為各個異味制定不同的檢測規(guī)則,來檢測異味SL,并實現(xiàn)了工具DAAP.通過實際運行程序,本文發(fā)現(xiàn)傳統(tǒng)的靜態(tài)程序分析檢測方法存在兩個問題:首先,根據(jù)檢測規(guī)則,只要是存在傳統(tǒng)for循環(huán)的方法,都被認(rèn)為是異味,而實際中并不是所有的傳統(tǒng)for循環(huán)都適合重構(gòu)為for-each循環(huán),因此,誤檢率較高.其次,傳統(tǒng)檢測方法只能定位到異味所在的類,而一個類中含有多個方法,使用者不能確定是哪個方法包含異味,還需要手動挑選.
隨著機(jī)器學(xué)習(xí)技術(shù)的迅速發(fā)展,及以Github開源代碼庫為支撐的“Big Code”的出現(xiàn)[18],軟件工程領(lǐng)域的學(xué)者們嘗試使用機(jī)器學(xué)習(xí)或者深度學(xué)習(xí)方法檢測代碼異味.Amorim等人[19]在4個中等規(guī)模的開源項目中證實了Kreimer[20]最初提出使用決策樹在兩個小型軟件系統(tǒng)上檢測長方法異味,發(fā)現(xiàn)這種預(yù)測模型可以獲得較高的精度.Khomh等人[8,21]提出使用貝葉斯信念網(wǎng)絡(luò)來檢測開源程序上的代碼異味實例.Fontana等人[11]使用一套16種機(jī)器學(xué)習(xí)模型來檢測4種代碼異味,在此研究中,所有的機(jī)器學(xué)習(xí)者都在交叉項目場景中獲得了高性能,其中J48和隨機(jī)森林分類器獲得了最高的精度.以上研究所用的特征集都是傳統(tǒng)的代碼度量.
隨著深度學(xué)習(xí)在各個領(lǐng)域的成功應(yīng)用,近年來,很多學(xué)者將深度學(xué)習(xí)技術(shù)引入了代碼異味檢測研究中.劉輝等人[13]提出一種基于卷積神經(jīng)網(wǎng)絡(luò)的方法檢測上帝類異味,檢測結(jié)果獲得了大幅度提升.在此基礎(chǔ)上,他們將卷積神經(jīng)網(wǎng)絡(luò)和長短時記憶網(wǎng)絡(luò)相結(jié)合,檢測4種代碼異味[22].以上兩項研究在訓(xùn)練模型時,不僅使用了傳統(tǒng)的代碼度量,還結(jié)合了代碼文本信息(變量的標(biāo)識符).Sharma等人[23]分別使用卷積神經(jīng)網(wǎng)絡(luò)、循環(huán)神經(jīng)網(wǎng)絡(luò)和自編碼器對4種代碼異味進(jìn)行檢測.Sharma認(rèn)為使用代碼度量會導(dǎo)致模型學(xué)習(xí)過程受限于度量信息,無法獲得度量以外的源代碼特征,同時,即便使用相同的度量,因為閾值的不同,導(dǎo)致不同檢測方法的檢測結(jié)果存在較大的差異.另外,度量選取的主觀性也會影響檢測效果[23].因此,Sharma沒有使用任何傳統(tǒng)的度量信息,而是使用程序文本信息作為模型的輸入.Sharma使用的程序文本信息不同于劉輝[22]使用的文本信息,Sharma將源代碼轉(zhuǎn)換為整數(shù),以數(shù)字向量形式表示程序的文本信息,這種文本信息涵蓋了程序的全部信息.本文借鑒這種方法,獲取源代碼文本特征.
綜上所述,已有使用深度學(xué)習(xí)或者機(jī)器學(xué)習(xí)檢測代碼異味的方法都只關(guān)注面向?qū)ο蟠a異味.還沒有使用深度學(xué)習(xí)檢測Android特有代碼異味的相關(guān)研究.因此,為了彌補傳統(tǒng)基于程序靜態(tài)分析的檢測方法的不足,提高Android特有代碼異味的檢測精度,本文提出一種基于深度學(xué)習(xí)的SL檢測方法.此外,為了準(zhǔn)確快速獲得學(xué)習(xí)模型所需的大量標(biāo)簽數(shù)據(jù),本文提出一個基于Android項目自動生成正負(fù)樣本集的方法并實現(xiàn)工具ASSD,自動生成學(xué)習(xí)模型需要的大量標(biāo)簽數(shù)據(jù).
本文提出的基于深度學(xué)習(xí)的SL檢測方法如圖1所示.首先,以開源的Android應(yīng)用程序作為實驗所需的代碼語料庫.使用工具ASSD對語料庫中的程序進(jìn)行SL異味檢測,自動將Java源程序中的每個正樣本(含有SL的代碼段)和負(fù)樣本(沒有SL的代碼段)提取出來生成對應(yīng)的文件,生成標(biāo)簽樣本集,即正樣本集合與負(fù)樣本集合.接著使用工具Tokenizer將標(biāo)簽樣本集中的源代碼轉(zhuǎn)換為整數(shù),以數(shù)字向量的形式表示程序的文本信息,作為模型輸入的特征集.深度神經(jīng)網(wǎng)絡(luò)分類器的預(yù)期輸出為樣本的標(biāo)簽(即是否為SL異味).經(jīng)過50次訓(xùn)練驗證后,最終獲得被訓(xùn)練好的分類器.然后通過測試集測試分類器性能,并給出代碼異味的檢測結(jié)果.后續(xù)章節(jié)將依次介紹每個關(guān)鍵步驟的具體細(xì)節(jié).

圖1 基于深度學(xué)習(xí)的SL檢測方法概圖Fig.1 Overview of SL detection method based on deep learning
本節(jié)詳細(xì)介紹生成標(biāo)簽數(shù)據(jù)集的過程及使用的工具.
2.2.1 實驗對象獲取
在開源代碼庫AndroZooOpen[24]中下載Android應(yīng)用程序,作為備選的代碼語料庫.AndroZooOpen是一個公開的Android應(yīng)用程序集合,包括Gitbub、F-Droid和Google Play在內(nèi)的多個數(shù)據(jù)來源.AndroZooOpen目前包含76466個由不同語言開發(fā)的Android 應(yīng)用程序.本文選擇主要由Java語言開發(fā)的Android應(yīng)用程序,因為Java 是目前移動應(yīng)用軟件開發(fā)的主流語言之一,而且目前Android特有代碼異味的檢測工具只能檢測由Java語言開發(fā)的應(yīng)用程序.
首先,從AndroZooOpen下載110個開源Android應(yīng)用程序.然后,使用工具RepoReapers去除低質(zhì)量的Android應(yīng)用程序.RepoReapers可以從8個方面對AndroZooOpen中的程序質(zhì)量進(jìn)行評分(分別是體系結(jié)構(gòu)、社區(qū)、持續(xù)集成、文件、提交歷史、許可證、問題和單元測試).如果待選應(yīng)用程序在8個方面的評分均大于零,本文認(rèn)為該程序是合適的.按照這些標(biāo)準(zhǔn)經(jīng)過篩選,最終確定70個Android應(yīng)用程序作為代碼語料庫,這70個應(yīng)用程序共有747264行代碼,9574個類和87320個方法.
2.2.2 樣本生成
在深度學(xué)習(xí)的傳統(tǒng)應(yīng)用領(lǐng)域,研究者可以免費獲得大量帶有標(biāo)簽的圖像數(shù)據(jù)集,用以構(gòu)建模型的訓(xùn)練集.而在代碼異味檢測研究中,目前還沒有類似的資源可供研究者使用[13].為了解決這個問題,劉輝團(tuán)隊和Sharma團(tuán)隊利用已有代碼異味檢測工具的輸出結(jié)果,構(gòu)建模型的訓(xùn)練集.本文借鑒這種方法,使用工具ASSD檢測異味SL并自動生成正、負(fù)樣本集.
本文使用的工具ASSD是在檢測工具DAAP的基礎(chǔ)上對其功能進(jìn)行擴(kuò)展,實現(xiàn)從異味檢測到樣本生成自動完成(它沒有改變DAAP的異味檢測規(guī)則,為區(qū)別于原DAAP工具,本文將改進(jìn)后的工具稱為ASSD).待分析的Java源代碼經(jīng)過JavaParser進(jìn)行解析生成抽象語法樹,然后,根據(jù)檢測規(guī)則遍歷語法樹,輸出異味.圖2中虛線部分所示為DAAP工具,虛線外的功能是本文擴(kuò)展的功能.

圖2 自動生成正負(fù)樣本的流程圖Fig.2 Flowchart of automatic generating of positive and negative sample sets
DAAP是現(xiàn)有Android特有代碼異味檢測工具中檢測異味種類最多、效果最好的工具,可以檢測包括SL在內(nèi)的25種Android特有代碼異味.Rasool[10]等人使用DAAP檢測4種應(yīng)用程序中SL異味的F1值,平均可以達(dá)到97.25%.同時DAAP是完全開源的工具,使用者不僅可以免費獲得,而且可以在其基礎(chǔ)上進(jìn)行功能擴(kuò)展,以滿足不同的需求.因此,本文首選DAAP檢測SL異味.但是,DAAP中檢測SL規(guī)則是將檢測到的所有傳統(tǒng)for循環(huán)都認(rèn)為SL.但在實際中,并不是所有傳統(tǒng)的for循環(huán)都適合改寫成for-each,這種不適合改動的傳統(tǒng)for循環(huán)語句并不屬于SL異味.此外,由于DAAP的檢測結(jié)果只能定位到類級別,不能定位到SL異味存在的語句,不利于后續(xù)重構(gòu)的操作.基于以上原因,本文在DAAP的基礎(chǔ)上,對其功能進(jìn)行擴(kuò)展,開發(fā)了工具ASSD.ASSD不僅可以將檢測出的SL異味定位到所在的代碼段,而且還可以自動生成正負(fù)樣本集合.因此,ASSD從異味檢測到樣本生成完全自動完成,從而獲取到充足的緩慢循環(huán)異味標(biāo)簽樣本集.算法1所示為正負(fù)樣本自動生成的過程,主要通過文件操作將檢測結(jié)果輸出,生成正負(fù)樣本集合.自動生成正負(fù)樣本步驟如下:
1)分別創(chuàng)建存儲正樣本的文件目錄smellyFile和存儲負(fù)樣本的文件目錄nonsmellyFile(對應(yīng)算法1第2~5行、第12~15行).
2)遍歷smellySets,將每一個傳統(tǒng)for 循環(huán)語句寫入一個.java文件,生成正樣本集合(對應(yīng)算法1第6~11行).
3)遍歷nonsmellySets,將每一個for-each 語句寫入一個.java文件,生成負(fù)樣本集合(對應(yīng)算法1第16~20行).
算法1.正負(fù)樣本自動生成
Input:smellySets,nonsmellySets
Output:smellyFiles,nonsmellyFiles
1.String folderPath=DRIVE+"\AndroidApp"
2.File file=new File(folderPath+"\"+smellyName)
3.if !file.exists()then
4. file.mkdirs()
5.endif
6.Stringfilepath=folderPath+"\"+smellName+"\"+filename+".java"
7. BufferedWriter bw=new BufferedWriter(new FileWriter(filepath))
8.foreach forStmt in smellySetsdo
9. bw.write(forStmt);
10.endfor
11.bw.close()
12.Filefile_1=newFile(folderPath+"\"+nonsmellySets)
13.if!file_1.exists()then
14. file_1.mkdirs()
15.endif
16.Stringfilepath=folderPath+"\Not_"+smellName+"\"+filename+".java"
17. BufferedWriter bw_1=new BufferedWriter(new FileWriter(filepath))
18.foreach forEachStmt in nonsmellySetsdo
19. bw_1.write(forEachStmt)
20.endfor
21.bw_1.close()
異味檢測工具ASSD的輸出結(jié)果是程序代碼,而深度學(xué)習(xí)模型需要的輸入是數(shù)字向量,因此,為了使用深度學(xué)習(xí)算法進(jìn)行異味檢測,需要將程序代碼轉(zhuǎn)換成數(shù)字向量.Sharma等人[23]認(rèn)為,只使用代碼度量作為特征集,模型受限于度量信息,無法學(xué)習(xí)到度量之外的程序特征.因此,Sharma使用工具Tokenizer將源代碼轉(zhuǎn)換為整數(shù),用數(shù)字向量表示程序文本信息,并將這些數(shù)字向量作為模型輸入的特征集.Tokenizer是一種開源工具,它提供了可以將源代碼轉(zhuǎn)換為整數(shù)的功能,其中不同的代碼信息匹配不同的整數(shù),圖3所示為一個SL異味方法的java源代碼由Tokenizer轉(zhuǎn)化成相對應(yīng)的整數(shù).

圖3 Tokenizer標(biāo)記SL示例Fig.3 Tokens generated by Tokenizer for SL example
由于緩慢循環(huán)異味存在于特殊的代碼段,與所在類或方法前后無密切聯(lián)系,而且作為深度神經(jīng)網(wǎng)絡(luò)模型輸入時,全部代碼文本過于冗長可能導(dǎo)致維度爆炸,所以需要對代碼文本進(jìn)行一部分的預(yù)處理操作.本文使用工具ASSD直接從代碼文本中提取出與緩慢循環(huán)代碼異味相關(guān)的代碼段作為特征集合,去除與學(xué)習(xí)任務(wù)無關(guān)的特征,以便降低模型構(gòu)建的難度.此外,在對Tokernizer提取的代碼文本特征統(tǒng)計和分析中發(fā)現(xiàn),80%的方法中不會超過66個元素.在本文中將模型輸入的單個樣本中的代碼文本特征個數(shù)固定在66個,即只針對被檢測樣本中的66個元素進(jìn)行預(yù)處理,將樣本中元素少于66個以全零向量來做補零擴(kuò)展.
本文使用兩種常用的深度神經(jīng)網(wǎng)絡(luò)模型進(jìn)行異味檢測,分別為卷積神經(jīng)網(wǎng)絡(luò)模型和循環(huán)神經(jīng)網(wǎng)絡(luò)模型,這兩種模型既是經(jīng)典的深度神經(jīng)網(wǎng)絡(luò)模型,也是在異味檢測中使用頻率最高的模型[20].本節(jié)詳細(xì)介紹這兩種模型的結(jié)構(gòu).
2.4.1 卷積神經(jīng)網(wǎng)絡(luò)模型
卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Networks,CNN)是深度學(xué)習(xí)的代表算法之一,是一種包含卷積計算的前饋神經(jīng)網(wǎng)絡(luò).本文使用的CNN模型結(jié)構(gòu)如圖4所示,表1為CNN模型結(jié)構(gòu)各層數(shù)選定的參數(shù)值.首先,將文本信息轉(zhuǎn)換成數(shù)值信息,然后,輸入到嵌入層(Embedding),進(jìn)行重新編碼等操作.為了避免輸入數(shù)組中填充零產(chǎn)生的噪聲,在嵌入層中將mask_zero參數(shù)設(shè)置為True,只考慮輸入數(shù)據(jù)的有意義部分.接著,進(jìn)入到卷積層(Convolution 1D)和最大池化層(Max pooling),卷積層根據(jù)指定的濾波器(filter)和內(nèi)核參數(shù)進(jìn)行卷積運算,相應(yīng)地計算到下一層的網(wǎng)絡(luò)權(quán)重,最大池化層降低了矩陣的維度進(jìn)而減小計算量和消除了數(shù)據(jù)噪音.最后一個池化層的輸出連接到Dropout層,根據(jù)相應(yīng)的概率刪除一部分的神經(jīng)元,然后開始訓(xùn)練,更新沒有被刪除的神經(jīng)元以及權(quán)重的參數(shù)將其保留,以防止過度擬合等問題.該層的脫落率設(shè)置為0.5,表示要被拿掉的神經(jīng)元是隨機(jī)選擇的,概率為0.5.Dropout層的輸出被輸入到Flatten層把多維的數(shù)據(jù)一維化,最后輸入到第一個全連接層(Dense),本全連接層激活函數(shù)為relu函數(shù)輸出一組包含800個輸出的數(shù)據(jù).再由一層全連接層映射到最終的Sigmoid輸出層,輸出層激活函數(shù)為sigmoid函數(shù),輸出維度為1,以便根據(jù)所調(diào)查的異味來預(yù)測一個實例是否屬于該異味.最終選取的模型損失函數(shù)(loss function)為binary_crossentropy函數(shù),優(yōu)化器(optimizer)為RMSProp,迭代次數(shù)(epoch)為50次.

表1 CNN模型層數(shù)參數(shù)值Table 1 CNN model layer parameter value

圖4 基于CNN的神經(jīng)網(wǎng)絡(luò)分類器Fig.4 Classifier based on CNN model
2.4.2 循環(huán)神經(jīng)網(wǎng)絡(luò)模型
循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrant Neural Networks,RNN)也是深度學(xué)習(xí)的代表算法之一,用以處理序列數(shù)據(jù).如圖5所示為檢測SL異味的RNN模型的體系結(jié)構(gòu)圖,表2為RNN模型結(jié)構(gòu)各層數(shù)選定的參數(shù)值,該結(jié)構(gòu)受到自然語言建模中RNN模型的啟發(fā),由一個嵌入層、一個長短時記憶網(wǎng)絡(luò)(Long Short-Term Memory,簡稱LSTM)、Flatten層和全連接層組成.同CNN模型類似,以數(shù)值信息作為輸入經(jīng)過嵌入層進(jìn)入到LSTM層.原始的RNN在訓(xùn)練中,隨著訓(xùn)練時間的加長以及網(wǎng)絡(luò)層數(shù)的增多,容易出現(xiàn)梯度爆炸或梯度消失的問題,導(dǎo)致無法處理較長序列數(shù)據(jù),從而無法獲取長距離數(shù)據(jù)的信息,為了解決這些問題,本文選用了長短時記憶網(wǎng)絡(luò).將LSTM層的輸出維度設(shè)置成128,經(jīng)過Flatten層輸入到3個全連接層,前兩層激活函數(shù)為relu函數(shù),最后輸出層激活函數(shù)為sigmoid函數(shù),最終輸出一維向量.與CNN模型一樣(選取的模型損失函數(shù)(loss function)為binary_crossentropy函數(shù),優(yōu)化器(optimizer)為RMSProp,迭代次數(shù)(epoch)為50次).

表2 RNN模型層數(shù)參數(shù)值Table 2 RNN model layer parameter value

圖5 基于RNN的深度神經(jīng)網(wǎng)絡(luò)分類器Fig.5 Classifier based on RNN model
本章實驗環(huán)境如下:操作系統(tǒng)是windows 10,處理器是Intel Core i7-7500U,內(nèi)存是12GB,實驗工具分別為為PyCharm、IntelliJ IDEA和weka,編程語言為Python和Java.此外,實驗相關(guān)工具(ASSD)和 數(shù) 據(jù) 已 上 傳 至https://github.com/lyqlq/SLDetection.
本文主要圍繞以下問題進(jìn)行實驗研究:
在實驗中,本文提出了4個研究問題(Research question,簡稱RQ),通過回答這些問題對本文所提方法進(jìn)行評估.
RQ1:使用深度學(xué)習(xí)的方法是否能準(zhǔn)確有效檢測出緩慢循環(huán)異味?如果可以檢測,哪種深度學(xué)習(xí)模型的檢測效果更好?
RQ2:使用相同數(shù)據(jù)集作為模型輸入,基于深度學(xué)習(xí)的檢測方法是否優(yōu)于基于機(jī)器學(xué)習(xí)的檢測方法?
RQ3:基于深度學(xué)習(xí)的檢測方法是否優(yōu)于現(xiàn)有的基于程序靜態(tài)分析的檢測方法?
RQ4:本文提出的方法用于生成訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)所需的數(shù)據(jù)集需要消耗多少時間?用于訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)模型又需要消耗多少時間?
RQ1關(guān)注深度神經(jīng)網(wǎng)絡(luò)分類器的檢測異味的能力和差異性.在相同輸入的前提下,使用查準(zhǔn)率、查全率和F1值3個指標(biāo)來評估不同分類器的有效性.對兩種神經(jīng)網(wǎng)絡(luò)分類器的檢測效果進(jìn)行比較.
RQ2關(guān)注檢測Android特有代碼異味時,深度神經(jīng)網(wǎng)絡(luò)分類器和機(jī)器學(xué)習(xí)分類器的差異性.以各個分類器在輸入相同數(shù)據(jù)集的情況下檢測出的最優(yōu)F1值作為衡量指標(biāo)進(jìn)行比較.
RQ3關(guān)注的是本文方法與現(xiàn)有基于靜態(tài)程序分析的檢測方法檢測SL異味上的差異.為了回答這個問題,本文選擇DAAP作為評估階段的對比實驗對象.之所以選擇DAAP,是因為DAAP是目前檢測Android特有代碼異味種類最多、檢測準(zhǔn)確度最好的工具.
RQ4關(guān)注本文所提方法時間性能的具體表現(xiàn).針對數(shù)據(jù)集上70個開源程序,記錄了在整個檢測SL代碼異味上的耗時情況.可以根據(jù)所提出方法在建立模型和訓(xùn)練數(shù)據(jù)中所需花費的時間成本,探討基于深度學(xué)習(xí)方法在時間性能上的具體表現(xiàn).
本章使用查準(zhǔn)率(Precision)、查全率(Recall)和 F1 值(F1-score)對不同分類器進(jìn)行評估,它們的計算方法如公式(1)~公式(3)所示:
(1)
(2)
(3)
其中,公式中的TP(True Positive)為被檢測為正的正樣數(shù),表示實際上陽性樣本的數(shù)量;FP(False Positive)為被檢測為正的負(fù)樣本數(shù),表示實際上是陰性樣本但被判定為陽性樣本的樣本數(shù)量;FN(False Negative)為被檢測為負(fù)的正樣本數(shù),表示實際上是陽性樣本但被判定為陰性樣本的樣本數(shù)量;TN(True Negative)表示實際上是負(fù)樣本的樣本數(shù)量,也被判定為陰性樣本.
實驗1.為了回答 RQ1,以2.2節(jié)構(gòu)建的程序文本數(shù)據(jù)集作為CNN和RNN模型的輸入,以相同的正負(fù)樣本訓(xùn)練模型.首先,將數(shù)據(jù)集按7:3的比例分成訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù),其中,使用測試數(shù)據(jù)的判別效果來估計模型在實際使用時的泛化能力,然后,將訓(xùn)練數(shù)據(jù)再按8:2的比例劃分為訓(xùn)練集和驗證集,根據(jù)驗證集上的性能來進(jìn)行模型選擇和調(diào)參,進(jìn)行50次訓(xùn)練驗證和1次測試得到檢測結(jié)果.最后,使用查準(zhǔn)率、查全率、F1 值對相同特征輸入的2種神經(jīng)網(wǎng)絡(luò)分類器的檢測效果進(jìn)行比較.
實驗2.為了回答RQ2,選取相同的數(shù)據(jù)集作為模型的輸入.深度神經(jīng)分類器的檢測方法同實驗一.首先,使用工具weka完成基于機(jī)器學(xué)習(xí)的代碼異味檢測,其中,參考Fontana等人[11]的研究結(jié)果,選取其中性能最好的4種機(jī)器學(xué)習(xí)模型,即隨機(jī)森林(RF)、J48、JRip、樸素貝葉斯(NB),作為本次實驗的基礎(chǔ)模型,然后,使用十折交叉驗證對模型進(jìn)行驗證.最后,以F1值作為標(biāo)準(zhǔn),對比出不同檢測方法的差異性.
實驗3.為了回答RQ3,首先,從Palomba等人[11]提出的一個開源Android應(yīng)用程序數(shù)據(jù)集(共60個Android應(yīng)用程序)中選取10個開源Android應(yīng)用程序作為測試程序.這10個應(yīng)用程序與模型訓(xùn)練階段的70個應(yīng)用程序沒有重疊.表3所示為使用的10個開源Android項目的詳細(xì)信息.然后,將新的測試集輸入已訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)分類器中進(jìn)行SL異味檢測.此外,為了進(jìn)行對比,本文使用檢測工具DAAP對這10個應(yīng)用程序進(jìn)行SL異味檢測.為準(zhǔn)確評價該工具的檢測結(jié)果,本文以Palomba等人[11]提出的SL異味目錄作為參考,使用查準(zhǔn)率、查全率和F1值對檢測方法的檢測效果進(jìn)行比較.

表3 測試集所用的10個Android應(yīng)用程序Table 3 10 Android apps used in the test set
實驗4.為了回答RQ4,本文在普通配置的筆記本電腦(12GB RAM,Intel Core CPU i7-7500)上運行實驗的全部流程,并記錄了耗時情況(以分鐘為單位),對所提出方法的時間性能進(jìn)行分析.
RQ1:表4所示為兩種神經(jīng)網(wǎng)絡(luò)分類器在相同特征輸入的檢測結(jié)果.

表4 不同模型在相同特征下的SL檢測結(jié)果Table 4 Results on SL detection of different models with the same features
如表4所示,兩種神經(jīng)網(wǎng)絡(luò)模型的查準(zhǔn)率、查全率和F1值都較高,因此使用深度神經(jīng)網(wǎng)絡(luò)檢測緩慢循環(huán)異味是可行的,且檢測效果很好.圖6所示為隨著迭代次數(shù)(Epochs)的增加,兩種模型的3項性能指標(biāo)分別在訓(xùn)練集和驗證集上的變化過程.其中,左圖(a)為CNN模型3個指標(biāo)隨著迭代次數(shù)增加的變化過程,右圖(b)為RNN模型3個指標(biāo)隨著迭代次數(shù)增加的變化過程.從曲線變化趨勢可以看出,隨著迭代次數(shù)的增加,兩種模型的3項指標(biāo)在訓(xùn)練集和驗證集都較高且差別不大,表明兩種模型把數(shù)據(jù)的規(guī)律擬合出來了,能夠很好的檢測代碼異味.

圖6 CNN和RNN模型的3個性能指標(biāo)隨著迭代次數(shù)增加的變化過程Fig.6 Change process of the three performance metrics of the CNN and the RNN model as the epoch increases
此外,如表4所示,3項性能指標(biāo)數(shù)據(jù),CNN模型的檢測效果均略優(yōu)于RNN模型.在深度學(xué)習(xí)的傳統(tǒng)應(yīng)用領(lǐng)域,由于RNN模型更擅長處理文本,因此在自然語言處理領(lǐng)域有著廣泛的應(yīng)用.而程序源代碼與自然語言文本有諸多相似之處,因此可以認(rèn)為,在檢測代碼異味時,RNN模型的檢測效果應(yīng)優(yōu)于CNN模型.但Sharma等人認(rèn)為[18],程序源代碼與自然語言文本也有很多不同之處,這些差異可能導(dǎo)致擅長處理自然語言文本的方法在處理程序源代碼時表現(xiàn)不佳.同時他們的研究結(jié)果表明[18],在檢測面向?qū)ο蟠a異味時,RNN模型的檢測效果因味道的不同而發(fā)生變化,不一定比CNN檢測效果好.根據(jù)表4結(jié)果,在檢測緩慢循環(huán)這種Android代碼異味時,需要識別類中的傳統(tǒng)for循環(huán)語句,在識別能力上,CNN與RNN模型都較強,但RNN模型略遜色于CNN模型.
RQ2:表5所示為深度神經(jīng)網(wǎng)絡(luò)分類器和機(jī)器學(xué)習(xí)分類器在輸入相同特征下的檢測結(jié)果.

表5 不同分類器的性能比較Table 5 Performance comparison of different classifiers
如表5所示,分類器對SL的檢測性能綜合排名為:卷積神經(jīng)網(wǎng)絡(luò)(CNN)>循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)>隨機(jī)森林(RF)>JRip>J48>樸素貝葉斯(NB).本文所使用的深度神經(jīng)網(wǎng)絡(luò)模型檢測結(jié)果普遍優(yōu)于機(jī)器學(xué)習(xí)模型,相對于檢測效果最好的隨機(jī)森林,CNN模型的檢測效果有所提升,F1值平均提高了9.43%=(95.65%~86.22%).
RQ3:對表1中的10個Android應(yīng)用程序,表6分別列出了兩種深度神經(jīng)網(wǎng)絡(luò)分類器與基于靜態(tài)程序分析檢測工具DAAP的檢測結(jié)果.

表6 不同檢測方法的結(jié)果比較Table 6 Comparison of the results of different detection methods
如表6所示,深度神經(jīng)網(wǎng)絡(luò)分類器對SL異味的檢測效果在查準(zhǔn)率、查全率和F1值上均優(yōu)于DAAP.其中,檢測效果提升最為明顯的是CNN模型,F1值平均提高了28.7%=(98.59%~69.89%).
RQ4:由于篇幅限制,表7僅列出了其中10個Android應(yīng)用程序生成訓(xùn)練集所花費的時間(以分鐘為單位),全部 70 個應(yīng)用程序所消耗時間的詳細(xì)信息請已上傳至如第3節(jié)所示的網(wǎng)址.可以看出,收集樣本數(shù)據(jù)耗時較多,為了從70個Android應(yīng)用程序中提取訓(xùn)練集數(shù)據(jù),累計花費了1061分鐘(約17.7小時),平均每個項目需要15.16分鐘.其中,耗時最多的項目是AndroidChromium(類數(shù)=1007、方法數(shù)=1183、代碼行數(shù)=89051,消耗149分鐘),耗時最少的項目是xCalc(類數(shù)=33、方法數(shù)=201、代碼行數(shù)=1548,耗時9分鐘).

表7 生成訓(xùn)練集數(shù)據(jù)消耗時間Table 7 Time of train set data genertion
與耗費17個多小時的訓(xùn)練集生成過程相比,深度神經(jīng)網(wǎng)絡(luò)分類器在70個開源項目上的訓(xùn)練過程消耗時間分別為:卷積神經(jīng)網(wǎng)絡(luò)為22.7s,循環(huán)神經(jīng)網(wǎng)絡(luò)為223.6s.因為自動生成訓(xùn)練集數(shù)據(jù)是可以提前實現(xiàn)的,所以并不需要在每次進(jìn)行緩慢循環(huán)異味檢測時生成訓(xùn)練集.同時,CNN模型僅考慮當(dāng)前輸入,而RNN模型考慮當(dāng)前輸入以及先前接收的輸入,故從時間上對比CNN模型在數(shù)據(jù)集訓(xùn)練上所需要的時間更少.
本文使用深度學(xué)習(xí)方法檢測緩慢循環(huán)這種Android特有代碼異味.首先,分析大量源碼,從中提取出與緩慢循環(huán)相關(guān)特征,并將源代碼轉(zhuǎn)換成數(shù)字信息作為模型輸入的特征集,然后,使用CNN和RNN兩種深度神經(jīng)網(wǎng)絡(luò)分類器進(jìn)行檢測.接著,在保證模型輸入的特征集相同的情況下,對比4種機(jī)器學(xué)習(xí)模型的檢測效果.實驗結(jié)果表明,基于深度學(xué)習(xí)的檢測方法不僅可以檢測Android特有代碼異味,而且檢測效果優(yōu)于基于機(jī)器學(xué)習(xí)的方法和基于程序靜態(tài)分析的檢測方法.此外,為了快速準(zhǔn)確生成有監(jiān)督機(jī)器學(xué)習(xí)模型所需的大量訓(xùn)練集和測試集,本文提出了一種利用開源Android應(yīng)用程序自動構(gòu)建正負(fù)樣本的工具的方法,并實現(xiàn)工具ASSD,實現(xiàn)從異味檢測到樣本生成自動完成.
目前,本文只對緩慢循環(huán)這一種Android特有代碼異味進(jìn)行檢測,Reimann等人的Android異味列表中共包括30種味道,在后續(xù)的研究中,將在本文方法的基礎(chǔ)上,對更多種類的Android特有代碼異味進(jìn)行檢測.目的是探索深度學(xué)習(xí)模型檢測Android代碼異味時,針對不同的異味,檢測效果是否有差異.同時研究異味的重構(gòu)方法,最終目的是提高軟件的質(zhì)量.