返回項目列表

協作項目・協助四人畢業專題團隊

YOLO 物件偵測(YOLO26)

  • 協作項目
  • 人工智能

協助一支四人香港都會大學畢業專題團隊,研究視覺模型在水底、惡劣天氣、細小物件及偽裝情況下的偵測能力:借助 Claude Code 建立共用程式碼,並完成全部四個方案的真實數據基準模型。

YOLO26nmAP50 0.584fish 0.878crab 0.000
據程式庫內容繪製的海報

數字紀錄

0.584mAP50,20 個 epoch 的水底基準模型
  • DAWN 惡劣天氣 mAP50,種子 00.635
  • COD10K 偽裝目標 S-measure,種子 00.853
  • VisDrone 細小物件 mAP50,種子 00.291
  • 張 Brackish 圖像按影片片段重新切分14,674
  • 個已撰寫的測試函式(main 分支)84
§ 01

概覽

一支四人香港都會大學畢業專題團隊正研究電腦視覺模型在水底、惡劣天氣、細小物件及偽裝情況下的偵測能力,所有工作集中於同一程式庫,並設有四分頁示範程式。本人借助 Claude Code 負責共用的訓練、評估及結果紀錄程式碼,並完成首個真實數據基準模型:YOLO26n 於 Brackish 訓練 20 個 epoch,單一隨機種子,驗證 mAP50 為 0.584、mAP50-95 為 0.345。同一次訓練中,蟹的 AP 為 0.000,原因至今未明。其後本人透過遙距連線,在隊友效能較佳的電腦上完成全部四個方案的第一階段基準訓練(PR #4,尚未合併),僅使用種子 0:DAWN 的 mAP50 為 0.635、VisDrone 為 0.291,COD10K 的 S-measure 為 0.853。這些只是基準模型:團隊的注意力、知識蒸餾及邊緣方法尚未以真實數據訓練。

§ 02

動態摘要

訓練驗證測試水底天氣細小偽裝
每段影片只會歸入訓練、驗證或測試其中一組,沒有片段橫跨;之後偵測器會在水底、惡劣天氣、細小物件及偽裝情況下接受測試。
§ 03

本人貢獻

  • 共用框架(PR #1,訓練圖表於 PR #2 加入),借助 Claude Code 編寫:資料集清單、統一的訓練、評估及記錄流程、結果 CSV 及訓練圖表。main 分支上的 Python 程式碼(包括起始檔案)合共 5,404 行,另有 84 個測試函式;此數字由原始碼點算得出,並非測試運行結果。
  • 為全部四個方案撰寫初稿(PR #1):各方案訓練腳本、自訂注意力及融合層,以及設有四個分頁的 Gradio 示範程式。其後以真實數據進行的訓練均為基準模型:自訂層尚未以真實數據訓練,示範程式亦只檢查過能否啟動並顯示四個分頁。
  • 水底基準模型(PR #2):YOLO26n 於 Brackish 訓練 20 個 epoch,單一隨機種子,驗證 mAP50 為 0.584、mAP50-95 為 0.345。同一次訓練中,蟹的 AP 為 0.000。最佳檢查點由驗證集選出,因此兩項數字略為樂觀。
  • 全部四個方案的第一階段基準訓練(PR #4,尚未合併),透過遙距連線於隊友的 RTX 5060 Ti 電腦上進行:由排程腳本於一夜之間訓練五個模型,歷時約 3 小時 40 分鐘。僅使用種子 0:Brackish 於 50 個 epoch 的 mAP50 為 0.597,DAWN 第 0 折學生模型為 0.635,VisDrone 為 0.291(AP small 0.083),COD10K 及 CAMO 的 S-measure 分別為 0.853 及 0.854。50 個 epoch 的水底訓練雖然較新,卻未見明顯進步:mAP50-95 由 0.345 變為 0.344,精確率由 0.754 變為 0.580。
  • 資料集修正(PR #1),借助 Claude Code 完成:將 Brackish 按影片片段重新切分,並按類別名稱重新編號 DAWN,再與其 1,027 張圖像的 PASCAL VOC 名稱核對。
  • 項目管治:撰寫計劃、階段關卡、共用檔案負責人安排及團隊工作流程文件。
  • 編寫全部四個方案的訓練驅動腳本,並加入防止重用不同 epoch 數訓練結果的檢查(PR #3)。該 PR 未經合併便已關閉,因此這部分工作沒有進入 main 分支。
§ 04

運作方式

  1. 資料集清單

    以一個 JSON 檔案記錄各資料集的來源、授權備註及下載步驟。

  2. 資料準備

    由腳本準備及檢查 Brackish 與 DAWN,再寫出固定的切分清單,其中 Brackish 按影片片段切分。

  3. 模型定義

    Ultralytics 固定於 8.4.175;以生成的 YAML 檔案及層對應表加入自訂層,毋須修改函式庫。

  4. 共用訓練流程

    方案 A、B、C 經同一流程以 640 像素及 nms=False 訓練;方案 D 則使用獨立的純 PyTorch 流程。

  5. 結果紀錄

    每次訓練新增一行 CSV,記錄指標、偵測頭、GPU、提交編號、函式庫版本及時間。

  6. 圖表

    每次訓練生成一幅 Matplotlib 圖表,顯示損失、mAP 及各類別分數;選定的圖表會連同結果一併提交。

§ 05

資料圖片

各類別 mAP50 及 mAP50-95 的棒形圖:crab(蟹)0.000 及 0.000、fish(魚)0.878 及 0.552、jellyfish(水母)0.812 及 0.512、shrimp(蝦)0.080 及 0.056、small_fish(小魚)0.740 及 0.336、starfish(海星)0.995 及 0.615。
20 個 epoch、單一隨機種子訓練所得的各類別驗證分數。蟹為 0.000,蝦為 0.080,而 0.584 的主要 mAP50 是六個類別的平均值。
六個類別加背景的正規化混淆矩陣。crab(蟹)一欄在 background(背景)處為 1.00;各類別判斷正確的比例為 starfish(海星)1.00、small_fish(小魚)0.90、fish(魚)0.86、jellyfish(水母)0.53、shrimp(蝦)0.08。
同一次訓練的正規化混淆矩陣。真實的蟹全部(1.00)被判為背景,真實的蝦亦有 0.54 同樣遺漏。
20 個 epoch 的驗證 mAP50 及 mAP50-95 折線圖。mAP50 約由第 7 個 epoch 起維持在 0.56 至 0.59 之間,mAP50-95 的最佳點標示於第 16 個 epoch,數值為 0.345。
整次訓練的驗證 mAP。第 16 個 epoch 為最佳檢查點,mAP50-95 為 0.345;約第 7 個 epoch 之後,單靠增加 epoch 已沒有多大幫助。
§ 06

工程札記

札記第 1 則

洩漏整段影片的資料切分

問題
Roboflow 版本的 Brackish 把全部 89 段來源影片同時分到訓練、驗證及測試集。同一片段的畫面出現在切分兩邊,會令驗證分數虛高。
決定
每段影片只放入一個切分。嘗試 500 種片段次序,選出驗證集及測試集最貼近整體類別比例的一種,過程不參考任何模型結果。
結果
14,674 張圖像分為訓練 11,612 張、驗證 1,575 張及測試 1,487 張,沒有片段橫跨兩個切分。不過同一固定鏡頭的不同片段,畫面仍可能相似。

札記第 2 則

在計時所用的偵測頭上量度準確度

問題
計劃原本以為 YOLO26 推論預設不需 NMS。細讀 Ultralytics 8.4.175 後發現,除非另行指定,驗證會使用一對多偵測頭及外部 NMS,而速度測試計時的卻是一對一偵測頭。
決定
在共用的訓練、驗證及預測程式碼中傳入 nms=False,並把所用偵測頭寫入結果列。
結果
main 分支上的兩行結果均記錄 head=one2one(nms=False)。速度尚未量度,因此目前只有準確度這一半的數據。

札記第 3 則

模型始終找不到的類別

問題
在 20 個 epoch 的訓練中,蟹的 AP 為 0.000,正規化混淆矩陣亦顯示真實的蟹全部(1.00)被判為背景。原因並非數據不足:蟹的訓練標註框最多,達 9,706 個。
決定
在 PR #2 中把這項失敗與主要數字並列報告,並建議先檢查蟹的圖像及標註,然後才比較各注意力變體。
結果
原因至今尚未查明。0.584 的 mAP50 是六個類別的平均值,因此同時掩蓋了蝦只有 0.080 的情況。
§ 07

原始碼摘錄

保持每段影片完整underwater/prepareBrackish.py
def assignClipsToSplits(imageCountByClip, seed):
    clipNames = sorted(imageCountByClip)
    random.Random(seed).shuffle(clipNames)
    totalImages = sum(imageCountByClip.values())
    assignment, filledImages = {}, {"val": 0, "test": 0}
    for clipName in clipNames:
        openSplit = next((splitName for splitName in ("val", "test") if filledImages[splitName] < holdOutFraction * totalImages), None)
        assignment[clipName] = openSplit or "train"
        if openSplit: filledImages[openSplit] += imageCountByClip[clipName]
    return assignment

驗證集及測試集按打亂後的片段次序填滿,餘下的全部作為訓練數據,因此每段影片只會整段分配。

準確度與速度共用一個偵測頭common/training.py
# Which YOLO26 head produces the reported accuracy. In Ultralytics 8.4.175, val, predict and the validation inside training use the
# one-to-many head with external NMS unless nms=False is passed (the default nms=None means external NMS). The speed protocol times
# the NMS-free one-to-one head, so accuracy is measured on that same head: every train, val and predict call passes these arguments.
# Models without a one-to-one head (YOLO11, YOLOv8 reference rows) ignore the setting and keep using NMS.
# This is a protocol decision for the whole team: change it only by agreement and record it in plans/decisions.md.
endToEndArguments = {"nms": False}
# Text added to the notes of every results row so the head used for a number is never ambiguous
endToEndNote = "head=one2one(nms=False)"

這項設定集中在一處,其備註會寫入結果列,因此紀錄的數字會標明由哪個偵測頭得出。

§ 08

開發時間線

此項目沒有錄影片段。

  1. 2026年10月23日

    全部四個方案的第一階段基準模型完成紀錄(PR #4,尚未合併)

    里程碑
  2. 2026年10月22日

    水底基準模型併入 main 分支(PR #2)

    里程碑
  3. 2026年10月22日

    全方案訓練驅動腳本完成初稿;PR #3 未經合併便關閉

    里程碑
  4. 2026年10月18日

    20 個 epoch 水底基準模型:mAP50 0.584,單一隨機種子

    里程碑
  5. 2026年10月17日

    加入結果圖表,完成首個真實訓練 epoch

    里程碑
  6. 2026年10月17日

    共用程式碼併入 main 分支(PR #1)

    里程碑
  7. 2026年10月17日

    Brackish 按影片片段重新切分

    里程碑
  8. 2026年10月11日

    四個方案的程式碼完成初稿;開啟 PR #1

    里程碑
  9. 2026年10月11日

    加入項目管治文件

    里程碑