標記狀況複查 — 陞訊生成資料 + 跌倒補料

2026-08-07 01:40 · session:模型訓練 · 資料源 CVAT2 即時查詢(非快取)

本頁盤點的是 8/6–8/7 這波新建的全部 CVAT task,數字一律回 CVAT API 現查, 不採用上傳腳本自己印的結果。每一段都附「怎麼驗的」,可自行複核。

🔴 過程中查出一個會影響上版決策的問題:全域 person 模型 v20260806_640 的訓練資料有 1,944 張圖的標註沒進去,導致它在陞訊 CH01 完全失效。詳見第八節。 demo 要用的模型部署清單見第十節。 電子圍籬 CH02–05 另有一個資料缺口,見第十一節。 車輛誤判的實地確認見第十二節。 推論層級的證偽測試見第十三節

一、總覽

資料類別task標註框 subset 分布可訓練狀態
陞訊 CH01 真實
人 + 車輛,標記師已確認
66731805 Test 104 · Train 462 · Validation 107 全部 acceptance
陞訊生成 v2
安全裝備 CH06–10 + 電子圍籬 CH02–05
27350387 Test 53 · Train 245 · Validation 52 全部 acceptance
陞訊生成 v1
多樣性不足版本,已被 v2 取代
90224256 Test 60 · Train 134 · Validation 30 90 個 job 未 acceptance
跌倒補料
新東陽 ch12 / ch21 真實負樣本
24922015 Test 492 全部 acceptance
合計 1251739 4463
要處理:生成 v1 的 90 個 task(224 張)全部停在未 acceptance
匯出時會被規則 3 濾掉,等於這批資料完全沒進訓練。
這個結果是對的——v1 的 prompt 沒指定人物外觀,模型反覆生出同一個人, 多樣性不足,本來就不該用;v2 就是為此重做的。
但成因是 v1 上傳腳本漏了設 acceptance 這段,不是刻意排除,屬於「僥倖正確」。
建議:直接刪掉這 90 個 task。留著只會讓日後盤點時誤以為有 224 張可用資料, 而且它們被切成每個 2–3 張圖的碎 task,翻頁很痛苦。

二、安全帽 / 反光背心的資料正確性

你問過純生成的資料要怎麼確保裡面的人裝備狀態是對的。做法是三層,每層都留下可複核的證據:

第 1 層 — 標籤來自 prompt,不是來自判讀

每張圖是「先決定要 hat=yes/vest=no,再叫模型照這個生」, 標籤在圖片存在之前就確定了,不存在「看圖猜裝備」的誤判空間。 四種組合的張數在送生成時就分配好(見下方分布)。

第 2 層 — 目視逐張確認

把 CVAT 裡的實際標註畫回圖上,依宣稱的標籤分組排列。 同一列的標籤相同,混進不符的一眼就看得出來。抽 24 張(每組合 6 張):

生成安全裝備標籤複查

上到下:hat=no/vest=no、hat=no/vest=yes、hat=yes/vest=no、hat=yes/vest=yes。 綠線是 SAM3 自動框的人形輪廓。24 張全部相符,輪廓也貼合。

第 3 層 — 拿真實影像測

純生成資料訓出來的模型,用 CH10 現場錄影的 299 個真實 person crop 測試, 抽驗 24 個全對,包含「頭上包白布 / 毛巾」這類難例都沒被騙。 這是最硬的一層——訓練資料和測試資料完全沒有共同來源。

多樣性有沒有真的買到

v2 花的 $27 買的就是多樣性,所以要驗。把 120 個 hard_hat=yes 的頭部區域取出做色相統計:

安全帽顏色張數實際占比prompt 設定
黃色5445.0%60%
橙/黃色2218.3%
白色1411.7%20%
藍色119.2%15%
紅色86.7%5%
綠色119.2%

黃 45.0% + 橙黃 18.3% = 63.3%,對上 prompt 設的 60%;紅 6.7% 對 5%。 白 11.7% 與藍 9.2% 略低於設定值。「綠色」是頭部裁切帶到螢光背心造成的誤判,不是真有綠色安全帽。 結論:加權有生效,實際產出 6 種帽色。

安全帽顏色多樣性

隨機抽 48 個頭部區域。人臉、年齡、帽色都有變化——v1「反覆同一個人」的問題已解決。

三、跌倒補料:找到一個真實的誤報機制

