您是否通過深度學習模型獲得了良好的準确性,卻發現推理時間不足以部署到生産環境中?您是否對如何優化模型的推理速度迷失了方向?那麼這篇文章是給你的
。
衆所周知,資料科學項目有一個奇特的特性,即項目者需要不斷轉換關注重點,根據業務或項目的不同需求。下面羅列了一些具體的關注點:
· 資料集是如何擷取的?是否是自己建立的資料集?(如果是自己的資料集,那麼準确的标簽是什麼?任務中需要使用的樣本需要多少?)
· 如何将模型充分利用到實際項目中來?即使一個模型很好,但是如何将這個模型投入到實際項目中?
· 你将使用什麼模型?目前,學術界已經發表了很多具有價值的算法模型,而我們在研究中時,很多技術已經發生了改變。
· 最重要的問題:這上述的一切是否可行,也就是按照目前的預算,是否可以用資料集訓練出一個模型?并且這樣的模型,是否能夠滿足我們應用場景的需要?
通常,在最後一個問題中,我們主要關注獲得資料集的最佳預測準确性。這是最有意義的,因為它允許我們驗證項目是否可行。反正,如果我們需要為模型投資更多資料以實作其目标,那麼我們就需要權衡效率的問題。對于某些項目,緩慢的推理速度并不是一個破壞因素。但是,如果真的是這樣的情況,那麼具體會發生什麼事情?
這種情況,可能發生在任何深度學習項目中,但在将涉及對象檢測的項目部署到生産時通常會出現這種情況。當操作來自相機的圖像時,每秒處理的每個幀都很重要。在硬體上投入更多資金絕對可以解決或緩解我們的問題。
但是,如果我們打算将我們的産品作為解決方案(比如帶有內建邊緣 GPU 的錄影機)銷售,這會線性增加産品的成本,直到我們無法從投資中獲得回報。
針對許多實際情況,我們經曆了上述的問題。這裡,我們給出不同的方法清單,用來提高算法模型的推理速度。
1.改變模型的權重
從我們的經驗來看,優化運作時模型的第一步是充分利用模型的架構。
1.1.1 訓練後量化
将算法模型權重的精度,由浮點類型(32-bits)轉換到整型(8-bits),将會降低模型的準确度。但是,從記憶體存儲角度來看,這大大降低了存儲消耗,反正提高了CPU和硬體加速器的延遲。
具體如何實作這個方法,主要取決于此算法模型所實作的架構。如果算法模型是用TensorFlow實作,那麼很幸運,因為TensorFlow給出了模型量化的封裝函數。如果算法模型使用PyTorch實作的,那麼稍微有點困難。就在寫本文的時候,PyTorch對于模型量化的支援,還局限于CPU端的支援。未來PyTorch對于模型量化的支援,将擴充到GPU端,就目前來看對于GPU端的模型量化支援還不夠成熟。
當然,針對PyTorch實作的算法進行量化優化也不是沒有任何方法,可以利用NVIDIA的TensorRT進行實作,但是為了能夠在TensorRT運作時上運作PyTorch模型,模型權重需要先轉化為中間模型權重格式。目前,可以實施的方法就是将PyTorch模型權重轉換為ONNX模型格式,最終轉化到TensorRT上進行運作。
1.1.2 實驗舉例
如果我們嘗試量化一個用PyTorch架構實作的Faster R-CNN算法模型,但是不幸的是,這回産生很多問題。在論文上,所有這些過程都是有道理的,應該很容易做到。然而,在實踐中,所有這些轉換都可能出現問題。這主要是因為 PyTorch、ONNX 和 TensorRT 的發展會朝着多個方向發展,當一項功能被添加到一個功能中時,舊的內建不一定支援它。可能的問題如下:
· 我們的模型可能能夠在ONNX上運作,但是問題可能發生在将ONNX格式的模型轉為TensorRT格式的過程中。尤其是,針對某些網絡層,例如是PyTorch中的Resize層。
· 在我們嘗試進行這種轉換時,我們碰巧使用PyTorch v1.3 或更高版本建構的模型可以轉換并在ONNX Runtime 中運作,但無法通過 ONNX 優化器運作(這在轉換網絡是尤其重要的一步)。
請記住,這些問題可能會出現也可能不會出現,這取決于我們模型的架構,我們在轉換簡單的CNN網絡時沒有問題,但是對于我們正在使用的Faster R-CNN實作,這是另一回事。一些使用者可以通過降級PyTorch設法解決了轉換過程中的問題。但是,這限制了我們可以通路的ONNX的opset,這反過來也限制了我們可以使用哪個TensorRT 版本來運作您的引擎。希望所有這些問題都能在不久的将來得到解決……但考慮到所有這些架構的開發速度都很快,很可能總會有短暫的不相容期。
訓練後量化絕對是一個強大的工具,雖然有些PyTorch模型不能用這種方法量化,但你仍然應該試一試,考慮到将你的模型導出到ONNX後,可以很友善的使用指令行 trtexec 來轉換模型。順便說一下,它與Nvidia TensorRT docker容器中的 TensorRT 一起很容易獲得。如果PyTorch量化失敗,那麼如果量化仍然是你想要的方法,我們建議你尋找TensorFlow實作。
1.2.1 将模型轉換為半精度
與之前的方法類似,這種替代方法旨在權衡速度和記憶體效率的準确性。它提供了FP32和UInt8之間的中間點,其中:
· 模型大小最多減少一半(而不是最多 75%)
· 精度的下降小于 UInt8,這使得精度權衡更接近 FP32。
· 大多數神經網絡權重已經落入這個範圍内,盡管進行這種轉換有梯度下溢(小梯度值變為零)的風險,這會阻止網絡正确學習任何東西。
考慮到如今 GPU 的架構已轉向針對 FP16 操作進行優化,尤其是使用張量核心,這種方法為提高速度提供了很好的權衡。此外,事實證明,并非網絡的所有層在推理過程中都花費大量時間。這意味着我們可以通過僅在需要速度提升的層(例如卷積)中使用半精度并将其餘部分留在 FP32 中來找到更好的權衡。更好的是,在 FP32 中有一些層有助于防止梯度下溢。這種方法稱為自動混合精度,它在量化方面的不同之處在于,不是對訓練模型的權重進行後處理,而是應該從一開始就使用混合精度來訓練模型。
1.2.2 實驗舉例
TensorFlow 通過為我們提供原生支援來實作這一目标,再次在很大程度上讓我們的實際開發更輕松。而如果您使用PyTorch,NVIDIA Apex是你應該使用的工具,幸運的是,根據我們的經驗,它比使用PyTorch 模型進行量化所帶來的痛苦要少得多。內建 Apex 被宣傳為僅在你的代碼中添加三行。而實際上,還不止這些。你必須對其進行初始化,更改反向傳遞調用以使用 Apex 的縮放損失,并修改你儲存和加載檢查點的方式,執行個體如下:
from apex.fp16_utils import *
from apex import amp, optimizers
...
# Initialization
opt_level = 'O1'
model, optimizer = amp.initialize(model, optimizer, opt_level=opt_level)
# Train your model
...
with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()
...
結果也很不錯。你獲得多少加速将在很大程度上取決于你正在訓練的模型。對于我們上面提出的Faster R-CNN模型執行個體來說,我們獲得了超過30%的速度提升,而對我們的Faster R-CNN 模型的準确性沒有任何影響。
2.尋找最優模型
我們試圖從我們的模型中提取最後一個dropout以縮短推理時間。但也許這還不夠。也許我們模型的架構對于我們試圖解決的問題來說太大了。減小模型的大小也會降低我們的準确性嗎?不必要!這不僅取決于我們希望模型解決的問題的具體性質。而且通過研究,不斷提出和試驗新的模型架構,通常會産生更纖薄的架構,通過設計實作更高的準确性!更好的是,如果我們實作了前面描述的任何方法,我們就可以重用該工作,并決定對我們的模型進行任何修改。
2.1 改變模型的backbone
在進行遷移學習時,我們可以檢視模型的主幹和對其進行預訓練的資料集,僅作為我們在網格搜尋中使用的超參數。我們不需要經過完全訓練的模型來評估推理時間。這使我們可以對多個主幹進行實驗,并檢視哪些可以更好地改善推理時間。我們應該期待我們的推理時間有相當大的改進,記住我們的模型仍然需要通過一個主幹,雖然相當數量的推理時間發生在主幹上,我們模型的外層仍然可以有對推理時間有很大影響。在研究了哪些主幹提供了更好的時序之後,使用它的模型需要完全重新訓練,以便我們分析主幹對模型準确性的影響。
2.2 改變整個模型
資料科學家的日常工作不僅是從事資料科學項目,還要密切關注研究以及它如何影響目前的技術狀态。盡管我們模型的主幹是我們模型的一個重要組成部分,但我們隻能通過嘗試優化一些保持其他事物靜态的東西來做這麼多。如果畢竟方法推理時間仍然不符合你的喜好,那麼是時候檢視新開發的模型并驗證這些模型所承諾的内容是否适用于你的實際用例。
2.3 實驗舉例
以物體檢測問題為例,一些模型專門針對速度進行了優化,例如 YOLO,而同時其他模型提供了多種配置,這些配置随神經網絡的深度和它們接收的輸入的大小而變化,例如 EfficentDet,允許你訓練和比較準确度與速度之間的權衡如何變化。更重要的是,令人驚歎的機器學習社群通常會提供這些模型的開源實作,供我們協作并幫助我們不要重新發明輪子!例如zylo117對EfficientDet 的 PyTorch 實作。
3.知識蒸餾
我們最後提出的改善模型推理時間的選項是通過知識蒸餾。假設我們有一個大模型(或模型的集合),它的預測精度很高,但其推理速度并不理想。Knowledge Distillation 建議通過使用我們的大模型作為訓練器來訓練一個具有較少參數的較小模型。這實質上是訓練我們的小模型輸出與我們的大模型或內建相同的預測。這樣做的一個很大優勢是我們不僅限于使用标記資料。
請注意,雖然我們的準确性可能會受到影響,但我們應該能夠從中獲得不錯的速度提升。不幸的是,我們沒有愉快地自己實施這種方法。但是知識蒸餾最近非常流行,并已用于對象分類、對象檢測、聲學模型和 NLP等。如果你想了解更多關于 知識蒸餾的資訊,請檢視Geoffrey Hinton等人的這篇論文。
4.總結
在本文中,我們描述了五種方法來改善深度學習模型的推理時間。特别是,我們建議你按照我們列出的順序實作它們,因為我們為實作模型量化和自動混合精度所做的任何編碼對于我們對模型進行的任何進一步更改都具有重要價值。
我們希望這篇文章對你有價值,無論是通過為你目前面臨的問題提供指導,還是在需要時用我們的知識武裝你的項目!如果你有本博文中未涵蓋的加速模型推理的方法,請告訴我們……對模型的推理速度優化有任何疑問嗎?如果可以,我們很樂意在評論中回答這些問題。
5.參考連結
https://www.tensorflow.org/api docs/python/tf/quantization/quantize
https://pytorch.org/docs/stable/quantization.html
https://developer.nvidia.com/zh-cn/tensorrt
https://github.com/onnx/onnx
https://arxiv.org/abs/1506.01497
https://github.com/onnx/onnx-tensorrt/issues/302
https://github.com/NVIDIA/TensorRT/issues/284
https://github.com/onnx/onnx/issues/2417
https://ngc.nvidia.com/catalog/containers/nvidiatensorrt
https://developer.nvidia.com/blog/mixed-precision-training-deep-neural-networks/
https://github.com/NVIDIAapex
https://github.com/zylo117Yet-Another-EfficientDet-Pytorch
https://arxiv.org/pdf/1503.02531.pdf