微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

AI前線發表於2019-02-15

微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

作者|微軟
編譯|Debra
編輯 | Emily
AI 前線導讀在過去五年中,基於深度學習的方法已經徹底改變了很多應用,例如可以理解圖片、語音和自然語言的應用。對於電腦科學家來說,他們自然而然會提出一個疑問:計算機是否學會了理解原始碼?乍一看,這似乎是一個微不足道的問題,因為程式語言的確被設計為可以被計算機所理解。但是,許多軟體 bug 的出現,實際上反映了開發者的真實心理:他們希望程式碼按他們想要的方式去執行,而不是按他們寫的去執行。換句話說,小的錯別字會帶來很嚴重的後果。

更多幹貨內容請關注微信公眾號“AI 前線”,(ID:ai-front)

以下是一個簡單的例子:

float getHeight {return this.width; }。

在這個例子中存在的問題對於人類,或一個可以理解術語“高度”和“寬度”含義的系統是顯而易見的。它的關鍵洞見是原始碼提供兩種功能。首先,它向計算機傳達應該準確執行哪些硬體指令。其次,它向其他程式設計師(或六週後向作者自己)傳達程式如何工作的資訊。後者是通過選擇名稱、程式碼佈局和程式碼註釋來實現的。通過識別兩個通訊渠道出現分歧的情況,自動系統會指出可能存在的軟體缺陷。

