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