2019年11月20日 星期三

【AI_Column】如何以YOLOv3訓練自己的資料集─以小蕃茄為例

在人工智慧電腦視覺領域中,最常見的應用包括影像分類、物件偵測、像素級物件影像分割(語義分割及實例分割),其中又以物件偵測應用範圍最廣。近年來物件偵測的算法(模型)不斷推陳出新,從最早的二階式(R-CNN, Fast R-CNN, Faster R-CNN)高精度算法演變到現在一階式(YOLO, SSD, R-FCN)高效算法。其中又以YOLO(You Only Look Once) [1]系列最受大家喜愛,目前已演進至第三代(以下簡稱YOLOv3),其主要搭配Microsoft COCO 物件偵測80分類[2]做為預設訓練資料集,如果只需偵測常見物件偵測(如人、動物、車輛等)那麼直接利用YOLO預訓練好的模型及權重值就可應用到實際場域了。不過這80類物件通常很難滿足我們的需求,因此如果我們想應用自己準備的資料集時,那如何收集(取像)、標註、訓練資料集及進行最後的推論就變得非常重要。接下來就以辨識(偵測)小蕃茄為例為大家介紹完整工作流程(如Fig.1所示)。

Fig. 1 建構、標註、訓練及推論自己的資料集工作流程。 (OmniXRI整理製作) (點擊圖片放大)



此次會以小蕃茄為例主要是受到去(2018)年FarmBot Taiwan User Group (FBTUG) 總召哈爸號召多位創客高手及科技農夫創作的台版「開源採收機器人(HarvestBot)」專案所激勵。當時有許多電腦視覺及人工智慧高手投入水果的物件辨識(位置及尺寸,不含深度距離)程式開發,主要實驗對象是彩椒和柑橘[3],但受限於人力及優先順序,因此小蕃茄辨識部份暫被擱置。今年剛好接到Intel邀約分享 RealSense D435深度感測器應用案例,因此就想到重啟小蕃茄辨識案,同時解決去年無法處理的深度(距離)計算的問題。由於撰文當下並不是小蕃茄產季,也沒有伙伴能提供實際場域取像,因此直接上網買了一串假的塑膠小蕃茄作為實驗對象。說實在的,除了綠色的蒂頭比較塑膠感外,整體來說非常逼真,很適合作為測試使用。以下所有範例程式、資料集皆可從本部落格Github取得,請多多利用。

https://github.com/OmniXRI/OpenVINO_RealSense_HarvestBot/tree/master/my_yolo3

【建立資料集】


為了使後續訓練的成果較理想,取像和訓練影像條件要儘可能接近,因此這裡直接使用RealSense D435取像(彩色影像),而不用其它高階、高解析度攝影機取像。另外這麼作還可以順便取得因攝影機本身受光照(白平衡、亮度、對比、色相等)及其它干擾產生的雜訊(如熱雜訊、低照度雜訊等)。

通常如果要做為深度學習的影像資料集,少說得取得幾千張,甚至數十萬張影像,但這裡只是示範如何建構,所以僅對小蕃茄的正反面各取數十張影像做為測試,其取像及測試環境如Fig. 2所示,後續可依需求自行增加影像的數量。至於如何取得RealSense D435的彩色影像可參考本部落格另一篇「【3D感測器】如何擷取Intel RealSense™串流影像到OpenCV」[4]

Fig. 2 建構小蕃茄影像資料集取像及測試環境示意圖。(OmniXRI整理製作) (點擊圖片放大)

雖然訓練影像不足時可以透過影像平移、旋轉、縮放、亮度、對比、色彩調整等方式來擴增,但物件的紋理、視角、光照、形變等差異則是不容易以資料擴增(Data Augmentation)方式完成的,因此資料集的多樣性部份在建立時就要特別注意。由於沒有實際場域可以取像,所以只能反向操作,令攝影機不動而移動、旋轉小蕃茄,以取得多種位置及視角影像,甚至包含因移動產生的模糊影像。這裡為了加快取像速度及加大影像間差異,令攝影機以連續取像模式進行取像,每隔1/3.3秒(10 frames @ 30 FPS)取一張影像並存成JPG格式。另外為減少後續訓練資料集時間及因計算所需記憶體空間大小,所以單張影像取像大小僅為640x480像素,而非RealSense D435的最大解析度1920x1080像素。最後,本範例共取得小蕃茄正面92張(如Fig. 3a)、反面79張(如Fig. 3b),合計171張影像,完整的影像資料集請參考本部落格Github[5]下VOC2007\JPEGImages。

