Try   HackMD

本文將會對OpenVINO部署YOLOv5的方法進行說明,較為冗長。

重要提示

1. 請勿在路徑中包含中文,否則各種錯誤請自行想辦法

2. 本篇後續指稱的YOLO皆指YOLOv5,為了撰文便利而縮寫

3. 此處有兩版YOLO,一為原版本YOLO,安裝~訓練時使用。第二個為OpenVINO的YOLO範例,變更了讀取模型格式與微調顯示方式,量化後使用。

參考檔案

我是包含兩版YOLO+法鬥圖片的共享資料夾

安裝

#OpenVINO
pip install "openvino-dev>=2023.1.0" "nncf>=2.5.0"
pip install psutil "seaborn>=0.11.0" matplotlib numpy onnx

#YOLOv5
git clone https://github.com/ultralytics/yolov5
cd yolov5
pip install -r requirements.txt

# 後續訓練時標記用的工具
pip install labelImg

測試

python detect.py --source data/images/zidane.jpg

第一次執行時會下載yolov5s的權重檔(副檔名.pt

在底下的run/detect/exp裡面應該可以正常看到這個結果

採樣與標記

首先,需要準備照片(廢話)。

隨便拿個webcam拍個幾張照片,這邊以桌上的法鬥犬玩具為例:

然後各個角度都稍微拍一下。練習的話通常10幾張就足夠,但官方建議是最好拍到1000張

拍攝完之後我們放到資料夾中,名稱可以使用流水號比較方便:

現在我們有了訓練用的法鬥模型資料集,接著是對資料進行標籤。

在前面我們安裝時,有輸入過這麼一行:

pip install labelImg

這行安裝了一個用於做這類RCNN標籤的工具labelImg。這個工具在windows和linux都可以使用,打開命令列之後輸入

labelImg

就會啟動標記用的程式,長得像這樣子:

這個程式可以標記很多不同RCNN模型使用的標籤類型,而我們現在準備使用的是YOLO,因此如果這個位置顯示的不是YOLO,就點選直到切換到YOLO為止:

調整完成後就可以開始標記了,我們點選左邊數下來第二個圖像--Open Dir來一口氣標記剛剛拍下的所有照片:

可以注意到,右下角有一個LIST,那邊顯示了接下來需要標記的資料夾中所有的圖片

標記的方法有兩個,對於我們這次練習任務只需要辨識一樣東西時,我們可以勾選右邊的Use default label,並給他名稱。之後使用右下角的Create Rectbox,就可以在圖片上拖曳標出物體了:

而如果是比較多類的對象,則不勾選Use default label,而是改選擇上方的difficult,並且在拖曳標記完之後輸入標籤名稱。

標記好一張照片後,我們要儲存標記完的標籤。點選左邊的save後,選擇一個資料夾統一放置。我們會建議在image的同層創建一個資料夾labels,並再創立一個資料夾train將標籤放進其中,以代表他是訓練時使用的標籤:

這個並沒有強制規定,因為後續撰寫參數時可以自行打上路徑,但我們還是建議依照慣例,在和別人交流或者合作時會比較便利。

儲存完之後,點選左邊的Next Image,重複標記存檔的動作直到標記完成。

訓練

準備完訓練用的照片和標籤之後,就是要來正式訓練了。

為了讓YOLO可以知道我們的訓練資料和對應標籤在哪邊,我們必須先撰寫一個參數檔。通常而言我們會在剛剛放置圖片和標籤的資料夾那層創建參數檔:

而其內容如下:

# 設定圖檔路徑
path:  D:/yolov5/data/fadou # 資料根目錄
train: images/train     # 訓練用資料集(相對於 path)
val: images/train       # 驗證用資料集(相對於 path)
test:                   # 測試用資料集(相對於 path,可省略)

# 物件類別設定
nc: 1           # 類別數量
names: ['fadou']  # 類別名稱

其內容就如註解所述,請自行依照存檔位置更改;而物件類別設定的部分可以參考我們存放標籤的上一層資料夾,裡面會有這麼一個檔案classes.txt:

點開之後裡面會按照順序寫出標籤名稱,依照順序填到類別名稱裡面即可。

處理完之後,我們回到YOLO的資料夾,在裡面開啟命令列(終端機)。windows可以點選上面的路徑,直接輸入cmd開啟;linux則是右鍵>Open in terminal。開啟後,在裡面執行這行指令:

python train.py --img-size 640 --batch 16 --epochs 500 --data data\fadou\fadou.yaml --weights yolov5s.pt

其中影響比較大的是epochs參數,這個參數指定了訓練幾次後停止。當然,越多次代表訓練時間越長,我們可以求快降低次數,但在練習這種樣本相對少的情況下,建議還是乖乖跑個幾百圈比較容易有正常的結果。

這行命令執行之後,就可以泡「壺」咖啡等待訓練完成了視電腦規格,時間可能數十分鐘至兩個小時不等。若使用Colab的話大概就可以壓在十幾分鐘,後續會解說方法。

訓練完成後,我們當然可以先用原版的YOLO確認結果。在YOLO>runs>train>exp中(可能會加上其他數字,稍微找一下即可)可以看到訓練結果和報告:

而我們訓練完成的權重在weights資料夾中,會有兩份檔案,一份是指定的訓練次數跑完後的權重last;另一份則是在訓練過程中結果最好的權重best。通常而言,我們會選擇best這個權重。

於是我們來執行看看:

python detect.py --source 0 --weight runs/train/exp/weights/best.pt

這次我們加上weight參數,讓他讀取我們剛訓練好的模型,並將影像來源指定為攝影機。接著就是觀察辨識結果有沒有好好地看到我們的法鬥模型了:

Colab訓練

注意:本步驟和上述本機訓練結果相同,僅是將運算交由雲端GPU進行,但訓練資料太多時請注意網路傳輸成本-.-

使用Colab的話,可以利用google的GPU進行訓練,若自己電腦沒有顯示卡,採取這方案的訓練速度會快很多--但時間成本就是轉嫁到網路傳輸上,因此請謹慎評估。

首先,我們在自己的google雲端硬碟增加一個colab檔案:

在第一個程式區塊加入以下程式碼:

!git clone https://github.com/ultralytics/yolov5
!pip install -r yolov5/requirements.txt

這步驟會在雲端配置好訓練用的環境。

上傳前,我們要先因應colab路徑更改一下yaml檔,大概會改成這個樣子:

# 設定圖檔路徑
path:  ./data/fadou     # 資料根目錄
train: images/train     # 訓練用資料集(相對於 path)
val: images/train       # 驗證用資料集(相對於 path)
test:                   # 測試用資料集(相對於 path,可省略)

# 物件類別設定
nc: 1             # 類別數量
names: ['fadou']  # 類別名稱

接著,將你剛剛標記好,包含圖像、標籤和yaml的資料夾壓縮並上傳:

將上傳好的zip放到YOLO的data資料夾中:

接著按下新增程式碼區塊,輸入以下程式:

%cd yolov5/data
!unzip fadou.zip

最後,新增第三個程式碼區塊,輸入以下程式:

!python train.py --img-size 640 --batch 16 --epochs 500 --data data/fadou/fadou.yaml --weights yolov5s.pt

訓練完成之後一樣可以找到best和last權重,將他們兩個下載下來放到跟單機訓練一樣的路徑即可執行相同的後續步驟

量化

注意,本處的量化僅量化至FP16,不會壓縮到INT8。其主因為INT8壓縮率較高,需要針對訓練資料進行微調才能保持精確度,難度落差較大,屆時另開篇幅說明。

要將一般模型轉換為OpenVINO有幾種方法,而在YOLO這個案例,我們會先將原本的模型轉換為ONNX這種中間格式,再轉換回OpenVINO使用的IR。

我們一樣在YOLO目錄底下打開命令列(終端機),並輸入以下指令並執行:

python export.py --weights runs/train/exp/weights/best.pt --imgsz 640 --batch-size 1 --include ONNX

如果您儲存模型的路徑(exp後面多了編號之類)有差異,請變更為您的模型實際所在的路徑。

注意,如果跳出錯誤有可能是因為numpy新版本問題,可以利用pip install -U numpy==1.20.3來還原成舊一點的版本

這個命令執行完之後,weights資料夾中應該會多一個副檔名為ONNX的檔案:

接著我們要將onnx檔案轉換成IR的bin和xml格式。在OpenVINO 2023版本中,這個動作是藉由python的API來完成,因此我們在weights資料夾中創建一個檔案--我們取名叫2openvino,其內容如下:

from openvino.tools import mo from openvino.runtime import serialize onnx_path = "best.onnx" # fp32 IR model fp32_path = f"fadou_fp32.xml" print(f"Export ONNX to OpenVINO FP32 IR to: {fp32_path}") model = mo.convert_model(onnx_path) serialize(model, fp32_path) # fp16 IR model fp16_path = f"fadou_fp16.xml" print(f"Export ONNX to OpenVINO FP16 IR to: {fp16_path}") model = mo.convert_model(onnx_path, compress_to_fp16=True) serialize(model, fp16_path)

最後,在weight資料夾內開啟命令列(終端機),並執行

python 2openvono.py

到這一步,我們就得到openvino能用的bin和xml檔案了。大功告成,接下來只需要執行就可以得到最後結果了--才怪。

在OpenVINO的YOLO中,預設的輸出層是output,但我們自己訓練的模型並不一定如此,我們可以點開其中一個xml看看:

是的,他是output0

所以我們在執行前的最後一步,是去更改OpenVINO的YOLOv5範例中,YOLOv5/models/common.py裡面的輸出層:

更改完後就可以正常執行了。我們在detect.py中,調整幾個參數:

主要是weight調整成我們訓練的權重,source可以改為0(攝影機),以及最重要的,data改成我們前面訓練時撰寫的yaml檔,才能夠正確的讀出標籤名稱。

記得啟動OpenVINO的虛擬環境後執行。如果一切正常,大概就能看到這種畫面:

經過上述努力,成功的把YOLO訓練成功,並部署到OpenVINO環境中。