NVIDIA TensorRT模型壓縮加速原理

介紹

TensorRT是一個高性能的深度學習推理優化器,可以為深度學習應用提供低延遲、高吞吐率的部署推理。TensorRT可用于對超大規模數據中心、嵌入式平臺或自動駕駛平臺進行推理加速。TensorRT現在已能支持TensorFlowCaffeMxnetPytorch等幾乎所有的深度學習框架,將TensorRTNVIDIAGPU結合起來,能在幾乎所有的框架中進行快速和高效的部署推理。目前的TensorRT庫支持C ++或者Python API部署。

TensorRT is a high-performance neural network inference optimizer and runtime engine for production deployment.

為什么對推理過程進行優化

深度學習模型一般分為訓練(training)和 推理(inference)兩個部分:

  1. 訓練: 訓練包含了前向傳播和后向傳播兩個階段,針對的是訓練集。訓練時通過誤差反向傳播來不斷修改網絡權值。
  2. 推理: 推理只包含前向傳播一個階段,針對的是除了訓練集之外的新數據。可以是測試集,但不完全是,更多的是整個數據集之外的數據。其實就是針對新數據進行預測,預測時,速度是一個很重要的因素。
training vs inference

一般的深度學習項目,訓練時為了加快速度,會使用多GPU分布式訓練。但在部署推理時,為了降低成本,往往使用單個GPU機器甚至嵌入式平臺,比如NVIDIA JetsonTesla T4等。由于訓練的網絡模型可能會很大,參數很多,而且部署端的機器性能存在差異,就會導致推理速度慢,延遲高。這對于那些高實時性的應用場合是致命的,比如自動駕駛要求實時目標檢測,目標追蹤等。

使用訓練框架執行推理很容易,但是與使用TensorRT之類的優化解決方案相比,相同GPU上的性能往往低得多。訓練框架傾向于實施強調通用性的通用代碼,而當優化它們時,優化往往集中于有效的訓練。TensorRT通過結合抽象出特定硬件細節的高級API和優化推理的實現來解決這些問題,以實現高吞吐量,低延遲和低設備內存占用。TensorRT 就是對訓練好的模型進行優化,由于TensorRT就只包含推理優化器,優化完的網絡不再需要依賴深度學習框架,可以直接通過TensorRT 部署在NVIDIA的各種硬件中。

TensorRT is a programmable inference accelerator.

TensorRT實踐

githubhttps://github.com/xiaochus/DeepModelDeploy/tree/main/TensorRT/

TensorRT加速原理

對于深度學習推理,有五個用于衡量軟件的關鍵因素:

吞吐量: 給定時間段內的產出量。 每臺服務器的吞吐量通常以推斷/秒或樣本/秒來衡量,對于數據中心的經濟高效擴展至關重要。
效率:每單位功率交付的吞吐量,通常表示為性能/瓦特。效率是經濟高效地擴展數據中心的另一個關鍵因素,因為服務器,服務器機架和整個數據中心必須在固定的功率預算內運行。
延遲:執行推理的時間,通常以毫秒為單位。低延遲對于提供快速增長的基于實時推理的服務至關重要。
準確性:訓練好的神經網絡能夠提供正確答案。 對于圖像分類算法,關鍵指標為top-5top-1
內存使用情況:需要保留以在網絡上進行推理的主機和設備內存取大小決于所使用的算法。這限制了哪些網絡以及網絡的哪些組合可以在給定的推理平臺上運行。這對于需要多個網絡且內存資源有限的系統尤其重要,例如,在智能視頻分析和多攝像機,多網絡自動駕駛系統中使用的級聯多級檢測網絡。

TensorRT的解決方案是:

權重與激活精度校準:通過將模型量化為 FP16INT8來更大限度地提高吞吐量,同時保持高準確度。
層與張量融合 :通過融合內核中的節點,優化GPU 顯存和帶寬的使用。
內核自動調整:基于目標 GPU 平臺選擇最佳數據層和算法。
動態張量顯存:更大限度減少顯存占用,并高效地為張量重復利用內存。
多流執行:用于并行處理多個輸入流的可擴展設計。

通過這些方法,TensorRT能夠在不損失精度的情況下顯著的提升模型的推理性能。

performace

Batch推理

GPU上使用較大的batch幾乎總是更有效,batch的作用在于能盡可能多地并行計算。模型的輸入只有單個batch的時候,單個batch的計算量并不能充分的利用CUDA核心的計算資源,有相當一部分的核心在閑置等待中;當輸入有多個batch的時候,由于GPU的并行計算的特性,不同的batch會同步到不同的CUDA核心中進行并行計算,提高了單位時間GPU的利用率。

