如何在邊緣裝置上適配大型神經網路?

特邀精選發表於2018-11-22

本文由機器之心經授權轉載自TalkingData(ID:Talkingdata),未經授權禁止二次轉載。

原文作者:Bharath Raj,譯者:TalkingData 趙磊,原文地址:http://t.cn/Rkofy1e。

對於任何想要建立可擴充套件服務的人來說,部署有記憶體限制的深度學習演算法都是一種挑戰。從長遠來看,相比昂貴的雲服務,在邊緣裝置上離線部署模型更為便宜,而且還有其他好處。唯一的缺點是它們缺乏記憶體和計算能力。

本文探索了一些可以用來在記憶體受限的裝置中適配神經網路的技術。因為“訓練”和“推理”階段用到了不同的技術,所以將把它們分開來討論。

訓練

某些應用程式需要線上學習。也就是說,模型要根據反饋或額外的資料進行改進。在邊緣裝置部署這樣的應用程式會給您的模型帶來一個切實的資源約束。這裡有4種方法可以減少此類模型的記憶體消耗。

  • 梯度檢查點

TensorFlow 這樣的框架需要消耗大量的記憶體用於訓練。在前向傳播過程中,計算圖中的每個節點的值都會被計算並儲存在記憶體中,因為在反向傳播時計算梯度時需要用到。

如何在邊緣裝置上適配大型神經網路?

每個節點的值在前向傳播後儲存,以便在反向傳播中計算梯度。原始碼:
https://github.com/openai/gradient-checkpointing

通常情況下,這是可以做到的,但當模型變得越來越複雜時,記憶體消耗會急劇增加。一個巧妙的迴避解決方案是在需要時重新計算節點的值,而不是將它們儲存到記憶體中。

如何在邊緣裝置上適配大型神經網路?

重新計算節點值用以計算梯度。注意,我們需要分別做若干次前向傳播來完成一個反向傳播。原始碼:
https://github.com/openai/gradient-checkpointing

然而,如上所示,計算成本將顯著增加。解決辦法是在記憶體中只儲存一些節點,而在需要時重新計算其他節點。這些儲存的節點稱為“檢查點”。這極大地減少了因深層神經網路而積累的記憶體消耗。如圖所示:

如何在邊緣裝置上適配大型神經網路?

左邊的第二個節點是檢查點節點。它在減少了記憶體消耗的同時提供了合理的時間成本。原始碼:
https://github.com/openai/gradient-checkpointing

  • 以速度換記憶體(重計算)

根據上面的想法,可以重新計算某些操作用以節省時間。Memory Efficient DenseNet 實現就是一個很好的例子。

如何在邊緣裝置上適配大型神經網路?

DenseNet 的一個 dense 塊

原文:https://arxiv.org/abs/1608.06993


DenseNets 具有很高的引數效率,但記憶體效率也很低。之所以出現這種矛盾,是因為拼接和批標準化操作造成的。

為了使卷積在 GPU 上更高效,必須連續地設定這些值。因此,在連線之後,cudNN 在 GPU 上連續地排列這些值。這涉及到大量的冗餘記憶體分配。類似地,正如本文所解釋的那樣,批標準化涉及到過多的記憶體分配。這兩種操作都會造成記憶體以二次增長。DenseNets 有大量的拼接和批標準化,因此它們的記憶體效率很低。

如何在邊緣裝置上適配大型神經網路?

將原始的拼接和批標準化操作
與它們的高效記憶體實現相比較原文:
https://arxiv.org/pdf/1707.06990.pdf

上述問題的一個簡潔的解決方案涉及兩個關鍵的觀察。

首先,拼接和批標準化操作不是時間密集型的。因此,我們可以在需要的時候重新計算這些值,而不是儲存所有冗餘記憶體。其次,我們可以使用“共享記憶體空間”來轉儲輸出,而不是為輸出分配“新”記憶體空間。

我們可以重寫這個共享空間來儲存其他拼接操作的輸出。也可以在需要的時候重新計算拼接操作來進行梯度計算。同理還可以將它擴充套件到批標準化操作。這個簡單的技巧節省了大量的 GPU 記憶體,通過增加少量的計算時間來換取。

