機器之心原創
作者:思源、一鳴
從程式設計師到數據工程師,編寫程序代碼是一項基本功,但是編寫冗長代碼的過程也極大地消耗了開發者的耐心。近來,有不少關於代碼補全工具的消息爆出,例如,來自美國的 Kite,來自加拿大的 TabNine 等,一時間獲得了不少程式設計師的關注。但其實很多人還並不知道,在這些國外產品不斷被媒體推送的背後,有一款能力更為強大、更早將深度學習應用於代碼補全的產品,一款源自中國的工具——aiXcoder,它的研發者們來自於北京大學。
在本文中,機器之心採訪了項目總負責人北京大學計算機科學技術系副教授李戈,請他為讀者朋友解讀自動代碼補全背後的技術,以及 aiXcoder 背後的技術特性和優勢。
aiXcoder 官網:https://www.aixcoder.com/#/
aiXcoder 的代碼補全效果
我們先看看寫 TensorFlow 時的代碼補全效果:
如上所示,aiXcoder 在 TensorFlow 的代碼環境下能夠直接「猜測」到模型建立後的一系列代碼流程。例如,在定義了 loss 之後需要定義 optimizer,之後需要 train_op、init 方法,然後最終定義模型的保存方式 saver,以及開始運行計算圖。這樣一個流程基本上是深度學習開發者所知曉的,但是按照流程寫下來非常繁瑣。在 aiXcoder 的提示下,開發速度得到了提升。
aiXcoder 支持 Java、C++/C、Python、PHP、JavaScript 等語言,以插件的方式集成到現有的 IDE 中,如 Pycharm、Android Studio、VS Code、Eclipse、Webstorm、Sublime 等,插件的背後是一個強大的雲端深度學習引擎。
針對開發者,該產品目前分為社區版、專業版和企業版。社區版是完全免費的,專業版也可以通過分享而免費獲得。它們間的不同之處在於模型會不會繼續學習,社區版主要利用事先訓練好的公用模型做預測,而專業版則會根據用戶的代碼習慣及結構作進一步的調整。
企業版是 aiXcoder 功能最為強大的版本,它能夠在企業內部的私有雲中進行部署,並能夠利用企業自己的代碼來進行模型的優化訓練,從而具有更高的準確率和運行性能。
aiXcoder 用起來怎麼樣
百聞不如一見,機器之心也對 aiXocder 進行了使用測試。
機器之心在 Pycharm 上試用了社區版/專業版,它們都是需要在線推斷。不同的地方在於專業版還需要額外的內存,因為每一個 Pro 用戶都需要額外的緩衝區來儲存模型「學到的」用戶習慣。當然,Pro 用戶的緩衝區是是只有該插件能訪問的。
使用體會
一般而言,當我們選擇 Python 和 PyCharm 時,代碼補全就自然用 IDE 自帶的工具。使用 aiXcoder 第一個感受是它比自帶的補全工具靈活得多,因為以前的補全主要體現在 Python 函數或其它包的 API,而 aiXcoder 還會預測變量名是什麼、運算是什麼、想調用的函數又是什麼。
雖然代碼補全的推斷過程全是在雲端完成的,但在我們的使用中,一般網絡環境甚至 4G 都能有實時的反饋,所以補全速度上基本和 Pycharm 自帶的工具差不多。李戈教授表示,目前 aiXcoder 絕大多數都能在 200ms 左右得到反饋,有部分地區的用戶由於網絡延遲問題可能會感覺到卡頓,aiXcoder 正在全國各個主要城市部署伺服器,以提升用戶體驗。同時,aiXcoder 團隊也特別關注模型壓縮技術,希望把基於 CPU 的推理運算時間壓縮到可接受的程度,從而推出能夠在 CPU 上運行的本地版。
總體而言,aiXcoder 提供的補全功能在預測變量名、函數名或關鍵字等效果上確實非常靈活,而且它還會學習開發者的代碼風格與編程模式,因此效果還是挺好的。
如下是自動補全的一些候選,一些函數名稱可能是開發者之間經常使用的,因此得到了推薦:
對於一些變量,aiXcoder 可根據變量類型提出該變量可能的操作,比如,對於下圖的變量「m」,aiXcoder 提出了一個對字符串進行增加的代碼:
對比測評
aiXcoder 官方也將產品和其他代碼補全工具進行了對比,包括 Kite 和 TabNine 等。
在對比過程中,aiXcoder 會使用 Kite 或 TabNine 官方提供的示例代碼,並測試完成這段代碼到底需要多少次按鍵。結果表明,aiXcoder 較其他插件在效率上提升 1.5 倍以上。
aiXcoder 是如何打造的
能夠實現高效代碼補全的 aiXcoder,背後有著強大的技術支撐。據李戈教授介紹,aiXcoder 很早就試過了語言模型,將代碼視為一種語言從而直接建模,這就和 Deep TabNine 一樣。但是研究者很快發現,只有語言模型是行不通的,它總會提出一些毫無意義、很不科學的補全建議。為此,aiXcoder 融合了基於序列的程序代碼語言模型、基於抽象語法樹和程序邏輯關係的圖神經網絡等方法,共同打造一個完整的系統。
為什麼直接生成代碼是困難的
如果深度學習模型能根據開發者的意圖,以端到端的方式直接生成對應的代碼,那麼這樣的模型會很「優雅」。但是經過研究發現,這樣的任務需求是很難實現的,這和任務本身所依賴的數據的性質有關係。
李戈教授從機器學習所依賴的數據性質的角度,對代碼生成任務和傳統的圖像處理任務、自然語言處理任務的不同,給出一種較為形象化的解釋。
對於圖像識別或圖像分類任務而言,機器學習的目標是建立一個連續的數據集(圖像數據)到一個近乎連續的、有著接近清晰邊界的數據集(標籤)之間的映射關係。
這樣一來,由於圖像數據異常的稠密,而標籤集又有足夠清晰的邊界,那麼這就相當於一個標籤擁有大量的數據可以學習。這樣的映射關係是比較容易建立的,這也是機器學習中和圖像相關的任務相對較為容易完成的原因。
對於自然語言處理任務而言,機器學習需要從一個較為連續的(離散度高於圖像)、有著較清晰邊界的數據集建立與另一個較為連續的、有著較清晰的邊界的數據集之間的映射關係。
而由於自然語言處理中的文本數據相比圖像數據更為稀疏,因此自然語言處理相關的任務更難取得較好的模型性能。
但是在代碼生成方面,從編程者的意圖(intent)生成程序代碼的問題,可以看做是「程式設計師意圖空間」到「程序代碼空間」的映射,其中意圖可以是由自然語言描述的信息。如上圖所示,這是從一個較為連續的、有著較清晰邊界的數據集,向一個更加離散而沒有清晰邊界的數據集進行映射。
換句話說,儘管代碼生成的意圖較為清楚,但是實現該意圖的代碼數據卻比較稀疏,而且即便對於相同的意圖,其對應的實現代碼之間仍存在較大差距,因此這樣的任務是非常難學習的。
為此,在 aiXcoder 的實際實現中,對不同應用領域的代碼都採用了特定的模型,它們僅使用該領域的數據進行訓練。例如,對 TensorFlow 或 PyTorch 等框架也有其特定的代碼補全模型。這樣做的主要目的就是加強程序分布的稠密性,在特定領域下,代碼分布更加接近連續性。
可見,根據編程者的「意圖」來「直接」生成完整代碼是非常困難的,但李戈教授表示,可以用類似的技術來輔助人類程式設計師來編寫代碼,我們可以從程式設計師已經寫下的代碼中獲取程式設計師的「編程意圖」,然後綜合分析代碼,的結構信息、變量引用信息、API 序列信息、繼承關係信息等等,以自動生成後續代碼。然而,在這個過程中,只有語言模型是遠遠不夠的,還需要對很多其它代碼特徵進行分析,才能做好生成式的代碼補全。
單純的預訓練語言模型又怎麼樣?
提起代碼補全,有些人可能會下意識的認為這僅僅是一個普通的語言建模任務,模型只需要根據開發者之前寫的代碼預測之後的代碼即可。因此使用最先進的預訓練語言模型,再在代碼數據上進行微調說不定是一種好方法。
但是李戈教授表示,這樣的想法是遠遠不夠的。預訓練語言模型在代碼補全任務中效果不佳,主要是因為代碼補全任務本身存在諸多不同於自然語言分析任務的挑戰。
首先是代碼文本中存在的語義抽象性問題。代碼的語義(功能語義)與其字面表示之間存在更大的差距。我們無法根據字面確定代碼的準確語義。例如,在代碼中,只改變一個字符就有可能完全改變整行代碼的功能,因此處理代碼的語言並準確提取其含義相比自然語言處理任務更棘手。
f = open('word_ids.txt','r')f = open('word_ids.txt','w')
上圖所示,在 Python 代碼中,打開某個文件時使用「r」和「w」會實現完全不同的功能。
此外,代碼的功能語義難以進行具體的表達和刻畫,而且代碼功能語義的表達方式多種多樣。例如,有多種代碼的形式文本用於實現某個功能,不能說某一種代碼是對的而另一種是錯的。
list_a = [] for i in items: result = test(i) list_a.append(result) list_a = [test(i) for i in items]
如圖所示,實現 list_a 的代碼可以是多種多樣的,但語言模型會將它們學習為完全不同的表徵。
同時,代碼文本本身的結構非常複雜。例如,代碼的語義與代碼結構(如行與行的縮進)之間存在較大的關聯性,代碼語義依賴於代碼結構進行表達。這是預訓練語言模型難以表示的特徵。
最後,代碼具有演化性的特徵。代碼較自然語言的迭代速度更快,因此預訓練語言模型不能夠及時捕捉演化特徵。
考慮到代碼語言中的諸多特性,單純的預訓練語言模型無法得到非常好的效果。
核心技術
既然單獨的語言模型不行,那麼 aiXcoder 又結合了哪些技術,它又是靠什麼來補全代碼的?總體而言,aiXcoder 主要依賴於其特有的對程序代碼進行學習的深度神經網絡模型,該模型能夠對程序的如下幾類特徵進行分析:
1. 程序的結構語義特徵:程序語言是一種結構性很強的語言,程序的結構信息也體現著程序的語義。例如,抽象語法樹是對代碼進行解析的一種較為通用的結構,它體現了代碼的語義特徵,aiXcoder 便充分利用了抽象語法樹,對程式設計師已經寫下的代碼的語義進行解讀。
2. 程序元素間的邏輯關係:程序代碼的不同元素之間存在著不同的關係,例如程序變量之間的引用關係、類之間的繼承關係、方法與參數之間的調用關係等等。程序本身又可以表示為多種圖,例如控制流圖、數據流圖、調用關係圖等等。aiXcoder 藉助圖神經網絡能夠對程序元素之間的多種關係進行建模,從而能夠對程序元素之間的複雜關係進行分析和推理。
3. 程序語言序列模型:當然,程序語言也具有與自然語言相似的一面,因此可以利用程序標識符之間的序列關係建立程序語言模型。aiXcoder 也使用了最新的深度學習語言模型對程序中的序列信息進行建模。
在獲得程序代碼的各種特徵之後,就該把這些特徵輸入深度神經網絡進行分析了,但這並不容易,因為在輸入神經網絡之前需要把這些特徵進行向量化表示。在研究過程中,北京大學提出了一系列解決程序語言成分相量化的辦法,並且在國際上最早發表了相關的論文,這些都為 aiXcoder 的構造打下了基礎。
團隊介紹
李戈教授所在的北京大學高可信軟體技術教育部重點實驗室是國內頂尖的軟體科學研究團隊,是北京大學計算機軟體與理論全國重點學科的主要支撐,其建設歷史可以追溯到 1955 年,至今已有 60 多年的學術沉澱和積累。該團隊在著名軟體科學家楊芙清院士和梅宏院士的帶領下,已經成長為該領域國際領先的研究團隊。
基於深度學習的代碼分析與生成一直是李戈教授的研究方向,也是北大高可信軟體技術教育部重點實驗室重點關注的領域,他們從 2013 年開始就開展了基於深度學習的代碼分析研究,從 2015 年開始就將深度學習用於代碼生成,是最開始進行相關研究的團隊之一。該團隊在 AAAI/IJCAI/ACL 等頂會上發表過很多代碼生成的相關論文,這也是一大筆技術累積。
李戈教授是 aiXcoder 創始人,北京大學計算機科學技術系副教授,CCF 軟體工程專委會秘書長,史丹福大學計算機系人工智慧實驗室訪問副教授。主要研究方向:程序分析,程序生成,深度學習。所在研究團隊聚焦於基於機器學習概率模型的程序語言處理,在代碼功能分析、代碼自動補全、代碼缺陷檢測等方面取得並保持了國際上領先的研究成果。
WAIC 2019 開發者日將於 8 月 31 日在上海世博中心舉辦,包含 1 個主單元、4 個分單元、黑客馬拉松比賽和開發者診所互動區。
屆時,全球頂尖 AI 專家、技術大牛、知名企業代表以及數千名開發者將齊聚上海,圍繞人工智慧前沿理論技術和開發實踐進行分享與解讀。