Yolo模型部署的兩種方法

目錄

1 需求描述

第1種:封裝darknet框架

第2種:weights模型轉pb模型

2 weights模型轉pb模型方法

3 重要備註

(1)關於預處理:

(2)關於模型輸入輸出的數據結構和節點名稱:

(3)關於NMS


1 需求描述

工程部署使用的C++,模型用darknet(AB版)YoloV3訓練的,格式爲weights。目前已實測通過了兩種方式調用yoloV3模型。

第1種:封裝darknet框架

將darknet框架封裝成dll,在C++中通過非常簡單易懂的語法直接調用weights,cfg這兩個原生文件。

優點:

編譯非常簡單:darknet(AB版)自帶編譯成dll的方法,操作過程也很簡單,沒有各種版本兼容性問題。

dll非常小:darknet框架編譯出來的dll只有1.7MB。

C++語法非常精簡易懂:完成模型調用和輸出只有5行代碼左右。

第2種:weights模型轉pb模型

如果已經將tensorflow框架封裝成dll,那麼也可以將weights模型轉成能供tensorflow直接使用的pb模型。

優點:使用tensorflow部署還是更可靠點,而且有時候一個項目不僅用一個算法,所有框架的模型都可以用tensorflow去實現。

2 weights模型轉pb模型方法

darknet框架實現的yolo3有兩個版本,一個是yolo3作者原版,還有一個是yolo4作者優化後的版本(AB版),雖然兩者的cfg文件中內容一模一樣,但是使用GitHub上找的不同版本的weights模型轉pb輪子,將會出現異常。即,使用原版darknet yolo3的weights轉pb的代碼去轉AB版yolo3 weights,雖然能轉換成功,但是這個pb模型的精度幾乎爲0。

原因據查是AB版對yolo3算法做了模型結構之外的一些改進,這可能導致weights轉pb的源代碼不通用。

由於AB版darknet相對原版做了很多優化,而且爲了能夠在未來部署AB版的yolo4算法,這次僅記錄下AB版yolo3的轉換和部署方法。

weights轉pb

轉換代碼在https://github.com/hunglc007/tensorflow-yolov4-tflite開源項目中,1100星,作者使用TF2.0框架將AB版darknet的yolo3和4復現了,裏面有yolo3,yolo4轉pb,轉tensorflow lite(移動部署),轉tensorRT模型的輪子。

步驟1:下載上述項目。(TF2.3版以上)

步驟2:CMD中執行如下指令即可轉換成功:(修改你的weights模型位置,和你的輸入大小)

python save_model.py --weights ./data/yolov3.weights --output ./checkpoints/yolov3-608 --input_size 608 --model yolov3

程序將會在當前項目路徑內,生成一個./checkpoints/yolov3-608/文件夾,裏面存放了如下轉換結果:

步驟3:將saved_model.pb和variables兩個東西放到你C++項目工程中就行了。

步驟4:C++中的模型調用和結果解析語法,可以參考如下博主的完整代碼:

tensorflow C++接口調用 目標檢測 pb模型代碼 https://www.cnblogs.com/cnugis/p/11506767.html

tensorflow C++接口調用 圖像分類 pb模型代碼:https://www.cnblogs.com/cnugis/p/11507872.html

3 重要備註

(1)關於預處理

上述轉換項目,僅僅是將yolo3模型原封不動的轉成pb模型,這就表示,在C++工程中,還需要將輸入pb模型的Mat圖像先歸一化,然後resize成608*608,最後纔去調用pb模型進行預測。這些步驟,如果不想在C++中寫,可以修改轉換代碼,即在python中先加一道預處理,修改模型輸入的數據結構,然後再將weights轉成pb。

(2)關於模型輸入輸出的數據結構和節點名稱

在save_model.py中,作者定義的pb模型輸出是:「pred = tf.concat([boxes, pred_conf], axis=-1)」,即所有檢測框座標和預測值全部由一個輸出變量表示。所以,C++那邊就只要定義一個輸出節點的名稱。

pb模型輸入輸出節點的名稱,以及輸入輸出的數據結構,可以在save_model.py中去尋找蹤跡,也可用使用Netron軟件打開pb模型去找到,比如:

例子1:yolo3一個輸入(float32,表示接受float格式圖像,大概率是要提前歸一化後才能輸入!),2個輸出節點:

例子2:yolo3一個輸入(uint8,表示圖像是0-255,接受非歸一化圖像!),4個輸出節點:

關於模型輸入輸出爲什麼是這個英文名,應該是程序默認的,除非自己定義了。

關於節點名稱後面新增的「:0, :1」什麼的,就是模型給予的默認的防止同名的輸入輸出節點。

(3)關於NMS

使用上述項目轉換,模型的預測結果並沒有經過NMS處理,這導致輸出裏面有很多近乎重疊的檢測框。在C++中寫NMS算法來篩選pb模型的檢測結果可能比較麻煩,我們可以在轉換模型過程中,將NMS也寫進去,這樣pb模型的輸出就直接是經過NMS操作後的結果。

方法:

①在上述開源項目中,打開detect.py文件。

②複製其中的:boxes, scores, classes, valid_detections = tf.image.combined_non_max_suppression(......)這行代碼。

③將上述代碼粘貼在save_model.py中的「pred = tf.concat([boxes, pred_conf], axis=-1)」之前,並註釋「pred = tf.concat([boxes, pred_conf], axis=-1)」,替換成「pred = (boxes, scores) 」,如下所示:

 

1