:從數(shù)據(jù)組織到推理驗(yàn)證全鏈路實(shí)戰(zhàn))
簡(jiǎn)介本資源面向計(jì)算機(jī)視覺初學(xué)者與工業(yè)檢測(cè)方向的開發(fā)者提供一套基于YOLO的實(shí)時(shí)物體檢測(cè)完整代碼與數(shù)據(jù)資料重點(diǎn)覆蓋齒條、螺栓、螺母及裂縫等工業(yè)場(chǎng)景目標(biāo)的識(shí)別與定位適合用于課程設(shè)計(jì)、項(xiàng)目復(fù)現(xiàn)與算法入門實(shí)踐。壓縮包共2000個(gè)文件約199.17MB以1903個(gè)txt標(biāo)注或說(shuō)明文件為主輔以49個(gè)h頭文件、46個(gè)c源文件及少量cpp與md文檔構(gòu)成從數(shù)據(jù)標(biāo)注到網(wǎng)絡(luò)層實(shí)現(xiàn)的完整工程結(jié)構(gòu)。內(nèi)容預(yù)覽可見data.c、parser.c、image.c、network.c、region_layer.c、yolo_layer.c等核心模塊涉及數(shù)據(jù)加載、圖像處理、網(wǎng)絡(luò)構(gòu)建與檢測(cè)層實(shí)現(xiàn)便于讀者理解YOLO將檢測(cè)轉(zhuǎn)化為回歸問(wèn)題的整體流程。目前已有67人學(xué)習(xí)下載適合希望掌握實(shí)時(shí)檢測(cè)原理、動(dòng)手調(diào)試工業(yè)缺陷檢測(cè)模型的讀者參考。1. 從一堆齒條螺栓里把缺陷挑出來(lái)這套 YOLO 檢測(cè)資源到底能干什么產(chǎn)線上最磨人的活不是擰螺栓是盯著一堆長(zhǎng)得幾乎一樣的齒條、螺栓、螺母靠人眼判斷哪個(gè)有裂紋、哪個(gè)缺角、哪個(gè)螺紋不對(duì)。人盯兩小時(shí)就花眼漏檢率蹭蹭往上漲。這套資源就是沖著這個(gè)場(chǎng)景來(lái)的用 YOLO 做實(shí)時(shí)物體檢測(cè)把齒條、螺栓、螺母這些標(biāo)準(zhǔn)件從畫面里框出來(lái)順帶識(shí)別裂紋這類表面缺陷。它適合兩類人——一類是手上已經(jīng)有工業(yè)質(zhì)檢需求、想快速跑通一個(gè)能落地的檢測(cè) demo 的工程師另一類是正在學(xué) YOLO、想拿一個(gè)真實(shí)零件數(shù)據(jù)集練手的學(xué)生或轉(zhuǎn)行者。資源本身給的是可復(fù)現(xiàn)的訓(xùn)練與推理鏈路不是一份 PPT 講解你拿到手就能配環(huán)境、跑訓(xùn)練、看結(jié)果。下面我按自己拆包復(fù)現(xiàn)的順序把選型理由、環(huán)境配置、數(shù)據(jù)組織、訓(xùn)練參數(shù)和踩過(guò)的坑一條條講清楚。2. 為什么這個(gè)場(chǎng)景選 YOLO實(shí)時(shí)性與小目標(biāo)檢測(cè)的取舍2.1 齒條螺栓檢測(cè)對(duì)模型的三條硬約束工業(yè)零件檢測(cè)和通用目標(biāo)檢測(cè)不一樣它有三個(gè)繞不開的約束。第一是實(shí)時(shí)性產(chǎn)線節(jié)拍通常按秒算模型單幀推理必須壓到幾十毫秒以內(nèi)否則堆幀就是堆成本。第二是小目標(biāo)密集螺栓螺母在整幅圖里占比很小齒條的裂紋更是細(xì)長(zhǎng)條特征弱、背景雜。第三是類別不均衡正常件遠(yuǎn)多于缺陷件模型很容易學(xué)會(huì)“全判正?!边@種偷懶策略。YOLO 系列之所以在這個(gè)場(chǎng)景里被反復(fù)選中核心原因是它把檢測(cè)當(dāng)成單階段回歸問(wèn)題一次前向就出框和類別沒有兩階段方法那種候選區(qū)域生成的額外開銷。YOLOv8 這一代在 neck 部分用了 PAN-FPN 結(jié)構(gòu)淺層高分辨率特征和深層語(yǔ)義特征融合得比較充分對(duì)小目標(biāo)比早期版本友好不少。我一般會(huì)優(yōu)先選 v8 的 n 或 s 規(guī)模做基線參數(shù)量小、推理快精度不夠再往上換。提示不要一上來(lái)就上 x 或 l 規(guī)模。工業(yè)零件類別數(shù)少、紋理相對(duì)固定小模型往往就夠大模型帶來(lái)的精度提升經(jīng)常抵不過(guò)推理延遲的代價(jià)。2.2 單階段檢測(cè)的損失函數(shù)在零件場(chǎng)景里意味著什么YOLO 的損失由三塊組成分類損失、邊界框回歸損失、目標(biāo)置信度損失。v8 用的是 TaskAlignedAssigner 做正負(fù)樣本分配配合 CIoU 類的回歸損失。放到齒條螺栓場(chǎng)景里這個(gè)設(shè)計(jì)的影響很直接——正樣本分配策略決定了小目標(biāo)能不能被“看見”。如果錨點(diǎn)或分配閾值設(shè)得不合適密集排列的螺母很容易被漏分配訓(xùn)練時(shí) loss 看著在降實(shí)際召回上不去。我踩過(guò)一次坑螺母挨得太近默認(rèn)分配策略下相鄰兩個(gè)框互相干擾mAP 卡在 0.6 上不去。后來(lái)把輸入分辨率從 640 提到 960同時(shí)檢查了標(biāo)注框有沒有重疊粘連召回才正常。所以選 YOLO 不是選完就完事?lián)p失函數(shù)和分配策略背后的參數(shù)才是決定這套資源能不能在你數(shù)據(jù)上跑通的關(guān)鍵。2.3 和兩階段、Transformer 方案的邊界對(duì)比有人會(huì)問(wèn)現(xiàn)在 Transformer 檢測(cè)那么火為什么還用 YOLO。我的判斷是看場(chǎng)景。DETR 類方法在密集小目標(biāo)上收斂慢、訓(xùn)練數(shù)據(jù)需求大工業(yè)缺陷樣本本來(lái)就少硬上容易翻車。兩階段方法精度可能略高但推理速度在產(chǎn)線節(jié)拍面前基本沒得談。YOLO 的邊界在于類別數(shù)不多、目標(biāo)尺度跨度不算極端、對(duì)實(shí)時(shí)性有要求——這正好是齒條螺栓檢測(cè)的畫像。如果你的場(chǎng)景是超大圖里找?guī)讉€(gè)極小目標(biāo)或者需要像素級(jí)分割那這套資源的方向就不對(duì)得換思路。3. 環(huán)境配置與數(shù)據(jù)組織把訓(xùn)練鏈路先跑通3.1 環(huán)境搭建的版本對(duì)齊問(wèn)題YOLO 環(huán)境最煩的不是裝不上是版本對(duì)不上導(dǎo)致訓(xùn)練中途崩。我一般用 conda 建獨(dú)立環(huán)境把 Python、PyTorch、CUDA 三者版本鎖死。下面是我在 v8 上跑通的一套組合你可以照抄再按自己顯卡調(diào)整。# 創(chuàng)建獨(dú)立環(huán)境Python 版本不要追新3.9/3.10 最穩(wěn) conda create -n yolo_parts python3.10 -y conda activate yolo_parts # 安裝 PyTorchCUDA 版本按 nvidia-smi 顯示的驅(qū)動(dòng)能力選 # 這里以 CUDA 11.8 為例驅(qū)動(dòng)版本要 對(duì)應(yīng)要求 pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 安裝 ultralytics不要手動(dòng)裝一堆依賴讓它自己解析 pip install ultralytics # 驗(yàn)證環(huán)境和 GPU 是否可見 python -c import torch; print(torch.__version__, torch.cuda.is_available())邏輯說(shuō)明先隔離環(huán)境避免和系統(tǒng)里的其他項(xiàng)目打架PyTorch 的 CUDA 版本必須和驅(qū)動(dòng)匹配否則cuda.is_available()返回 False訓(xùn)練會(huì)靜默掉到 CPU 上慢到你以為卡死了。參數(shù)上torch2.1.0配cu118是我驗(yàn)證過(guò)和 ultralytics 兼容性較好的一組如果你顯卡驅(qū)動(dòng)較新可以換 cu121 的對(duì)應(yīng)版本。注意訓(xùn)練中 BN 層崩潰報(bào)錯(cuò)里帶 batch norm 或 NaN loss十有八九是 batch size 太小或?qū)W習(xí)率太大。單卡顯存不夠時(shí)寧可降分辨率也別把 batch 壓到 2 以下。3.2 數(shù)據(jù)集目錄結(jié)構(gòu)與標(biāo)注格式Y(jié)OLO 要求的數(shù)據(jù)組織很固定別自己發(fā)明結(jié)構(gòu)。圖像和標(biāo)簽分開放標(biāo)簽是每行一個(gè)目標(biāo)的 txt格式為類別索引 中心x 中心y 寬 高全部歸一化到 0~1。齒條、螺栓、螺母、裂紋這幾類類別索引從 0 開始連續(xù)編號(hào)。parts_dataset/ ├── images/ │ ├── train/ # 訓(xùn)練圖 │ └── val/ # 驗(yàn)證圖 ├── labels/ │ ├── train/ # 與訓(xùn)練圖同名的 txt │ └── val/ └── data.yaml # 數(shù)據(jù)集描述文件data.yaml是訓(xùn)練入口寫錯(cuò)路徑是最常見的低級(jí)錯(cuò)誤# data.yaml path: /home/user/parts_dataset # 數(shù)據(jù)集根目錄絕對(duì)路徑最穩(wěn) train: images/train # 相對(duì) path 的路徑 val: images/val nc: 4 # 類別數(shù)齒條/螺栓/螺母/裂紋 names: [rack, bolt, nut, crack]邏輯說(shuō)明path用絕對(duì)路徑能避免訓(xùn)練腳本工作目錄變化導(dǎo)致的找不到文件nc必須和 names 長(zhǎng)度一致多一個(gè)少一個(gè)都會(huì)在訓(xùn)練啟動(dòng)時(shí)報(bào)維度錯(cuò)誤。標(biāo)注這塊我建議用 labelImg 或 roboflow 導(dǎo)出 YOLO 格式別手寫坐標(biāo)歸一化算錯(cuò)一位整個(gè)框就飛了。3.3 從原始標(biāo)注到可訓(xùn)練集的一次性檢查數(shù)據(jù)進(jìn)訓(xùn)練前我會(huì)跑一段腳本做體檢統(tǒng)計(jì)每類框數(shù)量、檢查有沒有越界坐標(biāo)、看圖像和標(biāo)簽是否一一對(duì)應(yīng)。這一步能省掉后面幾小時(shí)的無(wú)效訓(xùn)練。import os from collections import Counter label_dir parts_dataset/labels/train img_dir parts_dataset/images/train counter Counter() bad [] for name in os.listdir(label_dir): if not name.endswith(.txt): continue # 圖像和標(biāo)簽必須同名缺圖就是臟數(shù)據(jù) img_name name.replace(.txt, .jpg) if not os.path.exists(os.path.join(img_dir, img_name)): bad.append(name) with open(os.path.join(label_dir, name)) as f: for line in f: parts line.split() if len(parts) ! 5: bad.append(name) continue cls, x, y, w, h parts # 歸一化坐標(biāo)必須在 0~1越界說(shuō)明標(biāo)注工具導(dǎo)出有問(wèn)題 if not all(0 float(v) 1 for v in (x, y, w, h)): bad.append(name) counter[int(cls)] 1 print(每類框數(shù)量:, dict(counter)) print(可疑文件:, bad[:20])邏輯說(shuō)明Counter統(tǒng)計(jì)各類別框數(shù)能立刻看出類別不均衡有多嚴(yán)重越界檢查攔住標(biāo)注導(dǎo)出錯(cuò)誤同名檢查攔住圖像標(biāo)簽錯(cuò)位。參數(shù)上如果某類框數(shù)只有個(gè)位數(shù)訓(xùn)練前就得考慮過(guò)采樣或加數(shù)據(jù)增強(qiáng)否則模型學(xué)不會(huì)這一類。4. 訓(xùn)練參數(shù)怎么設(shè)從基線到能用的調(diào)參路徑4.1 啟動(dòng)訓(xùn)練與關(guān)鍵參數(shù)含義環(huán)境數(shù)據(jù)都齊了訓(xùn)練命令本身很短但每個(gè)參數(shù)都影響結(jié)果。下面這條是我在齒條螺栓數(shù)據(jù)集上的基線配置。yolo detect train \ dataparts_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz960 \ batch16 \ lr00.01 \ patience20 \ projectruns/parts \ namebaseline邏輯說(shuō)明modelyolov8s.pt用官方預(yù)訓(xùn)練權(quán)重做遷移學(xué)習(xí)比從零訓(xùn)收斂快得多這是小數(shù)據(jù)集能跑出效果的前提。imgsz960是我針對(duì)小目標(biāo)特意提上去的默認(rèn) 640 對(duì)螺栓螺母這種小框召回不夠。lr00.01是初始學(xué)習(xí)率配合默認(rèn)的余弦退火策略。patience20表示 20 輪驗(yàn)證指標(biāo)不漲就早停省時(shí)間也防過(guò)擬合。參數(shù)怎么改顯存不夠先降batch再降imgsz召回低優(yōu)先提imgsz過(guò)擬合明顯就加dropout或減epochs。別一次改一堆參數(shù)那樣出了問(wèn)題你根本不知道是哪個(gè)引起的。4.2 數(shù)據(jù)增強(qiáng)在零件檢測(cè)里的取舍YOLO 默認(rèn)開了一堆增強(qiáng)但工業(yè)零件場(chǎng)景不能照單全收。翻轉(zhuǎn)、縮放、色彩抖動(dòng)一般沒問(wèn)題但上下翻轉(zhuǎn)對(duì)齒條這種有方向性的零件可能引入錯(cuò)誤先驗(yàn)馬賽克增強(qiáng)在小目標(biāo)密集時(shí)會(huì)把框拼得亂七八糟。# 在 data.yaml 同級(jí)或訓(xùn)練參數(shù)里覆蓋增強(qiáng)配置 hsv_h: 0.015 # 色調(diào)抖動(dòng)零件顏色固定可調(diào)小 hsv_s: 0.7 hsv_v: 0.4 degrees: 0.0 # 旋轉(zhuǎn)角度零件有方向就別亂轉(zhuǎn) translate: 0.1 scale: 0.5 flipud: 0.0 # 上下翻轉(zhuǎn)關(guān)掉齒條方向敏感 fliplr: 0.5 mosaic: 0.5 # 馬賽克降一半小目標(biāo)密集時(shí)別開滿邏輯說(shuō)明flipud0.0是因?yàn)辇X條上下翻轉(zhuǎn)后語(yǔ)義變了模型會(huì)學(xué)到錯(cuò)誤特征mosaic降到 0.5 是血淚經(jīng)驗(yàn)開滿時(shí)密集小框拼接后標(biāo)注容易錯(cuò)位驗(yàn)證指標(biāo)虛高但實(shí)際推理拉胯。參數(shù)調(diào)整的原則是先關(guān)掉可能破壞語(yǔ)義的增強(qiáng)再逐步加回來(lái)看驗(yàn)證集表現(xiàn)。4.3 訓(xùn)練過(guò)程監(jiān)控與指標(biāo)解讀訓(xùn)練跑起來(lái)后別只盯著 loss 看。真正決定模型能不能用的是驗(yàn)證集上的 mAP50 和 mAP50-95以及混淆矩陣?;煜仃嚳偤筒晃ㄒ皇呛芏嗳擞龅降男W(xué)問(wèn)題本質(zhì)是預(yù)測(cè)框和真實(shí)框的匹配閾值導(dǎo)致的不同 IoU 閾值下統(tǒng)計(jì)口徑不同別慌。# 訓(xùn)練結(jié)束后用驗(yàn)證集評(píng)估輸出各類別指標(biāo) yolo detect val \ modelruns/parts/baseline/weights/best.pt \ dataparts_dataset/data.yaml \ imgsz960邏輯說(shuō)明best.pt是驗(yàn)證指標(biāo)最好的權(quán)重不是最后一輪的。評(píng)估時(shí)imgsz要和訓(xùn)練一致否則小目標(biāo)指標(biāo)會(huì)掉??唇Y(jié)果重點(diǎn)看裂紋那一類的召回缺陷類召回低意味著漏檢產(chǎn)線上這是最不能接受的。5. 避坑與排查那些讓訓(xùn)練白跑的常見問(wèn)題5.1 訓(xùn)練 loss 正常但 mAP 極低現(xiàn)象loss 一路下降驗(yàn)證 mAP 卻卡在 0.1 以下。原因通常是標(biāo)注格式錯(cuò)了——坐標(biāo)沒歸一化、類別索引和 names 對(duì)不上或者圖像標(biāo)簽文件名不匹配。解決回到 3.3 的體檢腳本逐項(xiàng)核對(duì)重點(diǎn)看坐標(biāo)是否在 0~1 之間、類別索引是否從 0 連續(xù)。5.2 訓(xùn)練中途 BN 崩潰或 loss 變 NaN現(xiàn)象跑幾十輪后突然報(bào) batch norm 相關(guān)錯(cuò)誤loss 變成 NaN。原因一般是學(xué)習(xí)率過(guò)大或 batch size 太小BN 層統(tǒng)計(jì)量不穩(wěn)定。解決把lr0降到 0.001batch提到 8 以上顯存不夠就降imgsz而不是繼續(xù)壓 batch。5.3 邊緣部署時(shí)誤檢率飆升現(xiàn)象本地驗(yàn)證 mAP 挺好部署到邊緣設(shè)備后誤檢一堆。原因多是推理分辨率被壓縮、量化精度損失或者現(xiàn)場(chǎng)光照和訓(xùn)練集差異大。解決部署時(shí)保持和訓(xùn)練一致的預(yù)處理別為了速度把imgsz砍太狠現(xiàn)場(chǎng)補(bǔ)采一批圖做微調(diào)比調(diào)閾值管用。5.4 混淆矩陣總和不唯一現(xiàn)象兩次評(píng)估混淆矩陣數(shù)字對(duì)不上。原因是匹配閾值和置信度閾值不同統(tǒng)計(jì)口徑變了。解決固定評(píng)估參數(shù)記錄每次用的conf和iou閾值對(duì)比時(shí)保證一致別拿不同口徑的結(jié)果互相印證。5.5 裂紋這類細(xì)長(zhǎng)缺陷召回上不去現(xiàn)象齒條裂紋漏檢嚴(yán)重。原因是細(xì)長(zhǎng)目標(biāo)特征弱默認(rèn)錨點(diǎn)尺度不匹配。解決提高輸入分辨率檢查標(biāo)注框是否把裂紋框得太緊導(dǎo)致特征丟失必要時(shí)單獨(dú)對(duì)裂紋類做過(guò)采樣。6. 進(jìn)階技巧把訓(xùn)練好的模型真正用起來(lái)訓(xùn)練出best.pt只是開始能不能落地看推理和驗(yàn)證。我一般會(huì)先用一段腳本在視頻或圖片上跑推理肉眼確認(rèn)框的位置和類別對(duì)不對(duì)再談部署。from ultralytics import YOLO # 加載訓(xùn)練好的權(quán)重 model YOLO(runs/parts/baseline/weights/best.pt) # 對(duì)單張圖推理conf 控制置信度閾值 results model.predict( sourcetest_rack.jpg, imgsz960, # 必須和訓(xùn)練一致 conf0.4, # 閾值太低誤檢多太高漏檢多按場(chǎng)景調(diào) iou0.5, # NMS 的 IoU 閾值密集目標(biāo)可適當(dāng)調(diào)低 saveTrue ) # 打印每個(gè)框的類別和置信度方便核對(duì) for box in results[0].boxes: print(model.names[int(box.cls)], float(box.conf))邏輯說(shuō)明conf和iou是推理階段最該調(diào)的兩個(gè)參數(shù)。缺陷檢測(cè)里我通常把conf設(shè)低一點(diǎn)保召回寧可多報(bào)也別漏報(bào)后處理再人工復(fù)核。imgsz必須和訓(xùn)練一致這是很多人忽略的細(xì)節(jié)改了分辨率小目標(biāo)直接消失。驗(yàn)證方法上我會(huì)做兩件事一是拿一批沒參與訓(xùn)練的現(xiàn)場(chǎng)圖跑一遍統(tǒng)計(jì)漏檢和誤檢二是把推理結(jié)果和標(biāo)注疊在一起看確認(rèn)框的貼合度。這兩步做完心里才有底。從那以后我每次訓(xùn)完模型都強(qiáng)制走一遍“現(xiàn)場(chǎng)圖推理 人工核對(duì)”的流程絕不只看驗(yàn)證集數(shù)字就上線。這套齒條螺栓檢測(cè)資源的價(jià)值不在于它給了多完美的模型而在于它把從數(shù)據(jù)組織到推理驗(yàn)證的鏈路完整擺出來(lái)了你照著跑一遍坑踩一遍就真正會(huì)用了。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取