戰(zhàn):7000張數(shù)據(jù)集訓(xùn)練與避坑指南)
簡介這是一份面向目標(biāo)檢測學(xué)習(xí)者的游泳與溺水圖像數(shù)據(jù)集適用于YOLO全系列網(wǎng)絡(luò)訓(xùn)練可支撐水域安全監(jiān)控、溺水預(yù)警等場景的模型開發(fā)與實(shí)驗(yàn)。數(shù)據(jù)已統(tǒng)一處理為YOLO格式標(biāo)注采用classes、x_centre、y_centre、w、h的相對坐標(biāo)類別涵蓋游泳、溺水等3類具體可參考class類別文本文件。資源包共2000個(gè)文件以1999個(gè)txt標(biāo)簽文件和1個(gè)show.py可視化腳本為主壓縮包約300.66MB按YOLOv5目錄結(jié)構(gòu)組織可直接投入檢測訓(xùn)練。數(shù)據(jù)劃分為訓(xùn)練集約5000張、驗(yàn)證集約1400張、測試集約700張均含對應(yīng)標(biāo)簽。show.py可將box目標(biāo)繪制在圖像上便于快速核驗(yàn)標(biāo)注質(zhì)量。目前已有1096人學(xué)習(xí)下載適合需要現(xiàn)成水域檢測數(shù)據(jù)、希望省去標(biāo)注與格式轉(zhuǎn)換環(huán)節(jié)的開發(fā)者與研究者。1. 游泳溺水檢測數(shù)據(jù)集7000 張圖背后的真實(shí)需求與選型判斷溺水檢測這個(gè)方向很多人第一次接觸是因?yàn)橛境匕卜理?xiàng)目。真實(shí)場景里攝像頭架在池邊畫面里幾十號人在水里撲騰救生員盯久了必然疲勞等發(fā)現(xiàn)異常再跳下去黃金救援時(shí)間往往已經(jīng)過去。用目標(biāo)檢測做輔助預(yù)警核心訴求就一個(gè)把「正常游泳」和「溺水掙扎」這兩類目標(biāo)框出來給后端報(bào)警邏輯提供觸發(fā)信號。這個(gè)標(biāo)題講的是一份超過 7000 張圖片帶標(biāo)注的游泳、溺水圖像檢測數(shù)據(jù)集屬于目標(biāo)檢測數(shù)據(jù)集里比較垂直的一類。它適合三類人做智慧場館安防的算法工程師、想拿它練手 yolov8 訓(xùn)練自己數(shù)據(jù)集的學(xué)生、以及需要快速驗(yàn)證溺水識(shí)別可行性的產(chǎn)品團(tuán)隊(duì)。數(shù)據(jù)集本身不解決全部問題但它是這條鏈路里最容易被低估、也最容易翻車的一環(huán)。2. 溺水檢測數(shù)據(jù)集到底長什么樣類別設(shè)計(jì)與標(biāo)注邊界2.1 類別體系為什么多數(shù)方案只分兩類拿到一份溺水?dāng)?shù)據(jù)集第一件事不是急著跑 yolov5 訓(xùn)練自己的數(shù)據(jù)集而是看它的類別定義。游泳溺水場景里常見做法是分兩類swimming正常游泳和 drowning溺水。也有更細(xì)的把「水中站立」「岸邊休息」單獨(dú)拆出來但類別一多標(biāo)注一致性就崩。我一般建議先用兩類跑通基線因?yàn)槟缢畽z測的最終目標(biāo)是二分類預(yù)警中間類別對報(bào)警邏輯沒有直接貢獻(xiàn)反而增加標(biāo)注噪聲。兩類體系下標(biāo)注框的邊界要統(tǒng)一。正常游泳的人框住頭部和肩部即可因?yàn)樯眢w大部分在水下框全身會(huì)引入大量水面反光區(qū)域。溺水目標(biāo)則要框住可見的掙扎肢體和頭部標(biāo)注時(shí)以「人眼能判斷這是一個(gè)人」為準(zhǔn)。這個(gè)規(guī)則必須在標(biāo)注文檔里寫死否則 7000 張圖里會(huì)出現(xiàn)三種框法訓(xùn)練時(shí)模型直接學(xué)懵。提示如果數(shù)據(jù)集沒有附帶標(biāo)注規(guī)范文檔先抽 50 張圖人工核對框的松緊程度再?zèng)Q定是否直接用于訓(xùn)練。2.2 圖像來源與場景分布決定模型泛化上限7000 張圖的來源通常分幾類公開泳池監(jiān)控截圖、網(wǎng)絡(luò)視頻抽幀、以及部分模擬拍攝。來源越雜場景分布越廣模型泛化越好但標(biāo)注質(zhì)量越難保證。你需要關(guān)注幾個(gè)維度室內(nèi)泳池 vs 室外泳池、白天 vs 夜間、水面平靜 vs 水花飛濺、單人 vs 多人。如果數(shù)據(jù)集里 80% 是室內(nèi)白天平靜水面那模型到了室外夜間場景基本報(bào)廢。常見做法是先用腳本統(tǒng)計(jì)圖像尺寸、亮度均值、人數(shù)分布再?zèng)Q定要不要做數(shù)據(jù)增強(qiáng)。下面這段代碼可以快速摸清數(shù)據(jù)集的底細(xì)import os import cv2 import numpy as np from collections import Counter img_dir dataset/images sizes [] brightness [] for name in os.listdir(img_dir): path os.path.join(img_dir, name) img cv2.imread(path) if img is None: continue h, w img.shape[:2] sizes.append((w, h)) # 轉(zhuǎn)灰度算平均亮度判斷白天/夜間分布 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) brightness.append(np.mean(gray)) print(尺寸分布:, Counter(sizes).most_common(5)) print(亮度均值:, np.mean(brightness)) print(亮度低于60的圖片占比:, np.sum(np.array(brightness) 60) / len(brightness))這段代碼的邏輯很直接遍歷圖像目錄記錄每張圖的寬高和灰度均值。尺寸分布告訴你需不需要統(tǒng)一 resize亮度分布告訴你夜間樣本夠不夠。參數(shù)上亮度閾值 60 是我在泳池場景里常用的經(jīng)驗(yàn)值低于這個(gè)值基本算弱光。如果弱光占比超過 20%訓(xùn)練時(shí)就要加亮度擾動(dòng)增強(qiáng)否則模型在夜間直接失效。2.3 標(biāo)注格式VOC 與 YOLO 的轉(zhuǎn)換坑數(shù)據(jù)集常見的標(biāo)注格式有兩種VOC 的 XML 和 YOLO 的 txt。如果你要用 yolov8 訓(xùn)練自己的數(shù)據(jù)集必須轉(zhuǎn)成 YOLO 格式。轉(zhuǎn)換本身不難坑在坐標(biāo)歸一化和類別映射。VOC 的 xmin/ymin/xmax/ymax 是絕對像素坐標(biāo)YOLO 需要?dú)w一化到 0-1 之間的中心點(diǎn)加寬高。下面是一個(gè)轉(zhuǎn)換腳本import xml.etree.ElementTree as ET import os classes [swimming, drowning] xml_dir dataset/annotations out_dir dataset/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 歸一化并轉(zhuǎn)中心點(diǎn)格式 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))邏輯說明先讀 XML 里的圖像寬高再對每個(gè)目標(biāo)框做歸一化。參數(shù)上classes列表的順序必須和訓(xùn)練時(shí)的 data.yaml 完全一致否則類別全錯(cuò)。:.6f保留六位小數(shù)是 YOLO 官方推薦的精度少了會(huì)導(dǎo)致小目標(biāo)框偏移。轉(zhuǎn)換完一定要抽幾張圖用可視化腳本核對我見過太多人轉(zhuǎn)完直接訓(xùn)練結(jié)果 mAP 一直是 0查了半天發(fā)現(xiàn)是寬高寫反了。3. 用 YOLOv8 跑通溺水檢測基線從 data.yaml 到第一輪訓(xùn)練3.1 環(huán)境與目錄結(jié)構(gòu)別在路徑上翻車訓(xùn)練之前先把目錄理清楚。YOLOv8 對目錄結(jié)構(gòu)有固定要求常見做法是dataset/ images/ train/ val/ labels/ train/ val/ data.yaml圖片和標(biāo)簽文件名必須一一對應(yīng)只是擴(kuò)展名不同。劃分比例一般 8:2 或 7:3溺水場景樣本少的話驗(yàn)證集不要低于 15%否則評估指標(biāo)波動(dòng)太大。data.yaml 內(nèi)容如下path: ./dataset train: images/train val: images/val nc: 2 names: [swimming, drowning]nc是類別數(shù)names順序必須和轉(zhuǎn)換腳本里的 classes 一致。路徑用相對路徑絕對路徑換機(jī)器就廢。3.2 訓(xùn)練命令與關(guān)鍵參數(shù)batch 和 imgsz 怎么定環(huán)境裝好后一條命令就能起訓(xùn)yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/drown \ namebaseline參數(shù)逐個(gè)說modelyolov8n.pt是最小的預(yù)訓(xùn)練權(quán)重適合先跑通流程顯存不夠就換 n夠就上 s。imgsz640是默認(rèn)輸入尺寸泳池場景里溺水目標(biāo)通常占畫面比例不大640 夠用但如果你的攝像頭分辨率是 4K建議切到 1280 再試一輪。batch16是 8G 顯存的安全值爆顯存就降到 8。lr00.01是初始學(xué)習(xí)率溺水?dāng)?shù)據(jù)集如果只有 7000 張這個(gè)值偏大可以降到 0.005。patience20是早停輪數(shù)驗(yàn)證集指標(biāo) 20 輪不升就停省時(shí)間。訓(xùn)練過程中重點(diǎn)看三個(gè)指標(biāo)box_loss、cls_loss、mAP50。box_loss 降不下去說明框回歸有問題常見原因是標(biāo)注框太松或太緊。cls_loss 震蕩說明類別不平衡溺水樣本遠(yuǎn)少于游泳樣本時(shí)可以加cls0.8調(diào)高分類損失權(quán)重。mAP50 到 0.7 以上算可用到 0.85 以上算不錯(cuò)但溺水檢測更看召回率漏報(bào)比誤報(bào)代價(jià)大得多。3.3 推理與閾值調(diào)整把 recall 拉上來訓(xùn)練完拿驗(yàn)證集跑推理yolo detect predict \ modelruns/drown/baseline/weights/best.pt \ sourcedataset/images/val \ conf0.25 \ iou0.5 \ saveTrueconf0.25是置信度閾值默認(rèn)值。溺水檢測里我一般會(huì)降到 0.15寧可多報(bào)幾個(gè)假陽性也別漏掉真溺水。iou0.5是 NMS 的 IoU 閾值多人重疊場景可以調(diào)到 0.6減少框被誤刪。推理結(jié)果重點(diǎn)看漏檢把 false negative 的圖挑出來看是標(biāo)注漏了還是模型沒學(xué)到。如果是標(biāo)注漏了補(bǔ)標(biāo)如果是模型沒學(xué)到加同類樣本或做 mosaic 增強(qiáng)。4. 溺水檢測訓(xùn)練避坑5 個(gè)血淚踩坑記錄4.1 水面反光被當(dāng)成溺水目標(biāo)現(xiàn)象模型在平靜水面場景下頻繁誤報(bào)把陽光反射區(qū)域框成 drowning。 原因訓(xùn)練集里溺水樣本多伴隨水花模型學(xué)到了「亮色區(qū)域溺水」的偽特征。 解決在增強(qiáng)里加隨機(jī)亮度擾動(dòng)和對比度擾動(dòng)同時(shí)補(bǔ)一批平靜水面無目標(biāo)的負(fù)樣本讓模型學(xué)會(huì)區(qū)分反光和人。4.2 小目標(biāo)漏檢嚴(yán)重現(xiàn)象遠(yuǎn)處泳池角落的溺水目標(biāo)幾乎全漏。 原因溺水目標(biāo)在 640 輸入下可能只有 20x20 像素YOLOv8 的 P3 特征圖感受野不夠。 解決把 imgsz 提到 1280或者在 data.yaml 里開啟rectTrue保持長寬比避免 resize 后小目標(biāo)進(jìn)一步縮小。也可以換 yolov8s 或加 P2 檢測頭。4.3 類別不平衡導(dǎo)致 drowning 召回率低現(xiàn)象swimming 的 mAP 0.9drowning 只有 0.5。 原因正常游泳樣本遠(yuǎn)多于溺水樣本模型偏向多數(shù)類。 解決用 copy-paste 增強(qiáng)把溺水樣本復(fù)制到不同背景或者訓(xùn)練時(shí)給 drowning 類更高損失權(quán)重。YOLOv8 不直接支持類權(quán)重可以通過過采樣實(shí)現(xiàn)。4.4 驗(yàn)證集和訓(xùn)練集場景重疊現(xiàn)象驗(yàn)證集 mAP 很高實(shí)際部署一塌糊涂。 原因劃分時(shí)隨機(jī)分同一段視頻的相鄰幀同時(shí)進(jìn)了訓(xùn)練和驗(yàn)證模型只是記住了場景。 解決按視頻源或時(shí)間段劃分確保驗(yàn)證集的泳池、光照、角度和訓(xùn)練集不重疊。這是最容易被忽略的坑也是模型上線翻車的主要原因。4.5 標(biāo)注框把水面波紋框進(jìn)去現(xiàn)象模型輸出的框比實(shí)際人體大一圈IoU 低。 原因標(biāo)注時(shí)把水花和波紋一起框了模型學(xué)到的是「大框」。 解決重新核對標(biāo)注規(guī)范框只包人體可見部分。已經(jīng)訓(xùn)完的模型可以用高 IoU 閾值篩一遍預(yù)測框反推標(biāo)注問題。5. 把溺水檢測做到可用的進(jìn)階技巧時(shí)序投票與誤報(bào)壓制單幀檢測永遠(yuǎn)有誤報(bào)溺水是一個(gè)持續(xù)過程不是某一幀的姿態(tài)。我一般會(huì)在檢測后面加一層時(shí)序投票對同一攝像頭連續(xù) 15 幀的檢測結(jié)果做統(tǒng)計(jì)如果 drowning 類在超過 60% 的幀里出現(xiàn)才觸發(fā)報(bào)警。這個(gè)邏輯用 Python 寫起來很簡單from collections import deque class DrownVoter: def __init__(self, window15, ratio0.6): self.window window self.ratio ratio self.buffer deque(maxlenwindow) def update(self, detections): # detections 是當(dāng)前幀的類別列表如 [swimming, drowning] has_drown 1 if drowning in detections else 0 self.buffer.append(has_drown) if len(self.buffer) self.window: return False return sum(self.buffer) / self.window self.ratiowindow15對應(yīng)大約 0.5 秒的視頻30fpsratio0.6是觸發(fā)比例。這兩個(gè)參數(shù)按實(shí)際幀率和場景調(diào)整泳池人多時(shí)調(diào)高 ratio 減少誤報(bào)人少時(shí)調(diào)低 ratio 提高靈敏度。這個(gè)投票器不依賴模型結(jié)構(gòu)任何檢測器輸出都能接。另一個(gè)技巧是負(fù)樣本挖掘。模型上線后把誤報(bào)的圖存下來人工確認(rèn)后加入訓(xùn)練集重新訓(xùn)一輪。我習(xí)慣每兩周做一次三輪之后誤報(bào)率通常能降一半。溺水檢測沒有一勞永逸的模型只有持續(xù)迭代的流程。這套方案值不值得做取決于你的場景有沒有真實(shí)報(bào)警需求如果有7000 張圖起步是夠的但別指望一次訓(xùn)練就上線。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取