?

面向基本路徑學習的代碼自動命名

2022-11-18 05:56王一凡趙逢禹
小型微型計算機系統 2022年11期
關鍵詞:代碼向量神經網絡

王一凡,趙逢禹,艾 均

(上海理工大學 光電信息與計算機工程學院,上海 200093)

1 引 言

方法名是一種用于標識程序方法的特殊標識符,標識符的質量被證明對源代碼的可讀性和維護性有顯著影響[1].一個恰當的方法名不僅要描述方法是什么,還要描述它的功能,因此方法的抽象命名通常具有挑戰性.

方法代碼抽象命名的相關研究已經有許多成果,如基于啟發式規則的方法[2]和應用文本分析技術的方法[3].文獻[2]提取方法代碼的數據流和控制流相關的特征,作為方法名模板對應的規則,根據啟發式規則去檢測具有與規則類似特征的方法代碼名稱是否準確.應用文本分析技術的方法的基本思想是將方法代碼當作自然語言文本進行處理.文獻[3]將源代碼中關鍵字標識符剔除,使用文本主題提取算法得到文本主題特征,將提取的文本摘要作為方法名.

最近,研究者將機器學習的技術引入到方法代碼的抽象命名中[4,7],這種方法的關鍵技術是將方法代碼抽象成新的代碼表示作為代碼特征,應用機器學習技術自動學習代碼特征,根據相似的代碼具有相似的特征的原則,從給定的方法名語料庫中匹配出已有類似的方法名或者組合出高質量新的方法名,因此抽象出的代碼表示要包含程序豐富的語義信息.文獻[4]引入log-bilinear神經語言模型,捕獲源代碼中能夠表示長距離上下文特征的token序列,將方法名以及方法名的子標記嵌入到高維空間,推薦在空間中與新方法具有相似嵌入的方法名.文獻[5]引入卷積注意力神經網絡,對方法的token序列進行卷積操作,將方法片段總結為具有描述性的方法名的過程看作一個翻譯任務.然而token序列缺失了方法代碼的結構信息,提取到的方法特征不能表示方法代碼的強結構性,為了提取代碼語義信息,文獻[6]提出一種通用的基于路徑的方法,使用AST中葉子節點之間的路徑和葉子節點相關聯的標識符作為一條路徑來表示方法,這使得深度學習模型能夠利用源代碼的結構特性,而不是將其視為一個扁平的token序列.在此結構基礎上,文獻[7]又提出code2vec,進一步引入注意力神經網絡將AST路徑集聚合成分布式向量,具有相似AST路徑的方法的分布式向量在高維連續空間彼此接近,從而捕獲方法之間以及方法名稱之間的語義相似性.

文獻[6]和文獻[7]的研究,考慮到了程序的強結構特性[8],然而對程序的控制流信息以及程序動態可執行性等特征的關注度不夠.

本文針對提取出的方法特征缺失控制流信息以及不能反映程序動態可執行的問題,提出基于基本路徑學習的方法來對方法代碼抽象命名.首先研究了方法代碼控制流圖的構建,接著從本文構建的控制流圖中抽取基本路徑作為方法代碼的中間表示.然后將基本路徑集轉換成向量形式輸入到引入注意力機制[9]的LSTM神經網絡[10],以方法標簽為輸出目標設計損失函數.最后為了驗證學習基本路徑訓練出的模型能更好理解方法代碼的語義信息,構建了基于文獻[6]的數據集的訓練樣本,訓練得到代碼自動命名模型,并對實驗結果進行了分析.

2 基于基本路徑學習的方法代碼自動命名

神經網絡是以向量作為輸入,所以需要將程序表示為向量,并且轉換出的向量能夠表示程序的語義信息.因此可以使用中間表示作為程序和轉換成的向量之間的橋梁.為了使神經網絡學習到程序的控制流以及動態可執行的語義信息,本文以方法作為程序大小的粒度,使用基本路徑作為方法代碼的中間表示,并將基本路徑轉換成向量形式輸入神經網絡,訓練出基于基本路徑學習的程序方法名代碼自動命名模型.

基于基本路徑學習的方法代碼自動命名分為兩個階段:訓練階段和自動命名階段.如圖1所示給出了訓練階段的流程,該流程包括構建控制流圖,提取基本路徑集,將提取的基本路徑轉換成向量表示以及訓練神經網絡模型4步.自動命名階段將待命名的方法代碼按照訓練階段前3步進行處理,第4步直接將向量輸入到訓練好的神經網絡中,解析出與方法名標簽庫中語義最接近的方法名.下面詳細介紹訓練階段的具體過程.