如何在邊緣裝置上適配大型神經網路?

  • 減少精度

在一篇優秀的部落格中,Pete Warden 解釋瞭如何用8位浮點值訓練神經網路。由於精度的降低,出現了一些問題,其中一部分如下:

  • 如本文所述,“啟用值、梯度和引數”有非常不同的範圍。一個定點表徵並不理想。這篇論文聲稱,一個“動態的固定點”表徵將非常適合於低精度的神經網路

  • 正如 Pete Warden 的另一篇部落格所述,較低的精度意味著更大的偏差。通常,如果錯誤是完全隨機的,那麼它們很有可能相互抵消。然而,0被廣泛用於 padding、dropout 和ReLU,在低精度浮點格式中,0的精確表示也許是不可能的,因此可能會在效能中引入總體偏差。

  • 神經網路的架構工程

架構工程涉及到設計準確率、記憶體和速度最優的神經網路結構。有幾種方法可以在空間和時間上優化卷積。

  • 將 NxN 的卷積分解成 Nx1 和 1 xN 卷積的組合。這可以節省大量的空間,同時也提高了計算速度。在 Inception 網路的更新版本中使用了這種方案以及其他一些優化技巧。

    更詳細的討論,請檢視這篇部落格:
    https://towardsdatascience.com/a-simple-guide-to-the-versions-of-the-inception-network-7fc52b863202

  • 在 MobileNet 和 Xception Net 中使用深度可分離的卷積。

    關於卷積的型別的詳細討論,請檢視這篇部落格:
    https://towardsdatascience.com/types-of-convolutions-in-deep-learning-717013397f4d

  • 使用1x1卷積作為一個瓶頸來減少傳入通道的數量。這種技術已經在幾種流行的神經網路中使用了。

如何在邊緣裝置上適配大型神經網路?

谷歌的 AutoML 的描述,視訊:
https://www.youtube.com/watch?reload=9&v=Y2VF8tmLFHw

一個有趣的解決方案是讓機器為特定的問題確定最佳的架構。神經架構搜尋使用 ML 來為給定的分類問題找到最好的神經網路體系結構。當在 ImageNet 上使用時,它生成的網路(NASNet)是迄今為止建立的最佳效能模型之一。谷歌的 AutoML 也遵循同樣的原則。

推理

為邊緣裝置推理擬合模型相對比較容易。本節將介紹可用於優化邊緣裝置神經網路的技術。 

  • 去除臃腫

TensorFlow 這樣的機器學習框架,消耗了大量的記憶體空間來建立計算圖。這個額外的空間對於加速訓練過程是很有用的,但是它不用於推理。因此,專門用於訓練的計算圖部分可以被刪除,我們把這部分稱為計算圖的“臃腫”。

建議將模型檢查點轉換為凍結的推理圖。這個過程會自動刪除消耗記憶體的“臃腫”部分。當轉換為一個凍結的推理圖時,從模型檢查點丟擲“資源耗盡的錯誤”的計算圖有時可以被適配進記憶體中。

  • 修剪特徵

在 Scikit-Learn 上的一些機器學習模型(如隨機森林XGBoost)會輸出一個名為特徵重要度(feature_importance)的屬性。這個屬性代表了分類或迴歸任務的每個特性的重要性。我們可以簡單地刪除那些最不重要的特徵。如果您的模型有大量的特徵,而且不能通過任何其他方法來減少,這將非常有用。

如何在邊緣裝置上適配大型神經網路?

一個特徵重要度可檢視的例子:

https://machinelearningmastery.com/feature-importance-and-feature-selection-with-xgboost-in-python/

類似地,在神經網路中,許多權重值接近於零。我們可以簡單地刪除這些連線。然而,刪除層間的單個連線會形成稀疏的矩陣。建立高效推理引擎(硬體)方面的工作正在進行,它可以無縫地處理稀疏操作。然而,大多數 ML 框架都是將稀疏矩陣轉換成密集的形式,然後再將它們傳送到 GPU 上。