這批 1,010 張真實影像原本只是要補三支鏡頭的測試樣本,過程中撈到比補資料更有價值的東西。

發現:模型把「bbox 寬扁」當成跌倒的判斷捷徑
用 factory_ppe_v20260806 的 fall head 對 2,015 個 person crop 全量推論, 機率 ≥0.3 的 17 個全部集中在新東陽 ch21 這一支。逐張看過之後, 17 個全是誤判——那是一支正上方俯視的鏡頭,框出來的人只有頭頂加肩膀, bbox 是 45×46、57×44、76×40 這種尺寸,寬高比 0.98–1.90, 與真正躺倒的人幾何特徵完全一致。模型抄了這條捷徑,俯視角的站立行人就中招。
跌倒誤判樣本

全部 17 個被 flag 的樣本(數字為模型輸出的跌倒機率)。可以看到都是坐著或走動的人,從正上方拍。

鏡頭抽幀有人person 框 需複核CVAT task
新東陽ch1240022511610#11568
新東陽ch2140026785417#11569
愛烙達頻道15210 00 無法建立
愛烙達頻道15 補不了測試資料
抽的 210 張(涵蓋 7/1–7/3 全時段)一個人都沒有。 已先照空白畫面的老問題查過像素 std(21–52,亮度 83–145),畫面是正常的,不是 RTSP 掉線。 實際內容是一段沒人走的樓梯間走廊,只有消防栓和地板。
這支鏡頭一週零人次,靠真實資料補測試集這條路走不通。 需要你決定:(a) 拉更長時間的錄影再抽,(b) 用生成資料補, 或 (c) 認定這種無人走廊本來就不需要跌倒偵測,從清單移除。
方法論限制,用這批資料評估時請務必注意
負樣本是「模型判定不是跌倒 + 人工複核高分區」得出的,不是獨立標註。 因此這批只能用來量測誤報(precision)不能用來量測漏報(recall)——拿它評估同一個模型的 recall 是循環論證。 這點也寫進了 CVAT task 的描述欄。

四、屬性標註分布

屬性值分布備註
age0 1436CH01 person 標註帶的預設值,訓練未使用
fallno 2015全部 no,見上一節說明
genderunset 1436同上,訓練未使用
hard_hatno 593 · yes 405生成資料四組合刻意配置,非自然分布
safety_vestno 837 · yes 152yes 偏少,因 CH01 真實資料多為未穿背心

未列出的屬性一律為 unknown,依規則 12 不參與 loss 與評估。

五、CH01 真實資料目視抽查

CH01 標註複查

藍框 = person,橘框 = 車輛類。標題列出該張圖的 subset 與各類別框數。

六、健檢結果

檢查項目結果
subset 有無落在 Default/空值(會被當 Train)無,全部明確指定
video-mode task(meta frames < size,會只匯出第一張)
零標註 task
job 停在非 acceptance生成 v1 的 90 個 task(見第一節)
同鏡頭跨 split 造成 leak跌倒補料全放 Test,未混入 Train

七、待你決定

八、🔴 全域 person 模型 v20260806_640 被污染,不可上版

本來只是要決定「CH01 的人員偵測用全域版還是專用版」,量下去發現全域版在 CH01 上 完全失效:把信心門檻降到 0.01 仍然一個框都不出。 同一張圖,其他 17 個 person 模型抓到 43–108 個人,原廠 yolo26m 抓 153 個。

先分離變因,確認不是影像的問題

測試偵測框數結論
其他 17 個 person 模型 → 同一張 CH01 圖43–108影像正常
原廠 yolo26m → 同一張 CH01 圖153影像正常
person640 → CH01 原檔 / 重新解碼 / numpy / 縮放0 / 0 / 0 / 0四種輸入全失效
person640 → 自己的 val 圖10–15模型本身沒壞(conf 0.95+)
person640 → 新東陽跌倒補料 / 陞訊 CH103–32 / 1–4其他場景正常

失效範圍恰好是 CH01 這一個場景,其餘全部正常。這種「單一場景歸零」指向訓練資料。

根因:1,944 張圖被當成「確定沒有人」餵進訓練

person_v20260806 共 41,296 張圖、802 個 task。其中 95 個 task 在磁碟上是整個零標註, 回 CVAT 逐一核對後發現 25 個 task 在 CVAT 明明有標註—— 它們的 label 檔是空的,而空 label 在 YOLO 是合法的「背景圖」, 等於明確告訴模型這些畫面裡沒有人。