第1步.構建方法代碼控制流圖

從源代碼中提取以方法為粒度的代碼段,通過編譯器將方法代碼分析優化得到三地址碼中間表示[11],然后通過本文給出的控制流圖構建算法,將三地址碼代碼段進行代碼段分塊,并在基本塊之間添加控制流邊得到控制流圖.

第2步.提取基本路徑集

將控制流圖中的每個基本塊作為基本路徑中的路徑節點,并且將路徑節點中變量的具體值抽象為具體的token以達到降低token數量級的目的,降低詞嵌入學習過程的復雜度[12].接著借鑒深度優先搜索的思想,構建算法提取出方法代碼的所有執行路徑作為方法的基本路徑集合.

第3步.將基本路徑集轉進行詞嵌入

首先將基本路徑集中的字符建立token詞表,此時基本路徑中的對應的token使用詞表中的token編號進行映射表示.然后將token通過word2vec技術[13]訓練得到每個token的詞向量.最后通過字典表映射關系將基本路徑轉換成向量形式.

第4步.訓練神經網絡模型

方法代碼的語義信息上下文依賴較長,本文選擇擁有記憶單元的LSTM神經網絡進行建模.首先將上一步得到的路徑向量輸入到LSTM神經網絡中,得到每條路徑的上下文基本路徑向量.然后將每條路徑向量聚合為代碼向量,在聚合過程中使用注意力機制對每條路徑向量計算權重,其中的權重是訓練過程中根據每條路徑的影響程度不斷更新.最后通過softmax層計算出代碼向量和方法名標簽的分布概率,損失函數計算出正確分布與預測分布的差值,使用梯度優化算法完成整個模型的閉環訓練.最終得到LSTM神經網絡以及注意力向量的參數.

3 模型算法

3.1 控制流圖構建

3.1.1 三地址碼與Soot

本文使用更加抽象和簡潔的三地址碼作為方法代碼的中間表示.三地址碼由一組類似于匯編語言的指令組成,每條指令最多由3個運算分量組成,且最多只執行一次運算,這里的運算通常是計算、比較或者分支跳轉.表1給出了源代碼對應的“三地址”指令序列的示例.

表1 三地址碼指令序列

考慮到模型的復用性,模型可以選擇特定于語言的編譯器來優化處理源代碼,以得到方法代碼的三地址碼序列.本文選擇Java語言編寫的源代碼作為研究對象,因此選擇分析Java程序的Soot靜態編譯器,生成Soot特有的三地址碼:Jimple[14].Soot能夠將Java代碼中的循環結構轉換成goto指令的形式,使代碼中只有分支和順序兩種控制流向,達到簡化方法代碼控制邏輯的目的.

3.1.2 控制流圖構建算法

傳統方法構建的控制流圖分支結構復雜,在此基礎上提取的基本路徑包含的路徑較長[15],使用LSTM神經網絡訓練時會出現梯度消失與梯度爆炸的問題.本文在三地址代碼序列基礎上構建程序控制流圖,優化程序控制結構,達到縮短基本路徑長度的目的.控制流圖中的每一個節點表示一個基本塊(基本塊表示控制流只能從基本塊第一條指令進入,除了基本塊的最后一條指令控制流在離開基本塊前不發生跳轉),每一條邊表示兩個基本塊之間的控制流轉移.

構建程序控制流圖的步驟如下:

1)通過靜態編譯器分析得到三地址代碼序列;

2)在三地址代碼序列的基礎上進行基本塊劃分;

3)基本塊之間添加邊信息,構建程序控制流程圖.

算法1.方法代碼控制流圖構建算法

輸入:方法代碼序列,Method={token1,…,tokenn},其中Method為方法代碼,tokenn為方法中的詞法單元.

輸出:方法代碼對應的控制流圖CFG={V,E},其中V={BasicBlock1,…,BasicBlockn},E={〈BasicBlocki,BasicBlockj〉,…}.

處理:

//Soot靜態分析器得到三地址碼序列

1.Jimple3AC←SootCompilerProcess(Method);

//每條三地址碼指令記錄在數組Stmts[]里

2.Stmts[]←Abstract3ACProcess(Jimple3AC);

3.V=φ;//控制流圖節點序列,每個節點代表一個基本塊BasicBlock

4.while(i

new BasicBlock;//創建一個新的基本塊

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中存在跳轉指令){

for each(BasicBlockjin V){

if(BasicBlockj中存在BasicBlocki的跳轉目標指令)

E=E∪{〈BasicBlocki,BasicBlockj〉};

}

}

}

