深度學習框架跑分測驗(TensorFlow/Caffe/MXNet/Keras/PyTorch)

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

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

測試詳情更新在Ilia Karmanov的GitHub項目DeepLearningFrameworks(github.com/ilkarman/Dee)內。不過作者表示,項目內的測試代碼並非專門為深度學習性能而編寫,目的僅在於簡單比較一下各框架之間的性能差異

以下為該項目的詳情,have fun!

翻譯 | 劉暢

編輯 | Donna

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

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

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

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

測試目標

創建深度學習框架的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類型)網路

性能對比- 圖像識別

該模型的輸入是標準的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,門控循環單元)

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

這個模型的輸入是標準的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(特徵提取)推斷性能對比

載入一個預訓練好的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有點困難。因此我這裡分享了一個腳本

關於RNN

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

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

作者 | Ilia Karmanov

原文地址:github.com/ilkarman/Dee

推薦閱讀:

深度卷積GAN之圖像生成
Docker--深度學習環境配置一站式解決方案
一個基於 TensorFlow 的「顏值評分」開源項目:FaceRank

TAG:深度学习DeepLearning | TensorFlow | PyTorch |