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

作者:許哲豪 Jack

在人工智慧的電腦視覺領域中,最常見的應用包括影像分類、物件偵測、像素級物件影像分割(語義分割及實例分割),其中以物件偵測的應用範圍最廣。

近年來物件偵測的算法(模型)不斷推陳出新,從最早的二階式(R-CNN、Fast R-CNN、Faster R-CNN)高精度算法演變到現在一階式(YOLO、SSD、R-FCN)高效算法,在這之中以 YOLO(You Only Look Once)系列最受大家喜愛。

目前 YOLO 已演進至第三代(下簡稱 YOLO v3),其主要搭配 Microsoft COCO 物件偵測 80 分類做為預設訓練資料集,如果只需偵測常見物件偵測(如人、動物、車輛等),那直接利用 YOLO 預訓練好的模型及權重值就可應用到實際場域了,不過這 80 類物件通常很難滿足我們的需求,因此如果想應用自己準備的資料集,如何收集(取像)、標註、訓練資料集及進行最後的推論就顯得非常重要,接下來就以辨識(偵測)小蕃茄為例為大家介紹完整的工作流程:

建構、標註、訓練及推論自己的資料集工作流程(圖片來源:OmniXRI)

此次會以小蕃茄為例主要是受到去年 FarmBot Taiwan User Group(FBTUG)總召哈爸號召多位創客高手及科技農夫創作的台版「開源採收機器人(HarvestBot)」專案所激勵。當時有許多電腦視覺及人工智慧高手投入水果的物件辨識(位置及尺寸,不含深度距離)程式開發,主要實驗對象是彩椒和柑橘,但受限於人力及優先順序,小蕃茄辨識部份暫被擱置。

由於今年接到 Intel 邀約分享 RealSense D435 深度感測器應用案例,因此想到重啟小蕃茄辨識案,同時也希望解決去年無法處理的深度(距離)計算的問題。由於撰文當下並不是小蕃茄產季,也沒有伙伴能提供實際場域取像,因此直接上網買了一串假的塑膠小蕃茄作為實驗對象,說實在的,除了綠色的蒂頭比較塑膠感外,整體而言非常逼真,很適合作為測試範本。以下所有範例程式、資料集皆可從筆者的OmniXRI部落格Github 取得,請多多利用。

建立資料集

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

通常如果要作為深度學習的影像資料集,少說得取得幾千張,甚至數十萬張影像,但這裡只是示範如何建構,因此僅對小蕃茄的正反面各取數十張影像當作測試,其取像及測試環境如下圖所示,後續也可依需求自行增加影像的數量,至於如何取得 RealSense D435 的彩色影像可參考【3D感測器】如何擷取Intel RealSense™串流影像到OpenCV

建構小蕃茄影像資料集取像及測試環境示意圖(圖片來源:OmniXRI)

雖然訓練影像不足時可以透過影像平移、旋轉、縮放、亮度、對比、色彩調整等方式來擴增,但物件的紋理、視角、光照、形變等差異是不容易以資料擴增(Data Augmentation)的方式完成,因此在建立資料集時,資料的多樣性部份要特別留意。

由於沒有實際場域可以取像,所以只能反向操作,讓攝影機不動,移動、旋轉小蕃茄,以這種方式取得多種位置及視角的影像,其中也包含因移動產生的模糊影像。這裡為了加快取像速度及與加大影像間的差異,設定攝影機以連續取像模式進行取像,每隔 1/3.3 秒(10 frames @ 30 FPS)取一張影像並存成 JPG 格式,另外也為減少後續訓練資料集時間和因計算所需的記憶體空間,所以單張影像取像大小僅為 640×480 像素,而非 RealSense D435 的最大解析度 1920×1080 像素,最後本範例共取得小蕃茄正面 92 張、反面 79 張,合計 171 張影像,完整的影像資料集請參考OmniXRI部落格 Github 下 VOC2007\JPEGImages。

實際取得的小蕃茄影像,(a)正面、(b)反面(圖片來源:OmniXRI)

標註資料集

有了自己的影像資料集後,接著要開始對其進行標註(Annatation),就是把每一張影像中的每一個小蕃茄的位置標上一個物件框,不過為了後續方便其它訓練框架也能輕鬆讀取標註資料,這裡採用 VOC2007 的物件標註格式,而不直接採用 YOLO 自訂的格式。

首先新增一個名為 VOC2007 的資料夾,其下分別新增 Annotations 資料夾存放標註資料(.xml)、ImageSets\Main 資料夾存放工作清單(.txt)及 JPEGImages 資料夾存放原始影像(*.jpg),如下圖:

VOC2007 資料集架構(圖片來源:OmniXRI)

有了影像後要如何標註呢?目前市面有許多免費開源工具可以使用,網路上也有許多教學範例,有需要的可以參考【AI實戰】手把手教你訓練自己的目標檢測模型(SSD 篇)【採果辨識】建立自己的YOLO影像辨識模型 — 以柑橘為例,這兩篇文章都是以 LabelImg 為例。

