如何客製化企業 RAG 知識庫?— 從資料庫到知識整合的實戰技術
|

【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() 即可。


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 列指定待推論影像檔(如果想改成外部參數或變成對影片檔連續推論可自行修改)。


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

下方是程式第 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 }

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 深度感測器的深度影像資訊,自動採收小蕃茄的機器手臂很快就能土炮出來了。

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

許 哲豪

訂閱MakerPRO知識充電報

與40000位開發者一同掌握科技創新的技術資訊!

Author: 許 哲豪

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

Share This Post On
468 ad

32 Comments

  1. 許先生你好,
    可否請教先進一個問題。
    我剛開始學習深度學習,
    現在只是按書本操練,
    但是我發現自己系統的一個問題,
    比如 Mnist的樣本有60000筆,
    我看書上一個Epoch
    它都是從1/50000到50000/50000.
    但是我的系統
    就只會出現
    1/1250到 1250/1250.
    可否給個建議,看是從哪裡著手來查,或是說我必須重灌電腦。

    目前我的記憶體有128GB
    顯卡記憶體也有24GB
    windows10
    tensorflow 2.5
    keras 也是2.4
    謝謝你。

    Post a Reply
    • 一般資料集會分為訓練集、驗證集及測試集,60000筆只拿了50000筆來訓練及驗證,所以一個Epoch才會只有50000。
      若你只出現1250,建議你檢查一下關於訓練相關的設定值。
      另外我最近有寫一篇新的Yolov4自定義資料集物件偵測文章可參考一下。
      https://omnixri.blogspot.com/2021/05/google-colabyolov4-tiny.html
      ps. mnist是影像分類不是物件偵測。

      Post a Reply
  2. 老师,为什么我训练到一半就结束了?没有完全训练完成。请问是不是图像集太少?

    Post a Reply
    • 訓練若已收歛,或說LOSS已低過某過數值,就會停止。資料集過少,的確有可能產生過擬合現象,提早結束。但實際狀況仍需以錯誤訊息為主。

      Post a Reply
  3. 老師您好,目前是在做辨識日月潭新八景的研究,目前預估有點困難因為山水在日月潭的重覆率太高了,我的研究是希望用一些小物件去,如自行車道欄杆,或者自行車等等去判別八景…
    老師滿想知道yolov3的辨識能力到哪裡,是否可以徵辨識特定的山水呢?或是還是需要找一些物件去代表其中幾個八景。

    Post a Reply
    • 在影像辨識中最重要的是建立良好的已標註資料集,不管是影像分類還是物件偵測,如果一般人看到一張照片都分不出來是那個分類,那就不要為難電腦了,因為在標註上肯定會出狀況。
      一般要有好的辨識結果模型雖很重要,但大部份問題還是會出在資料集及標註,若資料集大小太小(只有幾百張)或分類極不平均時,通常不會得到太好的成果。
      如果反過來從網路上收集大家拍過的八景照片,或許能更明確指出分類重點為何,同時也被清楚標註,那資料集一多,才有討論模型辨識能力的空間。

      Post a Reply
  4. 您好想請問一下,訓練的資料每個物件大約需要幾張呢?各種角度個需要幾張才算是合適,如果像向山遊客中心這樣大型的物件是不是要分成幾個部份去訓練呢?

    Post a Reply
    • 資料集越多越好,且要多樣性(不同視點、光照等),通常要數千張才會有較理想訓練結果。
      有時沒這麼多(只有數十張)可採資料擴增(Data Augmentation)或改以遷移學習(Transfer Learning)方式處理,但和本範例方式做法不同,須另外參考其它人的文章。
      另外訓練時一定要一起訓練,目前常見的模型多半都不支援分成多個部份訓練後再組合。

      Post a Reply
  5. 您好 我訓練完的weights包含兩種class,不過在我開啟webcam辨識的時候,會在一些奇怪的地方框出1信任度的框框,例如Chair calss,在全黑的時候 他會框出chair信任度為1,我目前就是簡單些,多設一個other class避免辨識出現問題,但other class是我不需要的,所以如果想將它disable掉的話,程式碼需要怎麼做修正呢?

    Post a Reply
  6. 您好, 請教一下, 因為想在 Intel OpenVINO 下執行, 所以訓練結果的 Keras Model 權重檔 .h5 格式 如何利用 MO 轉 IR? MO 是否支援 DarkNet Model *.weights 格式進行轉換 IR?

    Post a Reply
    • 本文中有提到“由於此次使用的是 Keras 框架,所以須把下載到的 YOLO 預訓練權重值轉成 Keras 格式(*.h5),執行本部落格 Github 下 convert.py 即可轉換完成並置於 \model_data 下,其完整指令如下所示。
      python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5
      ”,所以MO目前是不支援Keras, Darknet轉IR,目前只支援ONNX, TensorFolw, MXNet, Kaldi。舊版的對於YOLOV3支援上時會有問題,從TensorFlow轉過來的YOLOV3會無法使用,要用OpenVINO指定YOLOV3工作程序。目前新版2020.3的對YOLOV3已較友善了。

      Post a Reply
  7. 不好意思想請教一下 如何在訓練過程中顯示accuracy呢
    目前用過在model.complie()加上metrics=[‘accuracy’]但會有list index out of range的error

    Post a Reply
  8. 不好意思想請教一下 有沒有辦法可以在訓練過程中顯示accuracy呢
    目前是在model.compile()加上metrics=[‘accuracy’] 但會有outputshape 問題

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

    Post a Reply
    • 你可以使用目前路徑或指定路徑,重點是轉出來的檔案路徑要再指定給下一個動作使用。

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

    Post a Reply
    • 關於錨點及YoloV3相關設定可參考https://github.com/OmniXRI/OpenVINO_RealSense_HarvestBot/tree/master/my_yolo3/my_yolov3.cfg,對於其重要參數設定說明可參考本文提到CH大的“【採果辨識】建立自己的YOLO影像辨識模型 — 以柑橘為例”,https://makerpro.cc/2019/08/build-your-yolo-recognition-model/。

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

    Post a Reply
    • 我原本就是用一般 WEBCAM 取像,為了方便訓練才把影像存成一個一個的檔案。所以你只須將WEBCAM取到的影像先存檔後再標註後即可訓練。訓練完後就WEBCAM讀入的影像可直接推論不用再轉存影像檔再讀檔才推論。以OpenCV舉例宣告一個 cv::VideoCapture cap; cv::Mat frame; cap >> frame; 再對frame進行推論即可。

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

        Post a Reply
        • 如同本文「影像推論」說明”訓練完成就可以來測試推論效果如何,執行本部落格 Github 下 my_yolo.py 即可,這裡為求簡單起見,直接於程式第 216 列指定待推論影像檔(如果想改成外部參數或變成對影片檔連續推論可自行修改)。

          Post a Reply
  12. 您好!想請教一下
    我在訓練自己蒐集的資料時
    下了訓練的指令(darknet.exe detector train …..)後
    顯示如下
    CUDA-version: 9010 (10020), cuDNN: 7.0.5, CUDNN_HALF=1, GPU count: 1
    CUDNN_HALF=1
    OpenCV version: 3.4.0
    yolov3-tiny
    0 : compute_capability = 610, cudnn_half = 0, GPU: GeForce GTX 1070 Ti
    net.optimized_memory = 0
    mini_batch = 32, batch = 64, time_steps = 1, train = 1
    [yolo] params: iou loss: mse (2), iou_norm: 0.75, cls_norm: 1.00, scale_x_y: 1.00
    17 route 13 -> 13 x 13 x 256
    18 conv 128 1 x 1/ 1 13 x 13 x 256 -> 13 x 13 x 128 0.011 BF
    19 upsample 2x 13 x 13 x 128 -> 26 x 26 x 128
    20 route 19 8 -> 26 x 26 x 384
    21 conv 256 3 x 3/ 1 26 x 26 x 384 -> 26 x 26 x 256 1.196 BF
    22 conv 24 1 x 1/ 1 26 x 26 x 256 -> 26 x 26 x 24 0.008 BF
    23 yolo
    [yolo] params: iou loss: mse (2), iou_norm: 0.75, cls_norm: 1.00, scale_x_y: 1.00
    Total BFLOPS 5.451
    avg_outputs = 325268
    Allocate additional workspace_size = 33.55 MB
    Loading weights from yolov3-tiny.weights…
    seen 64, trained: 32013 K-images (500 Kilo-batches_64)
    Done! Loaded 24 layers from weights-file
    Learning Rate: 0.001, Momentum: 0.9, Decay: 0.0005
    If error occurs – run training with flag: -dont_show
    Saving weights to D://darknet-master/build/darknet/x64/data/tiny_yolov3-train/backup/yolov3-tiny_final.weights
    Create 6 permanent cpu-threads

    然後就結束了(跳回要輸入指令的狀態)
    這樣似乎是沒有成功開始訓練(?)
    想請問您是否知道會是什麼情況下會造成這種結果,該如何解決呢?

    謝謝你

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

    get_session

    is not available ‘

    RuntimeError:

    get_session

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

    Post a Reply
    • 這個範例用的是Tensorflow 1.4版,但你好像用的是Tensorflow 2.0版,所以會出現這個問題。

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

    Post a Reply

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *