天天看點

【模型推理】從部署的角度看 bn 和 in 算子

歡迎關注我的公衆号 [極智視界],擷取我的更多筆記分享

O_o

>_<

o_O

O_o

~_~

o_O

  本文介紹一下從部署角度來看 bn 和 in 的實作與對比。

  做深度學習的同學應該都接觸過殘差網絡 (Resnet) 這個經典且實用的網絡,一般有 resnet18、resnet34、resnet50、resnet101、resnet152,其主要差別是 ResidualBlock 的多少。resnet 創新的提出了殘差塊結構,裡面的恒等映射既不會引入額外的計算參數又有利于資訊的傳導,它最開始證明了網絡能夠向更深的方向發展,能夠解決網絡退化問題。resnet 的殘差塊結構如下:

【模型推理】從部署的角度看 bn 和 in 算子

  對于檢測、識别任務來說,resnet 是一個非常不錯的 backbone,能夠十分有效的幫你提取到特征,後面再接 neck、head 就能組成完整的網絡。介紹 resnet 是因為這裡要說的 bn、in 的實作是嵌在 resnet 裡的,接下來回到主題,來說一下 bn 和 in。

文章目錄

    • 1、談談 bn
    • 2、談談 in
    • 3、談談部署中的 bn 和 in

1、談談 bn

  bn 全稱是 batch normalization,是通過标準化讓資料分布線上性區間,加大了梯度,讓模型更大膽的進行梯度下降,可以解決因網絡深度加深而導緻梯度彌散問題,并且由于破壞了原來的資料分布,可以一定程度上解決過拟合問題。

  batch normalization 的原理是這樣的,如下圖,bn 是整個 batch 每一張圖的同一個通道一起做 normalization 操作,通俗點就是把每個通道 C 的 NHW 單獨拿出來做歸一化處理。

【模型推理】從部署的角度看 bn 和 in 算子

  整個 bn 算子的計算過程數學表達如下:

【模型推理】從部署的角度看 bn 和 in 算子

  前面提到的 resnet 裡面有許多的 conv + bn + activation 的結構,這個結構對于做算法加速的人來說應該十分敏感了,一看到就想把它們揉在一塊做算子融合。以最經典的 conv + bn 融合為例,數學原理上是這樣的:

  conv 層:

【模型推理】從部署的角度看 bn 和 in 算子

  bn 層:

【模型推理】從部署的角度看 bn 和 in 算子

  進行融合,把 conv 代入 bn:

【模型推理】從部署的角度看 bn 和 in 算子

  融合後成了一個大卷積,相當于:

【模型推理】從部署的角度看 bn 和 in 算子

  到這裡其實比較關鍵了,有想過為什麼要做算子融合嗎,直覺上考慮,算子融合能減少算子數量、減少算子間通信與中間存儲,進而達到算法加速的目的。這當然沒問題,不過對于 conv + bn 的融合來說,最實質的提升在于:由于 bn 的 mean 和 var 是離線計算好的,是以 conv + bn 融合後的大卷積裡的 w_new 和 b_new 完全可以提前計算好,這相當于什麼呢,就是原先我需要做一次卷積 (矩陣乘,tensorcore / cube 等矩陣運算單元) 和 一次 bn (點乘,vector 矢量計算單元),融合後我就把 矢量計算單元的運算 咔擦掉了,同時将兩次計算降為1次,你想想是不是性能提升會非常多。

2、談談 in

  in 全稱是 instance normalization,适用于圖像風格遷移,因為圖像生成的結果主要依賴于單個圖像,是以像 bn 那樣對整個 batch 歸一化并不适合,在風格遷移中使用 in 來做歸一化不僅能夠加速模型收斂,還可以保持每個圖像執行個體之間的獨立性。

  instance normalization 的原理是這樣的,如下圖,in 是單張圖檔的單個通道單獨做 normalization 操作,通俗點就是把每個 HW 單獨拿出來做歸一化處理,不受 通道 和 batchsize 的影響。

【模型推理】從部署的角度看 bn 和 in 算子

  整個 in 算子的計算過程數學表達如下:

【模型推理】從部署的角度看 bn 和 in 算子

  其中,

  • t:表示圖檔的 index;
  • i:表示 feature map 的 index;

  既然 in 主要用于風格遷移領域,那麼跟我們的檢測 / 識别或其他領域有毛線關系呢?讓我們來看一下下面的網絡結構和一組資料你就清楚了。如下是 IBN 結構 和 前面提到的 resnet 中殘差結構的對比圖,可以看到 IBN-a 的做法是将 feature map 對半切,一半走 in,一半走 bn,形成 bn 和 in 并聯結構;而 IBN-b 是在殘差後 relu 激活前加入一個 in,形成 bn 和 in 串聯結構。

