1. 程式人生 > >谷歌TensorFlow全面評估報告:好的壞的及令人討厭的

谷歌TensorFlow全面評估報告:好的壞的及令人討厭的

行研究和程式設計的工程師對它進行了全面評估:好的、壞的、令人討厭的的

一位從 TensorFlow 開放以來就一直使用它進行研究和程式設計的工程師對它進行了全面評估:好的、壞的、令人討厭的的。評估之前,這位研究員非常走心地研究了自己能找到的所有案例、教程、文件、程式碼片段,因此,較之媒體上對 TensorFlow 的主觀性報道,相信這篇測評會更有說服力。

一、好的一面

參與社群是最重要的事

提到機器學習,很容易把重點放在技術本身(特徵、效能、基準等)。但是,優秀的程式設計師都知道,寫一段人使用的程式碼,要比寫一段機器編譯、執行的程式碼要困難的多。關於 TensorFlow ,我最喜歡的一點是這樣一個簡單事實:機器學習社群中的每個人也都知道它,大多數人都以開放的心態去實現它,並且希望有足夠的人可以用它做有用的事。解決問題的想法越多,巨人的肩膀就越多!

現在,很多開發人員和學生對深度學習感興趣,源自聽說了 TensorFlow。最近,谷歌 Deepmind 聲稱,他們將從 Torch 遷移到 Tensorflow 上,所以,在不遠的未來,我們將看到升級版 TensorFlow 強化學習模型的釋出。當社群擁有更多的開放、簡潔的 API 、更多有用的模組、再加上網路上樂於助人的態度,未來還是很光明的。

大多數技術限制因素已被消除

去年十一月,我們撰寫了第一版 TensorFlow 測評文章,那時,的確存在著不少真實的或潛在的限制因素。現在,我很高興地告訴大家,大多數限制因素已經被解決了。

多 GPU 支援。現在已經可以使用了;相關文件簡潔清楚。你仍需要思考如何分解和克服問題,但這不也是一項樂趣麼?

利用分散式資源訓練(也就是雲端計算)。v 0.8 版本已經支援分散式計算。

將例如資料讀取和圖片預處理這樣的操作排入佇列。

使用 TensorBoard 就可實現圖形視覺化。當建立和除錯新模型時,容易迷失在雜項之中。對於我來說,為了解決一個困難問題而建立一個新框架或模型時,維持一個好的精神狀態,很不容易,所以,檢視模型的一個完全不同的表現方式,真的有用;TensorBoard 圖形視覺化化最適合做這個。

與 TensorBoard 互動的日誌記錄檔案。在 UNIX/Linux ,我喜歡在命令列使用 tail -f <log_file> 來監測任務輸出,做出快速的總體檢查。TensorFlow 中的日誌記錄檔案同樣可以做到這一點,通過從圖表中發出事件、總結,然後通過 TensorBoard 不停的監測輸出資料(例如學習率、損失值、訓練/測試準確性)。

模型檢查點。訓練一會兒模型,停下來做個評估,然後從檢查點載入,繼續訓練。

效能和 GPU 記憶體的用法與 Theano 相似,其他的部分使用了 CUDNN 。對早期釋出版本效能的抱怨主要是因為使用了 CUDNNv2,所以,TensorFlow v 0.8 版本在這方面有很大提升(使用了CUDNNv4)。

一些高質量的元框架(metaframeworks)

Keras 在 TensorFlow 和 Theano 後端都存在。如果你不想深究 TensorFlow 的內部細節而是隻要模組化使用的話,Keras 是一個好的選擇。 

TensorFlow Slim 是影象模型的一個很好的參考。即使你更喜歡編寫自己低水平 Tensorflow 程式碼,Slim 的文件仍然可以作為 Tensorflow 介面使用、模型設計等的參考文獻。

Skflow 將 Tensorflow 的方法隱藏在一個 scikit-learn 型別的 API 中。 相比只是為了多種 sklearn metrics 匯入和插入 python 程式碼,我用 Skflow 有點奇怪。

PrettyTensor 提供了表現類似於 tensor 的目標,並有著一個可連結語法,所以,你能快速的混合某些型別的模型。

釋出計劃

維護一個流行的開源工程是一項挑戰,特別是像 Tensorflow 這種技術複雜的工程。向這些維護人員致敬!我們感謝他們的這個策略:在還沒寫入文件之前,就把新特性和測試整合起來,這樣,使用者就可以早早嘗試這些新東西。如果你對版本釋出的細節和日期感興趣,就來看看這個版本語義日誌吧:https://www.tensorflow.org/versions/r0.8/resources/versions.html.

