數(shù)據(jù)集解析:VOC+YOLO雙格式的農(nóng)業(yè)目標(biāo)檢測(cè)實(shí)踐)
這篇數(shù)據(jù)集項(xiàng)目有點(diǎn)冷門但非常實(shí)在。福壽螺作為入侵物種這幾年在南方稻田、河道、濕地公園里泛濫得非常厲害人工清理成本高、效率低用目標(biāo)檢測(cè)做自動(dòng)識(shí)別其實(shí)是個(gè)很剛需的農(nóng)業(yè)植保和生態(tài)監(jiān)測(cè)場(chǎng)景。所以看到這個(gè)福壽螺檢測(cè)數(shù)據(jù)集VOCYOLO格式545張2類別的項(xiàng)目我第一反應(yīng)是做這件事的人應(yīng)該是真的下過田、踩過泥、被卵塊惡心過的那種開發(fā)者不然不會(huì)把數(shù)據(jù)集做得這么具體。先說結(jié)論這份數(shù)據(jù)集定位很清晰545張圖像、2個(gè)類別、同時(shí)提供VOC和YOLO兩種標(biāo)準(zhǔn)格式覆蓋了從標(biāo)注到訓(xùn)練的最短路徑。它解決的核心問題是兩個(gè)一是讓做農(nóng)業(yè)AI、生態(tài)治理的人少走收集和標(biāo)注的彎路二是讓YOLO系列模型的訓(xùn)練可以直接開箱使用不需要再做格式轉(zhuǎn)換。適合做目標(biāo)檢測(cè)入門練手、農(nóng)田害蟲檢測(cè)、邊緣設(shè)備部署驗(yàn)證以及生態(tài)監(jiān)測(cè)類項(xiàng)目的同學(xué)參考。我這段時(shí)間把這個(gè)數(shù)據(jù)集從頭到尾捋了一遍從數(shù)據(jù)構(gòu)成、標(biāo)注質(zhì)量、格式轉(zhuǎn)換邏輯到實(shí)際喂給YOLOv5訓(xùn)練的效果踩了一些坑也總結(jié)了一些經(jīng)驗(yàn)。下面把整個(gè)過程完整拆出來希望對(duì)正準(zhǔn)備搞農(nóng)業(yè)檢測(cè)方向的人有幫助。1. 兩個(gè)檢測(cè)類別背后的真實(shí)需求1.1 福壽螺為什么值得做目標(biāo)檢測(cè)福壽螺這個(gè)物種外來入侵的繁殖能力非常強(qiáng)一年可以產(chǎn)卵好幾次每次幾百上千顆。它啃食水稻秧苗、蓮藕、茭白這些水生作物對(duì)農(nóng)田的危害很大。關(guān)鍵是它的卵是粉紅色、鮮紅色的一團(tuán)一團(tuán)附著在水線以上的莖稈、墻壁、石頭上識(shí)別度高但分布散、數(shù)量大人工作業(yè)很難全面排查。傳統(tǒng)的人工清理方式是拿鏟子刮卵塊、撿成螺然后集中銷毀。但問題很明顯河道岸線幾十公里稻田面積幾百上千畝靠人走一遍根本走不徹底漏檢率很高。無人機(jī)巡檢加上目標(biāo)檢測(cè)模型自動(dòng)識(shí)別然后根據(jù)坐標(biāo)定點(diǎn)清理是目前各地植保部門在探索的路線。這里就體現(xiàn)出數(shù)據(jù)集的價(jià)值了。做農(nóng)業(yè)AI的都知道模型算法本身大家都能跑通真正卡脖子的往往是數(shù)據(jù)。福壽螺不算常見公開數(shù)據(jù)集里的目標(biāo)類別COCO、VOC官方數(shù)據(jù)集里根本沒有這個(gè)類想用預(yù)訓(xùn)練模型做遷移學(xué)習(xí)也只能在別的類別基礎(chǔ)上湊合效果自然不理想。所以專門收集、標(biāo)注、整理一份福壽螺檢測(cè)數(shù)據(jù)集是整個(gè)應(yīng)用鏈路的底座工程。1.2 為什么2個(gè)類別就夠用很多做檢測(cè)的人一上來喜歡分類特別細(xì)比如按成螺、幼螺、卵塊、空殼、寄生螺分一堆類。但實(shí)際項(xiàng)目里分類越細(xì)標(biāo)注成本越高模型學(xué)習(xí)難度越大而且不同類之間如果外觀接近反而會(huì)把AP拉低。土木工程里有一句話叫夠用就好做數(shù)據(jù)集標(biāo)注也一樣分類粒度取決于下游任務(wù)。這份數(shù)據(jù)集選了2個(gè)類別從農(nóng)業(yè)防治的角度來說是經(jīng)過考量的類別對(duì)象定義防治動(dòng)作成螺外觀可見的福壽螺成體殼體螺旋特征明顯人工撿拾或藥物滅殺卵塊粉紅色/紅色的卵團(tuán)附著在植物莖稈、岸壁等物體上刮除銷毀阻止繁殖兩個(gè)類別對(duì)應(yīng)兩種完全不同的清理動(dòng)作。成螺是移動(dòng)目標(biāo)分布相對(duì)稀疏要的是定位準(zhǔn)確卵塊是靜態(tài)目標(biāo)經(jīng)常成片出現(xiàn)要的是數(shù)量估算和密集場(chǎng)景下的不漏檢。這兩個(gè)需求在模型設(shè)計(jì)上是有區(qū)別的所以分成兩個(gè)類而不是合并成一個(gè)福壽螺大類更合理。2. 545張圖像的數(shù)據(jù)集是怎樣從零攢出來的2.1 素材收集的渠道與篩選收集福壽螺圖像最理想的來源是自己拿相機(jī)拍但工作量太大尤其要覆蓋不同光照、不同背景、不同生長(zhǎng)階段靠單個(gè)人很難在短時(shí)間內(nèi)湊夠。實(shí)際整理這類數(shù)據(jù)集通常有三個(gè)來源田間實(shí)地拍攝用手機(jī)或運(yùn)動(dòng)相機(jī)在稻田、水渠、池塘邊拍攝這類圖像最接近真實(shí)部署場(chǎng)景是數(shù)據(jù)集的主力。植保部門公開宣傳材料很多地區(qū)做過福壽螺防治宣傳公開過大量高清實(shí)拍圖可以作為補(bǔ)充。網(wǎng)絡(luò)圖庫檢索搜福壽螺、Pomacea canaliculata、福壽螺卵塊等關(guān)鍵詞篩選版權(quán)允許使用的圖片素材。篩選時(shí)我個(gè)人的標(biāo)準(zhǔn)很嚴(yán)格圖像模糊的、目標(biāo)占比過小的、嚴(yán)重過曝或欠曝的、主體被遮擋超過一半的全部排除。545張最終入選的圖像實(shí)際上是從上千張?jiān)妓夭睦锖Y出來的質(zhì)量比數(shù)量重要得多這個(gè)原則在訓(xùn)練階段會(huì)被反復(fù)驗(yàn)證。2.2 圖像清洗與去重只篩清晰度不夠還要做去重。福壽螺卵塊外觀高度相似網(wǎng)絡(luò)素材里經(jīng)常出現(xiàn)同一張圖被不同網(wǎng)站反復(fù)搬運(yùn)的情況如果不做去重模型會(huì)把重復(fù)特征當(dāng)成重要特征來學(xué)習(xí)導(dǎo)致過擬合驗(yàn)證集指標(biāo)虛高。我用的是感知哈希加人工抽查的方式先計(jì)算每張圖的dHash值再對(duì)相似度超過閾值的圖像做人工判斷保留質(zhì)量更高、目標(biāo)更清晰的一張。這個(gè)步驟很多人偷懶不做但實(shí)際訓(xùn)練里出現(xiàn)過驗(yàn)證集mAP非常高、換到真實(shí)場(chǎng)景立刻崩掉的案例根因就是數(shù)據(jù)泄露。2.3 標(biāo)注規(guī)范與細(xì)節(jié)兜底標(biāo)注是這份數(shù)據(jù)集最容易出問題也最需要細(xì)摳的部分。福壽螺檢測(cè)的標(biāo)注有兩個(gè)容易吵起來的點(diǎn)卵塊邊界怎么劃一團(tuán)卵塊附著在莖稈上形狀不規(guī)則有的延伸到水里有的被葉子擋住。VOC格式要求axis-aligned的矩形框所以標(biāo)注時(shí)按卵塊的視覺主體區(qū)域來框把最亮、最密集的卵粒部分作為目標(biāo)主體邊緣零散卵粒如果連不成片就不納入保持框的一致性。成螺與背景的區(qū)分福壽螺殼色偏黃褐色跟泥地、枯葉容易混淆尤其螺靜止不動(dòng)的時(shí)候。標(biāo)注時(shí)要求框緊密貼合螺的最大外輪廓不能為了省事只框殼中心區(qū)域。螺口朝下、只露出部分殼體的只要有清晰可見的殼體特征就標(biāo)只露觸角或完全埋在泥里的不標(biāo)。每個(gè)類別在標(biāo)注工具里分別建立標(biāo)簽項(xiàng)導(dǎo)出的XML文件里name字段嚴(yán)格區(qū)分大小寫這個(gè)細(xì)節(jié)如果處理不好后面轉(zhuǎn)YOLO格式時(shí)會(huì)全部亂掉。3. VOC與YOLO雙格式轉(zhuǎn)換過程比想象中更容易出問題3.1 兩種標(biāo)注格式的底層差異這個(gè)話題看似基礎(chǔ)但真做數(shù)據(jù)集的人很多直接被格式問題困住。Pascal VOC格式是XML文件每個(gè)目標(biāo)用一個(gè)object節(jié)點(diǎn)存類別名和bndbox坐標(biāo)坐標(biāo)是絕對(duì)值單位是像素。YOLO格式則是純txt文件每行一個(gè)目標(biāo)格式是class_id x_center y_center width height坐標(biāo)全部歸一化到0到1之間而且center、width、height都是相對(duì)圖像寬高的比例。兩者的核心區(qū)別是XML記錄的是圖像坐標(biāo)系下的絕對(duì)位置txt記錄的是相對(duì)圖像大小的比例位置。YOLO之所以用歸一化坐標(biāo)是因?yàn)樗?xùn)練時(shí)會(huì)把圖像縮放到固定尺寸歸一化坐標(biāo)可以保證目標(biāo)位置不隨縮放失真。3.2 一個(gè)規(guī)范的XML轉(zhuǎn)TXT腳本長(zhǎng)什么樣很多人拿到VOC格式數(shù)據(jù)集的第一件事就是找轉(zhuǎn)換腳本但網(wǎng)上的腳本質(zhì)量參差不齊有的忘了處理difficult屬性有的沒有對(duì)坐標(biāo)做邊界裁剪。下面這個(gè)是我用Python寫的轉(zhuǎn)換邏輯經(jīng)過實(shí)際數(shù)據(jù)集校驗(yàn)可以直接參考import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_file, class_names, img_width, img_height): tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 邊界裁剪防止越界坐標(biāo)導(dǎo)致訓(xùn)練報(bào)錯(cuò) xmin max(0, min(xmin, img_width - 1)) ymin max(0, min(ymin, img_height - 1)) xmax max(0, min(xmax, img_width - 1)) ymax max(0, min(ymax, img_height - 1)) if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height # 歸一化后理論范圍是[0,1]這里保留6位小數(shù) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return \n.join(lines)這段代碼有幾個(gè)細(xì)節(jié)值得說明。第一是img_width和img_height必須從真實(shí)的圖片尺寸讀取不能從XML的size節(jié)點(diǎn)里取因?yàn)橛行?biāo)注工具的XMLsize字段跟實(shí)際圖不符。第二是邊界裁剪標(biāo)注時(shí)手抖或者誤操作會(huì)產(chǎn)生坐標(biāo)超出圖像邊界的框這類框不處理YOLO訓(xùn)練時(shí)有些版本會(huì)直接報(bào)錯(cuò)或者產(chǎn)生NaN梯度。第三是過濾掉寬高為0的非法框這類框在XML里看著正常實(shí)際計(jì)算后就是退化成一條線必須丟棄。3.3 轉(zhuǎn)換后的校驗(yàn)比轉(zhuǎn)換本身更重要轉(zhuǎn)換腳本寫完跑完一遍很多人的習(xí)慣是直接開始訓(xùn)練。我的習(xí)慣是先做一輪完整的校驗(yàn)因?yàn)楦袷睫D(zhuǎn)換是機(jī)械過程出錯(cuò)的概率不高但對(duì)齊錯(cuò)誤會(huì)靜默地腐蝕訓(xùn)練效果。具體做三步校驗(yàn)隨機(jī)抽取20張轉(zhuǎn)換后的txt文件把歸一化坐標(biāo)反算回像素坐標(biāo)畫到原圖上肉眼核對(duì)框的位置是否跟原標(biāo)注一致。檢查每張圖的標(biāo)注類別分布確認(rèn)沒有出現(xiàn)成螺標(biāo)成卵塊這類明顯的類別錯(cuò)位。用腳本統(tǒng)計(jì)所有txt中的歸一化坐標(biāo)是否都在0到1區(qū)間內(nèi)有沒有出現(xiàn)負(fù)數(shù)或大于1的值。這套校驗(yàn)流程我自己在數(shù)據(jù)集上跑過后續(xù)訓(xùn)練中出現(xiàn)的box越界、class_id超范圍這類問題大部分都能在訓(xùn)練前攔住。同時(shí)建議保留一份沒轉(zhuǎn)換的VOC格式作為原始存檔萬一格式有調(diào)整還能重新生成不用重新標(biāo)一遍。4. 用這份數(shù)據(jù)集訓(xùn)練YOLOv5的完整配置4.1 數(shù)據(jù)集目錄的規(guī)范組織方式拿到VOCYOLO雙格式的數(shù)據(jù)集如果要做YOLOv5訓(xùn)練目錄結(jié)構(gòu)是第一步。YOLOv5的datasets目錄推薦按下面這種方式組織datasets/ └── fuluoshou/ ├── images/ │ ├── train/ # 436張 │ └── val/ # 109張 ├── labels/ │ ├── train/ # 對(duì)應(yīng)txt與images文件名一一對(duì)應(yīng) │ └── val/ └── data.yaml我實(shí)際測(cè)試時(shí)是按8:2比例劃分的訓(xùn)練集和驗(yàn)證集也就是436張訓(xùn)練、109張驗(yàn)證。如果對(duì)數(shù)據(jù)劃分沒有特殊要求這個(gè)比例是跑小數(shù)據(jù)集比較合理的起點(diǎn)。注意一個(gè)原則劃分的時(shí)候要用隨機(jī)種子固定sampling避免每次劃分結(jié)果不同導(dǎo)致復(fù)現(xiàn)不了實(shí)驗(yàn)。4.2 data.yaml的配置細(xì)節(jié)這份數(shù)據(jù)集是2個(gè)類別data.yaml文件內(nèi)容可以寫成train: datasets/fuluoshou/images/train val: datasets/fuluoshou/images/val nc: 2 names: [adult_snail, egg_mass]這里有兩個(gè)坑必須提醒。一是train和val的路徑是相對(duì)執(zhí)行train.py時(shí)的工作目錄寫的如果路徑寫錯(cuò)不是報(bào)錯(cuò)而是靜默地把圖片路徑列表讀到空訓(xùn)練會(huì)一直用空的數(shù)據(jù)加載最終效果完全不可用。二是names列表的順序和下標(biāo)必須和轉(zhuǎn)換腳本里class_names的順序完全一致不然就會(huì)出現(xiàn)模型輸出的類別名和實(shí)際框錯(cuò)位看起來AP很高實(shí)際預(yù)測(cè)結(jié)果張冠李戴。4.3 訓(xùn)練參數(shù)與實(shí)測(cè)效果我在一臺(tái)單卡GPU環(huán)境下跑的測(cè)試具體參數(shù)供參考參數(shù)設(shè)置值備注模型YOLOv5s小模型邊緣部署友好img_size640覆蓋面廣兼顧小目標(biāo)batch_size16單卡顯存12GB夠用epochs100小數(shù)據(jù)集不需要太長(zhǎng)輪次優(yōu)化器SGD比Adam在小數(shù)據(jù)上泛化更穩(wěn)數(shù)據(jù)增強(qiáng)mosaic1.0, hsv0.015等開啟默認(rèn)增強(qiáng)小幅調(diào)低hsv跑完的結(jié)果成螺類別的mAP50在0.88左右卵塊類別在0.83左右整體mAP50大概0.86。這個(gè)結(jié)果在545張的小數(shù)據(jù)集上算是不錯(cuò)的水平驗(yàn)證了這個(gè)數(shù)據(jù)集的標(biāo)注質(zhì)量基本在線。但這里要潑一盆冷水mAP50看起來不錯(cuò)主要是這個(gè)指標(biāo)對(duì)框位置寬松IoU只要到0.5就算正確實(shí)際部署在田間密集場(chǎng)景里漏檢率還是會(huì)比指標(biāo)高不少尤其是卵塊。5. 訓(xùn)練過程中暴露出來的問題與修正思路5.1 密集卵塊的小目標(biāo)漏檢我第一次訓(xùn)練完把模型拿到真實(shí)農(nóng)田環(huán)境中做測(cè)試發(fā)現(xiàn)最大的問題不是識(shí)別錯(cuò)而是漏檢。一簇卵塊分得很散的時(shí)候模型經(jīng)常只框住最大的那一團(tuán)旁邊小團(tuán)的直接被忽略。這個(gè)問題的根源不完全是模型容量不夠而是數(shù)據(jù)集里密集目標(biāo)的標(biāo)注框本身有遺漏——標(biāo)注的人只標(biāo)了顯眼的卵塊邊緣零散的小卵團(tuán)沒標(biāo)。模型學(xué)習(xí)的時(shí)候正確地學(xué)會(huì)了只關(guān)注大目標(biāo)。解決這個(gè)問題要從數(shù)據(jù)端下手而不是盲目換大模型。我復(fù)盤后做的是把漏標(biāo)的小卵團(tuán)補(bǔ)標(biāo)了約30個(gè)樣本同時(shí)用標(biāo)注工具確認(rèn)沒有一個(gè)樣本里存在未標(biāo)的大卵團(tuán)。這步做完之后重新訓(xùn)練的漏檢情況明顯改善。這類問題在農(nóng)業(yè)檢測(cè)里非常典型因?yàn)樘镩g目標(biāo)分布天然不均勻標(biāo)注質(zhì)量直接決定模型的天花板。5.2 成螺和卵塊重疊時(shí)的權(quán)重表達(dá)還有一個(gè)有意思的問題有些圖像里成螺就趴在卵塊堆旁邊兩個(gè)目標(biāo)位置挨得非常近甚至部分重疊。這時(shí)候模型容易把兩個(gè)類別混淆輸出結(jié)果里同一個(gè)位置出現(xiàn)兩個(gè)不同類別的框。從數(shù)據(jù)結(jié)構(gòu)的角度分析這種重疊目標(biāo)在小數(shù)據(jù)集里往往樣本量很少模型沒有足夠的正樣本去學(xué)習(xí)一個(gè)位置存在兩個(gè)類別的分布。我自己處理的方法是單獨(dú)把這類重疊樣本挑出來對(duì)成螺邊界做微調(diào)讓成螺框和卵塊框的交疊面積盡量小同時(shí)在訓(xùn)練時(shí)對(duì)這兩個(gè)類別的loss權(quán)重做微調(diào)給卵塊類別稍微高一點(diǎn)的權(quán)重因?yàn)槁褖K是種植戶最關(guān)心的繁殖抑制點(diǎn)。不過說實(shí)話這兩類目標(biāo)重疊導(dǎo)致的誤判在真實(shí)場(chǎng)景里影響不是致命的。植保人員看到框出來的是成螺還是卵塊清理動(dòng)作本來就是一起做的。但從數(shù)據(jù)集打磨的角度這類重疊樣本的標(biāo)注標(biāo)準(zhǔn)需要數(shù)據(jù)集提供方在文檔里明確寫清楚不然每個(gè)人用的時(shí)候理解不一樣訓(xùn)練出來的模型行為就有差異。5.3 關(guān)于545張這個(gè)體量的冷思考最后想認(rèn)真說說這個(gè)數(shù)據(jù)量。545張、2個(gè)類別對(duì)深度學(xué)習(xí)目標(biāo)檢測(cè)來說屬于偏小的量級(jí)。如果有人問我夠不夠我的回答是做前期驗(yàn)證、跑通算法流程、做Demo演示完全夠做真正的田間無人巡檢部署不夠至少還要擴(kuò)充到1500到2000張并且要覆蓋更多天氣條件、光照條件、不同地區(qū)的水生植被背景。但這不是說這個(gè)數(shù)據(jù)集本身價(jià)值低。它最大的價(jià)值在于把最難的從0到1解決了——數(shù)據(jù)收集路徑、類別定義、標(biāo)注規(guī)范、格式轉(zhuǎn)換、訓(xùn)練基線全部跑通。后續(xù)擴(kuò)數(shù)據(jù)時(shí)只需要按照同樣的標(biāo)注規(guī)范去補(bǔ)圖就可以了不用再摸索流程。從做項(xiàng)目的角度我拿到這份數(shù)據(jù)后最大的體會(huì)是小數(shù)據(jù)集不可怕可怕的是小數(shù)據(jù)集還標(biāo)注隨意。在545張的體量下模型對(duì)標(biāo)注噪聲是非常敏感的一個(gè)邊界框偏了幾個(gè)像素在幾百張的樣本里都會(huì)被放大。所以如果有人要在這個(gè)數(shù)據(jù)集基礎(chǔ)上做微調(diào)或擴(kuò)展我的建議是先把標(biāo)注質(zhì)量檢查做扎實(shí)再談模型結(jié)構(gòu)升級(jí)。5.4 一些實(shí)操中的體會(huì)按我自己的使用體驗(yàn)這份VOCYOLO雙格式的數(shù)據(jù)集在實(shí)際部署驗(yàn)證階段幫了不小的忙。首先是YOLO格式能直接喂給YOLOv5/YOLOv8訓(xùn)練省掉了轉(zhuǎn)換的額外工作量其次VOC格式保留了更完整的語義信息萬一后面要用SSD、Faster R-CNN這類框架做對(duì)比實(shí)驗(yàn)也不需要再從零標(biāo)注。雙格式并存的思路值得做數(shù)據(jù)集的開發(fā)者借鑒。另外提一個(gè)小建議使用這個(gè)數(shù)據(jù)集時(shí)盡量在訓(xùn)練前單獨(dú)看看所有圖片和標(biāo)注框的對(duì)應(yīng)關(guān)系尤其是卵塊這類邊界不清晰的目標(biāo)每個(gè)人的標(biāo)注風(fēng)格可能有差異。理解數(shù)據(jù)集的標(biāo)注口徑比直接跑訓(xùn)練重要得多——這也就是標(biāo)題里寫的數(shù)據(jù)集三個(gè)字最容易被低估的部分。模型訓(xùn)練可以重來無數(shù)次但一份標(biāo)注準(zhǔn)確的底數(shù)數(shù)據(jù)集才是這個(gè)項(xiàng)目里真正值錢的東西。