作者丨趙越
學校丨北卡羅萊納州立大學博士生
研究方向丨編譯器,程式語言
隨著深度學習的發展,人工智慧迎來了新一輪的熱潮。在影像處理和自然語言處理方面,深度學習已經展現出強大的能力,成為了這些領域的主流方法。與此同時,深度學習也在程式設計領域開始得到應用。畢竟廣義上,程式設計本身就是人工智慧的一部分,機器如果可以學會自動編寫程式,那麼人工智慧豈不是可以實現自舉了?
在最近的新聞報導中,大家也會時不時看到一些報導說某個研究可以實現自動程式設計,甚至任務在不遠的將來機器會代替代程式設計師的工作。實際情況是什麼樣的呢?本文將通過一些最進的研究探討一下當前深度學習在程式設計領域的發展現狀,儘可能讓讀者有一個客觀的瞭解。本文的目的既不是討論深度學習的技術細節,也不是要做一個全面的綜述,而是通過幾個典型的應用和相關文章來管中窺豹,看一看當前的現狀和挑戰。
直觀的看,程式語言與自然語言有很多相似的地方,比如都是有一個個符號(token)組成,都可以表達為語法樹(parse tree)等等。但是相比在自然領域的應用,深度學習在程式分析或者自動程式設計領域並沒有展現出它獨特的優勢。這與程式語言和自然語言的區別分不開。我們知道,自然語言具有大量的不確定性和歧義性,所以基於規則的方法很難處理大量的特殊情況。而程式語言則是人為的根據規則嚴格定義和設計的語言。在語法層面或者低端的語義方面,機器(比如編譯器)都可以準確的解析和理解。因此,在這個層面上,基於規則的方法具有天然的優勢。
當然,隨著程式變得越來越複雜,程式的行為也越來越難靠人工去分析,與此同時,在程式語言的使用上,程式設計師們需要去記憶或者查詢的東西也越來越多。如果人工智慧的方法可以讓機器理解程式高層的邏輯或者語義,那麼程式設計師就可以從重複低階的任務中解放出來。
實際上,傳統的 AI,比如基於邏輯推導的方法,或者基於統計的,貝葉斯推斷的機器學習方法在程式生成,分析領域已經有很長的應用歷史。深度學習在一些方向可以替代這些方法,達到更好的效果。接下來我們就選擇幾個典型的程式程式設計領域來探討一下他的特點以及深度學習目前的發展。
1
程式生成
第一個方向是程式生成,也就是讓機器自動編寫程式,這是當前AI發展的最大的挑戰之一。不過這也是深度學習在程式程式設計領域最熱門的方向。相比其他領域,程式生成更需要高階的“智慧”,因此在這個領域,深度學習最可能超越傳統方法。
在這個領域下最主要的一類是歸納式程式合成(Inductive Program Synthesis, IPS)。在這種模式下,機器通過觀察一系列輸入輸出的樣例來生成一個程式,使得它的行為滿足樣例中的資料。
使用深度學習的方法來做 IPS 有兩類思路:一是 Google 的 Neural turing machines [1] 採用的思路,直接通過樣例去訓練一個深度學習網路,利用深度學習網路強大的表達能力去“模仿”真實的程式,使其行為與輸入輸出一致。也就是說最終生成的程式就是網路本身。
正如 Yann LeCun 的觀點認為深度學習的關鍵是可微分程式設計:
make traditional computing elements differentiable so they can be integrated in gradient-based learning systems.
這類方法的關鍵在於將程式的行為可微分化。我們知道程式的行為是離散的,每一次指令都是讀出或者寫入一個儲存(比如暫存器,記憶體等)。只有讓離散的行為可微分化,才能利用 back propagation 去訓練模型。NTM 巧妙的利用了 [attention][distll] 機制將 RNN 模型和外部的儲存陣列連線起來。每次的輸入和輸出操作都連線到所有的地址,而每個連線都有一個權重(weight)來決定它的有效性。每次輸出的結果可以看作是輸入和權重的運算,而權重則可以通過網路訓練得到。因此,計算機的離散行為就(通過 attention 機制等)轉換成了連續的可微分化操作。沿著這類思路的工作還有 [3] 和 [2] 等。
歸納程式設計的另一種思路是讓神經網路生成程式,也就是說網路本身只是程式生成器,並不是程式本身。事實上,這也是歸納程式設計原本的定義。在神經網路還沒有出現前之前這一領域就已經有很長的研究歷史了。一個複雜的程式可以看成是由更基本的程式語言元素構成的,理論上我們可以採用列舉的方式用排列組合的方式將這些基本元素組合成程式,然後從中選擇正確的程式。我們把所以程式組成的集合看作是“程式空間”,歸納程式設計的問題就是要在這個程式空間中執行搜尋任務,找到一個滿足約束的例項(約束包括程式本身的正確性以及滿足輸入輸出)。
很顯然,對於我們常見的程式語言來說,這個空間不僅無限大而且非常稀疏。列舉的方法顯然不可行。因此,現在處理歸納程式設計就要從兩方面入手,一是縮小程式空間,二是設計更好的搜尋演算法。對於前者,我們可以看到當前的研究大多都是在一個自定義的領域語言上(DSL)來生成程式。相比實際的程式語言,這個 DSL 語言包含的元素會少很多,而且往往沒有複雜的控制流。其次,在生成過程中,也會限定程式的長度。對於後者,人們常常使用的是基於規則的啟發式搜尋。這種方法需要大量的專家知識,因此在擴充套件性上比較差。而且,基於規則的方法無法自我進化。另一種方法就是基於機器學習的演算法。本質上就是通過樣例資料去學習程式的概率分佈。比方說,如果資料樣例中,輸出都是輸入的 2 倍,那麼在程式空間中,y=2*x 這樣的函式就有很高的概率。對於更復雜的程式,模型需要學習基本元素的出現概率以及他們之間的組合。
相比基於規則的搜尋,機器學習的方法具有良好的擴充套件性和自我進化,適合設計資料驅動的自動化模型。其中,深度神經網路由於具有強大的學習複雜概率分佈的能力和良好的生成模式而被越來越多的用於程式生成。其中具有代表性的工作是來自微軟的 Deepcoder [4] 以及 RobustFill [5] 。他們也是採用了自己設計的 DSL 語言來降低程式的複雜度,比如圖 1,可以看到左邊的 DSL 基本資料流是很簡單的,而且文中限定了程式長度為 5。
此外,這類模型都採用了編碼器-譯碼器的結構。編碼器(某種神經網路,比如 LSTM)負責把輸入輸出的樣例轉化為模型內部的隱式表示(latent representation),而譯碼器(另一個 LSTM 模型)則利用這個內部表示預測和生成程式碼。
目前這些工作大部分還只是侷限於實驗室裡自創的簡單的 DSL 上。不過在某些特定場合下也可以找到應用。比如微軟就將可以將 RobustFill 技術用在了 Excel 表格的自動填充上(如下圖,表格可以根據幾個樣本推匯出填充公式)。
當然,除了從樣本來學習程式,深度學習網路還可以結合它在其他領域的優勢來建立更加複雜和智慧的模型。比如最近的 Pix2code,通過將卷積網路和 LSTM 網路結合起來識別 UI 的圖片,從而自動生成 DSL 程式碼,然後再進一步編譯成實際的程式碼。類似的,也可以結合自然語言處理,去通過自然語言的邏輯描述去自動生成程式碼。
2
程式碼補全及軟體修復
由於程式的複雜性,機器自動程式設計還很難實用化。不過,讓電腦輔助程式設計師完成工作倒是更加實用。比如 IDE 裡的自動補全(Code completion)可以大大的提高程式設計師的效率。
傳統的自動補全(也是現在編輯器裡用到的)基於文字或者語法。程式設計師必須知道做什麼,而自動補全只是幫助程式設計師減少了打字或者簡單的查詢。隨著軟體的日益複雜,程式設計師往往要花費大量的時間來學習記憶現有的庫函式和 API 介面,以及如果組合這些 API 呼叫實現某個功能。因此在工業界,人們急切希望能夠提高編寫程式的效率。比如 DARPA(美國國防部)就以及啟動了專案自助在這一領域的研究。很多公司如微軟,Google,Facebook 等也越來越重視程式設計工具的效率。如果能利用更復雜的機器學習模型,比如深度學習網路,機器可以根據上下文更加智慧的預測程式設計師接下來該做什麼,幫助程式設計師更加高效的完成任務。
在這一類任務中,模型需要過學習現有的大量的軟體庫,比如 Github 上的某個語言的程式碼庫,從而學習在當前上下文環境下的語句概率分佈。因此本質上講,這裡的模型也是基於統計的方法。這裡的挑戰是如何將程式碼抽象為標準化的表達。尤其是程式碼中,變數名和函式名的處理。比如在 [7] 和 [8] 中都是利用了神經網路強大的長期記憶能力,使得模型相比之前的方法,能夠捕獲更長的上下文資訊。在下面的例子中,加粗的部分整體完全是由演算法提示的。也就是說,機器可以根據情景主動提示合適的 API 呼叫,而不像常規的自動補全,需要程式設計師先輸入前面的字元。
除了自動補全,類似的方法還可以用於軟體錯誤的檢測和修復。因為既然可以根據上下文自動補全資訊,自然可以讓機器根據上下文檢測錯誤。比如 DeepFix [9] 是一個端到端的解決方案。通過訓練一個帶有 attention 機制的序列到序列(Seq2seq)模型,可以用於預測程式中某個語法錯誤的位置以及正確的格式。這類工作還可以用於幫助線上程式設計學習系統檢測和修復學生提交的程式碼中的大量語法錯誤 [10] 。不過,目前來看,這些修復都還是非常表面的語言使用錯誤。實際上這些錯誤都可以被編譯器很好的找到。在這點上深度學習模型並沒有特別大的優勢。不過隨著這個方向的發展,希望基於深度學習的模型能夠理解程式的更高層面的語義,從而幫助程式設計師修復邏輯層面的錯誤。
3
結語
程式語言和自然語言的區別決定了深度學習不同的發展狀況。不過,深度學習在程式語言上的應用中可以使用到很多在自然語言處理上的技術,比如對符號的處理,詞向量的表徵等。在模型上很多都可以使用自然語言處理的模型,比如 RNN 模型和 CNN 模型都會用到,還有一些基於某些結構的模型如基於圖或者樹的神經網路模型等。
在應用上,深度學習已經被用於簡單的程式生成,程式碼自動補全。但是更多的領域還沒有太多深度學習的身影,比如程式效能的分析和優化。在這個方向,深度學習面臨的第一個很大的挑戰就是資料。我們知道深度學習在影像和自然語言處理領域的快速發展離不開大量高質量標記資料的產生。但是在程式分析領域,一方面是整個社群對深度學習的支援不足,另一方面,程式的很多行為特性與實際的系統緊密相關,因此,我們很少能看到大量高質量的公開資料可供研究。另一個挑戰就是這個方向往往需要很多程式語言設計和編譯器原理等背景知識,而且對每一種語言都要做專門的語法處理和大量的準備工作,這也極大的提高了研究的門檻。
可以預計,在短期內深度學習在程式設計領域還會繼續發展,但是很難像其他領域一樣達到非常突出的效果。讓機器替代人類程式設計,估計還要很久。
[1] Graves, Alex, Greg Wayne, and Ivo Danihelka. “Neural turing machines.” arXiv preprint arXiv:1410.5401 (2014).
[2] Bošnjak, Matko, et al. “Programming With a Differentiable Forth Interpreter.” arXiv preprint arXiv:1605.06640 (2016).
[3] Neelakantan, Arvind, Quoc V. Le, and Ilya Sutskever. “Neural prorammer: Inducing latent programs with gradient descent.” arXiv preprint arXiv:1511.04834 (2015).
[4] Balog, Matej, et al. “Deepcoder: Learning to write programs.” arXiv preprint arXiv:1611.01989 (2016).
[5] Devlin, Jacob, et al. “RobustFill: Neural Program Learning under Noisy I/O.” arXiv preprint arXiv:1703.07469 (2017).
[6] Emilio Parisotto, et al. “Neuro-Symbolic Program Synthesis”, 5th International Conference on Learning Representations (ICLR 2017)
[7] Raychev, Veselin, Martin Vechev, and Eran Yahav. “Code completion with statistical language models.” ACM SIGPLAN Notices. Vol. 49. No. 6. ACM, 2014.
[8] Bhoopchand, Avishkar, et al. “Learning Python Code Suggestion with a Sparse Pointer Network.” arXiv preprint arXiv:1611.08307 (2016).
[9] Gupta, Rahul, et al. “DeepFix: Fixing Common C Language Errors by Deep Learning.” AAAI. 2017.
[10] Bhatia, Sahil, and Rishabh Singh. “Automated correction for syntax errors in programming assignments using recurrent neural networks.” arXiv preprint arXiv:1603.06129 (2016).