如何在邊緣裝置上適配大型神經網路?

去掉一個無關緊要的過濾器,原文:
http://machinethink.net/blog/compressing-deep-neural-nets/

相反,可以移除無關緊要的神經元,並稍微對模型進行重新訓練。對於 CNNs,也可以刪除整個過濾器。研究和實驗表明,通過使用這種方法,可以保留大部分的精確度,同時獲得大規模的縮小。

  • 權重共享

為了更好地說明權重的共享,參考一下這篇深度壓縮論文:https://arxiv.org/pdf/1510.00149.pdf中給出的例子。一個4x4的權重矩陣。它有16個32位浮點值。我們需要512位(16*32)來表示這個矩陣。

我們將權重值量化到4個級別,但是保留它們的32位性質。現在,4x4的權重矩陣只有4個唯一的值。4個唯一的值儲存在一個單獨的(共享)記憶體空間中。我們可以分別給這4個唯一值一個2位地址(可能的地址值為0、1、2和3)。

如何在邊緣裝置上適配大型神經網路?

可以通過使用2位地址來引用權重。因此,這裡獲得了一個新的4x4矩陣,其中包含2位地址,矩陣中的每個位置都指向共享記憶體空間中的一個位置。這個方法需要160位(162+432)來表示整個矩陣,得到了3.2的大小減少因子。

不用說,這種規模的縮小伴隨著時間複雜度的增加。但是,訪問共享記憶體並不會消耗太多的時間。

  • 量子化和更低的精度(推理)

回想一下,在本篇文章的“訓練”部分中提到了精度的降低。對於推理來說,精確度的降低並不像訓練過程那麼麻煩。權重可以被轉換成更低的精度格式,併傳送到推理過程中。但是,可能需要輕微的權重調整來防止精確度的急劇下降。

  • 編碼

修剪和量子化的權重可以通過編碼進一步優化。Huffman 編碼可以用更少的位表示最頻繁的權重值。因此,在位元級別上,一個 Huffman 編碼的字串比普通字串佔用更小的空間。

深度壓縮探討了使用無失真壓縮技術的編碼,如 Huffman 。然而,研究也探討了有失真壓縮技術的使用。這兩種方法的缺點是翻譯的開銷。

  • 推理優化器

到目前為止,我們已經討論了一些很有建設性的想法,但是若從頭開始實現它們需要相當長的時間。這就是推理優化器發揮作用的地方。例如,Nvidia 的 TensorRT 包含了所有以上這些想法。並提供了一個經過訓練的神經網路的“優化推理引擎”。

如何在邊緣裝置上適配大型神經網路?

TensorRT:
https://developer.nvidia.com/tensorrt

此外,TensorRT 可以優化模型,使其能夠更好地利用 Nvidia 的硬體。下面是一個使用 TensorRT 優化的模型更有效地利用了 Nvidia 的 V100 的例子。

如何在邊緣裝置上適配大型神經網路?

在 Nvidia 的 V100 上使用 TensorRT 優化的模型:

https://devblogs.nvidia.com/tensorrt-3-faster-tensorflow-inference/

  • 知識蒸餾

我們可以“教”較小的模型來模擬更健壯、更大的模型,而無需花哨的優化技術。這項技術被稱為“知識蒸餾”,它是谷歌“ Learn2Compress ”的重要組成部分。

如何在邊緣裝置上適配大型神經網路?

Teacher-Student 模型:

https://ai.googleblog.com/2018/05/custom-on-device-ml-models.html

通過使用這種方法,我們可以強制使用更小的模型,這些模型可以適配在邊緣裝置上,以達到大型模型的效能水平。據統計,精確度的下降是最小的。

更多資訊可以參考Hinton的論文:

https://arxiv.org/pdf/1503.02531.pdf

本文由機器之心經授權轉載自TalkingData(ID:Talkingdata),未經授權禁止二次轉載。

原文連結:https://mp.weixin.qq.com/s/p7DR11sZr7aO473Nt4gQfg

相關文章