1. 程式人生 > >帶你測試對比深度學習框架!TensorFlow,Keras,PyTorch...哪家強?(附資料集)

帶你測試對比深度學習框架!TensorFlow,Keras,PyTorch...哪家強?(附資料集)

640?wx_fmt=png&wxfrom=5&wx_lazy=1

授權自AI科技大本營(ID: rgznai100)

本文長度為3556字建議閱讀7分鐘

亞馬遜MXNet在CNN、RNN與NLP情感分析任務上效能強勁,而TensorFlow僅擅長於特徵提取。

深度學習框架哪家強:TensorFlow?Caffe?MXNet?Keras?PyTorch?對於這幾大框架在執行各項深度任務時的效能差異如何,各位讀者不免會有所好奇。

微軟資料科學家Ilia Karmanov最新測試的結果顯示,亞馬遜MXNet在CNN、RNN與NLP情感分析任務上效能強勁,而TensorFlow僅擅長於特徵提取。

測試詳情更新在Ilia Karmanov的GitHub專案DeepLearningFrameworks(https://github.com/ilkarman/DeepLearningFrameworks)

內。不過作者表示,專案內的測試程式碼並非專門為深度學習效能而編寫,目的僅在於簡單比較一下各框架之間的效能差異。

以下為該專案的詳情,have fun!

我們做這個榜單的初衷是為了好玩,所以省略了很多重要部分的比較。比如:幫助和支援,自定義圖層(可以建立一個膠囊網路嗎?),資料載入器,除錯,不同的平臺支援,分散式訓練等等。

我們不確定是否能對框架的整體效能提出任何建議,因為本專案主要還是在演示如何在不同的框架中建立相同的神經網路。

例如,使用Caffe2在Python中建立CNN,然後在Julia中使用KNet複製這個網路,或者也可以在PyTorch中嘗試建立一個RNN並在Tensorflow中複製它。你可以在Chainer中進行一些特徵提取,然後在CNTK中複製這個操作。

因為Microsoft Azure深度學習虛擬機器NC6上的框架已經更新到了最新版本,所以notebooks程式碼選擇在上面執行,僅佔用了顯示卡Nvidia K80 GPU一半的效能。

640?wx_fmt=png

測試目標

建立深度學習框架的Rosetta Stone(譯者注:一個非常好用的外語學習軟體),使資料科學家能夠輕鬆地將他們的專業知識從一個框架轉移到另一個框架(通過翻譯,而不是從頭開始學習)。另外,是為了更加透明地在模型訓練時間和預設選項方面進行比較。

許多線上教程使用非常低級別的API,雖然這些API非常詳細,但對於大多數用例來說,並沒有多大意義,因為大多數時候有更高級別的幫助程式可用。在這裡,我們直接忽略衝突的預設值的條件下,嘗試採用最高級別的API,以便在框架之間進行更容易的比較。

下面的結果將證明,一旦使用更高階的API,程式碼結構變得非常相似,並且可以粗略地表示為:

  • 載入資料;  x_train,x_test,y_train,y_test = cifar_for_library(channel_first =?,one_hot =?)

  • 生成CNN / RNN網路結構(通常在最後一層上不啟用)

  • 指定損失函式(交叉熵與softmax是一起指定的),優化器並初始化網路權重+會話

  • 用mini-batch的方式來訓練訓練集並使用自定義迭代器(所有框架都使用公共的資料庫)

  • 在測試集的mini-batch上面進行預測

  • 計算準確率

本質上,我們是在比較一系列確定性的數學運算(儘管是隨機初始化),所以比較跨框架的準確性就是沒有意義了。相反,它會提示我們去檢查想要的匹配(?),以確保我們正在比較的是相同的模型架構。

測試結果(2017年11月24日)

在CIFAR-10資料集上訓練CNN(VGG型別)網路 

效能對比- 影象識別

640?wx_fmt=png

該模型的輸入是標準的CIFAR-10資料集,包含五萬個訓練影象和一萬個測試影象,均勻分佈在10個類別中。每個32×32畫素的影象轉化為張量的形式(3,32,32),畫素值從0-255歸一化到0-1。 例如:汽車影象的相關引數 y=(0,1,0,0,0,0,0,0,0,0),其標籤是= [飛機,汽車,鳥,貓,鹿,狗,青蛙,馬,船 ,卡車]

在IMDB資料集上訓練RNN(GRU,門控迴圈單元)

效能對比 - 自然語言處理(情感分析)

640?wx_fmt=png

這個模型的輸入是標準的IMDB電影評論資料集,包含兩萬五千個訓練評論和兩萬五千個測試評論,統一分為2個等級(正面/負面)。 下載的評論已經是單詞索引的張量形式,例如 (如果你喜歡像南方公園這樣的成人喜劇漫畫)將被表示為(1 2 3 4 5 6 3 7 8)。

遵循Keras框架的處理方法,其中起始字元被設定為1,詞彙外(使用3萬大小的詞彙庫)被表示為2,因此詞索引從3開始。通過零填充/截斷的方式,把每條評論都固定到150個字。

在可能的情況下,我會嘗試使用cudnn的方式來優化RNN(由CUDNN = True開關來控制),因為我們有一個可以輕易降低到CuDNN水平的簡單的RNN。例如,對於CNTK,我們使用optimized_rnnstack而不是Recurrence(LSTM())函式。 雖然它不太靈活,但是速度要快得多。

例如,對於CNTK,我們不能再使用類似層歸一化的更復雜的變數。在PyTorch中,這是預設啟用的。但是對於MXNet,我無法找到這樣的RNN函式,而是使用稍慢的Fused RNN函式。 

Keras最近剛得到了cudnn的支援,但是隻有Tensorflow後端可以使用(而不是CNTK後端)。 Tensorflow有許多RNN變種,其中包括他們自己定製的核心。這裡有一個很好的基準,我將嘗試更新使用CudnnLSTM的樣例而不是當前的方法。

注:CNTK框架是支援動態軸,這意味著我們不需要將輸入填充到150個字,就可以按原樣輸入,但是由於我找不到與其他框架做到這一點的方法,所以我還是採用填充的方法。這樣對CNTK框架有點不公平,因為會低估了它的能力。

分類模型建立大小為(150x125)的嵌入矩陣,然後採用100個門控迴圈單元,並將最終輸出(不是輸出序列也不是隱藏狀態)作為輸出。 

ResNet-50(特徵提取)推斷效能對比

640?wx_fmt=png

載入一個預訓練好的ResNet50模型並在avg_pooling結束後變成(7,7)向量處截斷,輸出一個2048維的向量。在這裡可以插入一個softmax層或其它的分類器,例如用激勵樹來實現遷移學習。此處,在CPU和GPU上向avg_pool層進行前向傳遞的時間均計算在內。

我從中學到了什麼?

關於CNN

以下提供了一些我在看到github上面提出的問題後比較跨框架的測試準確率時的一些見解。

1、上面的例子(Keras除外),為了便於比較,嘗試使用相同級別的API,因此都使用相同的生成器函式。 對於MXNet和CNTK,我嘗試了一個更高級別的API,在這裡我使用了框架的訓練生成器函式。在這個例子中,速度的提高是微不足道的,因為整個資料集都是作為NumPy陣列載入到RAM中的,而且在處理的時候每個迭代的資料都是隨機的。我懷疑框架的生成器是非同步執行隨機的。

奇怪的是,框架的隨機操作似乎是在一個批次層次上而不是在一個觀察層次上進行的,因此會略微降低測試精度(至少在10個迭代之後)。 對於我們會進行的輸入輸出活動以及可能在執行中進行預處理和資料增強的情況,自定義的生成器將對效能產生更大的影響。

2、讓CuDNN自動調整/窮舉搜尋引數(能選擇最有效的CNN演算法來固定影象的大小)能在效能上帶來一個巨大的提升。Chainer,Caffe2,PyTorch和Theano這四個框架都必須手動啟動它。CNTK,MXNet和Tensorflow三個框架是預設啟用CuDNN的。

賈揚清提到了cudnnGet (預設)和cudnnFind之間效能的提升。然而,其在TitanX GPU上的差異小得多。

現在看來,在K80 +上採用的新的cudnn使其效能差異問題更為突出。由於在目標檢測各種影象大小的組合上執行cudnnFind會出現較大的效能下降,所以窮舉搜尋演算法應該是不能在目標檢測的任務上使用了。

3、使用Keras時,選擇與後端框架相匹配的[NCHW]排序很重要。CNTK是channels first,我曾經在Keras上錯誤的配置為channels last。這樣就必須在每一個batch上改變它的順序,同時會造成效能嚴重的下降。通常,[NHWC]是大多數框架的預設設定(如Tensorflow),[NCHW]是在NVIDIA GPU上使用cuDNN訓練時可以使用的最佳順序。

4、Tensorflow,PyTorch,Caffe2和Theano四個框架都需要一個提供給dropout層的布林值來指示我們是否訓練,因為這對在測試集上的準確率有很大的影響,72 vs 77%。因此,在這種情況下不應該使用Dropout來測試。

5、使用Tensorflow框架時需要兩個改變:通過啟用TF_ENABLE_WINOGRAD_NONFUSED,同時還改變提供給channel first而不是channel last的維度(data_format ='channels_first')。對卷積操作啟用WINOGRAD,自然而然的就將keras變成改成以TF作為後端。

6、Softmax層通常與cross_entropy_loss()函式一起用於大部分的功能,你需要檢查一下你是否要啟用最終的全連線層,以節省使用兩次的時間。

7、不同框架的核心初始化器可能會有所不同,並且會對準確性有±1%的影響。我儘可能統一地指定xavier / glorot,而不要太冗長的核心初始化。

8、為了SGD-momentum中momentum型別的實現,我不得不關閉unit_gain。因為它在CNTK框架上是預設關閉,以此來跟其他框架的實現保持一致。

9、Caffe2對網路的第一層(no_gradient_to_input = 1)進行了額外的優化,可以通過不計算輸入的梯度產生了一個比較小的速度提升。 Tensorflow和MXNet可能已經預設啟用了此功能。 計算這個梯度對於研究和像deep-dream的網路是有用的。

10、在max-pooling之後使用ReLU啟用意味著你在減少維度之後才執行一個計算,從而能夠減少幾秒鐘。這可以使採用MXNet框架的執行時間縮短3秒。

11、一些可能有用的額外檢查:

  • 是否指定的核心(3)變成了對稱元組(3,3)或1維卷積(3,1)?

  • 步長(最大池化中的)是否是預設為(1,1)或等於核心(Keras這樣做的)?

  • 預設填充通常是off(0,0)或valid,但檢查一下它不是on/'same'是很有用的

  • 卷積層上預設的啟用是否是'None'或'ReLu'的

  • Bias值的初始化可能不能(有時是沒有bias值)

  • 梯度的下降和無窮大的值或 NaNs的處理可能因框架不同而不同

  • 有些框架支援稀疏的標籤,而不是one-hot編碼型別的(例如我使用的Tensorflow有f.nn.sparse_softmax_cross_entropy_with_logits函式)

  • 資料型別的假設可能是不同的-例如,我曾經試著用float32和int32型別來初始化X和Y。但是在torch中Y需要double型別(是為了可以使用在torch.LongTensor(y).cuda函式中)的資料

  • 如果框架有一個稍微低階一點的API,請確保在測試過程中不要通過設定training= False來計算梯度。

12、據說安裝支援python3.5版本的Caffe2有點困難。因此我這裡分享了一個指令碼

640?wx_fmt=png

關於RNN

1、大多數框架(例如Tensorflow)上,都有多個RNN實現/核心; 一旦降低到cudnn LSTM / GRU級別,執行速度是最快的。但是,這種實現不太靈活(例如,可能希望層歸一化),並且接下來如果在CPU上執行推理可能會出現問題。

2、在cuDNN這個層面,大部分框架的執行時間是非常相似的。這個Nvidia的部落格文章寫到過幾個有趣的用於迴圈神經網路cuDNN優化的方法,例如,融合 - “將許多小矩陣的計算結合為大矩陣的計算,並儘可能地對計算進行流式處理,增加與記憶體I / O計算的比率,從而在GPU上獲得更好的效能。”

編輯:文婧

0?wx_fmt=jpeg