生成Python函數一半沒問題,當前最「正統」的代碼生成是什麼樣?

2021-01-08 機器之心Pro

機器之心原創

參與:思源

大家都說深度神經網絡能力很強,那麼從函數注釋生成函數代碼,以及從函數代碼總結函數注釋這種最基礎的代碼任務到底能不能行?像 Python、Java 這樣的通用高級語言,到底在代碼生成上能達到什麼水平?本文介紹的就是這樣一篇北大前沿研究。

開發者寫代碼,和數學家寫公式一樣是非常自然的一件事。開發者將完成某個任務的步驟和邏輯,一行行寫成代碼,並期待達到預定的效果。數學家從某個事實出發,將思考過程一行行寫成表達式,並期待找到複雜邏輯背後的簡單關係。

這兩者經常會有交叉,也會有融合。數學推導結果可以大量簡化代碼,並提供新的解決路徑;而代碼可以快速驗證推導過程,並用於實際的生活中。代碼和表達式都是一種形式化語言,而另一種必不可少的是用來描述它的自然語言,也就是注釋或文檔。

通過注釋,我們能知道這段代碼幹了什麼,甚至很自然地想到「如果是我,這段代碼該怎麼寫」。通過閱讀代碼,我們能沿著開發者的思路走一遍,總結出它到底幹了什麼。這兩者似乎是一種對偶關係,從代碼到注釋、從注釋到代碼,這就是代碼生成與代碼總結兩大任務。

在這篇文章中,我們將介紹代碼生成與總結的最新進展,北大 Bolin Wei、李戈等研究者提出的對偶學習在 Python 和 Java 代碼生成上獲得了新的 SOTA,並且被接收為 NeurIPS 2019 論文。

如下是北大新研究根據注釋生成的兩段代碼,其中 dcsp 表示 tab 鍵、dcnl 表示換行符,它們控制 Python 代碼的縮進結構。

值得注意的是,在 Python 語言上,根據注釋這種自然語言,生成有效的代碼已經達到了 51.9% 的準確率。也就是說,生成的一半代碼能通過詞法分析、語法分析,並生成正確的抽象語法樹。

代碼生成與總結,是一對兄弟

之前這兩項研究大多都是獨立的,代碼總結會利用 Encoder-Decoder、抽象語法樹和 Tree RNN 等技術生成意圖,代碼生成會利用 Seq2Seq、語法規則和基於語法的結構化 CNN 來生成代碼,這些研究並沒有深入挖掘它們之間的關係。

而北大的這一項研究從對偶學習出發,探索如何利用它們之間的關係促進提升學習效果。

具體而言,研究者考慮了概率與注意力權重中的對偶性,從而設計了一種正則項來約束對偶性。更直觀而言,這種「對偶性」表示代碼生成任務的輸入"意圖"同樣是代碼總結的輸出,反之亦然。其中意圖指開發者寫這一段代碼的目的,一般而言會通過注釋的方式用自然語言表達。

利用對偶學習,研究者獲得了當前最優的效果。其實這種提升也非常合理,例如當前效果最好的神經機器翻譯模型 Transformer Big + BT,它就大量採用回譯機制,希望根據原語與目標語之間的相互翻譯,從而得到更好的最終模型。

統一的聯合訓練框架

如下所示為代碼生成、總結的對偶學習框架,總體上生成與總結兩條路徑都非常容易理解,它們都採用了常規基於注意力機制的 Seq2Seq 模型。現在重要的是理解中間的對偶約束,該約束用於給損失函數加正則項,從而令它們之間相互促進。

對偶訓練的整體過程,代碼生成模塊與總結模塊會聯合訓練。

上面 Seq2Seq 的過程就不再贅述了,它們採用的損失函數也是常規將所有時間步上的損失相加。不過需要注意的是,原始碼的詞彙量要比注釋更大一些,因此代碼生成模塊輸出層的參數量要大於代碼總結的輸出層參數量。

聯合概率來約束

如前所述,對偶訓練框架包含了非常重要的對偶約束,它由兩個對偶正則項組成,分別用於約束兩個模型的對偶性。這兩種正則項受到了注意力權重具有對稱性的啟發,也受到了兩種模型之間概率相關性的啟發。

