王一凡,趙逢禹,艾 均
(上海理工大學(xué) 光電信息與計算機工程學(xué)院,上海 200093)
方法名是一種用于標(biāo)識程序方法的特殊標(biāo)識符,標(biāo)識符的質(zhì)量被證明對源代碼的可讀性和維護性有顯著影響[1].一個恰當(dāng)?shù)姆椒粌H要描述方法是什么,還要描述它的功能,因此方法的抽象命名通常具有挑戰(zhàn)性.
方法代碼抽象命名的相關(guān)研究已經(jīng)有許多成果,如基于啟發(fā)式規(guī)則的方法[2]和應(yīng)用文本分析技術(shù)的方法[3].文獻(xiàn)[2]提取方法代碼的數(shù)據(jù)流和控制流相關(guān)的特征,作為方法名模板對應(yīng)的規(guī)則,根據(jù)啟發(fā)式規(guī)則去檢測具有與規(guī)則類似特征的方法代碼名稱是否準(zhǔn)確.應(yīng)用文本分析技術(shù)的方法的基本思想是將方法代碼當(dāng)作自然語言文本進行處理.文獻(xiàn)[3]將源代碼中關(guān)鍵字標(biāo)識符剔除,使用文本主題提取算法得到文本主題特征,將提取的文本摘要作為方法名.
最近,研究者將機器學(xué)習(xí)的技術(shù)引入到方法代碼的抽象命名中[4,7],這種方法的關(guān)鍵技術(shù)是將方法代碼抽象成新的代碼表示作為代碼特征,應(yīng)用機器學(xué)習(xí)技術(shù)自動學(xué)習(xí)代碼特征,根據(jù)相似的代碼具有相似的特征的原則,從給定的方法名語料庫中匹配出已有類似的方法名或者組合出高質(zhì)量新的方法名,因此抽象出的代碼表示要包含程序豐富的語義信息.文獻(xiàn)[4]引入log-bilinear神經(jīng)語言模型,捕獲源代碼中能夠表示長距離上下文特征的token序列,將方法名以及方法名的子標(biāo)記嵌入到高維空間,推薦在空間中與新方法具有相似嵌入的方法名.文獻(xiàn)[5]引入卷積注意力神經(jīng)網(wǎng)絡(luò),對方法的token序列進行卷積操作,將方法片段總結(jié)為具有描述性的方法名的過程看作一個翻譯任務(wù).然而token序列缺失了方法代碼的結(jié)構(gòu)信息,提取到的方法特征不能表示方法代碼的強結(jié)構(gòu)性,為了提取代碼語義信息,文獻(xiàn)[6]提出一種通用的基于路徑的方法,使用AST中葉子節(jié)點之間的路徑和葉子節(jié)點相關(guān)聯(lián)的標(biāo)識符作為一條路徑來表示方法,這使得深度學(xué)習(xí)模型能夠利用源代碼的結(jié)構(gòu)特性,而不是將其視為一個扁平的token序列.在此結(jié)構(gòu)基礎(chǔ)上,文獻(xiàn)[7]又提出code2vec,進一步引入注意力神經(jīng)網(wǎng)絡(luò)將AST路徑集聚合成分布式向量,具有相似AST路徑的方法的分布式向量在高維連續(xù)空間彼此接近,從而捕獲方法之間以及方法名稱之間的語義相似性.
文獻(xiàn)[6]和文獻(xiàn)[7]的研究,考慮到了程序的強結(jié)構(gòu)特性[8],然而對程序的控制流信息以及程序動態(tài)可執(zhí)行性等特征的關(guān)注度不夠.
本文針對提取出的方法特征缺失控制流信息以及不能反映程序動態(tài)可執(zhí)行的問題,提出基于基本路徑學(xué)習(xí)的方法來對方法代碼抽象命名.首先研究了方法代碼控制流圖的構(gòu)建,接著從本文構(gòu)建的控制流圖中抽取基本路徑作為方法代碼的中間表示.然后將基本路徑集轉(zhuǎn)換成向量形式輸入到引入注意力機制[9]的LSTM神經(jīng)網(wǎng)絡(luò)[10],以方法標(biāo)簽為輸出目標(biāo)設(shè)計損失函數(shù).最后為了驗證學(xué)習(xí)基本路徑訓(xùn)練出的模型能更好理解方法代碼的語義信息,構(gòu)建了基于文獻(xiàn)[6]的數(shù)據(jù)集的訓(xùn)練樣本,訓(xùn)練得到代碼自動命名模型,并對實驗結(jié)果進行了分析.
神經(jīng)網(wǎng)絡(luò)是以向量作為輸入,所以需要將程序表示為向量,并且轉(zhuǎn)換出的向量能夠表示程序的語義信息.因此可以使用中間表示作為程序和轉(zhuǎn)換成的向量之間的橋梁.為了使神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)到程序的控制流以及動態(tài)可執(zhí)行的語義信息,本文以方法作為程序大小的粒度,使用基本路徑作為方法代碼的中間表示,并將基本路徑轉(zhuǎn)換成向量形式輸入神經(jīng)網(wǎng)絡(luò),訓(xùn)練出基于基本路徑學(xué)習(xí)的程序方法名代碼自動命名模型.
基于基本路徑學(xué)習(xí)的方法代碼自動命名分為兩個階段:訓(xùn)練階段和自動命名階段.如圖1所示給出了訓(xùn)練階段的流程,該流程包括構(gòu)建控制流圖,提取基本路徑集,將提取的基本路徑轉(zhuǎn)換成向量表示以及訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型4步.自動命名階段將待命名的方法代碼按照訓(xùn)練階段前3步進行處理,第4步直接將向量輸入到訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)中,解析出與方法名標(biāo)簽庫中語義最接近的方法名.下面詳細(xì)介紹訓(xùn)練階段的具體過程.
第1步.構(gòu)建方法代碼控制流圖
從源代碼中提取以方法為粒度的代碼段,通過編譯器將方法代碼分析優(yōu)化得到三地址碼中間表示[11],然后通過本文給出的控制流圖構(gòu)建算法,將三地址碼代碼段進行代碼段分塊,并在基本塊之間添加控制流邊得到控制流圖.
第2步.提取基本路徑集
將控制流圖中的每個基本塊作為基本路徑中的路徑節(jié)點,并且將路徑節(jié)點中變量的具體值抽象為具體的token以達(dá)到降低token數(shù)量級的目的,降低詞嵌入學(xué)習(xí)過程的復(fù)雜度[12].接著借鑒深度優(yōu)先搜索的思想,構(gòu)建算法提取出方法代碼的所有執(zhí)行路徑作為方法的基本路徑集合.
第3步.將基本路徑集轉(zhuǎn)進行詞嵌入
首先將基本路徑集中的字符建立token詞表,此時基本路徑中的對應(yīng)的token使用詞表中的token編號進行映射表示.然后將token通過word2vec技術(shù)[13]訓(xùn)練得到每個token的詞向量.最后通過字典表映射關(guān)系將基本路徑轉(zhuǎn)換成向量形式.
第4步.訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型
方法代碼的語義信息上下文依賴較長,本文選擇擁有記憶單元的LSTM神經(jīng)網(wǎng)絡(luò)進行建模.首先將上一步得到的路徑向量輸入到LSTM神經(jīng)網(wǎng)絡(luò)中,得到每條路徑的上下文基本路徑向量.然后將每條路徑向量聚合為代碼向量,在聚合過程中使用注意力機制對每條路徑向量計算權(quán)重,其中的權(quán)重是訓(xùn)練過程中根據(jù)每條路徑的影響程度不斷更新.最后通過softmax層計算出代碼向量和方法名標(biāo)簽的分布概率,損失函數(shù)計算出正確分布與預(yù)測分布的差值,使用梯度優(yōu)化算法完成整個模型的閉環(huán)訓(xùn)練.最終得到LSTM神經(jīng)網(wǎng)絡(luò)以及注意力向量的參數(shù).
3.1.1 三地址碼與Soot
本文使用更加抽象和簡潔的三地址碼作為方法代碼的中間表示.三地址碼由一組類似于匯編語言的指令組成,每條指令最多由3個運算分量組成,且最多只執(zhí)行一次運算,這里的運算通常是計算、比較或者分支跳轉(zhuǎn).表1給出了源代碼對應(yīng)的“三地址”指令序列的示例.
表1 三地址碼指令序列
考慮到模型的復(fù)用性,模型可以選擇特定于語言的編譯器來優(yōu)化處理源代碼,以得到方法代碼的三地址碼序列.本文選擇Java語言編寫的源代碼作為研究對象,因此選擇分析Java程序的Soot靜態(tài)編譯器,生成Soot特有的三地址碼:Jimple[14].Soot能夠?qū)ava代碼中的循環(huán)結(jié)構(gòu)轉(zhuǎn)換成goto指令的形式,使代碼中只有分支和順序兩種控制流向,達(dá)到簡化方法代碼控制邏輯的目的.
3.1.2 控制流圖構(gòu)建算法
傳統(tǒng)方法構(gòu)建的控制流圖分支結(jié)構(gòu)復(fù)雜,在此基礎(chǔ)上提取的基本路徑包含的路徑較長[15],使用LSTM神經(jīng)網(wǎng)絡(luò)訓(xùn)練時會出現(xiàn)梯度消失與梯度爆炸的問題.本文在三地址代碼序列基礎(chǔ)上構(gòu)建程序控制流圖,優(yōu)化程序控制結(jié)構(gòu),達(dá)到縮短基本路徑長度的目的.控制流圖中的每一個節(jié)點表示一個基本塊(基本塊表示控制流只能從基本塊第一條指令進入,除了基本塊的最后一條指令控制流在離開基本塊前不發(fā)生跳轉(zhuǎn)),每一條邊表示兩個基本塊之間的控制流轉(zhuǎn)移.
構(gòu)建程序控制流圖的步驟如下:
1)通過靜態(tài)編譯器分析得到三地址代碼序列;
2)在三地址代碼序列的基礎(chǔ)上進行基本塊劃分;
3)基本塊之間添加邊信息,構(gòu)建程序控制流程圖.
算法1.方法代碼控制流圖構(gòu)建算法
輸入:方法代碼序列,Method={token1,…,tokenn},其中Method為方法代碼,tokenn為方法中的詞法單元.
輸出:方法代碼對應(yīng)的控制流圖CFG={V,E},其中V={BasicBlock1,…,BasicBlockn},E={〈BasicBlocki,BasicBlockj〉,…}.
處理:
//Soot靜態(tài)分析器得到三地址碼序列
1.Jimple3AC←SootCompilerProcess(Method);
//每條三地址碼指令記錄在數(shù)組Stmts[]里
2.Stmts[]←Abstract3ACProcess(Jimple3AC);
3.V=φ;//控制流圖節(jié)點序列,每個節(jié)點代表一個基本塊BasicBlock
4.while(i new BasicBlock;//創(chuàng)建一個新的基本塊 BasicBlock.add(Stmts[i]); while(Stmts[i]與Stmts[i+1]能夠組成基本塊){ BasicBlock.add(Stmts[i+1]); i++; } V=V∪{BasicBlock}; i++; } 5.E=?//控制流圖邊序列 //添加基本塊之間的控制流邊 6.for each(BasicBlockiin V){ if(BasicBlocki有后繼基本塊) E=E∪{〈BasicBlocki,BasicBlocki+1〉}; if(BasicBlocki中存在跳轉(zhuǎn)指令){ for each(BasicBlockjin V){ if(BasicBlockj中存在BasicBlocki的跳轉(zhuǎn)目標(biāo)指令) E=E∪{〈BasicBlocki,BasicBlockj〉}; } } } 7.return new CFG = {V,E};//返回控制流圖 算法1首先將方法代碼通過Soot靜態(tài)分析器得到三地址碼指令序列,然后將指令信息封裝記錄在數(shù)組里.接著劃分本塊集合作為控制流圖的節(jié)點集,在劃分過程中,步驟4兩條指令是否能夠劃分到同一基本塊的依據(jù)是判斷Stmts[i+1]是否為跳轉(zhuǎn)指令的目標(biāo)指令或者是跳轉(zhuǎn)指令的下一條指令,如果是則不能劃分到同一基本塊.最后在基本塊與其后繼基本塊之間以及與其跳轉(zhuǎn)到的目標(biāo)基本塊之間添加控制流邊.基本塊之間的控制流信息包含順序流向和跳轉(zhuǎn)流向,分別反映出方法代碼中的順序和分支循環(huán)邏輯. 基本路徑的提取是在控制流圖的基礎(chǔ)上,通過深度優(yōu)先搜索算法提取所有的基本路徑,構(gòu)成基本路徑集[16].基本路徑集中的元素是基本路徑,基本路徑是指控制流圖中從起始結(jié)點到終止結(jié)點所經(jīng)過的一系列基本塊序列,且該路徑上至少有一條邊在其它路徑中沒有出現(xiàn)過,即包含其它路徑未曾引入的基本塊.基本路徑滿足覆蓋方法代碼中的所有分支單元和循環(huán)單元,能夠覆蓋控制流圖的各個執(zhí)行路徑以及覆蓋所有邏輯控制條件,所以從側(cè)面反映出方法代碼的上下文語句執(zhí)行的因果關(guān)系,因此將基本路徑作為方法代碼的中間表示能夠表示程序的動態(tài)可執(zhí)行的特征. 基本路徑集定義: 1)基本路徑集是由多條基本路徑組成,且每條基本路徑是集合中的獨立路徑; 2)集合中的路徑包含控制流圖的所有節(jié)點和邊; 3)每條獨立路徑至少包含一條在其它路徑中未出現(xiàn)過的邊. 算法2.基本路徑提取算法 輸入:方法代碼對應(yīng)的控制流圖CG={V,E},其中V={v0,…,vn},E={〈vi,vj〉,…〈vm,vn〉}. 輸出:基本路徑集BPS={path1,…,pathn},其中pathi= 〈vi,…,vm〉. 處理: 1.初始化: BasicPathSet=?;//基本路徑集 Stack.push(v0);//控制流圖開始節(jié)點入棧 VisitedV[]=?;//記錄已被訪問節(jié)點 VisitedEdge[]=?;//記錄已被訪問邊 2.while(Stack is not null){ curV = Stack.peek();//讀取當(dāng)前棧頂節(jié)點 Visited[curV];//記錄訪問過得節(jié)點 if(curV是終結(jié)節(jié)點){ //返回一條基本路徑,該路徑序列為棧頂?shù)綏5椎哪嫘蛐蛄?/p> path←TraversalStack(); BasicPathSet=BasicPathSet∪{path}; while(Stack.peek()不存在未訪問過的邊){ Stack.pop();//彈出棧中不存在未訪問過邊的節(jié)點 } }else if(curV不存在未訪問過的邊){ Stack.pop();//回退狀態(tài) }else{ e=〈curV,nextV〉∈E且未訪問過; if(nextV未被訪問過){ VisitedEdge[e]=true;//記錄訪問過該條邊 Stack.push(nextV);//壓入堆棧 }else if(nextV不存在未訪問過的邊){ VisitedEdge[e]= true; path1←TraversalStack(); path2←SearchExistedSubseq(nextV); //返回BasicPathSet中任意一條基本路徑中存在nextV節(jié)點的后繼序列 BasicPathSet=BasicPathSet∪{〈path1,path2〉}; } } } 算法從控制流圖入口節(jié)點開始深度優(yōu)先遍歷,首先判斷棧頂節(jié)點curV是否為終結(jié)節(jié)點,如果是則輸出棧的逆序序列作為一條基本路徑.不是則判斷curV是否存在未訪問過的邊,如果不存在則彈出curV開始下一次循環(huán).如果存在則取以curV為開始節(jié)點的邊e且后繼節(jié)點為nextV,此時判斷nextV是否訪問過,如果未被訪問過則記錄邊e被訪問且將nextV壓入堆棧;否則如果nextV被訪問過且不存在未訪問過的邊,則此時基本路徑集中的某條基本路徑pathi必定存在nextV節(jié)點,輸出棧的逆序序列并拼接pathi中從nextV開始的序列作為一條基本路徑. 模型輸入:方法代碼對應(yīng)的基本路徑集BPS={path1,…,pathn}的向量表示,其中pathi=〈bi1,…,biε〉;因為每條基本路徑有不同數(shù)量的token,為了讓神經(jīng)網(wǎng)絡(luò)擁有相同長度的向量輸入,引入超參數(shù)ε作為基本路徑向量的固定長度.方法名標(biāo)簽序列嵌入向量y. 訓(xùn)練結(jié)果:得到LSTM神經(jīng)層和注意力層的權(quán)重參數(shù),以及方法代碼的向量. softmax層:調(diào)用TensorFlow框架的softmax層計算代碼向量v在方法名標(biāo)簽嵌入空間的預(yù)測概率分布,以cross-entropy損失函數(shù)[17]計算正確分布p和預(yù)測分布q之間的誤差,使用Adam梯度優(yōu)化算法更新模型參數(shù)訓(xùn)練網(wǎng)絡(luò)[18].其中正確分布是方法代碼的標(biāo)簽在標(biāo)簽庫的真實分布(是正確標(biāo)注值為1,否則為0),模型預(yù)測得到的概率分布q是由方法代碼向量v和標(biāo)簽庫中對應(yīng)標(biāo)簽嵌入向量的點積得到. 為了驗證將基本路徑作為方法代碼的中間表示能夠包含更豐富的語義信息,訓(xùn)練得到的模型能夠更準(zhǔn)確的命名方法名,本文與先前工作進行實驗對比,并從學(xué)習(xí)開銷的角度分析方法優(yōu)劣.為了統(tǒng)計分析模型在對不同復(fù)雜度的方法代碼命名的表現(xiàn),以圈復(fù)雜度作為衡量代碼復(fù)雜度的依據(jù)進行實驗. 本文采用文獻(xiàn)[6]中的Java-med數(shù)據(jù)集,該數(shù)據(jù)集包含了1000個高質(zhì)量Java項目,約400萬個方法,其中900個項目作為訓(xùn)練集,100個作為測試集,并將該數(shù)據(jù)集經(jīng)過如下處理構(gòu)建實驗訓(xùn)練樣本. 首先將項目中的類依據(jù)方法簽名(即返回值類型、方法名、參數(shù)類型)拆分出各個方法,并抽取方法名作為該方法的標(biāo)注,在拆分過程中過濾掉類中的set、get類型方法,這些方法對于模型訓(xùn)練沒有多少價值[19].然后,將拆分出的方法代碼按照3.1.2節(jié)的方法構(gòu)建出控制流圖.最后,將控制流圖按照3.2節(jié)中論述的方法提取出基本路徑集,以方法代碼的基本路徑集作為模型的輸入,方法名標(biāo)簽作為標(biāo)注,構(gòu)成如表2所示的訓(xùn)練數(shù)據(jù)格式. 表2 訓(xùn)練數(shù)據(jù)格式 本文在操作系統(tǒng)為Ubuntu 16.04.7 LTS的服務(wù)器上進行相關(guān)實驗.設(shè)備硬件環(huán)境為:處理器是Intel?CoreTMi7-7700 CPU@4.20 GHz*8,內(nèi)存為64GB,顯卡為NVIDIA GeForce GTX 1080Ti.設(shè)備軟件環(huán)境為:深度學(xué)習(xí)框架是TensorFlow_gpu-1.4.0,Python使用Python3.7版本. 步驟1.構(gòu)建控制流圖 將數(shù)據(jù)集中預(yù)處理好的方法代碼依次保存在單獨文件中,通過處理程序調(diào)用soot工具包中的Main處理類將方法源代碼文件轉(zhuǎn)換成Jimple三地址碼文件.編寫B(tài)asicBlock類作為抽象基本塊的數(shù)據(jù)結(jié)構(gòu),編寫Edge類記錄基本塊之間邊信息,編寫CFG類聚合基本塊以及邊信息作為控制流圖保存的數(shù)據(jù)結(jié)構(gòu). 步驟2.提取基本路徑集 編寫處理程序讀入控制流圖數(shù)據(jù),調(diào)用3.2節(jié)設(shè)計的算法得到基本路徑集合,將每個方法代碼以{方法名標(biāo)注,基本路徑集}的數(shù)據(jù)組合形式輸出到具體的訓(xùn)練集和測試集的文件中. 步驟3.將基本路徑和方法名標(biāo)簽量化表示 將基本路徑集中的每條基本路徑的三地址碼序列使用moses切詞工具得到token,并對token建立字典庫.使用word2vec詞向量嵌入工具對token嵌入學(xué)習(xí),得到每個token對應(yīng)的詞向量.根據(jù)字典映射關(guān)系將基本路徑中的每個token使用詞向量表示.方法名標(biāo)注組成的標(biāo)簽庫中的方法名token隨機生成一個唯一數(shù)字對標(biāo)注進行量化表示,以便softmax計算方法代碼向量在標(biāo)簽庫中的概率分布. 步驟4.訓(xùn)練LSTM神經(jīng)網(wǎng)絡(luò) 將訓(xùn)練集采用十折交叉驗證的方法用于訓(xùn)練LSTM神經(jīng)網(wǎng)絡(luò),網(wǎng)絡(luò)初始化參數(shù)設(shè)置為深度學(xué)習(xí)廣泛使用的值,并且設(shè)置不同LSTM神經(jīng)網(wǎng)絡(luò)的隱藏層數(shù),觀察其對結(jié)果的影響,最終選擇256個隱藏層數(shù)效果最好.基本路徑轉(zhuǎn)換成的向量固定長度設(shè)置為200,能夠覆蓋大多數(shù)基本路徑序列長度,因此LSTM網(wǎng)絡(luò)的時間步長也設(shè)置為ε.dropout設(shè)置為0.25,更新模型參數(shù)的minibatch設(shè)置為64,迭代訓(xùn)練epoch設(shè)置為4,使用Adam梯度優(yōu)化算法進行訓(xùn)練,學(xué)習(xí)速率初始設(shè)置為0.01,然后當(dāng)交叉驗證時準(zhǔn)確率停止改善時,讓學(xué)習(xí)速率減半繼續(xù)迭代,最終得到代碼自動命名模型. 步驟5.模型評估對比 將本文構(gòu)建的訓(xùn)練集按照文獻(xiàn)[6,7]的方法訓(xùn)練得到對比模型.將測試集分別輸入到對比模型和本文得到的模型,采用文獻(xiàn)[7]中的評價指標(biāo)對模型結(jié)果統(tǒng)計分析. 實驗1.模型對比結(jié)果分析 如表3所示本文提出基于基本路徑學(xué)習(xí)的代碼自動命名模型與其它基線模型:Paths+CRFs[6]、Paths+Attn[7]的對比情況.本文提出的模型記為BasicPaths+Attn,其中BasicPaths表示不使用注意力機制進行的對比實驗,具體做法是在聚合上下文基本路徑向量時直接使用求和取平均的方法得到方法代碼向量. 表3 模型得分評估 觀察實驗結(jié)果得出本文提出的模型得分優(yōu)于其它模型.文獻(xiàn)[7]提出的AST路徑更多的表示上下文token的依賴關(guān)聯(lián),而本文提取的基本路徑不局限于單個上下文token,而是上下文關(guān)聯(lián)的基本塊之間的依賴關(guān)系,并且LSTM網(wǎng)絡(luò)能夠捕獲到長距離的上下文依賴,因此本文模型能夠更好理解方法代碼的語義.并且本文使用注意力機制作用于方法代碼的關(guān)鍵基本路徑,使聚合得到的方法代碼向量更能夠表示代碼的特征信息,從而訓(xùn)練得到的模型效果更好. 實驗2.模型訓(xùn)練開銷分析 將基線模型與本文模型在處理好的數(shù)據(jù)集上進行訓(xùn)練,并且基線模型使用他們默認(rèn)的超參數(shù).對于本文模型,調(diào)整LSTM神經(jīng)網(wǎng)絡(luò)隱藏層以及基本路徑序列長度等參數(shù),觀察對于F1值的影響,確定最佳的超參數(shù).在調(diào)整過程中神經(jīng)網(wǎng)絡(luò)超參數(shù)根據(jù)硬件性能從大往小進行調(diào)整,基本路徑序列的長度從短到長進行設(shè)置,最終確定LSTM隱藏層數(shù)為256,基本路徑序列長度固定為200.本文也基于該超參數(shù)討論與基線模型的訓(xùn)練時間開銷對比,統(tǒng)計結(jié)果如圖2所示. 圖2 模型訓(xùn)練時間開銷對比 觀察訓(xùn)練時間開銷可知,Paths+Attn模型在較短時間內(nèi)達(dá)到最佳效果,本文模型在27小時之后分?jǐn)?shù)超過基線模型并在42個小時達(dá)到最佳效果.Paths+Attn模型的輸入吞吐量達(dá)到每分鐘1000個方法,所以能夠在較短時間內(nèi)學(xué)習(xí)大量的方法代碼,因此能夠在短時間內(nèi)達(dá)到較好的效果.而本文模型采用的LSTM神經(jīng)網(wǎng)絡(luò)計算開銷高于基線模型,模型輸入吞吐量較小,然而隨著學(xué)習(xí)大量數(shù)據(jù)網(wǎng)絡(luò)記憶單元的參數(shù)進行調(diào)整,網(wǎng)絡(luò)輸出的上下文基本路徑向量包含更多的上下文信息,因此隨著訓(xùn)練時間的推移本文模型的效果優(yōu)于基線模型. 實驗3.方法代碼復(fù)雜度影響分析 本文在構(gòu)建控制流圖過程中根據(jù)點邊計算法得到方法代碼的圈復(fù)雜度,以統(tǒng)計分析復(fù)雜度對模型的性能影響.針對本模型,方法代碼圈復(fù)雜度數(shù)量增大,其控制流圖包含的邊和節(jié)點線性增加,為了滿足邏輯覆蓋以及分支覆蓋,提取出的基本路基長度也隨著線性增加,在訓(xùn)練過程中受LSTM的時間步的限制,基本路徑向量上下文關(guān)聯(lián)性會逐漸丟失,進而影響模型效果.為了評估方法代碼邏輯復(fù)雜度對本模型的影響,本文選擇圈復(fù)雜度數(shù)在1-12之間的方法代碼檢驗?zāi)P偷腇1得分情況,并且與基線模型進行對比. 如圖3所示本文提出的模型與基線模型在方法代碼圈復(fù)雜度數(shù)相同時的對比情況.從圖中可以看出,隨著代碼復(fù)雜度的增加,模型性能均逐漸下降.在復(fù)雜度較低情況下,Paths+Attn模型由于模型設(shè)計簡單且此時AST路徑較短,模型能更好的學(xué)習(xí)到代碼特征.然而隨著復(fù)雜度的提升,AST路徑增多且變長,基線模型效果降低,但是基本路徑的數(shù)量與控制結(jié)構(gòu)的數(shù)量相關(guān),因此本文模型所需要學(xué)習(xí)的路徑數(shù)量較少,并且本文模型中采用LSTM神經(jīng)網(wǎng)絡(luò)能夠捕獲更長的上下文依賴性,因此在復(fù)雜度提升的情況下,本文模型表現(xiàn)的更好. 圖3 方法代碼復(fù)雜度影響對比 本研究提出基于基本路徑學(xué)習(xí)的代碼自動命名模型.首先使用編譯器對源代碼優(yōu)化分析,得到方法代碼的三地址碼序列并構(gòu)建基于三地址碼的控制流圖.然后構(gòu)建算法提取基本路徑集合.最后構(gòu)建神經(jīng)網(wǎng)絡(luò)模型學(xué)習(xí)基本路徑集的特征,迭代訓(xùn)練得到自動命名模型. 實驗結(jié)果表明,本文提取能夠反映控制流信息以及方法代碼可執(zhí)行特征的基本路徑作為代碼中間表示,訓(xùn)練出的模型能夠更好的捕獲代碼語義信息,并且對復(fù)雜度更高的代碼,模型能更好學(xué)習(xí)到代碼特征.在未來的工作中,將繼續(xù)研究將得到的代碼向量應(yīng)用于代碼缺陷檢測的任務(wù)中.3.2 基本路徑提取算法
3.3 神經(jīng)網(wǎng)絡(luò)模型
4 實驗與結(jié)果
4.1 數(shù)據(jù)集
4.2 實驗環(huán)境
4.3 實驗過程
4.4 實驗結(jié)果與分析
5 結(jié) 論