類別task遺失的框處置
時序問題:export 跑在標註完成之前
資料集 08-06 03:58 建立,CH01 標註 06:17 才完成
3448 1101 重跑 export 即可
真的漏掉:標註 6–7 月就存在卻仍被丟
全為 acceptance/completed、label 都是 person、22/25 是一般 image-mode
221496 1472 export 腳本要修
本來就沒標註(正當負樣本)701843 0不用動

常見的幾種成因都排除了:不是 video-mode(22/25 是 image-mode)、 不是沒到 acceptance(全部 completed)、不是 label 名稱對不上(都是 person)、 rectangle 與 polygon 都有。成因見下一段。

被漏掉的 task(標註早於 export,屬於真 bug)

taskCVAT 實際框數名稱
9136438439RAIVISION_[DAVIDMAC]_20260701_007
9464213213RAIVISION_[DAVIDMAC]_20260706_007
9467212212RAIVISION_[DAVIDMAC]_20260706_010
9470161161RAIVISION_[DAVIDMAC]_20260706_016
90735555RAIVISION_[DAVIDMAC]_20260701_008
90765555RAIVISION_[DAVIDMAC]_20260701_009
49301631JUJIA_[jujia_B8]_UNKNOWN_009
49313429JUJIA_[jujia_A1]_20260508_004
94652727RAIVISION_[DAVIDMAC]_20260706_008
94692727RAIVISION_[DAVIDMAC]_20260706_012
94662727RAIVISION_[DAVIDMAC]_20260706_009
94682626RAIVISION_[DAVIDMAC]_20260706_011
49323325JUJIA_[jujia_A7]_20260515_004
49271622JUJIA_[jujia_B12]_20260323_003
94722121RAIVISION_[DAVIDMAC]_20260706_018
94712020RAIVISION_[DAVIDMAC]_20260706_017
49335119JUJIA_[jujia_A7]_20260505_004
49291616JUJIA_[jujia_B10]_20260310_003
49281616JUJIA_[jujia_C11]_20260318_003
49241515JUJIA_[jujia_C7]_20260325_003
49261414JUJIA_[jujia_C5]_20260511_005
492532JUJIA_[jujia_B6]_20260506_005

為什麼指標看不出來

這個錯誤不會以任何形式報錯。圖片數、目錄結構、資料量全都正常, 訓練與驗證指標也正常(val mAP50 0.772)——因為 val 集有相同的缺漏, 偏差一致就互相抵銷了。只有拿一批乾淨的外部測試資料去打,才會看到 0.000。

根因:export 只吃 polygon,rectangle 全部靜默丟棄

把被漏的 task 與正常進入的 task 逐項比對,前面幾個假設都被推翻:

假設結果
video-mode task 只匯出第一張推翻——22/25 是一般 image-mode
job 沒到 acceptance 被濾掉推翻——全部 acceptance/completed
label 名稱對不上推翻——全部是 label id 1 person
task 擁有者權限問題推翻——chen / system / claude 名下都有大量 task 正常進入
frame index 因 deleted_frames 錯位推翻——索引全在範圍內,deleted_frames 多為 0
export 只處理 polygon 型別 成立——被漏的 22 個 task 是純 rectangle,對照組 30 個全含 polygon,判別完全乾淨
這就是規則 2 記載的 forklift v518 同一個 bug
2026-05-19 forklift v518 因為「只取 polygon、丟掉 rectangle」漏了 72% 的標註, export 在 v519 修好了——之後每一版 forklift 資料集都是乾淨的。
但 person 的 export 從來沒修過。 所以 JUJIA 那 10 個 task 從 2026-05-26 起,橫跨 7 個版本一路被漏到今天。

擴大稽核:46 個 YOLO 資料集,13 個受影響

既然成因是共用的 export 邏輯,就把所有資料集都掃一遍。 判準相同:磁碟上整個 task 零標註,但該 task 在 CVAT 有標註。

資料集被漏 task被漏圖遺失框
forklift_v20260518211059710624
person_v202608062519442573
person_v2026052075302186
person_v202607092214961472
person_v20260715
← 現役 production person 模型的訓練資料
2214961472
person_v20260706b13762738
forklift_v2026052544423
person_v2026052610214189
person_v2026052710214189
person_v2026060210214189
person_v2026060810214189
person_v2026070610214189
forklift_v202608041171
兩個值得注意的點