測試非常的棒!

對於確認功能是否有效,以及瞭解事物是如何運作的,測試非常有價值。當你發現 TensorFlow 裡的一些功能不如你的預期,或者你正在學習一個方法或引數的 quirks……請上 Github 上搜索一個測試,看看測試是怎麼做的!

二、壞的一面

相比 Theano,Tensorflow 的遞迴神經網路(RNNS)仍然有些不足

這些年,Theano 團隊花了很多功夫去優化他們遞迴神經網路的實現。可喜的是,這個差距在縮小,幾個月之內 TensorFlow 就有可能成為執行遞迴神經網路的一個不錯平臺。特別是:

我們還沒見過處理可變長度序列輸入的精緻方法。Bucketing 可行,卻以大多數模型都不需要的複雜性為代價。在許多案例中,將所有序列填補成一個固定的長度,也是一種有效的方法(特別是使用 batches 和 GPU 時),但是,有人認為這個方案不令人滿意。遞迴神經網路的動態展開可能是一個解決方案,但是,在 tensorflow.python.ops.rnn 模組中動態 RNN  的使用還非常新,也沒有任何相關記錄。我們仍在對此試驗。

效能和記憶體使用。儘管很難做一個準確的同類比較,但在兩個框架之下運行了很多相同模型之後,我們的看法是,對於遞迴神經網路,在一塊 GPU 的情形下,Theano 可能比 TensorFlow 快一些,而且佔用 GPU 記憶體小一點,這可能是由於元素智慧(element-wise)特性。在多 GPU 情形下和編譯時間方面,Tensorflow  勝出。

缺少資料輸入的權威案例

TensorFlow 文件和案例主要使用一些著名的學術資料集來闡述各種特性或功能。這是有一定道理的,是刻畫系統總體特性的優先方法。但是,現實中的問題幾乎不能簡單替換這些資料集。當學習一個新的深度學習框架時,運用張量輸入和形狀(shapes)可能成為一個真正的障礙,所以,一兩個展示如何運用雜亂的輸入資料(奇怪的形狀、分佈、填充、標記等)的例子,可能會為未來的開發者和工程師省去很多麻煩。

文件可能不一致

裡面有很多 TensorFlow 的教程,程式碼本身也被註釋得很好。但是,機器學習/深度學習範圍很廣,在解釋如何建立模型方面,新功能和文件/教程之間存在滯後現象。一些我們喜愛的教程如下:

Nathan’s Github repo 簡單教程。它展示了機器學習基本工作原理。如果你熟悉 numpy 或 Theano,可以從這開始。

Udacity 課程,出自谷歌的 Vincent Vanhoucke。 如果你剛接觸深度學習,可以從這開始。

官方 MNIST 教程。作為新手學完了 Udacity 教程之後,就可以看這個了。MNIST 是「機器學習的果蠅(Drosophila of machine learning)」,也是一個好的測試基準和完整性檢查。

Tensorflow 介面文件。針對 TensorFlow的 參考文獻。使用Control+F 來找東西!

不幸的是,特別是對於遞迴神經網路來說,文件和教程之間仍然存在概念上的鴻溝,例如,簡單或不重要的例子與全面的、最高階的例子之間的鴻溝。對於同時學習概念和框架的人來說,這是一個實際存在的障礙。比如 ,Udacity 教程和遞迴神經網路教程,非常清楚闡釋了使用 Penn TreeBank 資料建立語言模型,感謝它們的簡單明瞭。如果學習概念的話,它們是很好的例證,但是,對現實世界建模的話,它們還是太簡單了。

我們發現的另一個權威的 TensorFlow 遞迴神經網路教程是一個全面的 seq2seq 模型,模型所使用的多單元遞迴神經網路(GRU 或 LSTM)了加入了attention、bucketing 和 sampled softmax 演算法。哇哦!這就像學習滑雪,你剛在訓練道上學習了滑雪就直接跑到山頂展示雪上特技(危險並可怕!?)……你可能不應該從最簡單的模型直接跳到最複雜的。應該根據你要解決的問題逐步增加複雜度。