標註時 LabelImg 預設路徑會將產生的標註檔(.xml)放在原始圖檔(.jpg)的路徑下,每一張 jpg 影像檔標註完後會產出一張同名的 xml 檔,可以等全部標註完後再一起搬到 \Annotations 下即可。標註時由於小蕃茄會有多層重疊問題,所以只標註靠外側(層)較完整的和第二層有露出超過 1/2 以上的,完整的影像標註內容可參考本Github 下 VOC2007\Annotations。

VOC2007 格式標註檔資料結構(圖片來源:OmniXRI)

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

訓練資料集

一般要訓練 YOLO v3 可選擇使用原始的 DarkNet 方式或使用常見的 AI 框架。本文主要參考【AI 實戰】動手訓練自己的目標檢測模型(YOLO 篇)完成實驗,而這篇又是參考 GitHub 上 qqwweee 以 Keras 框架為基礎完成的,所以本文也可說是簡化它後完成的,完整程式請參考本Github

在程式執行前請先依照【AI 實戰】動手訓練自己的目標檢測模型(YOLO 篇)將 Keras 及相關依賴元件安裝好,並另行下載 YOLO v3 預訓練好的權重文件 yolov3.weights(約 243 MB)。

  • 資料集格式轉換

VOC2007 格式所產生標註檔(.xml)內容會如上圖所示,主要表示物件框的方式為左上(xmin、ymin)及右下(xmax、ymax)座標,和 YOLO 格式不同,所以要依資料集清單檔(.txt),將個別標註檔(*.xml)轉換成物件編號加上物件框中心座標及尺寸格式後再寫至另一個清單檔。

這裡只需執行執行本部落格 Github 下 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 下 convert.py 即可轉換完成並置於 \model_data 下,其完整指令如下所示。

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

執行模型訓練

接下來就可以執行本部落格 Github下 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() 即可。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 下 my_yolo.py 即可,這裡為求簡單起見,直接於程式第 216 列指定待推論影像檔(如果想改成外部參數或變成對影片檔連續推論可自行修改)。


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

下方是程式第 22~30 列,推論前除指定權重檔、錨點定義檔、標籤檔外,最重要的是指定置信度門檻和重疊區比例,而值要設多少就看實際偵測到多少個物件及外框尺寸精度來調整。


1
2
3
4
5
6
7
8
9
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 }

YOLO v3 偵測小蕃茄實驗結果,左:正面,右:反面(圖片來源:OmniXRI)

實驗結果動畫 GIF 檔(圖片來源:OmniXRI)

實驗結果動畫 GIF 檔(圖片來源:OmniXRI)

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

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

小結

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

(本文轉載自歐尼克斯實境互動實驗室原文連結;責任編輯:賴佩萱)

許 哲豪

Author: 許 哲豪

工作經驗超過二十年,主要專長機電整合、電腦視覺、人機互動、人工智慧、專利分析及新創輔導。曾任機電整合工程師、機器視覺研發副理、技轉中心商業發展經理。目前擔任多家公司兼任技術顧問並積極推廣實境互動相關技術。 主持歐尼克斯實境互動工作室(OmniXRI): http://omnixri.blogspot.com

Share This Post On
Subscribe
Notify of
guest
17 Comments
Inline Feedbacks
View all comments
葉葉

你好,想請教一下在訓練自己的資料時yolov3.cfg不需要更改嗎?

葉葉

了解,我大概了解整個流程了,我是用keras yolov3來做的!另外在請教一下文中提到繼續訓練的部分,還可以另外增加其他的類別和數據集嗎?如增加一項葡萄?

ting

File “C:\Users\FangMing.conda\envs\tensorflow\lib\site-packages\keras\backend\tensorflow_backend.py”, line 381, in get_session

1
get_session

is not available ‘

RuntimeError:

1
get_session

is not available when using TensorFlow 2.0.
請問這個錯誤該怎麼解決呢?

Cynthia

您好!想請教一下 我在訓練自己蒐集的資料時 下了訓練的指令(darknet.exe detector train …..)後 顯示如下 CUDA-version: 9010 (10020)… Read more »

Cynthia

好的
非常感謝

阿誌

想請問一下,如果想要改成用鏡頭當作輸入,要改那些程式碼

阿誌

我有訓練成功並用影片測試完了,請問要怎麼對frame推論,是用detect_image(frame)嗎,還是要用detect_video

Chiang0804

請教一下,錨點的檔案是怎麼做出來的?有沒有需要針對不同類型的樣本訓練做改變嗎?如果是的話,要怎麼改,或者說怎麼去生成這個檔案?

Kevin

您好,我想請問一下我在執行convert.py時不太清楚output_path需要改成哪個路徑嗎?

17
0
Would love your thoughts, please comment.x
()
x