要做的事

  1. 修 person 的 export:改成同時吃 rectanglepolygon, 照 forklift v519 的作法。rectangle 直接就是 bbox,polygon 取外接矩形
  2. 加 fail-loud:某個 task 在 CVAT 有標註卻產出 0 個 label 時要中止並報錯, 不能靜默寫出空檔案
  3. 加時序檢查:export 完成時間必須晚於所有 task 的最後標註時間,否則警告
  4. 修完重跑 export → 重訓,約 15–18 小時

災情範圍:只限 person 這一支

既然成因是 export 的 shape 型別過濾,就把所有 export 腳本掃一遍,確認還有沒有別的中招:

export 腳本寫法判定
export_p1_*(person) if sh["type"] == "polygon" rectangle 被丟 — 已修
export_p9_*(forklift) if sh["type"] in ("polygon", "rectangle") 正確(v519 修過)
export_p12_*(PPE 22-attr) bbox_of():rectangle 取 pts[:4],其餘取 min/max 兩種都涵蓋
export_p41_dialysis in ("polygon", "rectangle")正確
export_p44_2attr / export_p48_fall bbox_of() 分支寫法兩種都涵蓋
export_smoke_seg_exp if sh["type"]=="polygon" 分割模型本來就需要 polygon,rectangle 產不出遮罩; 稽核顯示該資料集無實際損失

結論:這個 bug 只存在於 person 的 export 血脈,其他模型的 export 都正確處理兩種型別。

已修好並驗證(2026-08-07 04:50)

改的是 export_p1_v20260806.py 第 123 行這一句:

# 舊 —— rectangle 全部被丟掉
shapes = [sh for sh in ann.get("shapes", []) if sh["type"] == "polygon"]

# 新
WANTED = ("polygon", "rectangle")
shapes = [sh for sh in ann.get("shapes", []) if sh["type"] in WANTED]
for tr in ann.get("tracks", []):                 # video-mode 的標註常放在 track
    for sh in tr.get("shapes", []):
        if sh.get("type") in WANTED and not sh.get("outside"):
            shapes.append({**sh, "frame": sh.get("frame", tr.get("frame", 0))})

poly_to_bbox() 本來就是取 points 的 min/max, rectangle 的 [x1,y1,x2,y2] 直接就能算出正確的框,不必另外處理。

另外補了兩道防線:

驗證結果

標註框task
person_v2026080641,296 152,547802
person_v2026080742,796 158,633808
差異+1,500 +6,086+6
先前被漏掉的 25 個 task:全部救回,0 遺漏
export 輸出的 shape 型別分布為 {'polygon': 177665, 'rectangle': 1472}—— rectangle 恰好 1,472 個,與稽核算出的遺失數字完全一致。 新加的自我核對也通過(每個有標註的 task 都有 label 寫出)。

修正版腳本:5090-2 ~/factory_ppe/scripts/export_p1_v20260807.py, 資料集:~/datasets/person_v20260807重訓尚未啟動——那是 15–18 小時的資源承諾,依規矩訓練決策要 operator 確認。 hyperparams 沿用 v806 不動,只換乾淨資料。

九、CH01 人員模型選型(已定案)

模型訓練 imgsz推論 imgsz mAP50mAP50-95PR
ch01_person_yolo26s_v20260806_1280
CH01 專用版 ← 採用
12801280 0.9050.788 0.9220.872
person_yolo26m_v20260806_640
全域版(被污染)
640640 0.0000.0000.0000.000
同上6401280 0.0410.0390.0710.013

同一個 CH01 test 集(70 張 / 149 個人)對照。專用版訓練與推論都是 1280, 符合場域遠景小目標的要求。

CH01 人員偵測對照

黃 = 人工標註,藍 = CH01 專用版(conf 0.25),紅 = 全域版(conf 0.01,畫面上一個都沒有)。 專用版與人工標註幾乎完全重合。

專用版 vs 現役 production:兩邊都測,確認該怎麼綁

只知道「專用版在 CH01 比較好」還不夠,得知道它能不能當通用模型用。 所以兩個模型各在兩個 test 集上都跑一次:

模型CH01 test
70 圖 / 149 人
全域 test
6,580 圖
CH01 專用版
ch01_person_yolo26s_v20260806_1280
mAP50 0.905
R 0.872 · P 0.922
mAP50 0.319
R 0.300 · P 0.499
現役 production
person_yolo26m_v20260715
mAP50 0.663
R 0.591 · P 0.672
mAP50 0.923
R 0.842 · P 0.939

兩個模型都以 imgsz 1280 推論。split 沿用 CVAT 的 task.subset, 版本間分配穩定,因此現役模型的訓練資料不會出現在全域 test 裡,比較是公平的。

結論:模型必須按鏡頭綁定,不能全域替換
建議

十、陞訊 demo 模型部署清單

下週二 demo 要用的模型。目前 ppe-demo 上一個陞訊模型都還沒註冊, ckpt 已備到 gx10-4t 的 /home/rai/model_viewer/models/(只放檔案,未改 app.py、未重啟服務)。

gx10 整台不通(LAN ping 100% 丟包、ppe-demo 回 502), 所以目前只能部署到備援機 ppe-demo-4t。兩台會處於不同步狀態,gx10 恢復後要補做。
用途 / 鏡頭檔名imgsz 成績(同 test 集)狀態
人員偵測
CH01 五谷王北街出入口
sx_ch01_person_v20260806.pt
sha 483d8a96b41a23c6 · 20.4MB
1280 mAP50 0.905 / mAP50-95 0.788
P 0.922 · R 0.872 · 70 圖 149 人
可上版
車輛偵測
CH01,含子母車 / 棧板誤判處理
sx_ch01_vehicle_v20260806.pt
sha 1834daacc82d4663 · 20.4MB
1280 mAP50 0.981
3 類 car / truck / motorcycle
可上版
安全裝備
CH06–CH10
sx_ppe2attr_ch0610_strat_v20260806.pt
sha 5a4902d09e7816c9 · 17.0MB
512×256 hard_hat AP 0.994(n=55)
safety_vest AP 1.000(n=15)
可上版
安全裝備
CH01
sx_ppe2attr_ch01_v20260806.pt
sha 75689089b219417d · 17.0MB
512×256 hard_hat AP 0.990(n=37)
safety_vest AP 0.000(n=0)
只有安全帽可用
電子圍籬
CH02–CH05
sx_fence_v20260806.pt
sha 563b8d3554a8c5e6 · 20.4MB
1280 真實畫面 R 0.667 / P 0.800
現役 person_v20260715 為 R 0.556 / P 0.909
建議不上版
⚠️ 有一個同名變體不能用
ppe2attr_sx_2attr_v20260806(未分層切分版)驗證分數 0.984 看起來正常, 但 test mAP 只有 0.493——因為 safety_vest 的正樣本全部落在 Train, 測試集 116 個有效樣本裡一個都沒有,AP 被記為 0。 分層重切之後的 _strat_ 版才是正確的(測試集有 15 個正樣本,AP 1.000)。 部署時務必認明檔名裡的 strat
CH01 的反光背心沒有正樣本,這是現場實況不是標註疏漏
CH01 測試集 84 個有效標註全部是「沒穿背心」。 所以 CH01 的 safety_vest 這一項無從評估,也無從訓練。
要用的話有兩條路:(a) 只啟用安全帽偵測,背心項關閉; (b) 若現場規定要穿背心,那目前的資料本身就說明了違規率 100%, 應該先確認是規定不同還是漏標。
車輛模型需要新的 handler,不是加一行 register 就好
app.py 現有的偵測 handler(PersonDetHandlerRiverDebrisHandler) 都寫死單一類別,CH01 車輛模型有 car / truck / motorcycle 三類, 所以要補一個泛用的多類別 handler。人員與安全裝備則沿用既有 handler,不必動。
已寫成可審閱、一行套用的 patch:scripts/patch_app_add_shenghsun.py (含 handler 與四個 register block,套用前會先做語法檢查; --dry 可先看會加什麼而不改檔)。尚未套用,等你決定。
CH01 安全裝備的 cascade 上游刻意接CH01 專用人員模型—— 該鏡頭 R 0.872 遠優於通用版的 0.591。

部署指令(gx10-4t,ckpt 已就位)