若現在給定輸入樣本<x, y>,其中假設 x 為代碼,y 為對應的代碼注釋。那麼代碼生成可以描述為 p(x|y)、代碼總結可以描述為 p(y|x)。現在如果要找到它們之間的概率相關性,那麼根據聯合概率與條件概率之間的關係式就可以快速得出:

也就是說,logP(x) + logP(y|x) 需要等於 logP(y) + logP(x|y),這是代碼生成與總結的內在聯繫。如果兩項差別很大,那麼至少可以判定代碼生成與總結都沒有達到最優。所以,常規的做法就是把這個約束構建為損失函數:

其中 P(x) 和 P(y) 分別是針對代碼和注釋的語言模型,它們都是邊緣分布。這個損失有點類似於回歸模型常用的均方誤差,如上所示,只要兩個子模型不滿足理論上的概率條件,那麼肯定會產生損失,在訓練中就會建立起代碼生成與總結的關係。

注意力權重也來約束

上面是其中一個正則項,另一個正則項主要是考慮兩個子模型之間的對稱性。在北大的這一項研究中,他們考慮了注意力權重的對稱性。研究者表明,因為注意力權重能度量原始碼 Token 與注釋 Token 之間的匹配關係,而這種匹配關係又是對稱的,所以注意力權重也需要是對稱的。

研究者舉了一個例子,例如代碼注釋為「find the position of a character inside a string」,那麼對應原始碼可能為「string . find ( character )」。現在,不論是從代碼到注釋還是從注釋到代碼,原始碼中的「find」一定需要匹配到注釋中的「find」,它們之間的關係是不變的。

所以,現在最直觀的思想是,我們希望兩個注意力權重矩陣 A_xy 和 A_yx,它們之間對應的元素儘可能相等。

因為 A_xy 表示代碼部分注意到注釋部分的程度,所以,A_xy 矩陣的每一行表示代碼的某個 Token,與注釋的所有 Tokens 之間的關係。同理 A_yx 表示注釋部分注意到代碼部分的程度,A_yx 的每一列表示代碼的某個 Token,和注釋的所有 Tokens 之間的關係。

具體而言,如果

,其中 i 表示 A_xy 的第 i 行;

,其中 i 表示 A_yx 的第 i 列。那麼很明顯,我們需要令 b_i 儘可能等於 b_i'。如果它們非常相近,那麼可以表明注意力權重矩陣是對稱的,原始碼和代碼注釋之間的匹配是成功的。因為經過 softmax 的 b_i 和 b_i'都是一種概率分布,所以北大研究者通過 JS 散度度量這兩類分布之間的距離。

最常見的 KL 散度是不對稱的,也就是說 KL(b_i || b_i') 不等於 KL(b_i' || b_i),而 JS 散度是 KL 散度的「對稱版」,所以採用 JS 散度非常合理。此外,因為 JS 散度是對稱的,所以代碼生成模型與代碼總結模型都能採用這樣的距離度量作為約束條件。

最後,以注意力權重的對稱性作為正則項,JS 散度可以表述為:

偽代碼帶你走近聯合訓練

現在兩種正則項都已經完成了,只需要聯合訓練兩個子模型就行了。如下算法 1 所示,輸入兩種數據源的語言模型預計對應的數據,模型就能開始學。

如上所示,對於每一個批量數據,模型會計算兩個子模型各自的預測損失,並同時計算兩個公共的對偶正則項。這樣的損失能算出對應的梯度,並分別更新兩個子模塊的權重。

目前該研究的開源實現已經放到了 GitHub,研究者使用 PyTorch 實現了整個模型的訓練過程。如上偽代碼所示,模型架構方面,Seq2Seq 大家已經比較熟了,我們需要重點理解的是目標函數。

如上代碼片段所示,損失函數主要由三部分組成:即常規的交叉熵損失函數,它度量生成序列與標註序列間的距離;對偶損失函數,它度量的是代碼與注釋的概率相關性;最後是注意力損失,它度量的是兩組注意力權重之間的分布距離。

通過這些訓練目標的約束,代碼生成與總結才會真正地相輔相成。

真實的 GitHub 代碼生成

這種最正統的代碼生成與總結無疑是非常困難的,它遠遠不能像 UI 界面那樣生成簡易的代碼。也許藉助卷積神經網絡,UI 界面的代碼生成已經能用於實際的界面設計,但是對於「更正統」的純代碼生成,目前的準確度還遠遠不能滿足我們的要求。

