蘇 佳, 蘇小紅, 王甜甜
(哈爾濱工業(yè)大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院, 哈爾濱150001)
知識(shí)圖譜(Knowledge Graph)是Google 在2012年正式提出的概念,它以圖的形式來表達(dá)客觀世界的實(shí)體(概念,人,事物)以及實(shí)體之間關(guān)系的知識(shí)庫,后來得到廣泛關(guān)注和應(yīng)用研究。 以其大規(guī)模、可解釋、可推理等特點(diǎn),現(xiàn)已經(jīng)應(yīng)用于智能問答、語義搜索、可解釋推薦、情報(bào)分析、決策支持、知識(shí)導(dǎo)航和醫(yī)療等領(lǐng)域。
知識(shí)圖譜是一個(gè)由知識(shí)和知識(shí)間的關(guān)系組成的結(jié)構(gòu)化的語義知識(shí)庫,典型的知識(shí)圖譜采用三元組(頭實(shí)體,關(guān)系,尾實(shí)體)描述事實(shí)。 知識(shí)圖譜每個(gè)節(jié)點(diǎn)表示客觀世界中存在的概念或者實(shí)體,邊則描述概念或者實(shí)體之間的語義關(guān)系。 知識(shí)圖譜提供了一個(gè)通用的結(jié)構(gòu)化框架來存儲(chǔ)和表示知識(shí),從而實(shí)現(xiàn)基于實(shí)體和關(guān)系的挖掘、推理和分析。 在軟件工程領(lǐng)域,代碼知識(shí)圖譜目前研究較少,相關(guān)表示方法主要有以下幾種:
Zeqi Lin 等人[1]分析了代碼中的結(jié)構(gòu)化信息,提取代碼元素之間的結(jié)構(gòu)依賴關(guān)系(方法調(diào)用、繼承關(guān)系)來構(gòu)造代碼圖譜(Code Graph),再利用TransR 方法學(xué)習(xí)共享表示空間的嵌入表示,再計(jì)算tf-idf 為代碼元素加權(quán),利用土堆移動(dòng)距離(EMD),通過移動(dòng)“分布質(zhì)量”的方式,把一個(gè)分布轉(zhuǎn)換為另一個(gè)分布所需要的最小工作量,來計(jì)算文檔與代碼之間的距離;同一個(gè)團(tuán)隊(duì)[2]又利用軟件源代碼中特定于軟件的概念知識(shí)來改進(jìn)API 學(xué)習(xí)資源的檢索,利用Recodoc 和基于關(guān)鍵字的啟發(fā)式算法提取文本中的API,生成API Graph,每個(gè)查詢或文檔都表示為一個(gè)加權(quán)的API 實(shí)體集合。 利用多關(guān)系數(shù)據(jù)嵌入算法TransR 計(jì)算API 實(shí)體相似性,在傳統(tǒng)方法獲得的檢索排名基礎(chǔ)上,用成對(duì)的API 實(shí)體相似性來計(jì)算文檔和查詢之間的概念相關(guān)性得分,對(duì)API 學(xué)習(xí)資源檢索結(jié)果進(jìn)行重排序(文檔獲得更高分?jǐn)?shù)排到頂部,反之底部),提高檢索準(zhǔn)確性。
Wang L 等人[3]從軟件歷史倉庫中收集bug 數(shù)據(jù),如bug 報(bào)告、commit 提交信息、代碼文件等。 用自然語言處理技術(shù)對(duì)數(shù)據(jù)進(jìn)行預(yù)處理,提取bug 描述信息和相關(guān)開發(fā)者的信息。 使用LDA 主題模型來處理bug 描述信息,建立不同數(shù)據(jù)之間的聯(lián)系。根據(jù)bug 報(bào)告的屬性(depend on 和duplicate)建立bug 之間的關(guān)系。 根據(jù)bug 描述信息的文本相似度,建立不同bug 之間的相似關(guān)系。 用同樣的方法建立commit 之間的相似關(guān)系。 最后利用bug id,來構(gòu)建bug、commit 之間的關(guān)系,最終得到bug 知識(shí)圖譜(Bug Knowledge Graph)。
LiuMingwei 等人[4]爬取了API 官方文檔,提取了API 相關(guān)的結(jié)構(gòu)性知識(shí)和描述性知識(shí),來構(gòu)建API 知識(shí)圖譜。 他們首次提出了API 概念,利用詞的詞匯相似性和上下文相似性,將詞進(jìn)行層次聚類,連接API 的不同的描述性句子,建立refer to 關(guān)系。在通用知識(shí)圖譜的基礎(chǔ)上,用分類器獲取軟件概念,將軟件概念和API 概念、API 實(shí)體用句向量計(jì)算相似度并建立related to 關(guān)系,生成了更好的面向task的API 摘要。
不同的應(yīng)用場(chǎng)景下,需要定義不同的知識(shí)圖譜的實(shí)體和關(guān)系來滿足不同的需要。 現(xiàn)有的代碼知識(shí)圖譜構(gòu)建的數(shù)據(jù)來源都較為單一,缺乏對(duì)代碼解釋性知識(shí)的獲取和融合。
本文在分析代碼相關(guān)知識(shí)圖譜國內(nèi)外研究現(xiàn)狀的基礎(chǔ)上,提出一種基于多源異構(gòu)數(shù)據(jù)的Java 代碼知識(shí)圖譜構(gòu)建方法,該代碼知識(shí)圖譜可用于代碼的知識(shí)檢索,代碼摘要等場(chǎng)景。
為了挖掘API 知識(shí)之間的顯、隱式關(guān)系和豐富代碼知識(shí),引入了API 相關(guān)概念[4],記為api_concept 實(shí)體。 本文設(shè)計(jì)的代碼知識(shí)圖譜包含package、 class、 method、 functional _ description、question_description、api_concept 等6 種實(shí)體,以及haveClass、 extend、 haveMethod、 hasFunctional Description、hasQuestionDescription、referTo 等6 種關(guān)系。 構(gòu)建流程如圖1 所示。
圖1 Java 代碼知識(shí)圖譜構(gòu)建流程Fig. 1 Java Code Knowledge Graph Construction Process
本文將從github、API 官方文檔以及Stack Overflow 問答社區(qū)(以下簡(jiǎn)稱SO)的Q&A 對(duì)等三種數(shù)據(jù)源進(jìn)行數(shù)據(jù)挖掘,提取Java 語言相關(guān)的代碼知識(shí)。 在獲取到來自不同數(shù)據(jù)來源的知識(shí)后,通過知識(shí)融合將它們合理、統(tǒng)一地組織到同一個(gè)圖網(wǎng)絡(luò)中,構(gòu)建Java 代碼知識(shí)圖譜。
本文將從三種數(shù)據(jù)來源中提取java 代碼的結(jié)構(gòu)性知識(shí)、描述性知識(shí)以及概念和關(guān)系,并對(duì)這些不同來源的知識(shí)進(jìn)行融合。 結(jié)構(gòu)性知識(shí)主要包括API的相關(guān)實(shí)體,package,class,method,還有parameter和return values 等,在其他應(yīng)用場(chǎng)景中還可以添加相關(guān)屬性,例如完全限定名稱,加入的版本號(hào)以及API Document URL。 代 碼 中 的 相 關(guān) 實(shí) 體, 除package,class,method 等外,還包括方法體內(nèi)的API調(diào)用序列。 描述性知識(shí)主要包括來自API 文檔的功能性描述以及SO 的問題性描述,前者主要描述了API 的功能,后者主要描述了使用者使用時(shí)遇到的相關(guān)問題。
1.2.1 基于AST 的JAVA 源代碼的代碼知識(shí)提取
從github 獲取的源碼中提取的知識(shí)有:(1)類相關(guān)的實(shí)體和關(guān)系。 (2)方法相關(guān)的實(shí)體和關(guān)系。(3)方法體中的API 調(diào)用序列。 具體來說,本文通過源碼獲取的實(shí)體有package,class,method;關(guān)系有haveClass,extend, havaMethod。
抽象語法樹(Abstract Syntax Tree, AST),是通過使用樹狀結(jié)構(gòu)來表示源代碼的抽象語法結(jié)構(gòu),它作為程序分析中一種重要的中間表示形式,在代碼分析、代碼重構(gòu)、語言翻譯等領(lǐng)域得到廣泛的應(yīng)用?,F(xiàn)有的一些相關(guān)工具中都含有將源代碼直接轉(zhuǎn)換為抽象語法樹的模塊。 用程序分析技術(shù),通過解析源代碼的AST,遍歷包定義、類定義、成員變量表以及方法定義表,可以獲取所需要的結(jié)構(gòu)性知識(shí)。 在方法體內(nèi)提取API 調(diào)用序列,通過訪問methodInvoke節(jié)點(diǎn),根據(jù)節(jié)點(diǎn)綁定的數(shù)據(jù)信息,進(jìn)行正則匹配和過濾即可獲得API 調(diào)用序列,它除了作為代碼方法級(jí)別的一種知識(shí),還在源代碼和代碼知識(shí)圖譜之間通過API 實(shí)體建立了聯(lián)系。
1.2.2 基于網(wǎng)絡(luò)爬蟲的API 文檔的代碼知識(shí)提取
基于網(wǎng)絡(luò)爬蟲從API 官方文檔爬取數(shù)據(jù)并提取代碼結(jié)構(gòu)性知識(shí)以及描述性知識(shí)的流程如圖2 所示。
圖2 基于網(wǎng)絡(luò)爬蟲的API 文檔的代碼知識(shí)構(gòu)建流程Fig. 2 Code knowledge construction process of API document based on web crawler
結(jié)構(gòu)性知識(shí)提取:利用正則表達(dá)式解析超鏈接,可以獲得package,class,method 實(shí)體,以及hasClass,hasMethod 關(guān)系。 識(shí)別表格中的<td></td>標(biāo)簽,可以獲得returnType,parameter 屬性。
描述性知識(shí)提?。簽榱双@取完整的描述性知識(shí),需要利用bs4 解析html 文檔,按以下規(guī)則進(jìn)行網(wǎng)頁內(nèi)容清洗:(1)恢復(fù)被“<p >”和“<li >”等標(biāo)記打斷的句子;(2)<blockquote></blockquote>用“_CODE__”替換代碼片段;(3)<code></code>標(biāo)簽直接過濾,留下內(nèi)容。 其中,從API 官方文檔中提取的method description涵蓋了API 的功能描述以及使用方法,可以提供Java方法的相關(guān)信息,作為java 代碼的一種知識(shí)。
1.2.3 基于啟發(fā)式規(guī)則的Stack Overflow 代碼知識(shí)提取
從SO 中獲取知識(shí),主要是獲取和API 相關(guān)的問題描述作為描述性知識(shí),具體流程如圖3 所示。
圖3 Stack Overflow 的代碼知識(shí)構(gòu)建流程Fig. 3 Code knowledge construction process from Stack Overflow
獲取SO 數(shù)據(jù)后,按照標(biāo)簽<java>獲取Q&A 對(duì),并進(jìn)行分詞。 由于問答語句是自然語言,是非結(jié)構(gòu)化數(shù)據(jù),為了將Q&A 對(duì)和API 聯(lián)系起來,需要識(shí)別和API有關(guān)的Q&A 對(duì),并識(shí)別自然語言描述中的API 實(shí)體。
根據(jù)之前提取API 文檔中的API 實(shí)體,分別記錄全限定名稱和非限定名稱,按照以下啟發(fā)式規(guī)則,獲取Q&A 數(shù)據(jù)和API 之間的對(duì)應(yīng)關(guān)系:
(1)如果API 的全限定名稱出現(xiàn)在標(biāo)題或者問題描述中,那么該問題和這個(gè)API 相對(duì)應(yīng)。 (2)如果question body 中含有指向API 文檔的超鏈接,那么該問題和鏈接的API 相對(duì)應(yīng)。 (3)如果question body中含有<code>標(biāo)簽,那么該問題和<code></code>中間的非限定名稱對(duì)應(yīng);為API 實(shí)體和Q&A 數(shù)據(jù)建立hasQuestionDescription 關(guān)系,如果一個(gè)API 實(shí)體對(duì)應(yīng)多個(gè)Q&A 數(shù)據(jù),那么只連接score 分?jǐn)?shù)最高的Q&A。
為了增強(qiáng)可擴(kuò)展性,本文在基于啟發(fā)式規(guī)則獲取到的Q&A 對(duì)和API 的對(duì)應(yīng)關(guān)系基礎(chǔ)上,采用NLP 領(lǐng)域中的命名實(shí)體識(shí)別模型和方法進(jìn)行Java API 實(shí)體識(shí)別。
命名實(shí)體識(shí)別(Named Entity Recognition,NER)是NLP 的基礎(chǔ)任務(wù),指從文本中識(shí)別出命名性指稱項(xiàng)。 在本文中,將用來識(shí)別SO 中的Q&A 語句中的API 實(shí)體。
條件隨機(jī)場(chǎng)(Conditional Random Field, CRF)是一種基于機(jī)器學(xué)習(xí)的方法,在馬爾科夫隨機(jī)場(chǎng)的基礎(chǔ)上增加了觀測(cè)變量,將所有特征進(jìn)行全局歸一化,可以獲得全局最優(yōu)解。 本文用啟發(fā)式方法選取如下特征來進(jìn)行CRF 模型訓(xùn)練:(1)當(dāng)前詞是否首字母大寫,其他字母小寫。 (2)當(dāng)前詞的詞性。 (3)前一個(gè)詞的詞性。 (4)當(dāng)前詞是否含有“.”。 (5)當(dāng)前詞是否全部為大寫。 (6)當(dāng)前詞的后綴。 (7)當(dāng)前詞是否含有數(shù)字。
近年來,越來越多的研究已經(jīng)說明了深度學(xué)習(xí)在NLP 任務(wù)上的有效性。 BiLSTM-CNNs-CRF 在雙向LSTM-CNNs 的基礎(chǔ)上,加入了CRF,從而對(duì)于輸出序列進(jìn)行優(yōu)化。 BERT 是一個(gè)用Transformer 作為特征提取器的深度雙向預(yù)訓(xùn)練語言理解模型,由多層的雙向Transformer 連接而成,利用Position Embedding 來學(xué)習(xí)位置信息, 通過訓(xùn)練 MLM(Masked Language Model) 和NSP (Next Sentence Prediction)任務(wù)獲取到豐富的語言知識(shí),在多種NLP 任務(wù)中獲得突破性進(jìn)展。 將BERT 應(yīng)用于NER 任務(wù),只需用BERT 替換word embedding 來進(jìn)行語義編碼。
本文使用CRF,BiLSTM-CNNs-CRF 以及用BERT 的三種模型對(duì)手工標(biāo)注數(shù)據(jù)進(jìn)行API 命名實(shí)體識(shí)別。
為了將從不同數(shù)據(jù)源獲得的知識(shí)統(tǒng)一在同一個(gè)知識(shí)圖譜當(dāng)中,需要對(duì)這些知識(shí)進(jìn)行融合。 融合主要包括兩方面:一方面是API 實(shí)體融合;另一方面是API 概念的融合。
API 實(shí)體融合指的是根據(jù)API 實(shí)體名稱進(jìn)行統(tǒng)一和融合,建立其他知識(shí)和API 實(shí)體之間的關(guān)系。利用API 官方文檔中獲取的API 知識(shí),構(gòu)建基本的API 知識(shí)圖譜作為基礎(chǔ),再往API 實(shí)體上補(bǔ)充來自源碼的結(jié)構(gòu)性知識(shí),以及SO 的問題描述,最后將API 功能性描述和SO 上的問題性描述和API 概念相連接。
API 概念融合指的是從和API 相關(guān)的描述性知識(shí)中提取API 概念,作為描述之間的橋梁,從而建立描述性知識(shí)之間的關(guān)系。 本文中的API concept是由基本名詞短語構(gòu)成,所以需要進(jìn)行基本名詞短語提取。
依存句法分析(Dependency Parsing, DP) 通過分析語言單位內(nèi)成分之間的依存關(guān)系揭示其句法結(jié)構(gòu)。 使用語義依存刻畫句子語義,通過詞匯所承受的語義框架來描述該詞匯,而其數(shù)目相對(duì)詞匯來說數(shù)量小。 這個(gè)框架表示大部分的句子,同時(shí)也能據(jù)此迅速提取句子的核心內(nèi)容。 本文使用斯坦福句法分析器(stanford corenlp)對(duì)前文提取的描述性知識(shí)句法分析并提取依存關(guān)系。 在句法分析后,獲取所有NP 節(jié)點(diǎn)作為候選節(jié)點(diǎn)。 在此基礎(chǔ)上,按如下規(guī)則進(jìn)行剪枝過濾:(1)去除句法樹上子節(jié)點(diǎn)含有NP的節(jié)點(diǎn)。 (2)根據(jù)依存關(guān)系{ compound 復(fù)合,nmod復(fù)合名詞修飾(只保留連詞or,and),amod 形容詞修飾(過濾常見詞) },保留NP 內(nèi)的節(jié)點(diǎn)。 (3)最后過濾停用詞和無關(guān)符號(hào),即可得到該句所需要的API概念集合。 api_conception 和對(duì)應(yīng)的描述性語句之間建立(description,referTo,concept)關(guān)系,從而連接了不同的描述性語句。
Neo4j 是一種圖數(shù)據(jù)庫管理系統(tǒng),屬于原生圖數(shù)據(jù)庫,其使用的存儲(chǔ)后端專門為圖結(jié)構(gòu)數(shù)據(jù)的存儲(chǔ)和管理進(jìn)行定制和優(yōu)化的,在圖上互相關(guān)聯(lián)的節(jié)點(diǎn)在物理地址也指向彼此,因此更能發(fā)揮出圖數(shù)據(jù)的優(yōu)勢(shì)。 知識(shí)圖譜非常適合用Neo4j 進(jìn)行存儲(chǔ),基于Neo4j 生成部分java 代碼知識(shí)圖譜的可視化結(jié)果如圖4 所示。
圖4 java 代碼知識(shí)圖譜的部分可視化結(jié)果Fig. 4 Partial visualization results of Java code knowledge graph
Q&A 數(shù)據(jù)作為有標(biāo)簽的樣本數(shù)據(jù),標(biāo)簽為Q&A含有的API。 為保證數(shù)據(jù)的質(zhì)量,從中按照score 得分,選取Top-2000 數(shù)據(jù),進(jìn)行人工標(biāo)注,API 命名實(shí)體識(shí)別。 標(biāo)注采用BIOES 標(biāo)注方法,用B 表示這個(gè)詞處于一個(gè)實(shí)體的開始(Begin), I 表示內(nèi)部(Inside), O 表示外部(Outside),E 表示這個(gè)詞處于一個(gè)實(shí)體的結(jié)束,S 表示這個(gè)詞自己就可以組成一個(gè)實(shí)體(Single)。 標(biāo)注示例如圖5 所示。
圖5 API 命名實(shí)體識(shí)別的BIOES 標(biāo)注示例Fig. 5 Examples of BIOES Labeling for API Named Entity Recognition
其中第一列是自然語言句子中的單詞,第二列是單詞的詞性,第三列為人工按BIOES 的標(biāo)注的標(biāo)簽。
本文在API 實(shí)體識(shí)別時(shí),采用如表1 所示的準(zhǔn)確率、精確率、召回率以及F1 值作為評(píng)價(jià)指標(biāo)。
表1 API 命名實(shí)體識(shí)別評(píng)價(jià)指標(biāo)Tab. 1 API Named Entity Identification Evaluation Criterion
其中:TP 將API 實(shí)體預(yù)測(cè)為API 實(shí)體數(shù),FN 將API 實(shí)體預(yù)測(cè)為其他類型數(shù),FP 將其他類型預(yù)測(cè)為API 實(shí)體數(shù),TN 將其他類型預(yù)測(cè)為其他類型數(shù)。
2000 個(gè)樣本隨機(jī)選用1600 個(gè)進(jìn)行訓(xùn)練,200 個(gè)作為驗(yàn)證集,200 個(gè)作為測(cè)試集。 測(cè)試句子中200個(gè)句子一共有1394 個(gè)token,203 個(gè)API 實(shí)體,實(shí)驗(yàn)結(jié)果如表2 所示。
表2 API 命名實(shí)體識(shí)別測(cè)試集實(shí)驗(yàn)結(jié)果Tab. 2 Experimental Results of API Named Entity Recognition %
可以看出深度學(xué)習(xí)模型相較于傳統(tǒng)的機(jī)器學(xué)習(xí)模型在API 識(shí)別方面具有優(yōu)勢(shì),而BERT 模型在四個(gè)指標(biāo)上均可以達(dá)到最好效果。
本文提出了一種Java 代碼知識(shí)圖譜的構(gòu)建方法,將github 源碼、API 文檔和Stack Overflow 問答社區(qū)的多源知識(shí)進(jìn)行提取和融合,構(gòu)建成圖譜,并通過實(shí)驗(yàn)驗(yàn)證了BERT 模型相比于其他現(xiàn)有模型可以獲得更好的API 實(shí)體識(shí)別效果。