Fig. 3實際取得的小蕃茄影像,(a)正面,(b)反面。(OmniXRI整理製作) (點擊圖片放大)

【標註資料集】


有了自己的影像資料集後,接著要開始對其進行標註(Annatation),就是把每一張影像中的每一個小蕃茄的位置標上一個物件框。不過為了後續方便其它訓練框架也能輕鬆讀取標註資料,這裡採用VOC2007的物件標註格式,而不直接採用YOLO自訂的格式。首先新增一個名為VOC2007的資料夾,其下分別新增Annotations資料夾存放標註資料(*.xml)、ImageSets\Main資料夾存放工作清單(*.txt)及JPEGImages資料夾存放原始影像(*.jpg),如Fig. 4所示。

Fig. 4 VOC2007資料集架構。(OmniXRI整理製作) (點擊圖片放大)

有了影像後要如何標註呢?目前市面有許多免費開源工具可以使用,網路上也有許多教學範例,有需要的可以直接參考「【AI實戰】手把手教你訓練自己的目標檢測模型(SSD篇)」[6],或者「建立自己的YOLO辨識模型–以柑橘辨識為例」[3],這兩篇文章都是以LabelImg[7]為例,這裡就不多贅述。標註時LabelImg預設路徑會將產生的標註檔(*.xml)放在原始圖檔(*.jpg)的路徑下,每一張jpg影像檔標註完後會產出一張同名的xml檔(完整格式請參考Fig. 5),可以等全部標註完後再一起搬到\Annotations下即可。標註時由於小蕃茄會有多層重疊問題,所以只標註較靠外側(層)較完整的和第二層有露出超過1/2以上的。完整的影像標註內容可參考本部落格Github[5]下VOC2007\Annotations。

Fig. 5 VOC2007格式標註檔資料結構。(OmniXRI整理製作) (點擊圖片放大)

再來要分配資料集大小,包括訓練集(train.txt)、驗證集(val.txt)、測試集(test.txt)及訓練加驗證集(trainval.txt)。執行本部落格Github[5]下my_dataset.py,就會自動隨機把影像資料集分成三個部份,其比例可自定,預設訓練加驗證集佔全部數量2/3(trainval_percent = 0.66),而訓練集佔前述比例的一半(train_percent = 0.5)。完成分配後,存放路徑在VOC2007\ImageSets\Main下,而各資料集清單檔(*.txt)即為不含副檔名的影像名稱清單。

【訓練資料集】


一般要訓練YOLOv3可選擇使用原始的DarkNet方式或者使用常見的AI框架。本文主要參考「【AI實戰】動手訓練自己的目標檢測模型(YOLO篇)」[8]完成實驗,而這篇又是參考GitHub上qqwweee以Keras框架為基礎完成的[9],所以本文亦參考[9]修改(簡化)後完成,完整程式請參考本部落格Github[5]。在程式執行前請先依[8]將Keras及相關依賴元件安裝好,並另行下載YOLOv3預訓練好的權重文件yolov3.weights(約243MB)[10]

*資料集格式轉換*

VOC2007格式所產生標註檔(*.xml)的內容會如Fig. 5所示,主要表示物件框的方式為左上(xmin,ymin)及右下(xmax,ymax)座標,和YOLO格式不同,所以要依資料集清單檔(*.txt),將個別標註檔(*.xml)轉換成物件編號加上物件框中心座標及尺寸格式再寫至另一個清單檔。這裡只需執行執行本部落格Github[5]下my_voc_annotation.py即可產生2007_tarin.txt, 2007_val.txt及2007_test.txt三個YOLO格式的清單檔。由於目前只有一個類別tomato,所以my_voc_annotation.py中只需定義classes = ["tomato"]一個項目即可。另外\model_data下有一個my_classes.txt即此次所需自義類別的標籤檔,同樣地只需定義tomato一個項目即可。