例如:FullyConnected層有V個輸入和K個輸出,對于1batch的實例,可以實現為1xVinput矩陣乘以VxKweight矩陣。如果是Nbatch的實例,這就可以實現為NxV乘以VxK矩陣。將向量-矩陣乘法變為矩陣-矩陣乘法,效率更高。此外,當網絡包含MatrixMultiply層或FullyConnected層時,如果硬件支持Tensor Core,對于FP16INT8的推理模式,將batch大小設置為32的倍數往往具有最佳性能。

batch efficiency

計算圖合并與優化

TensorRT對于網絡結構進行了重構,把一些能夠合并的運算合并在了一起,針對GPU的特性做了優化。一個深度學習模型,在沒有優化的情況下,比如一個卷積層、一個偏置層和一個激活層,這三層是需要調用三次cuDNN對應的API,但實際上這三層的實現完全是可以合并到一起的,TensorRT會對一些可以合并網絡進行合并。

TensorRT對計算圖主要執行以下優化:

  • 消除輸出不被使用的層。
  • 消除等同于無操作的操作。
  • 卷積,偏置和ReLU操作的融合。
  • 匯總具有足夠相似的參數和相同的源張量的操作(例如,GoogleNet v5的初始模塊中的1x1卷積)。
  • 通過將層輸出定向到正確的最終目的地來合并串聯圖層。
  • 在構建階段還會在虛擬數據上運行各層,以從其核目錄中選擇最快的核,并在適當的情況下執行權重預格式化和內存優化。

以一個典型的inception block為例,優化過程如下:

  1. 首先對網絡結構進行垂直整合,即將目前主流神經網絡的convBNRelu三個層融合為了一個層,稱之為CBR

  2. 然后對網絡結構進行水平組合,水平組合是指將輸入為相同張量和執行相同操作的層融合一起。inception block中將三個相連的1×1CBR組合為一個大的1×1CBR

  3. 最后處理concat層,將contact層的輸入直接送入下面的操作中,不用單獨進行concat后在輸入計算,相當于減少了一次傳輸吞吐。

graph merge

量化

量化是將數值x映射到 y的過程,其中 x的定義域是一個大集合(通常是連續的),而 y 的定義域是一個小集合(通常是可數的)。大部分深度學習框架在訓練神經網絡時網絡中的張量都是32位浮點數的精度(Full 32-bit precision,FP32)。一旦網絡訓練完成,在部署推理的過程中由于不需要反向傳播,完全可以適當降低數據精度,比如降為FP16INT8的精度。量化后模型的體積更小,將帶來以下的優勢:

減少內存帶寬和存儲空間

深度學習模型主要是記錄每個layer(比如卷積層/全連接層)的weightsbias。在FP32的模型中,每個 weight數值原本需要32-bit的存儲空間,通過INT8量化之后只需要8-bit即可。因此,模型的大小將直接降為將近 1/4。不僅模型大小明顯降低,activation采用INT8之后也將明顯減少對內存的使用,這也意味著低精度推理過程將明顯減少內存的訪問帶寬需求,提高高速緩存命中率,尤其對于像batch-normreluelmentwise-sum這種內存約束(memory bound)的element-wise算子來說效果更為明顯。

提高系統吞吐量(throughput),降低系統延時(latency)

直觀的來講,對于一個專用寄存器寬度為512位的SIMD指令,當數據類型為FP32而言一條指令能一次處理 16個數值,但是當我們采用INT8表示數據時,一條指令一次可以處理64個數值。因此,在這種情況下可以讓芯片的理論計算峰值增加4倍。

Tesla T4 GPU 引入了 Turing Tensor Core 技術,涵蓋所有的精度范圍,從 FP32FP16INT8。在 Tesla T4 GPU 上,Tensor Cores 可以進行30萬億次浮點計算(TOPS)。通過TensorRT我們可以將一個原本為FP32weight/activation浮點數張量轉化成一個fp16/int8/uint8的張量來處理。使用 INT8 和混合精度可以降低內存消耗,這樣就跑的模型就可以更大,用于推理的mini-batch size可以更大,模型的單位推理速度就越快。

range

為什么模型量化在提升速度的同時,也并不會造成太大的精度損失?在Why are Eight Bits Enough for Deep Neural Networks? 以及Low Precision Inference with TensorRT 這兩篇博文中作者提到:

網絡在訓練的過程中學習到了數據樣本的模式可分性,同時由于數據中存在的噪聲,使得網絡具有較強的魯棒性,也就是說在輸入樣本中做輕微的變動并不會過多的影響結果性能。與圖像上目標間的位置,姿態,角度等的變化程度相比,這些噪聲引進的變動只是很少的一部分,但實際上這些噪聲引進的變動同樣會使各個層的激活值輸出發生變動,然而卻對結果影響不大,也就是說訓練好的網絡對這些噪聲具有一定的容忍度。

正是由于在訓練過程中使用高精度(FP32)的數值表示,才使得網絡具有一定的容忍度。訓練時使用高精度的數值表示,可以使得網絡以很小的計算量修正參數,這在網絡最后收斂的時候是很重要的,因為收斂的時候要求修正量很小很小(一般訓練初始 階段學習率稍大,越往后學習率越小)。那么如果使用低精度的數據來表示網絡參數以及中間值的話,勢必會存在誤差,這個誤差某種程度上可以認為是一種噪聲。那也就是說,使用低精度數據引進的差異是在網絡的容忍度之內的,所以對結果不會產生太大影響。

FP16

TensorRT支持高度自動化的FP16推斷(Inference)。與FP32FP64相比,使用半精度(FP16)可以降低神經網絡的內存使用。FP16支持部署更大的網絡,同時比FP32FP64花費更少的時間。

NVIDIAVolta硬件架構開始采用了稱為Tensor Cores的矩陣數學加速器。Tensor Cores提供4x4x4矩陣處理陣列,用于執行操作D = A * B + C,其中A, B, C and D4×4的矩陣。矩陣乘法輸入ABFP16矩陣,而累加矩陣CD可以是FP16FP32矩陣。

TensorCore

當使用FP16檢測到推理時,TensorRT會自動使用硬件Tensor CoresTensor CoresNVIDIA Tesla V100上的峰值性能比雙精度(FP64)快一個數量級,而吞吐量比單精度(FP32)提高了4倍。

speed

INT8

TensorRT可以將以單精度(FP32)或者半精度(FP16)訓練的模型轉化為以INT8量化部署的模型,同時可以最小化準確率損失。由于INT8的表達范圍遠遠小于FP32,生成8位整數精度的網絡時不能像FP16一樣直接縮減精度,TensorRT對精度為FP32的模型進行校驗來確定中間激活的動態范圍,從而確定適當的用于量化的縮放因子。

INT8量化的本質是一種縮放(scaling)操作,通過縮放因子將模型的分布值從FP32范圍縮放到 INT8 范圍之內。按照量化階段的不同,一般將量化分為quantization aware training(QAT)post-training quantization(PTQ)QAT需要在訓練階段就對量化誤差進行建模,這種方法一般能夠獲得較低的精度損失。PTQ直接對普通訓練后的模型進行量化,過程簡單,不需要在訓練階段考慮量化問題,因此,在實際的生產環境中對部署人員的要求也較低,但是在精度上一般要稍微遜色于QAT

PTQ的量化方法分為非對稱算法和對稱算法。非對稱算法那的基本思想是通過收縮因子和零點將FP32張量的min/max映射分別映射到UINT8數據的min/maxmin/max -> 0~255。對稱算法的基本思路是通過一個收縮因子將FP32中的最大絕對值映射到INT8數據的最大值,將最大絕對值的負值映射到INT8數據的最小值 ,-|max|/|max| -> -128~127

對稱算法中使用的映射方法又分為不飽和映射和飽和映射,兩種映射方的區別就是FP32張量的值在映射后是否能夠大致均勻分布在0的左右。如果分布不均勻,量化之后將不能夠充分利用INT8的數據表示能力。

簡單的將一個tensor 中的 -|max||max|FP32值映射到-127127 ,中間值按照線性關系進行映射。這種對稱映射關系為不飽和的(No saturation)。

Quantize(x, max) = round(s * x) where s = 127 / max
no saturation

根據tensor的分布計算一個閾值|T|,將范圍在 ±|T|FP32值映映射到±127的范圍中,其中|T|<|max|。超出閾值 ±|T|的值直接映射為 ±127。這種不對稱的映射關系為飽和的(Saturate)。

Quantize(x, r) = round(s * clip(x, -r, r)) where s = 127 / r
saturation

