檢測實(shí)戰(zhàn):數(shù)據(jù)集解析與訓(xùn)練避坑指南)
簡介這份YOLO船舶目標(biāo)檢測數(shù)據(jù)集面向計(jì)算機(jī)視覺學(xué)習(xí)者與算法工程師用于訓(xùn)練和驗(yàn)證水面船只識別模型適合從入門練手到模型對比實(shí)驗(yàn)的多種場景。壓縮包共2000個(gè)文件約114.65MB其中1817個(gè)txt為YOLO格式標(biāo)注文件178個(gè)jpg為船舶、皮劃艇、獨(dú)木舟等水上目標(biāo)圖像另有5個(gè)yaml配置文件目錄已按train、valid、test劃分完畢并附帶data.yaml類別僅boat一類yolov5、yolov7、yolov8等主流框架可直接讀取訓(xùn)練無需再自行整理標(biāo)注。目前已有1523人學(xué)習(xí)下載說明該數(shù)據(jù)集在實(shí)際項(xiàng)目中被較多驗(yàn)證。讀者拿到后可直接復(fù)現(xiàn)訓(xùn)練流程觀察單類別小目標(biāo)在復(fù)雜水面背景下的檢測表現(xiàn)也可用于數(shù)據(jù)增強(qiáng)、模型遷移與精度對比實(shí)驗(yàn)配套博文還提供了檢測結(jié)果參考便于快速判斷數(shù)據(jù)質(zhì)量與訓(xùn)練效果。1. 從 yolo-boat-detect-dataset-1.zip 說起船舶目標(biāo)檢測數(shù)據(jù)集到底解決什么問題內(nèi)河航道監(jiān)控、港口靠泊管理、海上執(zhí)法巡查這些場景里最耗人力的環(huán)節(jié)不是看而是盯——盯著幾十路攝像頭畫面判斷哪一幀里出現(xiàn)了船、船在哪、是什么船型。用 YOLO 做船舶目標(biāo)檢測第一道門檻從來不是模型結(jié)構(gòu)而是數(shù)據(jù)。yolo-boat-detect-dataset-1.zip 這類命名方式本質(zhì)上是把船舶這個(gè)垂直場景的標(biāo)注圖像打包成一個(gè)可直接喂給 YOLO 訓(xùn)練流程的數(shù)據(jù)集壓縮包省掉從零采集、標(biāo)注、清洗的幾周時(shí)間。它適合三類人一是剛?cè)腴T YOLO、想找一個(gè)非 COCO 非 VOC 的真實(shí)場景練手的人二是做水域安防、漁政、航運(yùn)監(jiān)管方向需要快速驗(yàn)證檢測可行性的人三是已有模型但想補(bǔ)充船舶類別樣本、做增量訓(xùn)練的人。這個(gè)數(shù)據(jù)集能解決的核心問題是讓模型認(rèn)識船但解決不了讓模型認(rèn)識你的船——后者要靠你自己的場景數(shù)據(jù)做微調(diào)。理解這個(gè)邊界比急著解壓跑訓(xùn)練更重要。2. 拆開壓縮包之前船舶檢測數(shù)據(jù)集的結(jié)構(gòu)與 YOLO 格式對齊2.1 一個(gè)目標(biāo)檢測數(shù)據(jù)集到底由什么組成不管壓縮包叫什么名字一個(gè)能直接用于 YOLO 訓(xùn)練的檢測數(shù)據(jù)集內(nèi)部結(jié)構(gòu)基本逃不出這幾樣?xùn)|西圖像文件、標(biāo)注文件、類別定義文件、以及劃分訓(xùn)練/驗(yàn)證/測試的索引。圖像是原始輸入標(biāo)注是監(jiān)督信號類別定義決定輸出頭的維度索引決定每輪 epoch 喂哪些數(shù)據(jù)。船舶檢測的特殊性在于目標(biāo)形態(tài)差異極大。遠(yuǎn)洋貨輪在畫面里可能只占幾十個(gè)像素近岸漁船可能橫跨半個(gè)畫面船頭船尾的朝向、水面反光、霧天低對比度都會(huì)讓標(biāo)注邊界框的松緊程度不一致。所以拿到一個(gè)船舶數(shù)據(jù)集第一件事不是看圖片好不好看而是看標(biāo)注框是否貼合、類別是否統(tǒng)一、有沒有把船和船的倒影標(biāo)成兩個(gè)目標(biāo)。常見做法是先統(tǒng)計(jì)一遍標(biāo)注分布每張圖平均幾個(gè)目標(biāo)、目標(biāo)框的寬高比分布、小目標(biāo)面積小于 32×32 像素占比。這三個(gè)數(shù)字直接決定你后面選哪個(gè) YOLO 版本、用多大的輸入分辨率、要不要開多尺度訓(xùn)練。2.2 YOLO 標(biāo)注格式與常見數(shù)據(jù)集格式的差異YOLO 用的是歸一化中心點(diǎn)格式每行一個(gè)目標(biāo)類別索引 中心x 中心y 寬 高四個(gè)坐標(biāo)都除以圖像寬高落在 0~1 之間。而很多公開數(shù)據(jù)集給的是 VOC 的 XML左上角右下角絕對坐標(biāo)或 COCO 的 JSON絕對坐標(biāo)類別 id。直接混用會(huì)導(dǎo)致框全錯(cuò)位這是新手最常翻的車。下面這段腳本用來檢查一個(gè) YOLO 格式數(shù)據(jù)集的基本健康度包括類別分布、框尺寸分布和越界框import os import glob from collections import Counter # 數(shù)據(jù)集根目錄按你解壓后的實(shí)際路徑改 DATA_ROOT ./yolo-boat-detect-dataset-1 LABEL_DIR os.path.join(DATA_ROOT, labels) IMG_DIR os.path.join(DATA_ROOT, images) cls_counter Counter() box_areas [] bad_lines 0 for txt in glob.glob(os.path.join(LABEL_DIR, **, *.txt), recursiveTrue): with open(txt, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: bad_lines 1 continue c, x, y, w, h parts x, y, w, h map(float, (x, y, w, h)) # 越界檢查歸一化坐標(biāo)必須在 0~1 if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad_lines 1 continue cls_counter[int(c)] 1 box_areas.append(w * h) print(類別分布:, dict(cls_counter)) print(異常標(biāo)注行數(shù):, bad_lines) if box_areas: small sum(1 for a in box_areas if a 0.01) # 相對面積小于1%視為小目標(biāo) print(f目標(biāo)總數(shù): {len(box_areas)}, 小目標(biāo)占比: {small/len(box_areas):.2%})邏輯說明腳本遍歷 labels 目錄下所有 txt逐行解析五元組。越界檢查是重點(diǎn)因?yàn)闃?biāo)注工具導(dǎo)出時(shí)偶爾會(huì)產(chǎn)生超出圖像邊界的框YOLO 訓(xùn)練時(shí)這類框會(huì)被靜默裁剪或直接報(bào)錯(cuò)。參數(shù)上0.01這個(gè)閾值是相對面積對應(yīng)約 640 分辨率下 64×64 像素的框你可以按自己場景調(diào)整。類別分布如果出現(xiàn)某個(gè)類別數(shù)量為 0 或極端不均衡就要考慮是不是標(biāo)注時(shí)漏標(biāo)了。2.3 訓(xùn)練/驗(yàn)證集劃分與 data.yaml 的寫法YOLO 系列v5/v8/v11 都類似靠一個(gè) yaml 文件告訴訓(xùn)練器去哪找數(shù)據(jù)、有幾個(gè)類別、類別叫什么。這個(gè)文件寫錯(cuò)訓(xùn)練要么直接崩要么類別名對不上導(dǎo)致推理結(jié)果全是錯(cuò)的。# data.yaml path: ./yolo-boat-detect-dataset-1 # 數(shù)據(jù)集根目錄 train: images/train # 訓(xùn)練集圖像相對路徑 val: images/val # 驗(yàn)證集圖像相對路徑 test: images/test # 可選 nc: 1 # 類別數(shù)船舶檢測通常先做單類 names: [boat] # 類別名順序必須和標(biāo)注里的類別索引一致參數(shù)說明path是根目錄train/val是相對 path 的子路徑Y(jié)OLO 會(huì)自動(dòng)把路徑里的images替換成labels去找標(biāo)注。nc和names長度必須一致且names的順序要和標(biāo)注文件里類別索引 0、1、2 對應(yīng)。如果數(shù)據(jù)集里船分了貨船/漁船/客船多類這里就要相應(yīng)改成多類但建議第一次訓(xùn)練先用單類跑通確認(rèn)流程無誤再細(xì)分。提示劃分訓(xùn)練驗(yàn)證集時(shí)不要隨機(jī)打散同一段視頻的連續(xù)幀。同一艘船相鄰幾幀幾乎一樣隨機(jī)劃分會(huì)導(dǎo)致驗(yàn)證集里出現(xiàn)訓(xùn)練集見過的船指標(biāo)虛高。按視頻源或時(shí)間段劃分更靠譜。3. 用 YOLO 在本地跑通船舶檢測的最小訓(xùn)練流程3.1 環(huán)境準(zhǔn)備與依賴安裝訓(xùn)練船舶檢測模型硬件上有一塊 8GB 顯存的顯卡就能起步batch 設(shè)小一點(diǎn)即可。軟件上Python 3.8~3.11 都行CUDA 版本要和 PyTorch 對應(yīng)。我一般用 conda 建獨(dú)立環(huán)境避免和系統(tǒng)里的包打架。conda create -n boat_yolo python3.10 -y conda activate boat_yolo # 按你的 CUDA 版本選對應(yīng)命令這里以 CUDA 11.8 為例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyyaml邏輯說明ultralytics 這個(gè)包把 YOLOv8/v11 的訓(xùn)練、驗(yàn)證、導(dǎo)出都封裝好了一條命令能跑完整個(gè)流程。CUDA 版本一定要和驅(qū)動(dòng)匹配裝錯(cuò)了會(huì)在torch.cuda.is_available()返回 False訓(xùn)練自動(dòng)掉到 CPU 上速度差幾十倍。裝完先跑一句驗(yàn)證import torch print(torch.cuda.is_available(), torch.cuda.get_device_name(0))如果輸出 False先別急著往下走回去查驅(qū)動(dòng)和 CUDA 版本對應(yīng)關(guān)系這是最常見的翻車點(diǎn)。3.2 從預(yù)訓(xùn)練權(quán)重開始微調(diào)船舶檢測數(shù)據(jù)量通常不大從零訓(xùn)練容易過擬合標(biāo)準(zhǔn)做法是從 COCO 預(yù)訓(xùn)練權(quán)重開始微調(diào)。COCO 里本來就有 boat 類所以遷移效果一般不錯(cuò)。yolo detect train \ data./data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ project./runs/boat \ nameexp1參數(shù)說明modelyolov8n.pt是最小的 nano 版本速度快、適合先驗(yàn)證流程確認(rèn)有效再換 s/m/l。imgsz640是輸入分辨率船舶小目標(biāo)多的話可以提到 960 或 1280但顯存占用和訓(xùn)練時(shí)間會(huì)明顯上升。lr00.01是初始學(xué)習(xí)率微調(diào)場景下如果發(fā)現(xiàn) loss 震蕩厲害可以降到 0.001。patience20表示驗(yàn)證指標(biāo) 20 輪不提升就早停省時(shí)間。batch16在 8GB 顯存上跑 640 分辨率一般夠用爆顯存就減到 8。訓(xùn)練過程中重點(diǎn)看三個(gè)東西box_loss 是否穩(wěn)定下降、mAP50 是否在漲、驗(yàn)證集的預(yù)測圖里有沒有大量漏檢。如果 box_loss 降但 mAP 不漲多半是過擬合或標(biāo)注有問題如果一開始 loss 就是 nan檢查學(xué)習(xí)率是不是太大或標(biāo)注里有非法值。3.3 推理與結(jié)果驗(yàn)證訓(xùn)練完在 runs/boat/exp1/weights/ 下會(huì)有 best.pt 和 last.pt。best.pt 是驗(yàn)證指標(biāo)最好的那輪推理一般用它。yolo detect predict \ model./runs/boat/exp1/weights/best.pt \ source./yolo-boat-detect-dataset-1/images/test \ conf0.25 \ iou0.45 \ saveTrue參數(shù)說明conf0.25是置信度閾值低于它的框不輸出船舶場景如果漏檢嚴(yán)重可以降到 0.1 看看但誤檢會(huì)變多。iou0.45是 NMS 的 IoU 閾值兩艘船靠得近時(shí)這個(gè)值調(diào)太大會(huì)把其中一艘壓掉調(diào)太小會(huì)重復(fù)框同一艘船。這兩個(gè)參數(shù)沒有萬能值要拿幾十張典型圖反復(fù)試。驗(yàn)證階段別只看 mAP 數(shù)字。把預(yù)測結(jié)果按正確檢測/漏檢/誤檢/重復(fù)框分類各挑幾張看比盯著一個(gè)小數(shù)點(diǎn)有用得多。船舶檢測里最常見的誤檢是把水面反光、橋墩、浮標(biāo)當(dāng)成船這類問題靠調(diào)閾值解決不了得補(bǔ)負(fù)樣本。4. 船舶檢測訓(xùn)練避坑5 個(gè)真實(shí)踩過的坑4.1 坑一驗(yàn)證集 mAP 很高實(shí)際部署全是漏檢現(xiàn)象訓(xùn)練日志里 mAP50 到 0.9 以上但拿現(xiàn)場視頻一跑遠(yuǎn)處的船基本檢測不到。原因驗(yàn)證集和訓(xùn)練集來自同一批圖像分布一致且驗(yàn)證集里可能沒有足夠的小目標(biāo)樣本。mAP 是在驗(yàn)證集上算的驗(yàn)證集不代表真實(shí)場景。解決單獨(dú)準(zhǔn)備一批來自實(shí)際部署場景的圖像做測試集不參與訓(xùn)練和驗(yàn)證。如果這批圖上指標(biāo)掉得厲害說明模型沒泛化到你的場景需要補(bǔ)充該場景的標(biāo)注數(shù)據(jù)做微調(diào)而不是繼續(xù)在原有數(shù)據(jù)上加 epoch。4.2 坑二標(biāo)注框把船的倒影一起框進(jìn)去現(xiàn)象模型在水面平靜、倒影清晰的圖上會(huì)把倒影也檢測成船或者框比實(shí)際船大一倍。原因標(biāo)注階段標(biāo)注員圖省事把船和倒影框在一起。模型學(xué)到的是船倒影的組合特征遇到倒影就觸發(fā)。解決重新檢查標(biāo)注倒影不屬于目標(biāo)本體框應(yīng)只貼船體。如果倒影樣本很多可以把倒影單獨(dú)標(biāo)一個(gè)類或作為負(fù)樣本讓模型學(xué)會(huì)區(qū)分。這個(gè)坑在船舶數(shù)據(jù)集里極其常見拿到任何船舶數(shù)據(jù)都要先抽查這一點(diǎn)。4.3 坑三類別索引和 names 順序?qū)Σ簧犀F(xiàn)象訓(xùn)練不報(bào)錯(cuò)但推理時(shí)所有船都被標(biāo)成錯(cuò)誤的類別名或者置信度異常低。原因data.yaml 里 names 的順序和標(biāo)注文件里的類別索引不一致。比如標(biāo)注里 0 是貨船、1 是漁船但 yaml 里寫反了。解決寫 yaml 前先統(tǒng)計(jì)標(biāo)注里出現(xiàn)過的所有類別索引按索引順序填 names。改完 yaml 后重新訓(xùn)練不要指望在舊權(quán)重上改名字就能對。4.4 坑四圖像和標(biāo)注文件名不匹配現(xiàn)象訓(xùn)練時(shí)警告 image not found 或 label missing大量樣本被跳過實(shí)際參與訓(xùn)練的數(shù)據(jù)遠(yuǎn)少于預(yù)期。原因YOLO 靠文件名不含擴(kuò)展名匹配圖像和標(biāo)注。如果圖像是boat_001.jpg標(biāo)注是boat_001.txt沒問題但如果標(biāo)注是boat_1.txt或帶了額外后綴就匹配不上。解決寫個(gè)腳本批量檢查圖像和標(biāo)注的文件名集合是否一致差集就是問題文件。統(tǒng)一重命名后再訓(xùn)練。這個(gè)檢查應(yīng)該在訓(xùn)練前做而不是等訓(xùn)練日志里出現(xiàn)警告才回頭查。4.5 坑五小目標(biāo)檢測效果差就盲目加大模型現(xiàn)象遠(yuǎn)處小船檢測不到第一反應(yīng)是換更大的模型n 換到 x結(jié)果速度慢了好幾倍小目標(biāo)召回只提升一點(diǎn)點(diǎn)。原因小目標(biāo)檢測的瓶頸往往在輸入分辨率不在模型容量。640 分辨率下一個(gè) 20 像素的船縮到網(wǎng)絡(luò)深層只剩幾個(gè)像素再大的模型也提取不出有效特征。解決優(yōu)先提高輸入分辨率640→1280或者用帶 P2 小目標(biāo)檢測層的 YOLO 變體。分辨率提升對小目標(biāo)的收益通常比換大模型明顯代價(jià)是顯存和推理時(shí)間。先試分辨率再考慮模型規(guī)模。5. 把船舶檢測做扎實(shí)從跑通到可用的幾個(gè)進(jìn)階技巧跑通一次訓(xùn)練只是起點(diǎn)。要讓船舶檢測真正可用有幾個(gè)我反復(fù)驗(yàn)證過的做法。第一是分場景評估不要只看總體 mAP。把測試集按近景/遠(yuǎn)景白天/夜間平靜水面/有浪分組分別算指標(biāo)。你會(huì)經(jīng)常發(fā)現(xiàn)總體 0.85但夜間只有 0.4。這種細(xì)分指標(biāo)才告訴你下一步該補(bǔ)哪類數(shù)據(jù)。第二是善用負(fù)樣本。船舶檢測最大的誤檢來源是水面反光、浮標(biāo)、橋墩、岸上建筑。專門收集一批不含船但包含這些干擾物的圖像標(biāo)注為空沒有標(biāo)注行加入訓(xùn)練集。這比調(diào)置信度閾值有效得多??諛?biāo)注文件在 YOLO 里是合法的表示這張圖沒有目標(biāo)。第三是推理時(shí)的切片策略。對于高分辨率監(jiān)控畫面整圖縮到 640 會(huì)丟失小目標(biāo)。常見做法是把大圖切成有重疊的小塊分別推理再合并結(jié)果。下面是一個(gè)簡化的切片推理思路import cv2 import numpy as np def sliced_infer(model, img, slice_size640, overlap128): h, w img.shape[:2] step slice_size - overlap all_boxes [] for y in range(0, h, step): for x in range(0, w, step): patch img[y:yslice_size, x:xslice_size] if patch.shape[0] slice_size or patch.shape[1] slice_size: patch cv2.copyMakeBorder( patch, 0, slice_size-patch.shape[0], 0, slice_size-patch.shape[1], cv2.BORDER_CONSTANT, value(114,114,114)) results model(patch, conf0.25, verboseFalse) for box in results[0].boxes: xyxy box.xyxy[0].cpu().numpy() xyxy[[0,2]] x # 還原到原圖坐標(biāo) xyxy[[1,3]] y all_boxes.append((xyxy, float(box.conf[0]))) # 跨切片去重這里用簡單 IoU 過濾實(shí)際可用 NMS return all_boxes邏輯說明把大圖按固定步長切成小塊每塊單獨(dú)推理再把框坐標(biāo)加回偏移量還原到原圖。overlap 是為了避免目標(biāo)正好落在切片邊界被切斷。最后需要做一次全局 NMS 去掉重疊切片產(chǎn)生的重復(fù)框。參數(shù)上slice_size 和模型訓(xùn)練分辨率一致overlap 一般取 slice_size 的 15%~25%。這個(gè)策略對高分辨率監(jiān)控畫面提升明顯代價(jià)是推理時(shí)間隨切片數(shù)線性增長。第四是模型導(dǎo)出。訓(xùn)練用的 PyTorch 權(quán)重不適合直接部署通常導(dǎo)出成 ONNX 或 TensorRT。導(dǎo)出時(shí)注意輸入尺寸要和訓(xùn)練時(shí)一致動(dòng)態(tài) batch 按部署需求設(shè)。導(dǎo)出后一定要拿同一批圖對比 PyTorch 和導(dǎo)出模型的輸出確認(rèn)沒有精度損失再上線。最后說個(gè)習(xí)慣每次訓(xùn)練前把 data.yaml、標(biāo)注統(tǒng)計(jì)結(jié)果、訓(xùn)練命令記在一個(gè)文本文件里和權(quán)重放一起。過兩周回頭看你會(huì)慶幸自己留了這些記錄。船舶檢測這行數(shù)據(jù)版本和參數(shù)組合一多沒有記錄就是黑匣子出了問題連后悔藥都沒得吃。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取