602
汽車大全
比人工還準確!看深度學習如何對源碼進行分類
編程語言是軟件開發行業的主要工具。自20世紀40年代以來,已經出現了數百種編程語言,並且每天都有大量的各種編程語言的代碼被推送到代碼庫中。
我們認為,根據代碼識別編程語言的源代碼分類器將是一個非常有用的工具,因為它可用於在線自動語法高亮和標簽建議,比如可用在StackOverflow和技術維基網站上。這個想法促使我們根據最新的AI技術編寫一個對代碼片段依據編程語言進行分類的模型。
我們使用GitHub API從GitHub上收集了數十萬個源代碼文件。在訓練模型之前,必須處理原始數據以消除和減少一些不需要的特征。最終完成的分類器的性能非常得好,你可以在本文結尾找到相關的結果,以及有關模型決策的一些解釋。
數據
我們根據編程語言的受歡迎程度選擇了其中的一部分。圖1顯示了2014年第四季度GitHub上最常用的49種語言[1]。此分析僅考慮活躍的代碼庫,即在此期間至少有一行代碼被推送進去的存儲庫。我們也將**HTML**和**XML**添加到列表中,雖然人們可能並不認為它們是編程語言,但畢竟它們是與軟件開發項目相關的。基於同樣的原因,我們也添加了SQL。
圖表1 - 活躍代碼庫(點擊查看原圖)
我們使用GitHub API來抓取代碼庫。下圖顯示了經過幾天抓取後的數據形狀。我們抓取了數千個代碼庫,但忽略了大小超過100MB的,以避免花費太多的時間進行下載和預處理。我們使用文件擴展名來標記每個樣本所用的編程語言(例如,file.php是PHP源文件)。 C#是代碼量最多的編程語言,而Arduino在我們爬取的資源中是最少的。為了避免訓練集的不平衡,每個種類我們最多使用了一萬個例子。
圖表2 - 抓取的文件(點擊查看原圖)
混雜的源代碼
仔細看一下原始數據,我們發現一些令人深思的行為和特征,這並不令人驚訝,因為這些數據是從實際的代碼庫中抓取的。最常見的情況是單個文件混雜了多個語言,這在網絡應用程序中很常見(如JavaScript、HTML、CSS、PHP和ASP)。下麵這段從.asp源文件中提取出來的ASP代碼說明了這種情況。
在這個例子中,我們隻想為每個文檔分配一個種類。因此,對於在單個源代碼文件中使用混合語言,我們隻保留該文件主要語言的代碼片段(從其擴展名推斷),並刪去其他語言的所有內容。為此,我們對於每種語言使用已知的保留詞和表達式。例如,我們知道<%php
和%>
之間的所有內容都是php代碼,所以如果是.php文件,我們隻保留這些內容,並刪除其他的內容。通過同樣的方法,我們可以使用正則表達式或Python中的內置解析器從代碼中剝離HTML標簽。
這些文檔中另一個常見的特征是嵌入式代碼片段。例如,在下麵的JavaScript腳本中,引號之間嵌入了C代碼段。這是另一種非常常見的混合代碼。我們通過用占位符替換引號之間的所有內容來化這個問題(在這個例子中,我們使用strv作為字符串占位符)。
圖2 - JavaScript代碼段中嵌入了“隱藏的”C代碼
符號化
在預處理之後,源代碼中仍然包括了轉義的換行符和製表符,因此,我們需要對所有文本進行符號化。在這個步驟中,我們必須保留代碼所有的語法信息。我們使用正則表達式[\w']+|[""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""\\]
來提取符號。完成這一步之後,這些數據就可以進行訓練了。
Python
print "This is using getsize"
for root, dirs, files in os.walk('/tmp'):
print root, "consumes"
符號化之後
print strv \n for root, dirs, files in os.walk(strv):\n\t print root, strv
預處理完成後
['print', 'strv', 'for', 'root', ',', 'dirs', ',', 'files', 'in', 'os', '.', 'walk', '(', 'strv', ')', ':', 'print', 'root,', '"', 'strv']
模型
近來,卷積神經網絡(CNN)正受到越來越多的NLP項目的青睞。特別是在文本分類方麵,深度學習模型取得了顯著的成果[2,3]。我們的模型使用了一個單詞嵌入層,然後是一個帶有多個過濾器的卷積層、一個最大池層,最後是一個softmax層(圖3)。我們使用了一個非靜態並且隨機初始化的嵌入層,因此需要從頭開始訓練矢量。
結果
我們對10%的數據進行了測試,並計算出每個標簽的準確性、精度、回調率和f1評分。總體結果分別為97%、96%、96%和96%。每個標簽的成績也相當得高(圖3)。
圖表3 - 每個種類的結果(點擊查看原圖)
結果看起來還不錯,讓我們來看一下預測的解釋說明來檢查分類器是如何做出決策的。我們使用LIME生成“解釋說明”,該“解釋說明”高亮了與每個標簽最相關的單詞。這樣,我們就知道了模型為什麼選擇這個標簽而不是另一個標簽。
Scala代碼片段:
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
結果說明:
Java代碼片段:
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter("filename", true));
out.write("aString");
} catch (IOException e) {
// error processing code
} finally {
if (out != null) {
out.close();
}
}
說明:
OCaml代碼片段:
# let average a b =
let sum = a +. b in
sum /. 2.0;;
val average : float -> float -> float =
未來的方向
雖然分類器的性能很高,但仍有改進的空間。例如,嚐試著使用直接從字符學習的字符級模型,這樣就用不著單詞嵌入層了[4]。此外,可以試著獲取每種編程語言的版本數據,這樣就能給源代碼添加特定版本號了。
參考資料
- Githut – https://githut.info/
- Kim, Y. (2014). 用於語句分類的卷積神經網絡。 有關2014屆自然語言處理會議的經驗方法 (EMNLP 2014), 1746–1751.
- Wang, P., Xu, J., Xu, B., Liu, C., Zhang, H., Wang, F., & Hao, H. (2015). 基於語義聚類和卷積神經網絡的短文本分類。 Proceedings ACL 2015, 352–357.
- Zhang, X., Zhao, J., & LeCun, Y. (2015). 用於文本分類的字符級卷積網絡, 1–9.
文章原標題《Source Code Classification Using Deep Learning》,作者:Robson Montenegro,譯者:夏天,審校:主題曲。
文章為簡譯,更為詳細的內容,請查看原文
最後更新:2017-09-07 15:32:30