7.return new CFG = {V,E};//返回控制流圖

算法1首先將方法代碼通過Soot靜態分析器得到三地址碼指令序列,然后將指令信息封裝記錄在數組里.接著劃分本塊集合作為控制流圖的節點集,在劃分過程中,步驟4兩條指令是否能夠劃分到同一基本塊的依據是判斷Stmts[i+1]是否為跳轉指令的目標指令或者是跳轉指令的下一條指令,如果是則不能劃分到同一基本塊.最后在基本塊與其后繼基本塊之間以及與其跳轉到的目標基本塊之間添加控制流邊.基本塊之間的控制流信息包含順序流向和跳轉流向,分別反映出方法代碼中的順序和分支循環邏輯.

3.2 基本路徑提取算法

基本路徑的提取是在控制流圖的基礎上,通過深度優先搜索算法提取所有的基本路徑,構成基本路徑集[16].基本路徑集中的元素是基本路徑,基本路徑是指控制流圖中從起始結點到終止結點所經過的一系列基本塊序列,且該路徑上至少有一條邊在其它路徑中沒有出現過,即包含其它路徑未曾引入的基本塊.基本路徑滿足覆蓋方法代碼中的所有分支單元和循環單元,能夠覆蓋控制流圖的各個執行路徑以及覆蓋所有邏輯控制條件,所以從側面反映出方法代碼的上下文語句執行的因果關系,因此將基本路徑作為方法代碼的中間表示能夠表示程序的動態可執行的特征.

基本路徑集定義:

1)基本路徑集是由多條基本路徑組成,且每條基本路徑是集合中的獨立路徑;

2)集合中的路徑包含控制流圖的所有節點和邊;

3)每條獨立路徑至少包含一條在其它路徑中未出現過的邊.

算法2.基本路徑提取算法

輸入:方法代碼對應的控制流圖CG={V,E},其中V={v0,…,vn},E={〈vi,vj〉,…〈vm,vn〉}.

輸出:基本路徑集BPS={path1,…,pathn},其中pathi=

〈vi,…,vm〉.

處理:

1.初始化:

BasicPathSet=?;//基本路徑集

Stack.push(v0);//控制流圖開始節點入棧

VisitedV[]=?;//記錄已被訪問節點

VisitedEdge[]=?;//記錄已被訪問邊

2.while(Stack is not null){

curV = Stack.peek();//讀取當前棧頂節點

Visited[curV];//記錄訪問過得節點

if(curV是終結節點){

//返回一條基本路徑,該路徑序列為棧頂到棧底的逆序序列

path←TraversalStack();

BasicPathSet=BasicPathSet∪{path};

while(Stack.peek()不存在未訪問過的邊){

Stack.pop();//彈出棧中不存在未訪問過邊的節點

}

}else if(curV不存在未訪問過的邊){

Stack.pop();//回退狀態

}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節點的后繼序列

BasicPathSet=BasicPathSet∪{〈path1,path2〉};

}

}

}

算法從控制流圖入口節點開始深度優先遍歷,首先判斷棧頂節點curV是否為終結節點,如果是則輸出棧的逆序序列作為一條基本路徑.不是則判斷curV是否存在未訪問過的邊,如果不存在則彈出curV開始下一次循環.如果存在則取以curV為開始節點的邊e且后繼節點為nextV,此時判斷nextV是否訪問過,如果未被訪問過則記錄邊e被訪問且將nextV壓入堆棧;否則如果nextV被訪問過且不存在未訪問過的邊,則此時基本路徑集中的某條基本路徑pathi必定存在nextV節點,輸出棧的逆序序列并拼接pathi中從nextV開始的序列作為一條基本路徑.

3.3 神經網絡模型

模型輸入:方法代碼對應的基本路徑集BPS={path1,…,pathn}的向量表示,其中pathi=〈bi1,…,biε〉;因為每條基本路徑有不同數量的token,為了讓神經網絡擁有相同長度的向量輸入,引入超參數ε作為基本路徑向量的固定長度.方法名標簽序列嵌入向量y.

訓練結果:得到LSTM神經層和注意力層的權重參數,以及方法代碼的向量.

softmax層:調用TensorFlow框架的softmax層計算代碼向量v在方法名標簽嵌入空間的預測概率分布,以cross-entropy損失函數[17]計算正確分布p和預測分布q之間的誤差,使用Adam梯度優化算法更新模型參數訓練網絡[18].其中正確分布是方法代碼的標簽在標簽庫的真實分布(是正確標注值為1,否則為0),模型預測得到的概率分布q是由方法代碼向量v和標簽庫中對應標簽嵌入向量的點積得到.