# 1. ckpt 已在 /home/rai/model_viewer/models/(本次已完成,不需重做)
# 2. 改 source repo 的 app.py 加 register block —— 規則 15:一定要進 repo 並 push,
#    只 docker cp 進 container 的話下次 redeploy 會被洗掉
cd scripts/model_viewer && git pull --rebase
python3 ../patch_app_add_shenghsun.py --dry   # 先看會加什麼
python3 ../patch_app_add_shenghsun.py         # 套用
git add app.py && git commit -m "feat: 加陞訊 CH01/CH06-10 模型" && git push origin main

# 3. 部署到 4t
scp app.py rai@192.168.53.17:/home/rai/model_viewer/app.py
ssh rai@192.168.53.17 'docker cp /home/rai/model_viewer/app.py model_viewer:/app/app.py \
  && docker restart model_viewer'

# 4. 驗證(規則 14:一定要 curl 確認,Success 不代表生效)
sleep 10 && curl -s https://ppe-demo-4t.intemotech.com/models | grep sx_ch01_person

# 5. gx10 修好後補做同樣步驟,兩台要一致

十一、電子圍籬 CH02–05:模型從未被真實資料驗證過

先前給的建議是「不上新版、沿用現役 person 模型」。要為這個建議負責, 就得知道沿用的代價是什麼,所以回頭量了一次——結果發現的問題比原本以為的更根本。

整個資料集沒有一張真實影像

split總圖真實生成 真實框生成框
train1450145 0187
val31031 037
test39039 064

sx_fence_v20260806 的 215 張全部是生成圖。 所以這個模型測出來的 P 0.934 / R 0.891 / F1 0.912,是生成資料自己跟自己比的結果。 依先前在陞訊同一天做過的對照,生成圖人物輪廓乾淨、對比清晰、姿態標準, 連沒看過它們的現役模型都能拿滿分——這種分數對真實場域完全沒有預測力。

真實素材一直都在,只是沒進標註流程

來源狀況
sx_frames 的 CH02–05 真實幀252 張(CH02 70 / CH03 72 / CH04 70 / CH05 40)
CVAT 專案 #1 裡的陞訊 task 共 63 個,其中只有 CH01 的 3 個是真實,其餘 60 個全是生成

也就是說,CH02–05 這四支鏡頭從頭到尾沒有任何一張真實影像被標註過。

已處置:預標並上 CVAT,等標記師複核

SAM3 對這 252 張做 person 預標,逐鏡頭分層切 train/val/test (讓每個 split 都有正樣本,避免正樣本全落在 Train 的老問題):

鏡頭subset預標 polygonCVAT
CH02Train3617#11570
CH02Validation189#11571
CH02Test165#11572
CH03Train3618#11573
CH03Validation189#11574
CH03Test1814#11575
CH04Train358#11576
CH04Validation185#11577
CH04Test172#11578
CH05Train2010#11579
CH05Validation104#11580
CH05Test104#11581
合計 252 105
stage 刻意停在 annotation,沒有設 acceptance
SAM3 預標沒有經過任何獨立驗證,直接當成真值就會重蹈本報告第八節那個覆轍—— 漏掉的框會讓那張圖變成「確定沒有人」的負樣本,代價比沒有資料還高。
請標記師複核時,除了確認框的正確性,也要確認沒有框的畫面是真的沒有人
對下週二 demo 的影響
CH02–05 這四支鏡頭目前沒有任何經真實資料驗證的模型。 新訓的 fence 模型只在生成圖上有分數,現役 person 模型則從未在這些鏡頭上量過。 標記師複核完這 12 個 task 之後才有辦法給出可信的數字。
在那之前,demo 若要涵蓋 CH02–05,建議明確說明這幾支是「尚未驗證」的狀態, 不要拿生成資料的分數對外報告。

十二、車輛模型:子母車與棧板誤判的實地確認

「子母車垃圾箱和載貨棧板被誤判成車輛」是這個案子最初就指定要處理的問題。 mAP 0.981 不能代表它解決了——誤判源如果在 test 集裡出現得少,整體指標根本看不出來。 所以直接對 60 張含垃圾箱的 CH01 畫面推論,用眼睛確認。

車輛誤判確認

藍框 = car,橘框 = truck,綠框 = motorcycle(conf 0.25)。