過去的程式分析主要集中在正式的、機器可解釋語義的程式上,或者它把程式視為自然語言的一個(有點奇怪的)例子。前者這個方法來源於數學邏輯(https://www.microsoft.com/en-us/research/research-area/programming-languages-software-engineering/),需要針對每個需要處理的新案例進行大量的工程設計工作。另一方面,自然語言方法則涉及自然語言處理工具(https://www.microsoft.com/en-us/research/research-area/human-language-technologies/)的應用,這些工具在純粹的句法任務上運作良好,但迄今還不能學習程式的語義。

在 ICLR 2018 年發表的一篇新論文中,來自微軟研究院和溫哥華西蒙弗雷澤大學的研究人員提出了一種將這兩者結合起來的新方法,並展示瞭如何用這種方法找出已釋出軟體中的錯誤。

程式圖

為了能夠從豐富的原始碼結構中學習,首先需要將它轉化為程式圖。圖的節點包括程式的標記(即變數、運算子、方法名稱等)及其抽象語法樹的結點(定義語言語法的元素,如條件語句)。程式圖包含兩種不同型別的邊緣:句法邊緣只表示應該如何解析程式碼,比如 while 迴圈和 if 塊;以及作為簡單程式分析結果的語義邊緣。


微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

句法邊緣包括簡單的“NextToken”邊,用於表示抽象語法樹的“Child”邊,和用於將原始碼與原始碼文字中最後一次發生連線的“LastLexicalUse”邊。圖 1 以語句 Assert.NotNull(clazz)為例展示了這種邊,其中與 token 對應的節點是灰色框,與程式語法的非終端對應的節點是藍色橢圓。子邊緣顯示為藍色實心邊緣,而 NextToken 邊緣為黑色雙邊緣。

語義邊緣包括將變數連線到上一次可能在程式執行中使用過的“LastUse”邊緣,“LastWrite”邊緣則連線到上一次寫入的變數,“ComputedFrom”邊將變數連線到計算值。利用程式分析工具箱中的其他工具(例如別名和點對點分析)以及路徑條件,可以獲得更多的語義邊緣。圖 2 展示了一小段程式碼中的一些語義邊緣(黑色)。


微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

LastUse 關係以綠色邊緣顯示。因此,例如,y 與迴圈之前的 y 的最後一次使用以及自身相關聯。同樣,LastWrite 關係顯示為紅色邊,因此在 while 條件中使用 x 與迴圈前的 x 賦值,以及迴圈內 x 的賦值相關聯。最後,ComputedFrom 關係由藍色邊緣表示,將變數連線到從中計算出來的變數。

句法邊緣大致對應程式設計師在閱讀原始碼時看到的內容。另一方面,語義邊緣則與程式如何執行相對應。通過將這些概念結合在一張圖中,系統可以同時從更多的資訊源中學習。

從圖形中學習

最近,從圖形結構資料中學習引起了一些關注,因為圖形是表示資料及其關係的標準方式,例如我們可以用一張圖包含一個組織或藥物成分的資訊。近期圖形深度學習比較成功的兩種方法有圖形卷積網路(graph convolutional networks,卷積網路的擴充套件,是影象理解的關鍵)和門控圖形神經網路(gated graph neural networks ,廣泛用於自然語言處理的遞迴神經網路的擴充套件)。

這兩種方法首先均對每個節點進行獨立處理,以獲得節點本身的內部表示(即低維空間中的向量)。隨後,重複地將每個節點的表示與它所連線的節點的表示相結合(兩種方法的組合方式有所不同)。因此,在第一個步驟之後,每個節點都包含其自身及其直接鄰居的資訊;在第二步之後,它獲得兩步之外的節點的資訊,以此類推。由於該方法的所有步驟都在(小)神經網路上進行,因此可以訓練它們從資料中提取與整體任務相關的資訊。

尋找錯誤

學習程式圖可用於查詢錯誤,例如本文開頭的示例中所示的錯誤。為了這個目的,該模型被賦予一個程式,該程式中的一個位置以及可以在該位置使用的變數列表,之後要求模型預測應該使用哪個變數。為了處理這個任務,程式被轉換成一個圖形,其中一個特殊節點對應要選擇的位置。通過權衡該特殊節點的計算表示以及與可用變數相對應的節點表示,網路可以計算每個變數可能性的大小。為這樣的模型提供數百萬行現有的程式碼,以及不需要特別註釋的資料集,就可以輕鬆進行訓練。

當在新程式碼上執行這個模型,並以很高的概率預測 var1,但程式設計師選擇了 var2 時,這可能表示這是一個錯誤。通過標記這些問題讓人類專家審查,我們就可以發現實際的錯誤。以 Microsoft C#編譯器 Roslyn 獲取的程式碼為例:


微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

請注意突出顯示的引數檔案路徑和欄位 _filePath 的用法,它們很容易被混淆。_filePath 是一個錯字,開發人員在研究人員報告了這個問題和類似問題後將之修復。在其他一些 C#專案中也發現了類似的 bug,被報告之後得到修復。

在量更大的定量評估中,這種新方法比傳統的機器學習技術要好得多。作為基準測試方法,它考慮到了直接在原始碼上工作的雙向遞迴神經網路(BiRNN)以及訪問資料流資訊的 BiRNN 的簡單擴充套件。為了評估不同的模型,他們分析了總共包含 290 萬行原始碼的開源 C#專案。在不同的機器學習模型中使用其中單個變數被刪除並被要求預測最初使用的變數(假設程式碼經過充分測試且總是正確的)的原始碼進行測試。在第一個實驗中,他們用已儲存的檔案對這些模型進行訓練。在第二個實驗中,使用全新專案的資料測試這些模型。結果如下表所示,使用新的程式圖測試結果更好。image

未來的應用

程式圖是一種將深度學習方法應用於程式的多功能工具。微軟將在明年的 AI Residency Program(https://www.microsoft.com/en-us/research/academic-program/microsoft-ai-residency-program/)中繼續研究其未來的應用。


微軟提出程式圖方法:從原始碼中學習,揪出惱人的Bug

相關連結


  • 開原始碼實現:https://github.com/Microsoft/gated-graph-neural-network-samples

  • Deep Program Understanding projecthttps://www.microsoft.com/en-us/research/project/program/

  • 門控圖神經網路論文https://arxiv.org/abs/1511.05493

  • 門控圖神經網路程式碼庫https://github.com/Microsoft/gated-graph-neural-network-samples

  • 學習用圖表來表示程式 - ICLR'18 論文https://arxiv.org/abs/1711.00740

原文連結:

https://www.microsoft.com/en-us/research/blog/learning-source-code/


相關文章