華爲海思AI芯片HI3516DV300上SSD代碼的移植和優化


前一段時間一直在做海思AI芯片 HI3516DV300上SSD代碼的優化工作,這裏總結一下優化的一些心得體會。之所以要對海思AI芯片 HI3516DV300上SSD代碼做優化,是因爲SSD有一部分是運行在CPU上,而HI3516DV300上的CPU性能比較差,網絡只要稍微複雜一點,CPU部分運行就會很慢。下表給出了一些模型在芯片上運行的時間,可以看到CPU部分運行的時間佔據了大部分。

在這裏插入圖片描述
注意:
這裏的Forward對應SAMPLE_SVP_NNIE_Forward()函數,這個函數基本上是運行在NNIE上的,GetResult對應SAMPLE_SVP_NNIE_Ssd_GetResult函數,這個函數是運行在CPU上的,後面直接使用GetResult表示這個函數



DetectionOutForward的優化

首先分析性能瓶頸。通過對GetResult函數分析發現主要耗時在兩個函數中:SVP_NNIE_Ssd_SoftmaxForward和SVP_NNIE_Ssd_DetectionOutForward,後面直接使用SoftmaxForward和DetectionOutForward表示。
在這裏插入圖片描述

進一步對DetectionOutForward函數進行分析,其中DetectionOutForward主要包含了兩個步驟,這兩個部分分別對應了兩個for循環:

  1. decode box: 將網絡輸出的預測值轉換爲實際的座標值,這一部分記爲Loop1
  2. NMS: 對每一類目標的前topk個置信度最高的bbox做NMS,這一部分記爲Loop2

發現主要耗時在Loop1中
在這裏插入圖片描述
Loop1對應瞭如下代碼:
在這裏插入圖片描述

分析Loop1代碼,Loop1主要功能是對所有anchor的預測值進行解碼轉換爲anchor在圖像中實際的座標值。
在這裏插入圖片描述
對每一個anchor都需要調用兩次exp()函數(上圖中紅框標出的部分),這個函數是一個非常耗時的函數,45120個anchor一共調用90240次exp(),通過測試發現90240次exp()調用需要耗時21ms,如果能夠減少exp()的調用次數,將會大大減少時間。
由於實際做NMS的時候,我們只對前topk個置信度最高的bbox做NMS,也就是說只會用到前topk個bbox的真實座標值,其他bbox的座標值用不到,所以我們只需要計算出前topk個bbox的真實座標值就可以了,這裏topK設置爲400,也就是說只需要執行800次exp操作就可以,而800次exp操作只需要0.2ms,可以大大減少時間。

但是最後做NMS的時候,使用的是圖1中藍框中的4個變量,而如果要計算出真實座標值,就必須要知道網絡輸出的預測值和priorbox的座標,也就是如果要計算出前topk個bbox的真實座標值,就必須要知道每個bbox對應的8個變量,圖1中紅框和綠框中的8個值。

在這裏插入圖片描述圖1

8個值:

ps32PriorBoxes[jSAMPLE_SVP_NNIE_COORDI_NUM]
ps32PriorBoxes[j
SAMPLE_SVP_NNIE_COORDI_NUM+1]
ps32PriorBoxes[jSAMPLE_SVP_NNIE_COORDI_NUM+2]
ps32PriorBoxes[j
SAMPLE_SVP_NNIE_COORDI_NUM+3]
ps32LocPreds[jSAMPLE_SVP_NNIE_COORDI_NUM]
ps32LocPreds[j
SAMPLE_SVP_NNIE_COORDI_NUM+1]
ps32LocPreds[jSAMPLE_SVP_NNIE_COORDI_NUM+2]
ps32LocPreds[j
SAMPLE_SVP_NNIE_COORDI_NUM+3]

下面的問題就是如何將每個bbox對應的這8個變量編碼到4個變量中,通過對每個bbox對應的8個變量分析發現:

  1. 由於網絡輸入大小是512,也就是圖像中的座標在[0,512]之間,也就是ps32PriorBoxes的值也在[0,512]之間,也就表示ps32PriorBoxes的值可以通過3位數來表示
  2. ps32LocPreds的值有正有負,而且值的範圍可以使用5位數表示

基於上面的發現,設計了以下編碼方式:

ps32LocPreds * 10000+ps32PriorBoxes * 10+flag

其中flag爲標記位,當ps32LocPreds值爲負數時,flag爲0,當ps32LocPreds值爲正數時,flag爲1
而且編碼之後的值全部轉換爲正數,便於解碼。
這個編碼的含義就是:最後一位是標記位,倒數第2到第4位是priorbox的值,剩下的位數是位置預測值
示例:
-3852,125→38521250
5017,96→50170961
-13590,145→135901450
在這裏插入圖片描述
最後,在NMS的時候,進行解碼就可以計算出真實座標值了,比如對每個bbox編碼之後的4個值的第一個值進行解碼:
在這裏插入圖片描述

這樣就可以得到需要的8個值,最後根據這8個值就可以計算出該bbox的真實座標值了
在這裏插入圖片描述

使用優化後的代碼進行速度測試
在這裏插入圖片描述
行人檢測器Loop1經過優化後從原來的34ms提高到了7ms,速度得到了明顯提高,由於Loop2增加了解碼的時間,所以Loop2會略微變慢,但是總體時間還是大大降低了。
優化之後對精度是完全沒有影響的。


NMS的優化

AI芯片中官方給的SSD代碼在對每一類做NMS的時候,實現上有點bug,官方代碼中,當類別爲背景的時候,也做了NMS。
當類別爲背景的時候,是不需要對所有的預測框做NMS的,這樣可以節省很多時間。
在這裏插入圖片描述

修改後的代碼:
在這裏插入圖片描述
優化後的時間:
在這裏插入圖片描述
我們可以看到優化後Loop2的時間大大降低,在CPU上運行的時間基本上降低爲原來的一半了。


Softmax的優化

上面分析了GetResult函數主要耗時在兩個函數中:SoftmaxForward和DetectionOutForward。上面對DetectionOutForward進行了優化,那麼SoftmaxForward是否可以優化呢?

分析了SoftmaxForward函數,發現計算所有anchor的置信度的時候,45120個anchor也需要調用90240次exp(),需要耗時21ms。

我們知道softmax概率的計算公式如下:
在這裏插入圖片描述
在對softmax進行優化的時候,我做了一個實驗,直接使用fi表示softmax置信度,然後在多個模型中做了測試:
數據集A:
在這裏插入圖片描述
數據集B:
在這裏插入圖片描述
我發現一個很奇怪的現象:直接使用fi代替softmax概率,大部分模型精度基本上沒有損失。同時也觀察了一系列模型的結果,發現softmax概率大的目標往往fi值也大。 目前還不知道具體原因。有知道其中原因的朋友歡迎留言討論。

在這裏插入圖片描述

由於去掉了softmax的計算,所以時間會大大減少,上表中可以看到去掉了softmax計算後,從原來的77ms直接降低到了18ms,速度提高了4倍多。但是由於目前對這種現象的原因還不明確,所以暫時是沒有使用的。


結束語

通過本次優化,深刻體會到了算法性能的重要性,PC上很快的一個操作,在其他平臺很可能就是性能的瓶頸。同時也體會到了深入理解算法的重要性以及將速度做到極致的那種成就感。


非常感謝您的閱讀,如果您覺得這篇文章對您有幫助,歡迎掃碼進行讚賞。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章