子母車垃圾箱一個都沒被框
六張畫面右側那兩個綠色子母車,在所有樣本中都沒有產生任何偵測框。 車輛判別也正確:黑色廂型車 → car、藍色帆布覆蓋的小貨車 → truck、 遠處街道的紅色車輛 → car。這個問題確認解決。
但有一件應用層要處理的事
60 張畫面共偵測到 228 個 motorcycle,平均每張 3.8 台—— 其中畫面右下角那台固定停放的機車每一張都會被偵測到。 模型判斷沒有錯,它確實是機車;但如果要拿來做「車輛進出」通報, 這台不會動的車會持續觸發。
建議在應用層處理:設 ROI 排除停車區,或加靜止物過濾 (連續 N 幀位置不變就不通報)。這是規則設計問題,不需要動模型。

十三、證偽測試:直接推論驗證,不只靠推理

前面的結論建立在「空 label 會讓模型學到該場景沒有人」這個推理上。 推理可能錯,所以做一個能推翻它的測試—— 拿 v806 去推論那 22 個 rectangle task 的圖, 如果它表現正常,整個結論就不成立。

模型召回精確 TP / FN完全沒偵測到的圖
v806
訓練時這批圖 label 全空
0.0780.966 115 / 13561345 / 1446
v806 @ conf 放寬到 0.01 0.0860.819 127 / 13441335 / 1446
原廠 yolo26m
完全沒看過這批資料
0.9890.960 1455 / 1611 / 1446

1,446 張圖、1,471 個真值框,IoU 0.5 判定。真值取自修正後的 person_v20260807。

召回率差 12.7 倍,推翻不了。 一個沒看過這批資料的原廠模型抓到 98.9% 的人, 而「看過但被教成這裡沒有人」的 v806 只抓到 7.8%—— 把信心門檻放寬到 0.01 也只升到 8.6%,代表不是門檻問題,是模型根本不認為那裡有人。

逐 task 拆開,看得更清楚

task來源真值框v806 抓到召回
9136RAIVISION43800.000
9464RAIVISION21300.000
9467RAIVISION21200.000
9470RAIVISION16100.000
9073RAIVISION5500.000
9076RAIVISION5500.000
4930JUJIA31280.903
4931JUJIA29240.828
9465RAIVISION2700.000
9469RAIVISION2700.000
9466RAIVISION2700.000
9468RAIVISION2600.000
4932JUJIA2500.000
4927JUJIA22180.818
9472RAIVISION2100.000
9471RAIVISION2000.000
4933JUJIA1900.000
4929JUJIA16120.750
4928JUJIA16150.938
4924JUJIA15100.667
4926JUJIA1480.571
4925JUJIA200.000
一個有意思的分化:RAIVISION 全滅,JUJIA 部分存活
12 個 RAIVISION task 全部召回 0.000,一個都沒抓到; JUJIA 則有 7 個維持在 0.571–0.938。
差別在於有沒有「反例」可以對照:JUJIA 在資料集裡還有其他用 polygon 正確標註的 task, 模型從那些學到「這種工地場景有人」,所以空 label 的傷害被稀釋; RAIVISION_[DAVIDMAC] 是獨立場域,在訓練資料裡的唯一代表就是那批空 label, 模型於是學到「這個場景=沒有人」。
這正好解釋了 CH01 為什麼是最慘的:CH01 的 448 張全部空 label, 沒有任何一張正確標註的同場景資料可以對沖。

順帶驗證:我自己的評估有沒有踩同一個坑

如果我拿來評估的 test 集本身缺框,那前面報的 mAP 與 recall 全都不可信。 所以逐 task 比對「磁碟 label 行數」vs「CVAT 扣除刪除幀後的框數」:

資料集磁碟框CVAT 有效框結果
ch01_person_v20260806
CH01 選型評估用
1,1011,101 ✅ 完全吻合,評估數字可信
person_v20260807
修正後,重訓要用
175,560176,345 ✅ 22 個 rectangle task 全數收進
person_v20260806
v806 訓練用
169,139172,494 28 個 task 不完整,少 2,766 框
v20260807 那 785 框的差距不是遺失,是小框過濾正常運作
差距集中在 3 個 task(269 / 267 / 485),全是大溪橋的人群密集場景,每張圖約 40 個框。 被濾掉的框正規化寬度是 0.00063–0.002,在 3840×1920 的影像上等於 2.4 至 7.7 像素寬——export 的 w < 0.002 門檻本來就該擋掉這種框, 2 像素寬的人框放進訓練只會是雜訊。frame 層級全數到齊,沒有整張圖被跳過。