*轉換權重文件*

由於此次使用的是Keras框架,所以須把下載到的YOLO預訓練權重值轉成Keras格式(*.h5),執行本部落格Github[5] 下convert.py即可轉換完成並置於\model_data下,其完整指令如下所示。

python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5

*執行模型訓練*

接下來就可以執行本部落格Github[5]下my_train.py進行小蕃茄影像像訓練。訓練前要設定好下列參數 (程式碼前二位數字為列號)。如果GPU記憶體不足導致訓練到一半程式中止時,可把第56列批次數量設小一些。如果想先大概測一下(未完全收斂),可將第62, 82,83列數值設小一點。最終訓練完的結果會存放在\logs\000\trained_weights_final.h5。若不滿意結果想從這個結果繼續訓練,則可將此權重檔複製到\model_data下,再將第32列程式修改成weights_path='model_data/ trained_weights_final.h5'即可。另外如程式第35, 36列,在訓練過程每隔幾個遍歷(Epoch)就會有檢查點並會存權重結果臨時檔,萬一不幸 還沒訓練完成程式就意外終止時,亦可使用這些檔案當成起始權重檔使用。不過由於程式每次存檔是以第幾次遍歷加上損失率做為檔名且不會自動刪除,所以可能會佔用巨量磁碟空間,建議可改為存同一個名稱,保留最後一次成果權重檔即可。另外如果需要將YOLO模型及權重完整存檔以供其它程式或框架使用,則可把第86列改成model.save()即可。

16 annotation_path = '2007_train.txt' #待訓練清單(YOLO格式)
17 log_dir = 'logs/000/' #訓練過程及結果暫存路徑
18 classes_path = 'model_data/my_classes.txt' #自定義標籤檔路徑及名稱
19 anchors_path = 'model_data/yolo_anchors.txt' #錨點定義檔路徑及名稱

32 freeze_body=2, weights_path='model_data/yolo_weights.h5') #指定起始訓練權重檔路徑及名稱

35 checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
36   monitor='val_loss', save_weights_only=True, save_best_only=True, period=3) #訓練過程權重檔名稱由第幾輪加上損失率為名稱

56 batch_size = 24 #批次處理數量,依GPU記憶體大小決定

62 epochs=50, #訓練遍歷次數
63 initial_epoch=0, #初始訓練遍歷次數

82 epochs=100, #訓練遍歷次數
83 initial_epoch=50, #初始訓練遍歷次數

86 model.save_weights(log_dir + 'trained_weights_final.h5') #儲存最終權重檔

【影像推論】


訓練完成就可以來測試推論效果如何,執行本部落格Github[5]下my_yolo.py即可,這裡為求簡單起見,直接於程式第216列指定待推論影像檔,如果想改成外部參數或變成對影片檔連續推論可自行修改。如程式第22~30列,推論前除指定權重檔、錨點定義檔、標籤檔外,最重要的是指定置信度門檻和重疊區比例,而要設多少值就看實際偵測到多少個物件及外框尺寸精度來調整高低。

22 _defaults = {
23    "model_path": 'model_data/trained_weights_final.h5', #指定YOLO訓練完成權重檔路徑及名稱
24    "anchors_path": 'model_data/yolo_anchors.txt', #指定錨點定義檔路徑及名稱
25    "classes_path": 'model_data/my_classes.txt', #指定自定義標籤檔路徑及名稱
26    "score" : 0.1, #最低置信度門檻(0.01~0.99)
27    "iou" : 0.45, #重疊區比例(0.01~1.0)
28    "model_image_size" : (416, 416), #影像尺寸
29    "gpu_num" : 1, #使用GPU數量
30 }

216 path = 'C:/Users/jack_/my_yolo3/VOC2007/JPEGImages/img_1550.jpg' #指定待測影像檔案路徑及名稱

Fig. 6 YOLOv3偵測小蕃茄實驗結果,左:正面,右:反面。(OmniXRI整理製作) (點擊圖片放大)