下面這張圖展示的是不同網絡結構的不同layer的激活值分布,有卷積層,有池化層。他們之間的分布很不一樣,激活值并不是均勻的分布在[-max, max]之間的,可以看出過大或者過小的激活值其實只占參數總體的一小部分,因此如果直接使用不飽和的映射關系不能有效利用INT8的表達范圍,就會導致比較大的精度損失。如果我們可以找到一個范圍,使網絡中tensor 的絕大多數的值都存在于這個范圍內,我們就可以利用這個范圍來對 tensor進行量化。只要閾值|T|選取得當,就能將分布散亂的較大的激活值舍棄掉,也就有可能使精度損失不至于降低太多。根據實驗,weights在這兩種方式上沒有顯著的差異,而對activation使用飽和的量化方式會有比較顯著的性能提升。因此TensorRT在模型上使用了這兩種方法進行了混合量化。

act range

由于網絡的每個層輸出值的分布都是不同的,飽和映射的方法就需要對每個層都計算出一個有效的閾值|T|。為了使INT8的輸出結果盡可能的減少精度損失,我們就需要通過校正數據集來衡量不同的INT8分布與原來的FP3F2分布之間的差異程度,選擇出一個與原始的FP32分布最為相似的INT8分布。這個衡量分布之間差異的指標就是KL散度(Kullback–Leibler divergence),KL散度通常用來衡量一個分布相比另一個分布的信息損失。

KL
KL

INT8量化的過程如下:

  1. 準備一個 FP32的模型。
  2. 從驗證集選取一個子集作為校準集,校準集應該具有代表性,多樣性,最好是驗證集的一個子集。
  3. 在校準數據集上進行FP32推理。
  4. 推理過程中遍歷網絡的每一層:
    • 收集這一層的激活值,并做直方圖,分成幾個組別(官方給的一個說明使用的是2048組),分組是為了下面遍歷 |T|時,減少遍歷次數;
    • 對于不同的閾值|T|進行遍歷,因為這里|T|的取值肯定在第128-2047組之間,所以就選取每組的中間值進行遍歷;
    • 選取使得 KL散度取得最小值的|T|
  5. 返回一系列|T|值,每一層都有一個|T|,創建CalibrationTable

官方校準的偽代碼:

//首先分成 2048個組,每組包含多個數值(基本都是小數)
Input: FP32 histogram H with 2048 bins: bin[ 0 ], …, bin[ 2047 ] 
  
For i in range( 128 , 2048 ): // |T|的取值肯定在 第128-2047 組之間,取每組的中點
    reference_distribution_P = [ bin[ 0 ] , ..., bin[ i-1 ] ] // 選取前 i 組構成P,i>=128
    outliers_count = sum( bin[ i ] , bin[ i+1 ] , … , bin[ 2047 ] ) //邊界外的組
    reference_distribution_P[ i-1 ] += outliers_count //邊界外的組加到邊界P[i-1]上,沒有直接丟掉
    P /= sum(P) // 歸一化
      
    // 將前面的P(包含i個組,i>=128),映射到 0-128 上,映射后的稱為Q,Q包含128個組,
    // 一個整數是一組
    candidate_distribution_Q = quantize [ bin[ 0 ], …, bin[ i-1 ] ] into 128 levels
    
    //這時的P(包含i個組,i>=128)和Q向量(包含128個組)的大小是不一樣的,無法直接計算二者的KL散度
    //因此需要將Q擴展為 i 個組,以保證跟P大小一樣
    expand candidate_distribution_Q to ‘ i ’ bins 
    
    Q /= sum(Q) // 歸一化
    //計算P和Q的KL散度
    divergence[ i ] = KL_divergence( reference_distribution_P, candidate_distribution_Q)
End For
//找出 divergence[ i ] 最小的數值,假設 divergence[m] 最小,
//那么|T|=( m + 0.5 ) * ( width of a bin )
Find index ‘m’ for which divergence[ m ] is minimal
threshold = ( m + 0.5 ) * ( width of a bin )

有了INT8的權重和對應的激活層的縮放表,TensorRT通過GPUDP4A硬件指令進行的卷積計算,該指令能夠執行8位整數4元向量點積(即一個4x4INT8卷積操作)來加速深度神經網絡推理。輸入的tensorINT8,卷積計算完之后是INT32的,然后將該值轉換成FP32,通過激活層后再通過縮放因子將FP32的激活值轉為INT8

DP4A (Dot Product of 4 8-bits Accumulated to a 32-bit)

通過這種混合量化的過程,TensorRTINT8模式在有效提升了推理速度的同時,只帶來了些許的精度損失。

performance
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容