在這篇論文中,北大研究者在 Java 與 Python 兩個數據集,測試了代碼生成與總結的效果。其中 Java 數據集是從 GitHub Java 項目中抽取的 Java 方法,以及對應的自然語言注釋,該自然語言了這個方法的用途。與 Java 類似,Python 數據集也是從 GitHub 中抽取的。兩種數據集的統計信息如下所示:

論文表 1,我們可以看到,訓練集有 5 萬到 7 萬段代碼,且確實一段 Python 代碼平均長度要遠遠少於 Java 代碼。

最後,我們可以看看北大研究者得出的最終效果。他們主要通過 BLEU 值、METEOR 和 ROUGE-L 三種度量方法評估模型生成的代碼注釋,這對於自然語言生成來說是比較常規的度量標準;此外,研究者通過 BLEU 值與有效代碼率(PoV)來評估代碼生成的效果,其中 PoV 指生成代碼能解析為抽象語法樹的比例。

如上所示為代碼生成與總結的總體效果,我們可以發現對偶訓練效果要超過其它方法,且相比獨立訓練的 Basic Model,效果也要更好一些。

值得注意的是,在代碼生成中,Java 和 Python 的 PoV 分別只有 27.4 與 51.9%。也就是說,生成的代碼首先不管是不是完成了自然語言描述的功能,它能通過詞法分析、語法分析,最終成功地構建成抽象語法樹,佔比並不高。

這樣的效果,也許代表著正統代碼生成,最前沿的水平。它離生成合理的代碼,輔助開發者完成實戰開發還太遠了。正如該論文作者李戈教授所說,程序的數據空間非常稀疏,而自然語言數據空間也比較稀疏,這兩個稀疏空間的變換肯定會比較困難。它並不能像圖像生成這種連續空間的變換,程序的生成還有很長的路要走。

