如果對深度學習有所了解的小夥伴們想必都知道,深度學習需要使用強大的伺服器、加速嵌入式平台(如nvidia的jetson)來運作深度學習算法,然而這也同樣意味着不菲的開支。
那麼問題來了,如果你想你想用樹莓派來做一個目标跟蹤器,為你看家守院,這可以實作嗎?換句話說,如果你需要在不帶加速器的arm cpu上運作卷積神經網絡了怎麼辦?
雷鋒網(公衆号:雷鋒網)想,大概就會像下圖這位小哥一樣,處境尴尬。
來自德國初創企業 buddyguard gmbh 的機器學習工程師 dmytro prylipko 就為我們提供了一個可行的“殺牛不用雞刀”的解決方案,雷鋒網編譯,未經許可不得轉載。
機器學習社群為縮短神經網絡的推理時間,已經研究了一段時間,研究得出可行的解決方案還是相當多的。本文将嘗試回答一個簡單的問題:什麼庫/工具包/架構可以幫助我們優化訓練模型的推理時間?本文隻讨論已為arm架構晶片提供c
/ c ++接口的工具包和庫(由于嵌入式裝置上使用 ,我們很少lua 或
python),限于文章篇幅,不闡述另外一種加速神經網絡推理的方法,即修改網絡架構,從sqeezenet架構可看出,修改網絡架構是一個可行的方案。基于上述原因,本文涉及的實驗隻涉及使用caffe,tensorflow和mxnet這3個開源的深度學習架構。
加速神經網絡模型在硬體平台計算速度,兩個主要有大的政策:
1)修改神經網絡的模型; 2)加快架構運作速度。
當然,将這兩個政策結合起來使用,也是一種不錯的思路。
修改神經網絡模型有兩種方法,一是通過降低權重精度實作,即降低特征量化的精度,二是通過權重剪枝來實作,權重剪枝的背後的思想是降低系統參數的備援。降低權重低精度通常采用(用定點數或動态定點數表示浮點數的方法,支援這種做法的原理是:推理過程并不需要高精度,因為在運算過程中,計算的線性性質和非線性的動态範圍壓縮,使得量化誤差僅在子線性地(sub-linearly)範圍内傳遞,進而不會引起數值的劇烈變動。更進一步,我們可以使用低精度乘法來訓練神經網絡模型。結合
simd 指令集,比如
sse3,可以使特征量化過程可以更為高效,進而加速訓練過程。然而,目前我們還很難找到同時使用了這兩者的解決方案。比如使用ristretto架構可以執行有限精度的自動量化,但它卻并沒有降低計算負載。tensorflow
也可以執行量化,但其推理時間實際上卻增加了 5 到 20
倍,因為tensorflow還引入了輔助量化/去量化的計算節點。是以在實際操作中,我們隻把量化作為壓縮網絡權重的方法,當存儲空間有限時可以這樣操作,至少這已經是目前最先進的技術。
從另外一個角度看,我們可采用加快架構的執行時間的方法,這種方法不會影響到模型的參數。這種政策主要上采用優化矩陣之間的乘法(gemm)類的通用計算技巧,進而同時影響卷積層(其計算通常是
im2col +
gemm)和全連接配接層。除此之外,可以使用神經網絡的加速包nnpack,就個人了解,nnpack的核心思路是使用快速傅裡葉變換将時間域中的卷積運算轉換成了頻域中的乘法運算。
加快架構執行速度另一種方法是将網絡模型和權重配置轉換成針對目标平台代碼,并對代碼進行優化,而不是讓它們直接在某一個架構内運作。這種方法的典型案例是
tensorrt。還有 caffepresso, 可以将
caffe中prototxt類型的檔案定制成适用于各種不同硬體平台的低規格版本。然而,tensorrt 的運作需要cuda,而且隻能在
nvidia的 gpu中才能使用,而 caffepresso 也需要某種硬體加速器(dsp、fpga 或
noc),是以這兩種方法都不适合用于我的測試硬體——樹莓派。
上述内容仔細地評估現有的解決辦法後,我發現以下幾種方法能夠加速目前流行的可用模型的推理:
如果你的架構中使用了 openblas(基礎線性代數程式集的開源實作),你可以嘗試使用其為深度學習進行過優化的分支: https://github.com/xianyi/openblas/tree/optimized_for_deeplearning
nnpack 能和其他一些架構(包括 torch、caffe 和 mxnet)聯合使用:http://github.com/maratyszcza/nnpack
将tensorflow編譯為在樹莓派平台的目标代碼時,你可以使用一些編譯優化标志,進而充分利用neon
指令集加速目标代碼的執行速度:http://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/makefile#raspberry-pi
基于以上3種方法,我概括出以下調測配置:
1. 使用以 openblas為後端的caffe 主分支(caffe-openblas); 2. 使用以 openblas為後端openblas 且為深度學習優化過的caffe分支版本(caffe-openblas-dl); 3. 編譯tensorflow時,使用優化編譯标志 optflags="-os" (tf-vanilla) 4. 編譯tensorflow時,使用優化編譯标志 optflags="-os -mfpu=neon-vfpv4 -funsafe-math-optimizations -ftree-vectorize" (tf-neon-vfpv4) 5. 使用以openblas實作基礎線性代數程式集的vanilla mxnet 6. 使用帶有 openblas 、且為深度學習優化過mxnet 分支版本(mxnet-openblas-dl)。
你可能會疑惑:配置中怎麼沒有
nnpack?這确實有點複雜,由 ajtulloch 建立的 caffe 分支提供了最直接的使用
nnpack方法。然而自從它被內建進去以後,nnpack 的api接口 就已經改變了,并且目前我無法編譯它。caffe2 對 nnpack
有原生支援,但我不會考慮 caffe2,因為它處于實驗性階段并且幾乎對 caffe 進行了尚未文檔化的重構。另外一個選項就是使用
maratyszcza的caffe-nnpack分支,但該分支比較老舊且已經停止維護。
另外一個問題就是出于nnpack本身。它隻提供了android/arm平台的交叉編譯配置,并不提供在
linux/arm
平台上的交叉編譯配置。結合mxnet,我嘗試編譯目标平台代碼,但結果無法在目标平台上正常運作。我隻能在台式電腦上運作它,但是我并沒有看到比
openblas 會有更好的性能。由于我的目标是評估已經可用的解決方法,是以我隻能推遲nnpack 的實驗了。
以上所有的這些方法都是在四核 1.3 ghz cpu 和 1 gb ram 的樹莓派 3 上執行。作業系統是 32 位的 raspbian,是以檢測到的 cpu 不是 armv8 架構,而是 armv7 架構。硬體規格如下:
model name : armv7 processor rev 4 (v7l) bogomips : 38.40 features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 cpu implementer : 0x41 cpu architecture: 7 cpu variant : 0x0 cpu part : 0xd03 cpu revision : 4
為了評估上述每個測試配置的性能,我制定的測試方案如下:使用相同的神經網絡。也就是一個有 3 個卷積層和2個全連接配接層且在頂層帶有softmax的小型卷積神經網絡:
conv1: 16@7x7 relu1pool1: max pool 2x2conv2: 48@6x6 relu2pool2: max pool 3x3conv3: 96@5x5 relu3fc1: 128 unitsfc2: 848 units softmax
該卷積神經網絡有
1039744 個參數。雖然非常小,但它已經足夠強大了,可以用來處理許多計算機視覺算法。該網絡使用 caffe
進行訓練人臉識别任務,并将其轉換為 tensorflow 和 mxnet
格式,進而使用這些架構進行評估。批量執行次數對性能有很大的影響,為了測量前向通過時間(forward pass
time),我們将批量執行的次數設定為 1 到 256。在不同次數的批量執行中,我們每次執行 100
次前向通過,并計算了每一張圖像的平均處理時間。
在下面的表格中,列出了平均前向通過的時間。其中,a 是
caffe-openblas, b 是 caffe-openblas-dl, c 代表 tf-vanilla, d 是
tf-neon-vfpv4, e 是 mxnet-openblas, f 是 mxnet-openblas-dl。
表1 不同測試配置在不同的批處理次數下的性能表現
圖1 線性尺度下不同配置的前向通過時間比較
在對數尺度尺度上我們再來看一下:
圖2 對數尺度下不同配置的前向通過時間比較
測試結果讓我大吃一驚。首先,我沒有預料到在
cpu 上運作 mxnet的性能會這麼差。但這看起來已經是一個衆所周知的問題。此外,受限于存儲空間,它無法運作 256
張圖檔的批處理。第二個驚奇是優化過的 tensorflow 竟有如此好的性能。它甚至比 caffe
的表現還好(批處理次數超過2時),光從原始架構上看是很難預料這個結果的。需要注意的是,上述測試配置中的優化标志并不是在任意 arm
晶片上都可以使用的。
caffe 因速度非常快和思路獨到而知名。如果你需要連續地處理圖檔,可以選擇使用優化過的 openblas 的 caffe,可得到最好的處理性能。如果想提升10ms 的性能,你所要做的就隻是簡單的輸入以下指令:
cd openblas git checkout optimized_for_deeplearning
為了将我的研究轉變成正式的東西,我仍需要做大量的工作:評估更多的模型,最終內建 nnpack,以及研究更多結合了blas 後端的架構。雷鋒網希望本文能幫助你了解目前最流行的解決方案的推理速度。
本文作者:林少宏