【模型推理】從部署的角度看 bn 和 in 算子

  來看一組實驗資料:

  對于分類問題來說,如下,IBN-a 和 IBN-b 相比 resnet50 具有更好的效果。

【模型推理】從部署的角度看 bn 和 in 算子

  對于分割來說,訓練集和測試集來自同一個資料的時候,IBN-a 模型的 mIoU 是能夠比原模型 resnet50 高 4 個點。而訓練集和測試集不同的時候,IBN-b 模型更占優勢,說明 IBN-b 能夠在跨域的問題上表現更好。

【模型推理】從部署的角度看 bn 和 in 算子

  關于 IBN,做一些小結:

  (1)IBN-a 适用于 目前域 和 目标域 一緻的問題,比如說需提升 resnet50 的分類能力時,可以用 IBN-a;

  (2)IBN-b 适合使用在 目前域 和 目标域 不一緻的問題,比如說在行人重識别中,經常涉及跨域場景,這也是為何 IBN-Net 在行人重識别領域用的非常多的原因;

  以上,現在有一些應用網絡針對不同的應用場景,開始使用帶 IBN 結構的 backbone,來提高 backbone 在特定場景的特征提取能力,是以我們對 in 算子的部署也需要有所研究。

3、談談部署中的 bn 和 in

  從部署的角度來看 bn 和 in,其實前面 bn 那一塊對 bn 的部署已經進行了一些介紹了,包括 conv + bn 的融合,以及為什麼融合能加速。這裡再談談 in 相對于 bn 在部署的時候有啥差別。

  in 最開始用于圖像風格遷移,對于風格生成來說,單圖獨立性和動态性就比較重要,這導緻 in 的 均值 mean 和 方差 var 往往是在推理時線上計算的,其實在 pytorch 的 nn.InstanceNorm2d() 算子中有參數可以在訓練時把 均值 和 方差 進行離線儲存。這個時候我們先來看一下,對于部署來說 bn 需要四個權重,分别是 mean、var、scale 和 bias,相應的 in 也是需要這四個權重,當然兩者計算過程不一樣。回到前面說的,如果訓練時我把 in 的 mean 和 var 進行離線儲存,然後我像 bn 一樣做一波融合操作,有了離線的四個權重我就可以提前算好融合後大卷積的 new_scale 和 new_bias,然後進 tensorcore 或 cube 矩陣運算單元 計算一下,豈不是完美,加速妥妥的,這樣看 in 和 bn 在部署上也沒啥差別…

  問題來了,我們實驗驗證,在我們的場景中,in 采用 均值 和 方差 離線存儲模式得到的模型精度相比 推理線上計算降了老多…這樣的話,我們就必須線上計算 in 的 均值 和 方差了,這樣即使你能做算子融合,融合後的 new_scale 和 new_bias 裡的 mean 和 var 也是需要動态計算的,這個時候其實是将原來 conv + in 需要四次計算 (conv 1次 + mean 1次 + var 1次 + in 1 次) 減少到了三次計算 (mean 1次 + var 1次 + conv 1 次),是會有性能提升,但是相比于 conv + bn 融合後的 一次計算來說還差很多。

  我之前有用 tvm 的 te 子產品實作過 bn 和 in 算子,我把我寫的計算部分拿出來給大家看一看。

  bn_compute 部分如下,可以看到關鍵部分其實隻有一個 out = te.compute…

【模型推理】從部署的角度看 bn 和 in 算子

  in_compute 部分如下,我這裡是把 mean 和 var 的計算過程拆開來寫的,其實也可以把 in_compute… 裡面的注釋打開,這樣相當于把 mean 、 var 和 in 的計算寫到了一起,但是不管怎麼樣,整體的 in 計算過程都是有三個 te.compute… 的。

【模型推理】從部署的角度看 bn 和 in 算子

  以上給出了 tvm compute 部分,其實 op schedule 也是一樣,in 相比 bn 會多出對 mean 和 var 計算排程的過程。

  小結一下,從部署的角度來看 bn 和 in 算子,如果 in 算子的 mean 和 var 是訓練時離線存儲的,那麼 in 和 bn 在部署和推理效率上和 bn 是差不多的;如果 in 算子的 mean 和 var 是推理時線上計算的,那麼 in 會比 bn 效率低,這樣的 in 其實對于部署不是很友好。

  好了,收工~ 歡迎讨論~

掃描下方二維碼即可關注我的微信公衆号【極智視界】,擷取更多AI經驗分享,讓我們用極緻+極客的心态來迎接AI !

【模型推理】從部署的角度看 bn 和 in 算子

繼續閱讀