相關焦點

  • 程式設計師的樂趣,生成自定義二維碼,5行Python代碼就搞定
    生成二維碼的工具也層出不窮,但多數需要在線完成,並且生成的圖案也千篇一律,過於單調。那麼有沒有辦法實現自定義生成二維碼呢?近日,一位熱衷於終身學習的工程師兼攝影師 Arindom Bhattacharjee 撰寫了一篇自定義生成二維碼的方法,並且整個生成過程只需要 5 行 Python 代碼即可完成。感興趣的讀者可以自己實現下。
  • AI加持,Kite增加智能代碼補全功能:減少一半操作,實時補全
    機器之心報導機器之心編輯部代碼補全工具 Kite 近日更新了最新的版本,增加了名為「Intelligent Snippets」的新功能。這一功能可以幫助開發者更為智能和高效的補全 Python 代碼中的函數命令了。
  • 「python opencv視覺零到實戰」八、圖片選區操作
    一、學習目標了解什麼是ROI了解floodFill的使用方法如有錯誤歡迎指出~目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰
  • 「每日一練」巧用python生成隨機數
    隨機數在我們的生產和生活中有很多的應用場景,比如說登錄驗證的隨機數字等等,那麼你知道在Python中怎麼生成隨機數嗎?往下看,就是這麼簡單!題目python中生成隨機整數、隨機小數、0--1之間小數方法代碼先上代碼~運行效果題目詳述程序分析:隨機整數:random.randint(a,b),生成區間內的整數隨機小數:習慣用numpy庫,利用np.random.randn(5)生成5個隨機小數0-1隨機小數
  • 基本初等函數 指數函數 代碼篇
    由於機器學習和數學密切相關,尤其是數學中的函數,因此我們非常有必要複習和了解基本的函數知識。上一篇文章中,我們為大家介紹了基本初等函數中的指數函數 基本初等函數 指數函數,本文將為大家介紹如何利用python語言完成函數的繪製。
  • 「python opencv計算機視覺零基礎到實戰」九模糊
    一、學習目標了解什麼是卷積了解模糊的使用方法與應用目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰」 第四節色彩空間
  • 「python opencv視覺零基礎」十、圖片效果毛玻璃
    一、學習目標了解高斯模糊的使用方法了解毛玻璃的圖片效果添加了解如何自己做一個噪聲圖片目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰
  • 寫Python 代碼不可不知的函數式編程技術
    本文作者是新加坡國立大學計算機學院和「USP」博學計劃學生 Raivat Shah,專注於編程與數據研究。頭等函數在 Python 中,函數是「頭等公民」(first-class)。也就是說,函數與其他數據類型(如 int)處於平等地位。
  • Python功能不夠多?來看看如何生成測試數據集吧!
    scikit-learn Python 庫提供一套函數,用於從可配置測試問題中生成樣本來進行回歸和分類在本教程中,你將學習測試問題及如何在 Python 中使用 scikit-learn 進行測試。完成本教程後,你將知道:如何生成多類分類預測測試問題如何生成二進位分類預測測試問題如何生成線性回歸預測測試問題讓我們開始吧。
  • 在pandas中使用pipe()提升代碼可讀性
    簡介我們在利用pandas開展數據分析時,應儘量避免過於「碎片化」的組織代碼,尤其是創建出過多不必要的「中間變量」,既浪費了「內存」,又帶來了關於變量命名的麻煩,更不利於整體分析過程代碼的可讀性,因此以流水線方式組織代碼非常有必要。
  • python基礎課程 第5章 奇妙的內建函數
    然而人生遠遠比計算機世界複雜,我們可以用python在計算機中實現這個原則,卻很難在人生中實現它。有人說工程師相對比較單純也是如此吧,工作久了,難免受機器的一些影響。畢竟作為高級動物的我們內心世界太過複雜和敏感,到現在為止甚至未來的很長的時間裡也沒辦法被人工智慧所取代。
  • 「python學習手冊-筆記」003.數值類型
    「浮點數:」 浮點數在標準的CPython中,採用的是C語言的雙精度浮點數.,叫「向下」取整,也就是比真正結果小的那個最接近的整數.」的最接近的整數.這個對於正數來說比較好理解,捨棄小數的部分.對於負數而言,就是比其結果小的最接近的負數.
  • 一文看懂生成對抗網絡
    這些巨大的並且不斷擴大的信息現在是很容易被機器獲取的,問題的關鍵是怎麼設計模型和算法讓機器更好的去分析和理解這些數據中所蘊含的寶藏。Generative models(生成模型)現在被認為是能夠實現這一目標的最有前景的方法之一。Generative models通過輸入一大堆特定領域的數據進行訓練(比如圖像,句子,聲音等)來使得模型能夠產生和輸入數據相似的輸出。
  • Python2 已終結,入手Python 3,你需要這30個技巧
    檢查所需的最低 Python 版本你可以在代碼中先檢查一下你的 Python 版本,以免當前用戶的 Python 版本與你的腳本不適配。實現的代碼很簡單:3. 使用 IPythonIPython 其實就是升級版的 shell,單單是自帶的自動補全功能就值得你使用它了。
  • 40行Python代碼,實現卷積特徵可視化
    在瀏覽通過最大化最後一層卷積層特徵圖的平均激活得到的 512 個模式時,我經常發出感慨「哇,這是一隻雞」,或「這不是一根羽毛嘛」。注意:雖然我使用第 40 層(卷積層)來生成我們當前正在查看的圖像,但我使用了第 42 層來生成顯示每個特徵圖的平均激活的圖。第 41 和 42 層是 batch-norm 和 ReLU。
  • python隨機函數random分配應用,隨機分配8名老師到3個教室中
    羽憶教程最近遇到一個問題,要隨機分配8名老師到3個辦公室中,這時小編想要了python中的隨機函數random來進行分配工作,感覺小編像個月老一樣。python隨機函數python隨機函數在python中,想要生成隨機數
  • 想用StyleGAN生成老婆?來看看這個小哥的復現細節吧
    圖 1:修改單一位置的空間特徵圖以生成動漫人物「寫在前面」這篇技術博客介紹了一個使用生成式對抗網絡完成的項目。由於這是一個個人項目,我採用了一個在專業領域中通常不會使用的動漫人物數據集「DANBOORU2018」。
  • 詳解Python隨機數的生成
    Python內置的random模塊提供了生成隨機數的方法,使用這些方法時需要導入random模塊。下面介紹下Python內置的random模塊的幾種生成隨機數的方法。1、random.random()隨機生成 0 到 1 之間的浮點數[0.0, 1.0)。
  • 隨機生成N個立方體,學習blender Python隨機數生成
    打開blender控制臺首先輸入import random print(random.randint(0,9))多次執行print(random.randint(0,9)),就能見到系統每次都生成0-9內的隨機整數使用了