高質量的教程會逐步增加複雜度:從簡單的遞迴神經網路語言模型,到可以學習倒換詞語的的普通 seq2seq 遞迴神經網路解碼架構,到更精妙的帶有注意(attention)的翻譯 seq2seq LSTM,再到使用多單元遞迴神經網路,對於初期的 TensorFlow 使用者來說,這些所有技巧非常有用。我猜,循序漸進例項的缺乏,可能就是這一現象的原因:為什麼社群在 TensorFlow 上重現了許多的流行模型,卻並沒看到許多新奇的架構或巧妙的融合。

TensorFlow 團隊首要關注於功能和特性,然後才是文件……,我們對此表示理解,要是我們可能也會這麼做!而好的文件是一項投資,我見過的最好文件反而不是出自這些官方文件作者之手,因為好的文件要保證至少有一個外人能理解。如果 TensorFlow 社群撰寫文件,就像他們要求新特性一樣急切,那會非常酷!

我們仍然在等待軌跡監控工具,EEG。

三、令人討厭的的一面

利用異質資源增加複雜性

在控制和簡單性之間,一個經典的工程學折中——如果你想精細化控制運算的執行(例如,使用哪塊GPU),那麼,你需要維持這些限制。在有些情況下,對於效能最大化來說,精細化控制是必要的。例如,在供給(feed)GPU之前,使用多執行緒獲取並預處理批量資料,這樣 ,GPU 就不會等待這類操作。使用CPU的非同步通道來供給GPU,或測試你的佇列,這方面更多細節請見 Luke 這篇非常好的論文,TensorFlow Data Input (Part 2): Extensions。

TensorFlow 貪婪佔用 GPU

同樣的,在初始化時, TensorFlow 嘗試去分配所有可用的GPU記憶體給自己。這是一把雙刃劍,取決於你的情景。如果你正積極的開發一個模型,使用的是本地計算機上的 GPU,你可能會想分配一部分 GPU 到其他的事上。然而,如果你在雲端計算環境下配置模型,那麼,你就想要了解可執行你的模型的硬體資源,與其他也可以接入相同硬體程式碼之間,沒有不可預期的相互作用。

你可以使用諸如下面這樣的程式碼,為一個特定程序的GPU記憶體設定上限,但是,如果你的一臺機器有多個GPU,我們還沒留意過怎麼控制每塊GPU的具體分配。

gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.5)

將它作為配置引數傳入你的會話:

sess = tf.Session(config = tf.ConfigProto(gpu_options = gpu_options))

在預設情況下, Theano 和 TensorFlow 會衝突 

我們有很多基於 Theano 的程式碼,從讀取資料到多種用途的功能。我們也讀了很多在 Theano 中執行的研究程式碼。然而,如果你在相同資源下執行 Theano 和 TensorFlow ,它們將會爭奪 GPU 記憶體,不好的事就會發生。為了在不同的 GPU 上執行完全不同的環境(比如,兩個 GPU 執行兩種模式),你可以限值 CUDA 在特定的環境中只能看到特定的裝置。這樣,當你執行 python 程式碼時,它只能看見(並且分配到)流處理器 CUDA 能看見的 GPU。如果你使用bash,下面這條程式碼將起作用:

export CUDA_VISIBLE_DEVICES=0,1  # only the first two GPUs are usable

注意:上面所示的 CUDA 裝置數字可能與你使用nvidia-smi所看到的裝置ID不同!如果你想要 Theano 只在CPU上執行,你可以在 Python 程式碼中完成。下面是具體的程式碼,將這行程式碼放在匯入(imports)行的最上方:

import os

os.environ['THEANO_FLAGS'] = "floatX=float32,device=cpu,fastmath=True,ldflags=-lopenblas"

當然,你也可以為 CUDA 內聯(inline)環境標識,但是,對於我的模型研發流程來說,更容易記住「一塊GPU執行一個程式。」

四、總結

在任何框架中執行端到端的工作流都非常的費勁,TensorFlow 也不例外。 TensorFlow 中的一些事,比如,佇列(queues)、某些圖運算(graph operations)、資源分配/情境管理(resource allocation/context management)、圖示視覺化(graph visualization))),對於深度學習場景來說,都是相當陌生的,我們仍在學習利用這些特徵的最好方式。其他框架已經提供了其他的一些東西。即使總體概念相似,但是,執行細節還是有區別的。

當社群內的一些人實現了一次非常聰明的攻擊或者發現瞭解決問題的新方法的時候,開源工具最妙的部分就顯現出來了。即使大部分人現在仍然處於學習使用 TensorFlow 階段,但是,我想機會已經出現!讓我們展望下一個紀元!