檢測:基于YOLOv8的三分類數(shù)據(jù)集訓練與優(yōu)化)
簡介面向水產(chǎn)養(yǎng)殖智能監(jiān)測與海產(chǎn)加工自動化場景的牡蠣狀態(tài)檢測數(shù)據(jù)集包含1,058張真實養(yǎng)殖環(huán)境圖片覆蓋閉合、過渡、開放三種關鍵生理狀態(tài)適用于構建養(yǎng)殖健康度評估、自動分揀與生態(tài)行為研究模型。壓縮包共2000個文件大小35MB其中940張jpg原圖配合1058個txt標注文件均采用YOLO格式邊界框與類別標簽另附y(tǒng)aml配置和docx說明可快速遷移至YOLOv5/v8等主流檢測框架。數(shù)據(jù)源來自實際水產(chǎn)養(yǎng)殖場景涵蓋不同光照、水質(zhì)條件、擺放角度及生長階段經(jīng)水產(chǎn)專家驗證邊界框定位精準度超過95%可為模型提供可靠的訓練與驗證依據(jù)。已有66人學習下載適合水產(chǎn)科研人員、AI開發(fā)者及農(nóng)業(yè)院校師生用于貝類行為識別與智能養(yǎng)殖技術實踐。1. 牡蠣狀態(tài)檢測一套能直接喂給 YOLOv8 的三分類數(shù)據(jù)集牡蠣養(yǎng)得好不好看殼就行——閉合是正常張開可能在攝食也可能在應激半開半合最難判斷。以前靠人撈起來一個個看現(xiàn)在可以做實時檢測。這套「牡蠣狀態(tài)檢測數(shù)據(jù)集」一共 1,058 張真實養(yǎng)殖場景圖片三分類Oyster-Closed閉合、Oyster-Indeterminate過渡、Oyster-Open開放全部用 YOLO 格式標注能直接拿來訓 YOLOv5、YOLOv8。對做水產(chǎn)養(yǎng)殖監(jiān)測、海產(chǎn)分揀流水線、海洋生態(tài)行為研究的人來說省掉了最苦的采集和標注階段。數(shù)據(jù)來自真實養(yǎng)殖環(huán)境不是實驗室擺拍光照、水質(zhì)、角度都有變化這也意味著模型訓出來能扛現(xiàn)場。我用這類數(shù)據(jù)集的習慣是先盤文件結構再跑一輪基線最后才調(diào)參——這套流程下面完整走一遍。2. 數(shù)據(jù)集目錄與 YOLO 格式先盤清 1,058 張圖的底細拿到數(shù)據(jù)集壓縮包別急著開訓。先把目錄結構、標簽分布、標注質(zhì)量都摸一遍這一步?jīng)Q定了后面調(diào)參是事半功倍還是事倍功半。2.1 三分類邏輯與標注粒度這個數(shù)據(jù)集的核心是三個類別。Oyster-Closed 很好理解外殼完全閉合正常生理狀態(tài)Oyster-Open 是外殼完全張開可能正在攝食也可能處于應激狀態(tài)最麻煩的是 Oyster-Indeterminate開合狀態(tài)不明確標注人員也不確定這類樣本模型最難學。從檢測任務的角度看Open 和 Closed 是清晰的二分類邊界Indeterminate 是模糊地帶。訓練時如果 Indeterminate 樣本占比太高模型會把置信度分散占比太低模型又學不到這個類別。所以拿到數(shù)據(jù)后第一件事就是統(tǒng)計三個類別的樣本量。import os from collections import Counter label_dir labels/train counter Counter() for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), r) as fp: for line in fp: cls_id int(line.strip().split()[0]) counter[cls_id] 1 class_names {0: Oyster-Closed, 1: Oyster-Indeterminate, 2: Oyster-Open} for cls_id in sorted(counter.keys()): print(f{class_names[cls_id]}: {counter[cls_id]} 個實例)這段代碼邏輯很簡單遍歷訓練集標簽目錄下所有 txt 文件每行讀取第一個字段——類別 ID——然后計數(shù)。YOLO 格式每行對應一個目標框格式是class_id x_center y_center width height坐標都歸一化到 0-1 之間。跑完這份統(tǒng)計你就知道類別是不是均衡。如果發(fā)現(xiàn) Indeterminate 數(shù)量明顯少后面訓練就要考慮類別權重或者用數(shù)據(jù)增強去補充。2.2 用腳本核對邊界框質(zhì)量標注質(zhì)量直接影響模型收斂速度。這個數(shù)據(jù)集簡介說邊界框定位精度超過 95%但下載下來的標注文件仍然建議自己做一次體檢。常見的坑有坐標越界x_center width/2 超出 1.0、框太小面積占比低于閾值、空標簽文件圖片存在但沒有對應 txt。def check_labels(img_dir, label_dir): img_files [f for f in os.listdir(img_dir) if f.endswith(.jpg)] orphan_labels 0 bad_boxes 0 for img_file in img_files: label_file os.path.join(label_dir, img_file.replace(.jpg, .txt)) if not os.path.exists(label_file): print(f缺標簽: {img_file}) orphan_labels 1 continue with open(label_file, r) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_boxes 1 continue _, x_c, y_c, w, h map(float, parts) if not (0 x_c 1 and 0 y_c 1 and w 0.01 and h 0.01): bad_boxes 1 print(f異??? {img_file} - {line.strip()}) print(f缺標簽文件數(shù): {orphan_labels}, 異??驍?shù): {bad_boxes}) check_labels(images/train, labels/train)坐標越界通常不會影響訓練因為 YOLO 內(nèi)部會做 clamp但會浪費計算??蛱∫馕吨繕嗽趫D片里占比低這類樣本如果集中在某個類別會拉低該類的 AP。跑完這個腳本基本就能判斷這份數(shù)據(jù)集的標注質(zhì)量能不能直接開訓。提示數(shù)據(jù)集里文件名帶rf.前綴如21_jpg.rf.1f266199a34857311cc37f1dadad1216.jpg這是 Roboflow 導出格式的特征。文件名本身不影響訓練但如果要用預訓練權重建議圖片尺寸統(tǒng)一調(diào)到 640。這個數(shù)據(jù)集一共 1,058 張圖訓練集 929 張、驗證集 113 張、測試集 16 張。對于三分類單類別目標檢測來說數(shù)據(jù)量不算大但勝在場景覆蓋廣實際養(yǎng)殖環(huán)境里的擺放角度、遮擋、光照變化都帶上了。下一步就是組織目錄結構跑第一版模型。3. 用 YOLOv8 訓練牡蠣狀態(tài)檢測從目錄組織到參數(shù)調(diào)優(yōu)數(shù)據(jù)集盤清楚了接下來是訓練流程。我直接用 YOLOv8 做示范因為它的 API 簡單、訓練效率高而且對中小數(shù)據(jù)集的支持比 YOLOv5 更友好。3.1 目錄組織與 yaml 配置YOLO 訓練對目錄結構有硬性要求images 和 labels 各自分 train/val 子目錄圖片和標簽文件名一一對應。把壓縮包里的文件按這個結構重新組織dataset/ ├── images/ │ ├── train/ # 929 張 │ ├── val/ # 113 張 │ └── test/ # 16 張 ├── labels/ │ ├── train/ # 929 個 txt │ ├── val/ # 113 個 txt │ └── test/ # 16 個 txt └── oyster.yamloyster.yaml是訓練入口配置內(nèi)容如下path: /path/to/dataset train: images/train val: images/val test: images/test names: 0: Oyster-Closed 1: Oyster-Indeterminate 2: Oyster-Open關鍵參數(shù)是path必須寫絕對路徑寫相對路徑 YOLOv8 雖然也能識別但有時候會找不到數(shù)據(jù)集目錄。train和val指向的是圖片目錄YOLO 會自動去對應的labels/目錄找同名 txt不需要顯式配置標簽路徑。3.2 訓練命令與關鍵參數(shù)解讀目錄結構沒問題直接跑訓練yolo detect train \ dataoyster.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch16 \ patience30 \ projectoyster_runs \ namebaseline幾個參數(shù)的實際含義過一遍。modelyolov8n.pt是 nano 版本參數(shù)量最小適合數(shù)據(jù)量不大的情況。如果用yolov8s.pt或更大模型1,058 張圖很容易過擬合。imgsz640是 YOLOv8 默認輸入尺寸Resize 到 640 再送進網(wǎng)絡這個尺寸對牡蠣這種中等大小目標夠用。batch16取決于顯存8GB 顯存跑 nano 沒問題顯存小就降到 8。patience30表示驗證集指標連續(xù) 30 輪不提升就提前停止防止無效訓練浪費時間。這里有個選擇邏輯要說明白為什么不直接上yolov8x因為數(shù)據(jù)量擺在這——929 張訓練圖每張圖可能只有 1-3 個牡蠣目標。大模型的擬合能力遠超數(shù)據(jù)能提供的信息量訓練集 loss 能降到很低驗證集 AP 反而上不去這就是過擬合。先跑 nano 拿一個 basline如果發(fā)現(xiàn) mAP 還不錯再考慮用 s 或 m 版本做遷移學習。3.3 類別不均衡下的權重與閾值調(diào)整訓練完第一版觀察驗證集輸出。三分類里最可能出問題的是 Indeterminate 類別如果它的 AP 明顯低于另外兩個類別或者 PR 曲線面積偏小就說明樣本量不夠或者特征不明顯。有兩個方向可以調(diào)。第一是類別權重YOLOv8 的detect命令沒有直接的cls_weights參數(shù)但可以通過自定義數(shù)據(jù)加載器實現(xiàn)或者簡單粗暴一點——把 Indeterminate 的訓練圖復制一份到訓練目錄變相提高采樣率。我一般先用復制的方式做快速驗證有效果再上正規(guī)的權重方案。第二是置信度閾值。默認驗證的時候置信度閾值是 0.001保證召回率但部署時不可能用這么低的閾值。對牡蠣狀態(tài)檢測來說Open 和 Closed 誤判的代價不同——把 Closed 判成 Open 可能只是虛驚一場把 Open 判成 Closed 可能漏掉應激個體。所以部署閾值要按類分別設Open 的閾值可以壓低保召回Closed 的閾值拉高保精確。from ultralytics import YOLO model YOLO(oyster_runs/baseline/weights/best.pt) results model.predict( sourcedataset/images/test, conf0.25, iou0.5, saveTrue )conf0.25是通用閾值實際用的時候我會對每個類別單獨掃閾值寫個循環(huán)從 0.05 到 0.5 步長 0.05找到每個類別 F1-score 最高的點。這一步能直觀看到哪些類別好分、哪些類別模糊也是后續(xù)部署做后處理的依據(jù)。注意如果驗證集 mAP0.5 低于 0.85先別急著調(diào)參。優(yōu)先檢查標簽有沒有錯位、圖片有沒有和標簽對不上、Indeterminate 類別是不是真的學得動。參數(shù)調(diào)整解決不了數(shù)據(jù)問題。4. 避坑排查三分類數(shù)據(jù)集的五個典型翻車現(xiàn)場牡蠣狀態(tài)檢測這類數(shù)據(jù)集有個特點類別語義有連續(xù)性。Closed 到 Open 不是跳變的中間有個逐漸張開的過程Indeterminate 就落在中間。這導致三個類別在特征空間里不是三個清晰的團而是一條連續(xù)的譜帶。以下坑是我實際踩過的按現(xiàn)象到原因到解決寫清楚。4.1 翻車現(xiàn)場一Indeterminate 類 AP 極低訓練時損失震蕩現(xiàn)象訓練到 80 輪以后Closed 和 Open 的 AP 都到了 0.9 以上Indeterminate 死活卡在 0.5 左右PR 曲線形狀奇怪換大模型也沒改善。原因Indeterminate 的標注本身就帶模糊性不同標注人員對「半開半合」的判定標準不一致。模型學到的不是「開合狀態(tài)」這個物理量而是「標注人員覺得像什么」——這天然不可學。另外這類樣本的數(shù)量通常最少梯度更新不穩(wěn)定。解決先看訓練集里 Indeterminate 的具體樣本如果標注一致性確實差就用聚類方法把該類別拆成兩個子類比如「略開」和「略合」分別訓練再合并如果樣本量不足就用復制加隨機增強亮度、輕微旋轉(zhuǎn)補充到其他類別數(shù)量的 70% 以上。注意別用翻轉(zhuǎn)增強——牡蠣左右翻轉(zhuǎn)沒問題但上下翻轉(zhuǎn)會破壞開合特征的語義。4.2 翻車現(xiàn)場二驗證集 mAP 高但實際視頻里瘋狂誤檢現(xiàn)象驗證集 mAP0.5 到 0.93看起來很漂亮一放到養(yǎng)殖池監(jiān)控視頻里連續(xù)出現(xiàn)把水泡、反光、甚至牡蠣的影子識別成牡蠣的情況。原因訓練集圖片是從 Roboflow 導出的靜態(tài)幀背景相對干凈。實際視頻場景里水面波紋、光照角度變化、牡蠣疊放產(chǎn)生的陰影都是訓練分布之外的模式。模型沒見過這些負樣本所以一切都「看起來像牡蠣」。解決收集視頻里誤檢的幀加入訓練集重新訓一輪。更快的辦法是調(diào)高conf閾值到 0.4 以上讓低置信度預測全部丟棄。這個數(shù)據(jù)集圖片本身就是從MP4視頻幀提取的比如Usable2_MP4-3_jpg.rf.*說明原始采集是視頻可以回到原始視頻里取更多幀做負樣本。4.3 翻車現(xiàn)場三Open 類別檢測框明顯偏大框住的不只是牡蠣現(xiàn)象驗證集計算出的檢測框面積比 Closed 類平均大 30% 左右可視化發(fā)現(xiàn) Open 狀態(tài)的牡蠣框內(nèi)混入了大量背景。原因牡蠣完全張開時殼的邊緣比閉合狀態(tài)更舒展標注人員在框選時傾向于把殼的最外側(cè)邊緣包括進來導致框偏大。偏大的框本身不算錯誤但會影響下游的尺寸統(tǒng)計和分揀定位。解決如果做分揀流水線需要精確的牡蠣輪廓而不是邊界框建議用 YOLOv8-Seg 做實例分割或者對檢測框做后處理對 Open 類輸出框的長寬比做統(tǒng)計發(fā)現(xiàn)明顯超過正常分布范圍的框按比例收縮。更直接的方案是標注時規(guī)定框必須緊貼牡蠣外殼輪廓的最外沿寧可切到一點殼邊也不能放大留白。4.4 翻車現(xiàn)場四不同光照條件下的 AP 差異巨大現(xiàn)象按圖片亮度把驗證集分成三組——高曝光、正常、低曝光——分別計算 mAP高曝光組比正常組低了 15 到 20 個百分點。原因數(shù)據(jù)集來自真實養(yǎng)殖場景不同時段的光照差異被完整保留了下來。但模型在訓練時如果暗光樣本不多就會把「亮度特征」當作類別判據(jù)的一部分導致高曝光圖片上誤判。解決訓練時加光度畸變增強YOLOv8 的hsv_h、hsv_s、hsv_v三個增強參數(shù)默認是 0.015、0.7 和 0.4如果光照差異大把hsv_v調(diào)到 0.6hsv_s調(diào)到 0.8讓模型不過度依賴亮度信息。如果還不夠就按亮度對訓練集做分層采樣保證每組光照條件下的樣本量均衡。4.5 翻車現(xiàn)場五導出 ONNX 后精度下降檢測結果全面偏移現(xiàn)象PyTorch 模型驗證 mAP 0.92用 ONNX 導出后在同樣測試集上 mAP 掉到 0.85而且 Open 類的偏移最嚴重。原因YOLOv8 的檢測頭輸出需要經(jīng)過解碼步驟才能變成坐標PyTorch 里是在模型內(nèi)部完成的轉(zhuǎn)到 ONNX 時如果固定了 batch size 或者輸入尺寸解碼邏輯不變但坐標精度會受影響。更隱蔽的問題是導出時沒有固定opset版本某些算子在不同版本實現(xiàn)不一致。解決導出時加上dynamicTrue保持動態(tài)尺寸opset12是兼容性較好的版本。部署端做推理時輸入圖像 Resize 的方式必須和訓練時一致——YOLOv8 訓練時用 letterbox 保持比例推理時如果直接拉伸坐標會整體偏移。寧可多寫幾行預處理代碼也不要在部署端省這一步。yolo export modeloyster_runs/baseline/weights/best.pt formatonnx opset12 dynamicTrue提示部署端最容易翻車的不是模型而是預處理管線。驗證集上一切正常、實機上全歪先檢查圖像的 Resize 方式是否和訓練時一致。5. 進階用法用開合比例與置信度追蹤把檢測結果變成養(yǎng)殖健康度指標模型跑通了檢測框也畫出來了但「能檢測」和「能指導養(yǎng)殖決策」之間還差一步。這一步我通常分三個層次來做置信度加權處理、群體狀態(tài)統(tǒng)計、時間維度上的趨勢判斷。第一層置信度加權替代硬閾值。對單張圖片上的多個目標如果只是簡單統(tǒng)計 Open 類個數(shù)很容易被邊框誤檢干擾。我的做法是對每個目標的置信度做加權計數(shù)——置信度高于 0.7 的算完整權重0.3 到 0.7 之間按置信度本身打折低于 0.3 直接忽略。這樣可以避免「一個高置信度誤檢框抵掉三個低置信度正確框」的問題。單幀的牡蠣目標數(shù)量通常在 3 到 8 個之間人工復核成本不高模型輸出和人工抽查的對比也有條件做。第二層群體狀態(tài)指標。單個牡蠣開合是行為整批牡蠣的開合分布才是健康度指標。我一般按養(yǎng)殖池或養(yǎng)殖籠為統(tǒng)計單元計算三類占比def compute_health_metrics(results, closed_idx0, indeterminate_idx1, open_idx2): total len(results.boxes) if total 0: return {open_ratio: 0, indeterminate_ratio: 0, closed_ratio: 0} open_count 0 indeterminate_count 0 closed_count 0 for box in results.boxes: cls int(box.cls[0]) conf float(box.conf[0]) weight 1.0 if conf 0.7 else conf if cls open_idx: open_count weight elif cls indeterminate_idx: indeterminate_count weight else: closed_count weight return { open_ratio: open_count / total, indeterminate_ratio: indeterminate_count / total, closed_ratio: closed_count / total, }參數(shù)說明conf 0.7給滿權重0.3 conf 0.7按實際置信度打折conf 0.3直接跳過。這套邏輯下Open 占比高不一定是壞事——牡蠣在攝食時就是張開的但要結合時間段判斷。如果某個養(yǎng)殖籠全天的 Open 占比持續(xù)高于 0.5且伴隨 Closed 占比下降大概率是應激反應需要檢查水質(zhì)。第三層時間維度趨勢分析。單張照片只能反映一個瞬間養(yǎng)殖監(jiān)測的價值在于趨勢。我的做法是對同一區(qū)域每 10 分鐘拍一張連續(xù)采樣 1 小時把 6 幀的檢測結果做滑動窗口投票。牡蠣開合是緩慢過程單幀的 Indeterminate 狀態(tài)不需要干預但如果連續(xù) 30 分鐘一個牡蠣都被判為 Indeterminate就觸發(fā)預警人工復核。實現(xiàn)上不需要額外的模型只需要對檢測結果做時間維度的聚合每幀推一個狀態(tài)按狀態(tài)變化頻次做決策。這套方法跑通以后檢測模型就不再是「畫框工具」而是養(yǎng)殖管理系統(tǒng)的數(shù)據(jù)入口。你也可以在這個基礎上繼續(xù)擴展把 Open 占比和水溫、溶解氧做關聯(lián)分析或者把多個攝像頭的檢測結果匯總成整個養(yǎng)殖區(qū)的健康熱力圖。數(shù)據(jù)集的邊界是一個固定的大小但使用場景可以繼續(xù)向兩端延伸。我從這類水產(chǎn)數(shù)據(jù)集上最深的教訓是模型的精度遠不如數(shù)據(jù)的場景覆蓋重要。第一版模型訓完我先花了一周把養(yǎng)殖現(xiàn)場的各種光照條件都拍了一遍用這些真實負樣本做增強效果比任何網(wǎng)絡結構改動都明顯。從那以后我凡是用水產(chǎn)類數(shù)據(jù)集訓練第一件事永遠是拉一遍三個類別的置信度分布先看清楚哪里是模型真能學會的哪里是標注本身的不確定性——先分清楚這兩類后面調(diào)參才不至于自己騙自己。希望這份數(shù)據(jù)集和使用流程能讓你少走幾步我走過的彎路。本文還有配套的精品資源點擊獲取