練工地安全目標(biāo)檢測(cè):600張圖從標(biāo)簽體檢到部署實(shí)踐)
簡(jiǎn)介建筑工地安全目標(biāo)檢測(cè)數(shù)據(jù)集提供了一套面向AI視覺(jué)開(kāi)發(fā)者和工地安防系統(tǒng)研發(fā)的YOLO標(biāo)準(zhǔn)訓(xùn)練資源。數(shù)據(jù)源自真實(shí)工地監(jiān)控畫(huà)面共600張已標(biāo)注圖片訓(xùn)練集426張、驗(yàn)證集117張、測(cè)試集57張覆蓋靴子、安全帽、機(jī)械、人員、安全背心等8個(gè)類(lèi)別同時(shí)標(biāo)注無(wú)安全帽、無(wú)口罩、無(wú)安全背心等違規(guī)行為可用于裝備佩戴檢測(cè)與違規(guī)預(yù)警。包體共1202個(gè)文件以jpg圖片與txt標(biāo)注文件為主另含yaml配置及docx說(shuō)明文檔整體壓縮后約37.43MB可接入YOLOv5、YOLOv8等常用框架快速部署。目前已有264人學(xué)習(xí)下載。依托這份數(shù)據(jù)用戶(hù)可開(kāi)展多類(lèi)別目標(biāo)檢測(cè)實(shí)驗(yàn)搭建工地安全智能監(jiān)控原型降低數(shù)據(jù)采集與標(biāo)注成本也能為工業(yè)安全管理及高校計(jì)算機(jī)視覺(jué)研究提供貼近真實(shí)場(chǎng)景的支撐素材。1. 建筑工地安全目標(biāo)檢測(cè)數(shù)據(jù)集600張真實(shí)監(jiān)控圖能訓(xùn)出什么做工地安全監(jiān)控的人拿到這份建筑工地安全目標(biāo)檢測(cè)數(shù)據(jù)集最容易犯的錯(cuò)是直接丟給YOLOv8開(kāi)訓(xùn)。600張真實(shí)工地監(jiān)控畫(huà)面、8個(gè)類(lèi)別、YOLO格式標(biāo)注看起來(lái)一步就能跑通實(shí)際上拆開(kāi)看才發(fā)現(xiàn)違規(guī)類(lèi)無(wú)安全帽、無(wú)口罩、無(wú)安全背心和裝備類(lèi)高度重疊機(jī)械類(lèi)樣本稀少類(lèi)名還有拼寫(xiě)坑。這些信息不提前盤(pán)清楚后面盯著訓(xùn)練日志調(diào)參就是瞎調(diào)。這篇文章按我拆數(shù)據(jù)集的習(xí)慣來(lái)寫(xiě)先查目錄和標(biāo)簽結(jié)構(gòu)再配置YOLOv8跑通訓(xùn)練評(píng)估時(shí)看混淆矩陣和badcase最后補(bǔ)兩個(gè)部署階段的實(shí)用動(dòng)作。適合做工業(yè)安防、工地智能監(jiān)控以及想拿真實(shí)場(chǎng)景練手目標(biāo)檢測(cè)的開(kāi)發(fā)者。2. 先盤(pán)數(shù)據(jù)集目錄結(jié)構(gòu)、YOLO標(biāo)簽格式與8個(gè)類(lèi)別分布拿到zip包第一件事不是解壓了就跑而是把壓縮包內(nèi)的目錄結(jié)構(gòu)搞清楚。這個(gè)數(shù)據(jù)集來(lái)自Roboflow導(dǎo)出從文件名里那一串_jpg.rf.就能看出來(lái)——這是平臺(tái)處理過(guò)后重命名的產(chǎn)物中間那段十六進(jìn)制哈希是圖片增強(qiáng)鏈路的標(biāo)記。真正在訓(xùn)練時(shí)起作用的是文件名主體和標(biāo)注文件的同名對(duì)應(yīng)關(guān)系哈希部分不影響訓(xùn)練但會(huì)影響你后續(xù)人工核對(duì)badcase時(shí)認(rèn)圖。2.1 解壓后的目錄600張圖片和600個(gè)txt怎么對(duì)應(yīng)Roboflow導(dǎo)出的YOLO格式壓縮包解壓后一般是train / valid / test三個(gè)子集每個(gè)子集里再分images和labels。這份數(shù)據(jù)集對(duì)應(yīng)關(guān)系是訓(xùn)練集426張、驗(yàn)證集117張、測(cè)試集57張。images里放.jpg圖片labels里放同名.txt標(biāo)注文件一個(gè)jpg對(duì)應(yīng)一個(gè)txt沒(méi)有txt的圖片在訓(xùn)練時(shí)會(huì)被靜默跳過(guò)。# 解壓后先核對(duì)三個(gè)子集數(shù)量 find train/images -name *.jpg | wc -l # 應(yīng)為426 find train/labels -name *.txt | wc -l # 應(yīng)為426 # 再檢查同名對(duì)應(yīng)缺標(biāo)簽的文件會(huì)被跳過(guò) for img in train/images/*.jpg; do txttrain/labels/$(basename $img .jpg).txt [ -f $txt ] || echo missing label: $img done這段腳本的價(jià)值在于YOLO訓(xùn)練框架不會(huì)因?yàn)槟闵賻讉€(gè)txt報(bào)錯(cuò)而是直接把對(duì)應(yīng)圖片剔除出訓(xùn)練隊(duì)列。如果缺失發(fā)生在某個(gè)稀有類(lèi)別上你連日志都看不出問(wèn)題只會(huì)發(fā)現(xiàn)某個(gè)類(lèi)的AP莫名偏低。我一般會(huì)把這個(gè)檢查腳本在train、valid、test三個(gè)子集上都跑一遍確保圖片和標(biāo)注是完整配對(duì)的。另外注意test子集在訓(xùn)練階段完全不用碰它是留給你最后驗(yàn)證模型泛化能力的。很多新手把test也塞進(jìn)訓(xùn)練導(dǎo)致評(píng)估時(shí)分?jǐn)?shù)虛高那叫自欺欺人。2.2 YOLO標(biāo)簽格式歸一化坐標(biāo)與標(biāo)簽體檢腳本YOLO格式的每個(gè)txt文件里一行代表一個(gè)目標(biāo)共5個(gè)數(shù)值類(lèi)別ID、歸一化中心點(diǎn)x、歸一化中心點(diǎn)y、歸一化寬度w、歸一化高度h。所有坐標(biāo)都除以了圖片寬高值域在0到1之間??床欢@個(gè)后面排查標(biāo)簽越界問(wèn)題就無(wú)從下手。import random from glob import glob txts glob(train/labels/*.txt) sample random.choice(txts) with open(sample) as f: for line in f.read().strip().splitlines(): class_id, cx, cy, w, h line.split() print(fclass{class_id}, cx{cx}, cy{cy}, w{w}, h{h})我們隨機(jī)抽一個(gè)標(biāo)簽文件打開(kāi)看class6, cx0.482, cy0.531, w0.118, h0.204。意思是類(lèi)別ID為6對(duì)應(yīng)Person類(lèi)目標(biāo)中心點(diǎn)在圖片水平48.2%、垂直53.1%的位置寬度占整圖11.8%高度占20.4%。要換算成像素坐標(biāo)就用cx*圖片寬、cy*圖片高。這種歸一化格式的好處是和圖片分辨率解耦訓(xùn)練時(shí)無(wú)論縮放到640還是1280坐標(biāo)比例都不變。下一個(gè)動(dòng)作是體檢檢查所有txt的坐標(biāo)值是否都在0到1范圍內(nèi)。越界坐標(biāo)會(huì)導(dǎo)致訓(xùn)練時(shí)邊界框計(jì)算異常輕則loss波動(dòng)重則訓(xùn)練崩掉。import os from glob import glob bad 0 for txt in glob(train/labels/*.txt) glob(valid/labels/*.txt): for line in open(txt).read().strip().splitlines(): cid, cx, cy, w, h map(float, line.split()) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(fout-of-range: {txt} - {line}) bad 1 print(ftotal bad lines: {bad})這個(gè)腳本看起來(lái)簡(jiǎn)單但實(shí)際作用很大。常見(jiàn)情況是某些增強(qiáng)腳本把物體放到了畫(huà)布外導(dǎo)致w或h超過(guò)1模型在loss計(jì)算時(shí)對(duì)這類(lèi)框給出的梯度是畸形的。我見(jiàn)過(guò)有人因?yàn)檫@類(lèi)越界標(biāo)簽訓(xùn)練了50輪loss都降不下去最后定位到是幾百個(gè)臟標(biāo)注在拖后腿。2.3 8個(gè)類(lèi)別逐個(gè)拆解裝備類(lèi)、違規(guī)類(lèi)和機(jī)械類(lèi)數(shù)據(jù)集涵蓋8個(gè)類(lèi)別按語(yǔ)義可以分成三組安全裝備Boots、Hardhat、Safetyvest、違規(guī)行為No-Hardhat、No-mask、No-safetyvest、以及人和設(shè)備Person、Machinary。類(lèi)別ID按摘要里列出的順序從0到7排列實(shí)際以壓縮包內(nèi)data.yaml的names字段為準(zhǔn)我按常見(jiàn)順序整理如下類(lèi)別ID名稱(chēng)類(lèi)型業(yè)務(wù)含義0Boots裝備工地防護(hù)靴1Hardhat裝備佩戴安全帽2Machinary設(shè)備起重機(jī)等施工機(jī)械3No-Hardhat違規(guī)未佩戴安全帽4No-mask違規(guī)未佩戴口罩5No-safetyvest違規(guī)未穿反光安全背心6Person人員工地工作人員7Safetyvest裝備穿戴反光安全背心這里有兩個(gè)容易被忽略的細(xì)節(jié)。第一Machinary這個(gè)類(lèi)名在數(shù)據(jù)集里就是拼寫(xiě)錯(cuò)誤的正確拼寫(xiě)是Machinery但訓(xùn)練時(shí)千萬(wàn)別手滑改成正確拼寫(xiě)因?yàn)闃?biāo)簽txt里的類(lèi)別ID和data.yaml的names是一一對(duì)應(yīng)的改名字不改ID不影響改錯(cuò)位置就會(huì)錯(cuò)位。第二違規(guī)類(lèi)和裝備類(lèi)是成對(duì)出現(xiàn)的一個(gè)人戴了安全帽圖上會(huì)有Person和Hardhat兩個(gè)框沒(méi)戴安全帽就會(huì)有Person和No-Hardhat。這就是第4章要展開(kāi)講的標(biāo)簽語(yǔ)義重疊問(wèn)題它直接影響你在混淆矩陣?yán)锟吹降恼`檢分布。從業(yè)務(wù)視角看這個(gè)數(shù)據(jù)集對(duì)應(yīng)的就是一個(gè)智能安全巡檢系統(tǒng)識(shí)別工人有沒(méi)有戴安全帽、穿反光背心、佩戴口罩同時(shí)識(shí)別工地上的大型機(jī)械。600張圖對(duì)8個(gè)類(lèi)別來(lái)說(shuō)屬于中小規(guī)模數(shù)據(jù)集如果某個(gè)類(lèi)別的樣本量特別稀少比如機(jī)械訓(xùn)練時(shí)就需要特殊處理這個(gè)在第4章的Machinary問(wèn)題里具體展開(kāi)。3. 用YOLOv8把它訓(xùn)起來(lái)目錄整理、data.yaml與訓(xùn)練命令數(shù)據(jù)集結(jié)構(gòu)盤(pán)清楚了接下來(lái)就是把它喂給YOLOv8。這里我不推薦拿著Roboflow原始目錄結(jié)構(gòu)直接用而是建議統(tǒng)一成Ultralytics官方推薦的布局images和labels兩個(gè)大目錄內(nèi)部再按train/val/test切分。這樣做的好處是后續(xù)換數(shù)據(jù)集、換模型版本時(shí)訓(xùn)練腳本和data.yaml可以完全復(fù)用。3.1 統(tǒng)一目錄結(jié)構(gòu)與data.yaml配置先建目錄、把文件歸位。這一步純粹是體力活但目錄不統(tǒng)一后續(xù)每次訓(xùn)練都要改路徑很煩。mkdir -p datasets/site_safety/images/train mkdir -p datasets/site_safety/images/val mkdir -p datasets/site_safety/images/test mkdir -p datasets/site_safety/labels/train mkdir -p datasets/site_safety/labels/val mkdir -p datasets/site_safety/labels/test cp train/images/*.jpg datasets/site_safety/images/train/ cp train/labels/*.txt datasets/site_safety/labels/train/ cp valid/images/*.jpg datasets/site_safety/images/val/ cp valid/labels/*.txt datasets/site_safety/labels/val/ cp test/images/*.jpg datasets/site_safety/images/test/ cp test/labels/*.txt datasets/site_safety/labels/test/復(fù)制完之后用前面的find | wc -l再核對(duì)一遍確保每個(gè)子集的圖片和標(biāo)簽數(shù)量一致。然后創(chuàng)建data.yaml。這里注意path字段使用相對(duì)路徑不要寫(xiě)/home/xxx/這種絕對(duì)路徑否則換機(jī)器跑就得改配置。# datasets/site_safety/data.yaml path: ./datasets/site_safety train: images/train val: images/val test: images/test names: 0: Boots 1: Hardhat 2: Machinary 3: No-Hardhat 4: No-mask 5: No-safetyvest 6: Person 7: Safetyvestnames的順序必須和標(biāo)簽txt里的類(lèi)別ID完全對(duì)齊這是YOLO系列訓(xùn)練框架的硬性要求錯(cuò)一位就是災(zāi)難性的類(lèi)別錯(cuò)位。test字段寫(xiě)上并不會(huì)影響訓(xùn)練只是在訓(xùn)練完調(diào)用val時(shí)可以指定用test子集來(lái)評(píng)估這樣得到的指標(biāo)才是模型在沒(méi)見(jiàn)過(guò)的數(shù)據(jù)上的真實(shí)表現(xiàn)。3.2 訓(xùn)練命令與關(guān)鍵超參img、batch、epochs怎么定600張圖、8個(gè)類(lèi)別這個(gè)規(guī)模下我不建議直接用YOLOv8m或者更大的模型。常見(jiàn)做法是從最小的yolov8n開(kāi)始用COCO預(yù)訓(xùn)練權(quán)重做遷移。COCO上訓(xùn)過(guò)的權(quán)重已經(jīng)學(xué)會(huì)了通用的邊緣、紋理和物體形狀特征遷移到工地場(chǎng)景能顯著緩解小數(shù)據(jù)量帶來(lái)的欠擬合問(wèn)題。# 在項(xiàng)目根目錄執(zhí)行 yolo detect train \ modelyolov8n.pt \ datadatasets/site_safety/data.yaml \ epochs100 \ img640 \ batch16 \ patience20 \ projectruns/site_safety \ nameyolov8n_600img逐項(xiàng)說(shuō)明參數(shù)含義。modelyolov8n.pt表示加載COCO預(yù)訓(xùn)練權(quán)重啟動(dòng)訓(xùn)練而不是隨機(jī)初始化如果不加這個(gè)參數(shù)從零訓(xùn)練600張圖的效果會(huì)差很多。epochs100對(duì)600張圖是合理范圍配合patience20早停機(jī)制如果驗(yàn)證集mAP連續(xù)20輪不漲就自動(dòng)停止避免浪費(fèi)時(shí)間。img640是訓(xùn)練分辨率也是默認(rèn)推理分辨率如果你發(fā)現(xiàn)監(jiān)控畫(huà)面里遠(yuǎn)處的人很小可以嘗試img1280效果可能有明顯提升但顯存占用和推理耗時(shí)都會(huì)上漲。batch16在8GB顯存上剛好如果顯存告急就降到8。Ultralytics默認(rèn)開(kāi)啟了mosaic、hsv擾動(dòng)等數(shù)據(jù)增強(qiáng)在小數(shù)據(jù)集上默認(rèn)配置通常不用改這也是YOLOv8這類(lèi)框架對(duì)小數(shù)據(jù)集友好的原因之一。我自己在600張圖規(guī)模上一般不加額外增強(qiáng)參數(shù)先跑一個(gè)baseline有問(wèn)題再針對(duì)性地開(kāi)增強(qiáng)。這里隱含的策略是先拿baseline暴露問(wèn)題而不是一上來(lái)就堆技巧。3.3 訓(xùn)練產(chǎn)物best.pt、results.csv、confusion_matrix去哪找訓(xùn)練結(jié)束后輸出目錄下會(huì)有完整的過(guò)程記錄和控制臺(tái)輸出的匯總ls runs/site_safety/yolov8n_600img/ # weights/ best.pt last.pt # results.csv 每輪的loss、mAP、PR曲線數(shù)據(jù) # confusion_matrix.png 驗(yàn)證集混淆矩陣圖 # PR_curve.png 每個(gè)類(lèi)別的PR曲線 # train_batch*.jpg 訓(xùn)練批次的增強(qiáng)可視化這里最重要的兩個(gè)文件是weights/best.pt和weights/last.pt。best.pt是驗(yàn)證集mAP最高那輪的權(quán)重后續(xù)做推理、導(dǎo)出ONNX都用它last.pt只是最后一輪的狀態(tài)通常直接用best。results.csv是判斷訓(xùn)練是否健康的關(guān)鍵依據(jù)看loss曲線是否平穩(wěn)下降、mAP50是否逐步爬升。如果loss降了但mAP不動(dòng)說(shuō)明模型在死記訓(xùn)練集而沒(méi)有學(xué)到可泛化的特征這種問(wèn)題在標(biāo)簽語(yǔ)義重疊的數(shù)據(jù)集里特別常見(jiàn)下一章展開(kāi)說(shuō)。4. 常見(jiàn)問(wèn)題排查這份工地?cái)?shù)據(jù)集訓(xùn)練時(shí)的五個(gè)坑這一章是整篇文章里最值錢(qián)的部分。前面幾章是流程這里是我實(shí)際拆數(shù)據(jù)時(shí)踩過(guò)的坑。每一條都按“現(xiàn)象→原因→解決”來(lái)寫(xiě)你在訓(xùn)練這份數(shù)據(jù)集時(shí)如果碰到類(lèi)似癥狀直接對(duì)號(hào)入座。4.1 標(biāo)簽體檢漏掉圖片比預(yù)期少某個(gè)類(lèi)完全學(xué)不出來(lái)現(xiàn)象訓(xùn)練日志里顯示的圖片數(shù)比426少或者某個(gè)類(lèi)別在混淆矩陣?yán)镆徽卸际呛诘腁P數(shù)值在0.05以下徘徊。原因jpg和txt不同名導(dǎo)致圖片被靜默跳過(guò)。Roboflow導(dǎo)出的文件名很長(zhǎng)里面帶哈希和rf標(biāo)記有些人用腳本批量移動(dòng)文件時(shí)按.切割文件名切出來(lái)的主體對(duì)不上造成label丟失。還有一種情況是txt里存在空文件YOLO訓(xùn)練框架讀到空標(biāo)簽會(huì)跳過(guò)該圖。解決訓(xùn)練前強(qiáng)制跑一遍2.1和2.2里的腳本把缺失標(biāo)簽的圖片和空txt文件全部列出來(lái)。缺失的看能不能從原始數(shù)據(jù)補(bǔ)補(bǔ)不了就直接刪掉對(duì)應(yīng)圖片保證訓(xùn)練集“圖標(biāo)配對(duì)”是干凈的。從那以后我每次拿到新數(shù)據(jù)集都會(huì)先做這一步幾分鐘的時(shí)間能省下后面好幾輪的排查。4.2 安全帽和人員互相誤檢Hardhat與Person嚴(yán)重混淆現(xiàn)象驗(yàn)證集上Hardhat的檢出框經(jīng)常壓在Person框上明明沒(méi)戴安全帽的人也被打上Hardhat標(biāo)簽?;煜仃?yán)颒ardhat和Person兩塊交叉位置亮度很高。原因這是標(biāo)注語(yǔ)義本身的問(wèn)題。一個(gè)戴安全帽的工人圖片上同時(shí)有Person框和Hardhat框兩個(gè)框的中心高度重疊模型學(xué)到的特征是“凡是像人的地方大概率有安全帽”。如果訓(xùn)練數(shù)據(jù)里戴帽子的正例遠(yuǎn)多于背景反例模型就會(huì)把Person和Hardhat當(dāng)成強(qiáng)綁定關(guān)系。解決先看混淆矩陣確認(rèn)混淆程度然后再?zèng)Q定策略。輕度的可以用后處理規(guī)則修正——同一位置如果同時(shí)出現(xiàn)Hardhat和No-Hardhat按置信度取高者嚴(yán)重的可以考慮干脆把標(biāo)注合并成二分類(lèi)只檢測(cè)Hardhat戴帽和No-Hardhat沒(méi)戴帽去掉Person類(lèi)讓模型專(zhuān)注解決戴沒(méi)戴的問(wèn)題。我在類(lèi)似場(chǎng)景做過(guò)這種刪類(lèi)合并因?yàn)閷?duì)工地安全監(jiān)控來(lái)說(shuō)真正要報(bào)警的就是“有人且沒(méi)戴帽”P(pán)erson類(lèi)本身并沒(méi)有獨(dú)立業(yè)務(wù)價(jià)值。4.3 Machinary類(lèi)持久學(xué)不出來(lái)mAP一直趨近于0現(xiàn)象loss正常下降其他類(lèi)別AP都在漲只有Machinary類(lèi)的AP曲線貼著0軸走混淆矩陣?yán)镞@一行幾乎全黑檢測(cè)結(jié)果偶爾把它預(yù)測(cè)成Person或背景。原因機(jī)械類(lèi)樣本太少。這份數(shù)據(jù)集總共600張圖、8個(gè)類(lèi)別分配到Machinary上的可能只有幾十個(gè)實(shí)例而且起重機(jī)和挖掘機(jī)在不同角度、不同距離下的外觀差異極大幾十個(gè)樣本根本覆蓋不了類(lèi)內(nèi)差異。模型在有限的迭代輪次里只能記住訓(xùn)練集里那幾張圖的樣子泛化自然無(wú)從談起。解決短期內(nèi)把Machinary類(lèi)別從訓(xùn)練里去掉把它對(duì)應(yīng)的框忽略掉先保住其他7個(gè)類(lèi)別的精度。長(zhǎng)期看如果你真的需要機(jī)械檢測(cè)就得單獨(dú)補(bǔ)充機(jī)械類(lèi)數(shù)據(jù)哪怕補(bǔ)100張也比在這600張圖里硬扛強(qiáng)。另外注意一個(gè)操作細(xì)節(jié)刪類(lèi)時(shí)標(biāo)簽txt里的其他類(lèi)別ID要重新排不能留空洞否則ID對(duì)不上names。4.4 遠(yuǎn)處安全帽漏檢近處能檢遠(yuǎn)處一塌糊涂現(xiàn)象靠近攝像頭的工人檢測(cè)效果不錯(cuò)越遠(yuǎn)的畫(huà)面漏檢越嚴(yán)重。mAP50可能還行但mAP50-95低得離譜。原因640分辨率下遠(yuǎn)處一個(gè)安全帽可能只有十幾個(gè)像素寬的小目標(biāo)特征。YOLOv8n本身對(duì)小目標(biāo)不敏感加上訓(xùn)練分辨率壓到640小目標(biāo)信息在特征圖下采樣過(guò)程中被抹掉了。工地監(jiān)控的特點(diǎn)是視野寬、目標(biāo)密集這個(gè)問(wèn)題幾乎一定會(huì)出現(xiàn)。解決第一步把img提到1280訓(xùn)練和推理通常小目標(biāo)AP會(huì)有肉眼可見(jiàn)的提升代價(jià)是顯存占用翻倍、推理速度變慢。如果后期要部署到實(shí)時(shí)監(jiān)控就用切圖推理把大圖切成512×512的塊分別檢測(cè)再合并結(jié)果。這個(gè)方案現(xiàn)在已經(jīng)很成熟推薦給有實(shí)時(shí)需求的場(chǎng)景。4.5 訓(xùn)練中途loss變成NaN先查臟標(biāo)簽和壞圖現(xiàn)象訓(xùn)練進(jìn)行到十幾輪loss突然變成nan之后每個(gè)epoch的loss都是nan訓(xùn)練實(shí)際上已經(jīng)失效。原因最常見(jiàn)的是數(shù)據(jù)集里混入了損壞圖片jpg文件不完整或者標(biāo)簽里有極端坐標(biāo)值比如w或h為0的框甚至類(lèi)別ID超出names長(zhǎng)度。這種臟數(shù)據(jù)在訓(xùn)練中被隨機(jī)采樣到導(dǎo)致loss計(jì)算出現(xiàn)除零或梯度爆炸。解決寫(xiě)一個(gè)腳本遍歷所有圖片用OpenCV讀取讀不出來(lái)的標(biāo)記為損壞并剔除同時(shí)用2.2的越界檢查腳本把所有txt過(guò)濾一遍發(fā)現(xiàn)w或h為0的行直接刪掉。如果是梯度爆炸導(dǎo)致的把lr0從默認(rèn)的0.01降到0.005再試但優(yōu)先還是處理數(shù)據(jù)。5. 驗(yàn)證模型而不是相信訓(xùn)練日志val評(píng)估、PR曲線與badcase分析訓(xùn)練完看loss曲線覺(jué)得“穩(wěn)了”就收工是很多新手的習(xí)慣。但在這個(gè)數(shù)據(jù)集上訓(xùn)練loss下降只能說(shuō)明模型記住了訓(xùn)練集驗(yàn)證集上的表現(xiàn)才是真正要關(guān)注的。第3章里訓(xùn)練日志最后輸出的mAP值只是訓(xùn)練過(guò)程中對(duì)驗(yàn)證集的一個(gè)快照遠(yuǎn)不夠全面。這里用測(cè)試集做獨(dú)立評(píng)估再結(jié)合badcase分析得到的是可落地判斷。5.1 用測(cè)試集評(píng)估命令與指標(biāo)取舍# 用57張測(cè)試集圖片評(píng)估而不是默認(rèn)的驗(yàn)證集 yolo detect val \ modelruns/site_safety/yolov8n_600img/weights/best.pt \ datadatasets/site_safety/data.yaml \ splittest \ conf0.25 \ iou0.5splittest是這里的要點(diǎn)。Ultralytics默認(rèn)用val目錄做評(píng)估但我們?cè)诘?章配置了test字段加上這個(gè)參數(shù)就會(huì)用那57張從未參與訓(xùn)練和驗(yàn)證的圖片來(lái)評(píng)估結(jié)果更接近部署時(shí)的真實(shí)表現(xiàn)。conf0.25是默認(rèn)置信度閾值iou0.5是NMS的IoU閾值剛開(kāi)始評(píng)估就用默認(rèn)值跑出baseline再說(shuō)。輸出會(huì)給出每個(gè)類(lèi)別單獨(dú)的mAP50和mAP50-95這才是判斷每個(gè)類(lèi)健康狀況的第一手資料。mAP50和mAP50-95的區(qū)別要明確mAP50只要求預(yù)測(cè)框和真實(shí)框交并比超過(guò)0.5就算對(duì)比較寬容mAP50-95會(huì)對(duì)從0.5到0.95的一系列IoU閾值平均要求極其嚴(yán)格。小目標(biāo)居多的數(shù)據(jù)集mAP50-95普遍偏低這不必焦慮——工地安全監(jiān)控這個(gè)場(chǎng)景框的位置大致準(zhǔn)就行mAP50才是主要參考指標(biāo)mAP50-95用來(lái)橫向?qū)Ρ饶P桶姹尽?.2 混淆矩陣的讀法重點(diǎn)看哪幾個(gè)位置confusion_matrix.png是一個(gè)8類(lèi)背景的方陣行代表真實(shí)類(lèi)別列代表預(yù)測(cè)類(lèi)別對(duì)角線越亮越好。拿到圖先不急著看整體準(zhǔn)確率而是盯著三塊位置一是Hardhat行里和Person列交叉的地方亮則代表安全帽被誤檢成人員二是No-Hardhat行里和背景列交叉的地方亮則代表未戴帽違規(guī)行為被漏檢三是背景行里如果有大塊亮區(qū)說(shuō)明模型在沒(méi)有任何目標(biāo)的地方畫(huà)了框這類(lèi)誤檢在巡檢場(chǎng)景會(huì)直接觸發(fā)誤報(bào)。讀混淆矩陣還有一個(gè)技巧不要只看亮不亮要看同行的其他列是否出現(xiàn)“結(jié)構(gòu)性偏斜”。比如Person行里Hardhat列也很亮說(shuō)明模型本質(zhì)上把“人”和“帽子”耦合了這就是4.2說(shuō)的語(yǔ)義重疊問(wèn)題在圖上會(huì)表現(xiàn)為戴帽和沒(méi)戴帽的檢測(cè)結(jié)果互相打架。解決方向在前面已經(jīng)說(shuō)了合并類(lèi)別或者加后處理規(guī)則而不是單純堆訓(xùn)練輪次。5.3 badcase可視化按置信度排序找最難樣本指標(biāo)是匯總數(shù)據(jù)badcase才是具體問(wèn)題。用訓(xùn)練好的模型跑一遍測(cè)試集把預(yù)測(cè)結(jié)果可視化輸出然后人工翻圖。注意不要隨機(jī)翻按置信度排序來(lái)看。from ultralytics import YOLO model YOLO(runs/site_safety/yolov8n_600img/weights/best.pt) results model.predict( sourcedatasets/site_safety/images/test, conf0.25, saveTrue, # 把標(biāo)注了框的圖片存下來(lái) projectruns/badcase, nametest_pred, save_txtTrue, # 同時(shí)輸出預(yù)測(cè)的txt標(biāo)注方便后續(xù)對(duì)比 )跑完之后runs/badcase/test_pred里每張圖都畫(huà)了預(yù)測(cè)框。我一般先從置信度最高的誤檢看起——高置信度還檢錯(cuò)說(shuō)明模型學(xué)到了一個(gè)錯(cuò)誤模式這是最要命的然后看漏檢特別是No-Hardhat的漏檢因?yàn)檫@是工地安全監(jiān)控的核心報(bào)警項(xiàng)。corresponding的txt文件可以用來(lái)和真實(shí)標(biāo)注做IoU對(duì)比IoU低的那批圖就是你需要針對(duì)性補(bǔ)數(shù)據(jù)的樣本或者需要調(diào)整后處理邏輯的樣本。6. 進(jìn)階部署到工地實(shí)時(shí)監(jiān)控前先做這兩個(gè)動(dòng)作訓(xùn)練好的模型要真正放到工地監(jiān)控里光有best.pt是不夠的。工地現(xiàn)場(chǎng)跑的一般是邊緣計(jì)算盒子或者帶GPU的NVR設(shè)備PyTorch直接推理在性能和部署友好度上都不過(guò)關(guān)。第一步先把模型導(dǎo)成ONNX格式。yolo export modelruns/site_safety/yolov8n_600img/weights/best.pt \ formatonnx \ opset12 \ imgsz640導(dǎo)出后得到一個(gè)best.onnx用ONNX Runtime或者OpenVINO都能直接跑。opset12是兼容性比較好的版本老設(shè)備也能識(shí)別。導(dǎo)出前可以先跑一遍val對(duì)比ONNX和PyTorch的mAP因?yàn)榱炕騩pset轉(zhuǎn)換偶爾會(huì)帶來(lái)精度損失如果掉了超過(guò)1個(gè)點(diǎn)就要檢查哪層出了問(wèn)題。第二個(gè)動(dòng)作是設(shè)計(jì)報(bào)警后處理規(guī)則這是工地場(chǎng)景區(qū)別于通用目標(biāo)檢測(cè)的地方。監(jiān)控視頻每秒25幀不可能每幀都報(bào)警否則誤報(bào)會(huì)淹沒(méi)真實(shí)違規(guī)。常見(jiàn)做法是幀采樣加持續(xù)確認(rèn)每5秒取一幀做檢測(cè)檢測(cè)結(jié)果不直接觸發(fā)報(bào)警而是送入一個(gè)計(jì)數(shù)器連續(xù)N幀確認(rèn)存在違規(guī)才推送告警。# 偽代碼安全帽違規(guī)報(bào)警邏輯 frame_count 0 warn_count 0 while True: frame capture_next_frame() # 每5秒抽一幀 dets model(frame) # 目標(biāo)檢測(cè) hardhat_off [d for d in dets if d.cls 3] # No-Hardhat persons [d for d in dets if d.cls 6] # Person # 核心規(guī)則違規(guī)框和人員框位置匹配才算數(shù) for h in hardhat_off: if has_overlap(h, persons, iou_thresh0.3): warn_count 1 break if warn_count 3: # 連續(xù)3次抽查都發(fā)現(xiàn)違規(guī) send_alert(工人未佩戴安全帽) warn_count 0 if frame_count 10: # 一段時(shí)間后重置計(jì)數(shù)避免重復(fù)報(bào)警 warn_count 0這段偽代碼里最值得注意是has_overlap這一步。前面花了很長(zhǎng)的篇幅說(shuō)Hardhat和Person標(biāo)簽語(yǔ)義高度重疊那么在推理階段No-Hardhat框如果沒(méi)有和Person框重疊大概率是誤檢不應(yīng)該進(jìn)入報(bào)警計(jì)數(shù)。另外No-Hardhat和Hardhat同時(shí)出現(xiàn)在同一位置時(shí)按置信度取高者這個(gè)規(guī)則能過(guò)濾掉相當(dāng)一部分模型自身的不穩(wěn)定輸出。工地安全監(jiān)控這個(gè)場(chǎng)景模型的mAP只要夠用就行真正決定產(chǎn)品體驗(yàn)的是后處理規(guī)則能不能把誤報(bào)率壓下去。從那以后我每次拿到一個(gè)新的目標(biāo)檢測(cè)數(shù)據(jù)集都會(huì)強(qiáng)制先跑一遍標(biāo)簽體檢腳本把越界坐標(biāo)、缺失標(biāo)簽、類(lèi)別分布打印出來(lái)貼到項(xiàng)目文檔里。這個(gè)習(xí)慣救過(guò)我很多次希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取