Fig. 7 實驗結果動畫GIF檔。。(OmniXRI整理製作) (點擊圖片放大)

Fig. 8 實驗結果動畫GIF檔。(OmniXRI整理製作) (點擊圖片放大)

由於本文使用的影像資料集非常少,且訓練的次數也不足一百次,所以小蕃茄被正確檢出的比例並不高,所以將置信度調的很低,以方便呈現檢出結果。另外一般測試時是絕對不能將訓練集和驗證集當作測試集,但本實驗因為影像資料集太小及訓練次數不多,所以就直接拿所有影像當成測試集以方便大家了解結果(如Fig. 6所示),不妥之處敬請見諒。完整的結果影像動畫GIF檔可參考本部落格Github[5]下\Result\tomato_ori.gif(原始影像檔)及tomato_yolo3.gif(推論結果檔)。

另外這裡補充兩個小程式,yolov3_keras_to_darknet.py方便大家將Keras訓練好的YOLO模型轉回DarkNet權重檔(*.weights)[11],而h5topb.py則可將模型和權重轉換到TensoFlow(*.pb)[12],有興趣的朋友可以試一下。不過這裡提醒一下,如果想在Intel OpenVINO下執行的朋友,請直接將權重檔從*.h5格式轉成DarkNet *.weights格式再送至Model Optimizer轉換成IR格式(*.xml, *.bin),而不要轉成TensorFlow *.pb格式再送到MO轉IR,以免無法順利執行。

【小結】


YOLOv3是非常普遍及高效的物件偵測深度學習模型,只要多花一點時間取得夠多的影像集並進行仔細地標註及長時間的訓練,相信像小蕃茄這類層疊疊的物件也能順利檢出,後續若再配上像Intel RealSense D435深度感測器的深度影像資訊,則自動採收小蕃茄的機器手臂很快就能土炮出來了。

參考文獻:


[1] YOLO: Real-Time Object Detection
https://pjreddie.com/darknet/yolo/

[2] Microsoft COCO Dataset
http://cocodataset.org/#download

[3] CH. Tseng 建立自己的YOLO辨識模型–以柑橘辨識為例
https://chtseng.wordpress.com/2018/09/01/%e5%bb%ba%e7%ab%8b%e8%87%aa%e5%b7%b1%e7%9a%84yolo%e8%be%a8%e8%ad%98%e6%a8%a1%e5%9e%8b-%e4%bb%a5%e6%9f%91%e6%a9%98%e8%be%a8%e8%ad%98%e7%82%ba%e4%be%8b/

[4] 【3D感測器】如何擷取Intel RealSense™串流影像到OpenCV
http://omnixri.blogspot.com/2019/11/3dintel-realsenseopencv.html

[5] Github OmniXRI HarvestBot my_yolo3
https://github.com/OmniXRI/OpenVINO_RealSense_HarvestBot/tree/master/my_yolo3

[6] 【AI实战】手把手教你训练自己的目标检测模型(SSD篇)
https://my.oschina.net/u/876354/blog/1927351

[7] 影像標註工具LabelImg
https://github.com/tzutalin/labelImg

[8] 【AI实战】动手训练自己的目标检测模型(YOLO篇)
https://my.oschina.net/u/876354/blog/1927881

[9] GitHub qqwweee/keras-yolo3
https://github.com/qqwweee/keras-yolo3

[10] YOLOv3預訓練權重檔
https://pjreddie.com/media/files/yolov3.weights

[11] 模型转换[yolov3模型在keras与darknet之间转换]
https://www.cnblogs.com/shouhuxianjian/p/10567201.html

[12] 將keras的h5模型轉換爲tensorflow的pb模型
https://www.twblogs.net/a/5bfae8b4bd9eee7aed32c9bb

沒有留言:

張貼留言

【頂置】簡報、源碼、系列文快速連結區

常有人反應用手機瀏覽本部落格時常要捲很多頁才能找到系列發文、開源專案、課程及活動簡報,為了方便大家快速查詢,特整理連結如下,敬請參考! Edge AI Taiwan 邊緣智能交流區 全像顯示與互動交流區 台科大(NTUST) 人工智慧與邊緣運算實務 開南大學...