1. 程式人生 > >254頁PPT!這是一份寫給NLP研究者的程式設計指南

254頁PPT!這是一份寫給NLP研究者的程式設計指南

機器之心編輯部、赤樂君。

最近 AllenNLP 在 EMNLP2018 上做了一個主題分享,名為「寫給 NLP 研究者的程式設計指南」(Writing Code for NLP Research)。該演講從寫原型和寫模組兩方面介紹了 NLP 研究該如何複製別人的程式碼、測試自己的程式碼塊、記錄及分享研究等,總之在研究者也要高效碼程式碼的年代,這是一份濃縮的實踐經驗。

這份內容乾貨滿滿,僅僅只是看了 slide 就知道是非常有意思的一次演講了。slide 共有 254 頁之多,在「赤樂君」知乎專欄分享內容的基礎上,機器之心為大家介紹 NLP 及深度學習研究者的程式設計指南。

讀者可以直接下載 PPT 瞭解詳細內容,其中每一頁 PPT 都帶有簡要的備註,根據這些備註可以將所有 PPT 以及整場演講串聯起來。

下面是整個分享的大綱。通過這次演講,你可以學到如何寫程式碼來促進你的研究,以及可復現的實驗。當然讀者最好還是知道一點 NLP 相關的知識,因為這一份分享會以深度學習中的 NLP 問題作為案例。此外,能夠大致讀懂 Python 程式碼也是很好的背景,這篇文章都是以 Python 介面呼叫 DL 框架為例。

這裡有兩種寫研究程式碼的模式,一種是寫原型,一種是寫元件。作為一名研究者,大多數時候我們都希望寫原型,但是在沒寫好元件前是寫不好原型的。而通過原型設計,有時候做出來的東西又是希望下次再複用的元件。因此這是編寫程式碼的兩種模式,它們並不獨立。

我們先從寫原型的方式開始介紹。

寫原型

當我們開始寫一個原型程式碼的時候,我們要做到下面三點。

1. 寫程式碼要快

2. 跟蹤實驗結果

3. 分析模型結果

快速開發

要做到快速程式設計,不要從頭開始寫所有內容,而是使用框架。這裡的框架不僅指 tensorflow 或 pytorch 之類的框架,也可以理解為模板。比如上圖中如果寫 training loop 的部分,已經有人寫好了。我們只要看懂後,直接拿來用就行,沒有必要從頭開始自己寫所有部分。

上面提到的一些內容,都是可以找到現成框架來套用的。很多時候我們在程式設計時遇到的問題不是構建模型,而是資料讀取、預處理和寫訓練迴圈等部分。如果有人把你想用的東西模組化了,還等什麼,直接拿來用啊!

當然拿來用也是有步驟的,首先我們應該獲得基線模型的效能,這也是一個很好的研究實踐。基線模型可能是別人的程式碼,你要是能修修改改就更好了。其次復現 SOTA 基線結果對於理解模型和做更多的研究是非常有幫助的。

要想快速開發,另一個建議就是先複製,再重構。要記住,我們是在寫原型,不用在乎什麼可用性,先把程式碼寫 work 了再說。如果實現的效果不錯的話,再回去重構。

另外,我們要有好的程式設計習慣。比如起有意義的變數名,寫註釋幫助理解。記住,我們是寫給人看的,不是機器!此外在使用基線模型做試驗的時候,我們可以現在小資料集上做測試,並確保模型能準確讀取資料。

如果在做原型設計時,我們將 LSTM 寫死了(hard-code),那麼在我們希望使用 Transformer 等模組的時候就需要重新改程式碼。因此使用多型可以藉助更高階的抽象擴充套件程式碼,這樣在換模組時就能只修改少量程式碼。

跟蹤實驗結果

在寫原型的時候你需要執行很多東西,這導致很難追蹤發生了什麼以及對應的程式碼部分。

可以準備一個 Excel 表格,來記錄實驗結果。

黑箱對比對於上下文理解有幫助,但不能深入理解兩個結果之間的關係,因為有太多的變數在同時變化。我們需要每次僅改變一個變數,可以在程式碼中設定「開關」,將開關配置一些全域性狀態/依賴注入。

每次只改變一個部分,方便跟蹤實驗結果的變化其原因在於哪裡。


這裡光是 embedder,我們就有很多種選擇。

使用設定檔案來記錄模型的改變,方便我們以後查詢當時的設定。

分析模型結果

在訓練的時候,視覺化對於分析模型表現是非常重要的。這個技能必須掌握。

Tensorboard 可以提供很多分析結果。

Tensorboard 能幫我們找到優化的 bug。比如上圖中的 embedding 梯度有兩個數量級的差別。

原因在於 embedding 的梯度是稀疏梯度,即只有一部分會被更新。但是 ADAM 中的動量係數是針對整個 embedding 計算的,所以解決方法是直接引入特定的優化器:DenseSparseAdam。

在解釋你的模型的預測輸出時,好的展示是靜態預測;更好的展示是互動地檢視預測;最好的展示是互動地檢視內部過程。

對於預測結果,如果可以做到互動式的方式來檢視的話,是最好的。

開發元件

與寫原型不同,開發可重複使用的元件有很多要注意的地方。我們的程式碼需要寫清楚,這樣就能聚焦於建模決策,而不考慮程式碼到底在做什麼。

Code Reveiw 是必不可少的。Review 的時候,不僅能發現錯誤,還能提高程式碼的可讀性。


如果我們不是軟體開發人員的話,對於持續整合 以及構建自動化 這兩個詞可能比較陌生。通常我們只說持續整合的時候,也包含了構建自動化的意思。想要做到這點,要多寫測試才行。

當然,如果我們不是開發一個很多人都會用到的庫,上面這些步驟是用不到的。不過測試很重要,如果是原型開發,也要做一些最基本的測試。

如上對讀取的資料進行測試,看是否正確。這裡在進行單元測試時常用的就是 assert 語句,如果程式有問題,執行到這邊就自然會報錯,這樣可以讓我們儘快找到錯誤。

如上所示,當然我們也可以使用 assert 語句檢查維度是否一致。這在模型運算部分經常會用到,例如判斷每個卷積層輸出結果的尺寸和深度等。可以看到這兩種測試的程式碼都不會很多。所以不要犯懶了,好好寫測試吧。

關於 AllenNLP 庫的一些介紹,這裡就不花時間討論了,感興趣的可以看 slide 中 p141~p205 的部分。下面直接進入分享的部分。

分享研究

簡化安裝的流程,令程式碼執行在任何平臺,使用隔離的環境。

下面是使用 Docker 的一些優點。

用 docker 開發的好處不用多說,大家想必也已經都知道了。當然,缺點也是有的。

至於 Python 的包管理系統,AllenNLP 採用了 ANACONDA。

Docker 是不錯,但不適合做本地開發,這樣的話,使用一些本地的包管理系統反而更方便。

最後做個總結。

  • 快速開發原型(要安全)

  • 寫安全的產品程式碼(要快)

  • 好的流程有利於做出好的研究

  • 使用正確的抽象

  • 檢視 AllenNLP(廣告)

這次分享的 slide 看了幾遍,很多地方看得自己臉上發熱,不寫測試什麼的說到了痛處。現在人工智慧領域對於演算法工程師的要求已經不是能掉個包,談談研究那麼簡單了,工程實踐能力已經變得越來越重要。寫優秀的程式碼,做優秀的研究,二者是一個互相促進的過程。