數(shù)據(jù)集實(shí)戰(zhàn):從標(biāo)注解析到訓(xùn)練調(diào)優(yōu)避坑)
簡(jiǎn)介這份YOLO船舶目標(biāo)檢測(cè)數(shù)據(jù)集面向計(jì)算機(jī)視覺(jué)學(xué)習(xí)者與水面目標(biāo)檢測(cè)開(kāi)發(fā)者適用于需要快速驗(yàn)證檢測(cè)算法、開(kāi)展課程設(shè)計(jì)或科研實(shí)驗(yàn)的場(chǎng)景。數(shù)據(jù)集已按train、val、test完成劃分并附帶data.yaml配置文件類別僅含boat一類yolov5、yolov7、yolov8等主流框架可直接讀取訓(xùn)練省去自行標(biāo)注與整理目錄的繁瑣工作。壓縮包共2000個(gè)文件以1817個(gè)txt標(biāo)注文件、178張jpg圖像和5個(gè)yaml配置為主整體約114.65MB標(biāo)注與圖像一一對(duì)應(yīng)便于直接投入訓(xùn)練與評(píng)估。目前已有1523人學(xué)習(xí)下載配套博文還提供了檢測(cè)結(jié)果參考方便對(duì)照模型表現(xiàn)。內(nèi)容覆蓋皮劃艇、獨(dú)木舟、賽艇、冰川皮劃艇、摩托艇等多種水上場(chǎng)景樣本場(chǎng)景豐富有助于提升模型在不同水域環(huán)境下的泛化能力適合作為船舶檢測(cè)任務(wù)的入門(mén)與進(jìn)階實(shí)踐素材。1. 拆開(kāi) yolo-boat-detect-dataset-1.zip船舶檢測(cè)數(shù)據(jù)集到底裝了什么拿到y(tǒng)olo-boat-detect-dataset-1.zip這個(gè)包第一反應(yīng)不該是解壓完直接train.py開(kāi)跑而是先搞清楚它屬于哪一類船舶目標(biāo)檢測(cè)數(shù)據(jù)集。內(nèi)河航道監(jiān)控、港口靠泊管理、近海執(zhí)法取證這些場(chǎng)景里船是唯一主體但船的尺度跨度極大——遠(yuǎn)洋貨輪在畫(huà)面里可能只占幾十像素近處漁船又能撐滿半屏加上水面反光、船尾浪花、橋墩遮擋通用 COCO 預(yù)訓(xùn)練權(quán)重直接遷移往往 mAP 掉得很難看。這個(gè)數(shù)據(jù)集的價(jià)值就在于把「船」這個(gè)類別單獨(dú)拎出來(lái)做密集標(biāo)注省掉你自己從零標(biāo)幾千張圖的功夫。它適合三類人做水域安防的算法工程師、拿 YOLO 做畢設(shè)的學(xué)生、以及想驗(yàn)證自定義數(shù)據(jù)集訓(xùn)練流程的入門(mén)者。下面按「先看懂?dāng)?shù)據(jù)、再跑通訓(xùn)練、最后調(diào)優(yōu)避坑」的順序拆。2. 船舶數(shù)據(jù)集的結(jié)構(gòu)與標(biāo)注格式先看懂再動(dòng)手2.1 目錄布局與 YOLO 標(biāo)注的對(duì)應(yīng)關(guān)系一個(gè)規(guī)范的 YOLO 格式船舶數(shù)據(jù)集解壓后通常長(zhǎng)這樣images/train、images/val、labels/train、labels/val四個(gè)目錄外加一個(gè)data.yaml。圖片和標(biāo)簽同名不同后綴0001.jpg對(duì)應(yīng)0001.txt。標(biāo)簽文件每行五個(gè)字段class_id x_center y_center width height后四個(gè)都是歸一化到 0~1 的浮點(diǎn)數(shù)。船舶檢測(cè)如果只有「船」一類class_id恒為 0如果區(qū)分貨船、漁船、客船才會(huì)有 0/1/2。先別急著信目錄名用腳本統(tǒng)計(jì)一遍再下結(jié)論。import os, glob from collections import Counter root yolo-boat-detect-dataset-1 for split in [train, val]: imgs glob.glob(f{root}/images/{split}/*) lbls glob.glob(f{root}/labels/{split}/*.txt) print(f{split}: {len(imgs)} images, {len(lbls)} labels) cls_counter Counter() empty 0 for lp in lbls: with open(lp) as f: lines [l.strip() for l in f if l.strip()] if not lines: empty 1 for l in lines: cls_counter[int(l.split()[0])] 1 print(f class distribution: {dict(cls_counter)}, empty labels: {empty})這段腳本做三件事核對(duì)圖片與標(biāo)簽數(shù)量是否一一對(duì)應(yīng)、統(tǒng)計(jì)每個(gè)類別的框數(shù)量、找出空標(biāo)簽文件。空標(biāo)簽在船舶數(shù)據(jù)集里很常見(jiàn)——純水面、純天空的負(fù)樣本圖它們不是臟數(shù)據(jù)反而能壓低誤檢率但數(shù)量超過(guò) 10% 就要警惕是不是標(biāo)注漏了。類別分布嚴(yán)重傾斜比如某類只有幾十個(gè)框時(shí)訓(xùn)練時(shí)該類基本學(xué)不動(dòng)得考慮合并類別或補(bǔ)充采樣。2.2 用可視化腳本驗(yàn)證標(biāo)注質(zhì)量數(shù)字統(tǒng)計(jì)看不出框偏沒(méi)偏必須把框畫(huà)回圖上。船舶標(biāo)注最容易出的問(wèn)題是框貼太緊切掉船頭船尾或者把浪花當(dāng)成船體一部分框進(jìn)去。抽 20 張圖疊框看一眼比看一百行統(tǒng)計(jì)有用。import cv2, glob, random root yolo-boat-detect-dataset-1 samples random.sample(glob.glob(f{root}/images/train/*.jpg), 20) for ip in samples: img cv2.imread(ip) h, w img.shape[:2] lp ip.replace(images, labels).rsplit(., 1)[0] .txt if os.path.exists(lp): for line in open(lp): c, x, y, bw, bh map(float, line.split()) x1, y1 int((x - bw/2)*w), int((y - bh/2)*h) x2, y2 int((x bw/2)*w), int((y bh/2)*h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(fvis_{os.path.basename(ip)}, img)x_center、y_center是框中心點(diǎn)歸一化坐標(biāo)width、height是歸一化寬高反算回像素要先乘回w、h。畫(huà)出來(lái)重點(diǎn)看三種情況框是否完整包住船體、密集停泊時(shí)框有沒(méi)有互相嚴(yán)重重疊、小目標(biāo)船的框是否小到只有幾個(gè)像素。如果發(fā)現(xiàn)大量框只框住船的一半說(shuō)明標(biāo)注規(guī)范不統(tǒng)一這種數(shù)據(jù)直接訓(xùn)練會(huì)讓模型學(xué)出「半條船」的特征驗(yàn)證集 mAP 虛高但實(shí)際部署漏檢嚴(yán)重。2.3 data.yaml 里必須確認(rèn)的三個(gè)字段data.yaml是訓(xùn)練入口字段寫(xiě)錯(cuò)訓(xùn)練直接報(bào)錯(cuò)或靜默學(xué)錯(cuò)類別。核心就三行train、val指向圖片目錄不是標(biāo)簽?zāi)夸沶c是類別數(shù)names是類別名列表。常見(jiàn)翻車點(diǎn)是nc寫(xiě)成 1 但names里列了三個(gè)類或者路徑用了絕對(duì)路徑換臺(tái)機(jī)器就找不到。建議統(tǒng)一改成相對(duì)路徑并在訓(xùn)練前用一行命令確認(rèn)python -c import yaml; dyaml.safe_load(open(data.yaml)); print(d[nc], d[names], d[train])輸出里nc必須等于len(names)train路徑必須真實(shí)存在。這一步花十秒能省掉后面半小時(shí)的報(bào)錯(cuò)排查。3. 用 YOLOv8 跑通船舶檢測(cè)訓(xùn)練從命令到參數(shù)3.1 環(huán)境準(zhǔn)備與最小訓(xùn)練命令船舶數(shù)據(jù)集規(guī)模通常在幾千張量級(jí)單卡 8G 顯存足夠跑 YOLOv8n/s。環(huán)境用 conda 隔離裝 ultralytics 即可它會(huì)把 torch 一起帶上。不要手動(dòng)去裝一堆版本對(duì)不上的依賴這是血淚經(jīng)驗(yàn)。conda create -n boat python3.10 -y conda activate boat pip install ultralytics yolo detect train datadata.yaml modelyolov8s.pt epochs100 imgsize640 batch16modelyolov8s.pt是從 COCO 預(yù)訓(xùn)練權(quán)重起步船舶屬于 COCO 里的 boat 類遷移效果比從頭訓(xùn)好得多。imgsize640是默認(rèn)值但如果你的數(shù)據(jù)集里小目標(biāo)船占比高可以提到 960 或 1280代價(jià)是顯存和耗時(shí)上升。batch16在 8G 卡上跑 640 分辨率基本安全爆顯存就降到 8。epochs100對(duì)幾千張圖通常夠收斂看results.csv里 mAP 曲線平了就早停。3.2 關(guān)鍵參數(shù)怎么調(diào)以船舶場(chǎng)景為例船舶檢測(cè)有幾個(gè)參數(shù)和通用目標(biāo)檢測(cè)不一樣。第一是imgsize內(nèi)河監(jiān)控里船可能只占 30 像素640 下縮到 15 像素以下基本學(xué)不到這種情況必須上 1280。第二是mosaic增強(qiáng)默認(rèn) 1.0 開(kāi)啟它把四張圖拼一張對(duì)小目標(biāo)友好但船舶場(chǎng)景里水面背景高度相似mosaic 可能拼出不合邏輯的水天線建議后期關(guān)掉設(shè) 0再微調(diào)幾十輪。第三是conf閾值船舶誤檢代價(jià)高把浪花當(dāng)船報(bào)警推理時(shí)conf可以設(shè)到 0.4~0.5比默認(rèn) 0.25 更穩(wěn)。yolo detect train datadata.yaml modelyolov8s.pt epochs150 imgsize1280 batch8 mosaic0.5 close_mosaic20 conf0.4close_mosaic20表示最后 20 輪關(guān)閉 mosaic讓模型在真實(shí)分布上收尾這是提升最終精度的常用技巧。mosaic0.5是降低拼接概率不是完全關(guān)。這些值不是死的先按這套跑一版看results.csv再?zèng)Q定往哪調(diào)。3.3 訓(xùn)練過(guò)程看什么指標(biāo)訓(xùn)練日志里重點(diǎn)盯三個(gè)box_loss、cls_loss、mAP50。box_loss降不下去說(shuō)明框回歸有問(wèn)題常見(jiàn)原因是標(biāo)注框尺度差異太大或?qū)W習(xí)率過(guò)高。cls_loss震蕩說(shuō)明類別不平衡或 batch 太小。mAP50是主指標(biāo)船舶單類任務(wù)里它到 0.85 以上算可用0.9 以上算好。如果mAP50高但mAP50-95低說(shuō)明框定位不夠準(zhǔn)多半是標(biāo)注框偏大或偏小。驗(yàn)證集 mAP 漲、訓(xùn)練集 mAP 更高但差距拉大就是過(guò)擬合加數(shù)據(jù)增強(qiáng)或減模型容量。4. 船舶檢測(cè)的避坑與排查五條踩坑記錄4.1 現(xiàn)象訓(xùn)練 loss 正常但驗(yàn)證 mAP 接近 0原因data.yaml里val路徑指向了標(biāo)簽?zāi)夸浕蚩漳夸浤P驮隍?yàn)證時(shí)讀不到圖指標(biāo)自然為 0。解決確認(rèn)val指向images/val且該目錄下圖片數(shù)量與標(biāo)簽數(shù)量一致用第 2 章的統(tǒng)計(jì)腳本再跑一遍。4.2 現(xiàn)象小目標(biāo)船全部漏檢原因imgsize太小小目標(biāo)在特征圖上只剩一兩個(gè)像素anchor 或檢測(cè)頭根本覆蓋不到。解決把imgsize提到 1280同時(shí)檢查數(shù)據(jù)集里小目標(biāo)框的寬高是否小于 8 像素小于這個(gè)值的框建議在標(biāo)注階段就剔除或合并它們對(duì)訓(xùn)練是噪聲。4.3 現(xiàn)象水面反光被大量誤檢為船原因反光區(qū)域紋理和船體相似模型沒(méi)學(xué)到足夠負(fù)樣本。解決往訓(xùn)練集里補(bǔ)純水面、純反光的負(fù)樣本圖空標(biāo)簽比例控制在 5%~10%同時(shí)推理時(shí)把conf提到 0.45 以上。4.4 現(xiàn)象換一臺(tái)機(jī)器訓(xùn)練報(bào)路徑錯(cuò)誤原因data.yaml里寫(xiě)了絕對(duì)路徑或者圖片和標(biāo)簽的相對(duì)層級(jí)和腳本假設(shè)不一致。解決統(tǒng)一用相對(duì)路徑并在訓(xùn)練腳本開(kāi)頭os.chdir到數(shù)據(jù)集根目錄避免路徑拼接歧義。4.5 現(xiàn)象訓(xùn)練到一半顯存爆了原因batch或imgsize設(shè)太大或者workers開(kāi)太多導(dǎo)致內(nèi)存泄漏。解決先把batch減半imgsize不動(dòng)還爆就把workers從 8 降到 4。船舶數(shù)據(jù)集圖片分辨率如果本身是 1920×1080imgsize1280時(shí) batch 8 在 8G 卡上是安全線。5. 把船舶檢測(cè)推到可用驗(yàn)證方法與一個(gè)提點(diǎn)技巧訓(xùn)練完拿到best.pt別只看驗(yàn)證集 mAP 就上線。船舶場(chǎng)景的驗(yàn)證要分場(chǎng)景做把測(cè)試集按「近景大船」「遠(yuǎn)景小船」「密集停泊」「強(qiáng)反光」四類分開(kāi)統(tǒng)計(jì) mAP你會(huì)發(fā)現(xiàn)整體 0.88 的模型在遠(yuǎn)景小船上可能只有 0.6。這種分場(chǎng)景評(píng)估才能暴露真實(shí)短板。推理時(shí)用yolo detect predict跑一批圖人工核對(duì)漏檢和誤檢各占多少漏檢多就補(bǔ)小目標(biāo)數(shù)據(jù)誤檢多就補(bǔ)負(fù)樣本。一個(gè)實(shí)操提點(diǎn)技巧船舶檢測(cè)里「船」和「水面」的邊界往往模糊可以在推理后處理階段加一個(gè)簡(jiǎn)單的長(zhǎng)寬比過(guò)濾。貨船和漁船的長(zhǎng)寬比通常在 2:1 到 6:1 之間如果檢測(cè)框接近正方形長(zhǎng)寬比小于 1.5大概率是把浪花或浮標(biāo)誤檢成了船直接過(guò)濾掉能降一批誤報(bào)。def filter_boxes(boxes, min_ratio1.5, max_ratio8.0): keep [] for b in boxes: x1, y1, x2, y2 b[:4] w, h x2 - x1, y2 - y1 ratio max(w, h) / (min(w, h) 1e-6) if min_ratio ratio max_ratio: keep.append(b) return keepmin_ratio和max_ratio要按你的實(shí)際船型調(diào)內(nèi)河小船可能更接近 2:1遠(yuǎn)洋集裝箱船能到 6:1 以上。這個(gè)過(guò)濾放在 NMS 之后、業(yè)務(wù)邏輯之前代價(jià)幾乎為零但能明顯壓低誤報(bào)。我自己做水域項(xiàng)目時(shí)習(xí)慣在模型上線前先跑一周的離線視頻把誤檢幀全部截出來(lái)看規(guī)律往往能發(fā)現(xiàn)模型在特定光照或特定碼頭背景下有系統(tǒng)性偏差這種偏差靠調(diào)參解決不了只能補(bǔ)數(shù)據(jù)。數(shù)據(jù)集和模型都是半成品真正決定能不能用的是你對(duì)場(chǎng)景的理解。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取