4 實驗與結果

為了驗證將基本路徑作為方法代碼的中間表示能夠包含更豐富的語義信息,訓練得到的模型能夠更準確的命名方法名,本文與先前工作進行實驗對比,并從學習開銷的角度分析方法優劣.為了統計分析模型在對不同復雜度的方法代碼命名的表現,以圈復雜度作為衡量代碼復雜度的依據進行實驗.

4.1 數據集

本文采用文獻[6]中的Java-med數據集,該數據集包含了1000個高質量Java項目,約400萬個方法,其中900個項目作為訓練集,100個作為測試集,并將該數據集經過如下處理構建實驗訓練樣本.

首先將項目中的類依據方法簽名(即返回值類型、方法名、參數類型)拆分出各個方法,并抽取方法名作為該方法的標注,在拆分過程中過濾掉類中的set、get類型方法,這些方法對于模型訓練沒有多少價值[19].然后,將拆分出的方法代碼按照3.1.2節的方法構建出控制流圖.最后,將控制流圖按照3.2節中論述的方法提取出基本路徑集,以方法代碼的基本路徑集作為模型的輸入,方法名標簽作為標注,構成如表2所示的訓練數據格式.

表2 訓練數據格式

4.2 實驗環境

本文在操作系統為Ubuntu 16.04.7 LTS的服務器上進行相關實驗.設備硬件環境為:處理器是Intel?CoreTMi7-7700 CPU@4.20 GHz*8,內存為64GB,顯卡為NVIDIA GeForce GTX 1080Ti.設備軟件環境為:深度學習框架是TensorFlow_gpu-1.4.0,Python使用Python3.7版本.

4.3 實驗過程

步驟1.構建控制流圖

將數據集中預處理好的方法代碼依次保存在單獨文件中,通過處理程序調用soot工具包中的Main處理類將方法源代碼文件轉換成Jimple三地址碼文件.編寫BasicBlock類作為抽象基本塊的數據結構,編寫Edge類記錄基本塊之間邊信息,編寫CFG類聚合基本塊以及邊信息作為控制流圖保存的數據結構.

步驟2.提取基本路徑集

編寫處理程序讀入控制流圖數據,調用3.2節設計的算法得到基本路徑集合,將每個方法代碼以{方法名標注,基本路徑集}的數據組合形式輸出到具體的訓練集和測試集的文件中.

步驟3.將基本路徑和方法名標簽量化表示

將基本路徑集中的每條基本路徑的三地址碼序列使用moses切詞工具得到token,并對token建立字典庫.使用word2vec詞向量嵌入工具對token嵌入學習,得到每個token對應的詞向量.根據字典映射關系將基本路徑中的每個token使用詞向量表示.方法名標注組成的標簽庫中的方法名token隨機生成一個唯一數字對標注進行量化表示,以便softmax計算方法代碼向量在標簽庫中的概率分布.

步驟4.訓練LSTM神經網絡

將訓練集采用十折交叉驗證的方法用于訓練LSTM神經網絡,網絡初始化參數設置為深度學習廣泛使用的值,并且設置不同LSTM神經網絡的隱藏層數,觀察其對結果的影響,最終選擇256個隱藏層數效果最好.基本路徑轉換成的向量固定長度設置為200,能夠覆蓋大多數基本路徑序列長度,因此LSTM網絡的時間步長也設置為ε.dropout設置為0.25,更新模型參數的minibatch設置為64,迭代訓練epoch設置為4,使用Adam梯度優化算法進行訓練,學習速率初始設置為0.01,然后當交叉驗證時準確率停止改善時,讓學習速率減半繼續迭代,最終得到代碼自動命名模型.

步驟5.模型評估對比

將本文構建的訓練集按照文獻[6,7]的方法訓練得到對比模型.將測試集分別輸入到對比模型和本文得到的模型,采用文獻[7]中的評價指標對模型結果統計分析.

4.4 實驗結果與分析

實驗1.模型對比結果分析

如表3所示本文提出基于基本路徑學習的代碼自動命名模型與其它基線模型:Paths+CRFs[6]、Paths+Attn[7]的對比情況.本文提出的模型記為BasicPaths+Attn,其中BasicPaths表示不使用注意力機制進行的對比實驗,具體做法是在聚合上下文基本路徑向量時直接使用求和取平均的方法得到方法代碼向量.

表3 模型得分評估

觀察實驗結果得出本文提出的模型得分優于其它模型.文獻[7]提出的AST路徑更多的表示上下文token的依賴關聯,而本文提取的基本路徑不局限于單個上下文token,而是上下文關聯的基本塊之間的依賴關系,并且LSTM網絡能夠捕獲到長距離的上下文依賴,因此本文模型能夠更好理解方法代碼的語義.并且本文使用注意力機制作用于方法代碼的關鍵基本路徑,使聚合得到的方法代碼向量更能夠表示代碼的特征信息,從而訓練得到的模型效果更好.

實驗2.模型訓練開銷分析

將基線模型與本文模型在處理好的數據集上進行訓練,并且基線模型使用他們默認的超參數.對于本文模型,調整LSTM神經網絡隱藏層以及基本路徑序列長度等參數,觀察對于F1值的影響,確定最佳的超參數.在調整過程中神經網絡超參數根據硬件性能從大往小進行調整,基本路徑序列的長度從短到長進行設置,最終確定LSTM隱藏層數為256,基本路徑序列長度固定為200.本文也基于該超參數討論與基線模型的訓練時間開銷對比,統計結果如圖2所示.

圖2 模型訓練時間開銷對比

觀察訓練時間開銷可知,Paths+Attn模型在較短時間內達到最佳效果,本文模型在27小時之后分數超過基線模型并在42個小時達到最佳效果.Paths+Attn模型的輸入吞吐量達到每分鐘1000個方法,所以能夠在較短時間內學習大量的方法代碼,因此能夠在短時間內達到較好的效果.而本文模型采用的LSTM神經網絡計算開銷高于基線模型,模型輸入吞吐量較小,然而隨著學習大量數據網絡記憶單元的參數進行調整,網絡輸出的上下文基本路徑向量包含更多的上下文信息,因此隨著訓練時間的推移本文模型的效果優于基線模型.

實驗3.方法代碼復雜度影響分析

本文在構建控制流圖過程中根據點邊計算法得到方法代碼的圈復雜度,以統計分析復雜度對模型的性能影響.針對本模型,方法代碼圈復雜度數量增大,其控制流圖包含的邊和節點線性增加,為了滿足邏輯覆蓋以及分支覆蓋,提取出的基本路基長度也隨著線性增加,在訓練過程中受LSTM的時間步的限制,基本路徑向量上下文關聯性會逐漸丟失,進而影響模型效果.為了評估方法代碼邏輯復雜度對本模型的影響,本文選擇圈復雜度數在1-12之間的方法代碼檢驗模型的F1得分情況,并且與基線模型進行對比.

如圖3所示本文提出的模型與基線模型在方法代碼圈復雜度數相同時的對比情況.從圖中可以看出,隨著代碼復雜度的增加,模型性能均逐漸下降.在復雜度較低情況下,Paths+Attn模型由于模型設計簡單且此時AST路徑較短,模型能更好的學習到代碼特征.然而隨著復雜度的提升,AST路徑增多且變長,基線模型效果降低,但是基本路徑的數量與控制結構的數量相關,因此本文模型所需要學習的路徑數量較少,并且本文模型中采用LSTM神經網絡能夠捕獲更長的上下文依賴性,因此在復雜度提升的情況下,本文模型表現的更好.

圖3 方法代碼復雜度影響對比

5 結 論

本研究提出基于基本路徑學習的代碼自動命名模型.首先使用編譯器對源代碼優化分析,得到方法代碼的三地址碼序列并構建基于三地址碼的控制流圖.然后構建算法提取基本路徑集合.最后構建神經網絡模型學習基本路徑集的特征,迭代訓練得到自動命名模型.

實驗結果表明,本文提取能夠反映控制流信息以及方法代碼可執行特征的基本路徑作為代碼中間表示,訓練出的模型能夠更好的捕獲代碼語義信息,并且對復雜度更高的代碼,模型能更好學習到代碼特征.在未來的工作中,將繼續研究將得到的代碼向量應用于代碼缺陷檢測的任務中.

猜你喜歡
代碼向量神經網絡
基于遞歸模糊神經網絡的風電平滑控制策略
向量的分解
聚焦“向量與三角”創新題
神經網絡抑制無線通信干擾探究
基于神經網絡的中小學生情感分析
基于Q-Learning算法和神經網絡的飛艇控制
神秘的代碼
一周機構凈增(減)倉股前20名
一行代碼玩完19億元衛星
向量垂直在解析幾何中的應用
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合