數(shù)據(jù)集900張圖微調(diào)YOLO全流程實(shí)戰(zhàn)指南)
簡(jiǎn)介面向計(jì)算機(jī)視覺(jué)目標(biāo)檢測(cè)任務(wù)尤其適合工業(yè)管道缺陷自動(dòng)識(shí)別場(chǎng)景。數(shù)據(jù)集包含超過(guò)900張帶有標(biāo)注信息的管道圖片已處理為YOLO格式并完成數(shù)據(jù)增廣可直接用于YOLO全系列模型訓(xùn)練滿足高校實(shí)驗(yàn)、算法調(diào)試及工業(yè)檢測(cè)方案驗(yàn)證等多種需求。壓縮包共1893個(gè)文件以945張jpg圖像與946個(gè)配套txt標(biāo)簽文件為主體另附可視化py腳本與class類別文件整體大小32.47MB便于快速下載與部署目前已有1940人瀏覽學(xué)習(xí)。資源劃分了訓(xùn)練集與驗(yàn)證集標(biāo)簽統(tǒng)一為defect類別借助show腳本可直觀查看邊界框繪制效果幫助評(píng)估檢測(cè)精度、排查漏檢與誤檢問(wèn)題。對(duì)希望快速上手YOLO管道缺陷檢測(cè)、或構(gòu)建工業(yè)安全檢測(cè)原型的開發(fā)者而言這是一份能直接復(fù)用的實(shí)操素材。1. 管道缺陷檢測(cè)數(shù)據(jù)集900張標(biāo)注圖到底能干什么管道缺陷檢測(cè)數(shù)據(jù)集這個(gè)標(biāo)題聽(tīng)著就像又一個(gè)普通標(biāo)注包但它恰恰是工業(yè)視覺(jué)里最典型的一種“小樣本但必須做”的場(chǎng)景。900多張圖片和標(biāo)簽放在通用目標(biāo)檢測(cè)里不算多可放在管道巡檢、管網(wǎng)檢修、焊縫復(fù)核這類細(xì)分方向上已經(jīng)是一份值得認(rèn)真對(duì)待的家底了。這類數(shù)據(jù)集里的缺陷類型通常集中在腐蝕、裂紋、焊縫缺陷、破損、變形等幾類圖片大多來(lái)自管道內(nèi)窺設(shè)備或檢修現(xiàn)場(chǎng)拍照光照差、背景雜、缺陷目標(biāo)小和自動(dòng)駕駛那種“大而全”的數(shù)據(jù)集完全是兩個(gè)物種。所以它的正確定位不是“拿來(lái)從零訓(xùn)練”而是“拿預(yù)訓(xùn)練模型做遷移學(xué)習(xí)微調(diào)”——用900張圖把模型從通用檢測(cè)器調(diào)成管道缺陷專用檢測(cè)器。這個(gè)方向值不值得做取決于你能不能把這900多張圖吃透、喂對(duì)、調(diào)好而不是圖省事直接開訓(xùn)。2. 先弄清楚數(shù)據(jù)集里有什么目錄結(jié)構(gòu)、標(biāo)注格式與缺陷類別拿到這類數(shù)據(jù)集第一件事千萬(wàn)別是解壓完就寫訓(xùn)練命令。管道缺陷數(shù)據(jù)集的坑有一半在標(biāo)注文件里。先花半小時(shí)把目錄結(jié)構(gòu)和標(biāo)注格式對(duì)清楚后面訓(xùn)練能少交三天學(xué)費(fèi)。2.1 解壓后先對(duì)賬images與labels的目錄規(guī)約這類數(shù)據(jù)集常見(jiàn)的組織方式是images和labels兩大目錄各自下面再按train和val分開標(biāo)注文件與圖片同名。拿到壓縮包后先看結(jié)構(gòu)再數(shù)數(shù)量別只看總圖片數(shù)要看train和val的分配比例。# 假設(shè)數(shù)據(jù)集根目錄是 pipe_defect/ # 統(tǒng)計(jì)圖片數(shù)量 find pipe_defect/images -name *.jpg | wc -l # 統(tǒng)計(jì)標(biāo)簽數(shù)量 find pipe_defect/labels -name *.txt | wc -l # 找出空標(biāo)簽文件空文件說(shuō)明該圖沒(méi)有標(biāo)注目標(biāo) find pipe_defect/labels -name *.txt -size 0 | head -20 # 看類別定義文件內(nèi)容確認(rèn)類別編號(hào)與名稱的對(duì)應(yīng)關(guān)系 cat pipe_defect/classes.txt這三條命令能幫你快速定位兩類問(wèn)題一是圖片和標(biāo)簽數(shù)量對(duì)不上說(shuō)明有漏標(biāo)或誤刪文件二是空標(biāo)簽文件過(guò)多如果超過(guò)10%你就要考慮這些空?qǐng)D對(duì)訓(xùn)練是幫助還是干擾。classes.txt是后續(xù)寫dataset.yaml的依據(jù)它每行的順序就是類別編號(hào)比如“corrosion”是0“crack”是1這個(gè)順序不能隨便改改了等于把所有標(biāo)簽重新編號(hào)。對(duì)賬的時(shí)候如果發(fā)現(xiàn)train和val的比例是隨機(jī)切的我一般會(huì)重新劃分一次。900多張圖的數(shù)據(jù)集理想分配是train占80%、val占20%但一定要保證每類缺陷在val里都出現(xiàn)否則驗(yàn)證時(shí)某一類AP永遠(yuǎn)是0你都不知道是模型沒(méi)學(xué)會(huì)還是val里壓根沒(méi)這類的樣本。2.2 標(biāo)注格式Y(jié)OLO txt還是VOC xml決定你的工作量管道缺陷數(shù)據(jù)集通常給兩種標(biāo)注格式之一YOLO txt或Pascal VOC xml。YOLO格式可以直接喂給YOLO系列訓(xùn)練框架VOC xml則需要轉(zhuǎn)換。二者的本質(zhì)區(qū)別在于坐標(biāo)體系YOLO txt里存的是歸一化中心點(diǎn)坐標(biāo)和寬高即x_center、y_center、width、height取值范圍0到1VOC xml里存的是像素坐標(biāo)xmin、ymin、xmax、ymax必須除以圖片寬高才能歸一化。如果你拿到的是VOC xml數(shù)據(jù)集就需要先轉(zhuǎn)換成YOLO txt。下面這段轉(zhuǎn)換腳本是我常用的基礎(chǔ)版本覆蓋了大多數(shù)情況import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path: Path, class_list: list, out_txt: Path): 將VOC格式XML轉(zhuǎn)換為YOLO格式txt - xml_path: 單張圖片對(duì)應(yīng)的XML標(biāo)注文件 - class_list: 類別名稱列表順序決定類別編號(hào) - out_txt: 輸出的txt標(biāo)簽文件路徑 tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) with open(out_txt, w) as out: for obj in root.iter(object): name obj.find(name).text if name not in class_list: continue cls_id class_list.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 關(guān)鍵點(diǎn)寬高要取差值不是直接填坐標(biāo)值 box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 中心點(diǎn)坐標(biāo)是兩端坐標(biāo)的均值除以寬高完成歸一化 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h # 防止邊緣坐標(biāo)越界把計(jì)算結(jié)果限制在[0,1]內(nèi) x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_w min(box_w, 1.0) box_h min(box_h, 1.0) out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\n)這個(gè)腳本最關(guān)鍵的是四個(gè)參數(shù)的計(jì)算邏輯寬高必須用xmax - xmin中心點(diǎn)必須用兩端坐標(biāo)的均值而且分母必須是圖片像素寬高。很多人在這一步翻車把歸一化理解成只除以寬或只除以高導(dǎo)致框變形。轉(zhuǎn)換完隨便挑三五個(gè)文件用標(biāo)注可視化腳本畫到原圖上肉眼看一眼位置是否貼合缺陷區(qū)域這比任何自動(dòng)化校驗(yàn)都直接。管道缺陷數(shù)據(jù)集里還有一種常見(jiàn)情況標(biāo)注框比實(shí)際缺陷區(qū)域大一圈。因?yàn)楣艿纼?nèi)部光線差標(biāo)注員很難精準(zhǔn)框住邊緣不規(guī)則的腐蝕區(qū)域框帶一點(diǎn)冗余是正常的。轉(zhuǎn)換腳本處理不了這類問(wèn)題只能靠可視化抽查來(lái)判斷要不要接受這批標(biāo)注。2.3 用Python腳本做質(zhì)量審計(jì)壞標(biāo)簽比沒(méi)有標(biāo)簽更傷模型900多張圖的標(biāo)注質(zhì)量直接決定微調(diào)效果的上限。標(biāo)注里最常見(jiàn)的四類問(wèn)題類別編號(hào)越界、中心坐標(biāo)不在圖內(nèi)、寬高為0或超界、文件格式少列。這些問(wèn)題不會(huì)讓訓(xùn)練報(bào)錯(cuò)但會(huì)讓模型學(xué)到錯(cuò)誤的邊界。下面這個(gè)審計(jì)腳本能幫你快速跑一遍全量標(biāo)簽from pathlib import Path def audit_labels(labels_dir: str, class_num: int): 檢查目錄下所有YOLO txt標(biāo)簽的合法性 - labels_dir: labels目錄路徑 - class_num: 類別總數(shù)用于判斷類別編號(hào)是否越界 bad_count 0 for txt in Path(labels_dir).glob(*.txt): with open(txt) as f: lines f.readlines() # 空標(biāo)簽文件不報(bào)錯(cuò)但要單獨(dú)統(tǒng)計(jì)數(shù)量 if not lines: print(f[EMPTY] {txt.name}) bad_count 1 continue for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: print(f[FORMAT] {txt.name} line {i 1}: {line.strip()}) bad_count 1 continue cls float(parts[0]) x_center float(parts[1]) y_center float(parts[2]) box_w float(parts[3]) box_h float(parts[4]) if cls 0 or cls class_num: print(f[CLASS] {txt.name} cls{cls}) bad_count 1 if not (0 x_center 1 and 0 y_center 1): print(f[CENTER] {txt.name} center({x_center:.3f},{y_center:.3f})) bad_count 1 if box_w 0 or box_h 0 or box_w 1 or box_h 1: print(f[SIZE] {txt.name} size({box_w:.3f},{box_h:.3f})) bad_count 1 print(f檢查完成問(wèn)題標(biāo)簽總數(shù): {bad_count}) # 用法audit_labels(pipe_defect/labels/train, class_num5)這段腳本跑出來(lái)的結(jié)果建議按問(wèn)題類型分別處理空標(biāo)簽文件直接保留訓(xùn)練時(shí)模型會(huì)把它當(dāng)作背景圖類別越界必須修正通常是classes.txt順序和標(biāo)注方文檔不一致中心點(diǎn)坐標(biāo)越界則說(shuō)明標(biāo)注時(shí)把人手畫到了圖片邊緣之外這類標(biāo)簽要么裁剪掉越界部分要么刪除整張圖不要硬留。管道缺陷圖片里很多目標(biāo)本身就貼著圖像邊沿這類問(wèn)題出現(xiàn)的頻率不低。跑完審計(jì)你會(huì)發(fā)現(xiàn)所謂“900張干凈標(biāo)注”其實(shí)經(jīng)不起細(xì)看。這很正常工業(yè)數(shù)據(jù)集的通病就是標(biāo)注質(zhì)量參差。審計(jì)的意義在于讓你提前知道哪些圖需要人工復(fù)核而不是等訓(xùn)練完才發(fā)現(xiàn)模型在某個(gè)類別上完全失靈。3. 把管道缺陷數(shù)據(jù)集喂進(jìn)YOLO從數(shù)據(jù)組織到訓(xùn)練參數(shù)審計(jì)和格式轉(zhuǎn)換都做完之后才能進(jìn)入訓(xùn)練環(huán)節(jié)。這一章說(shuō)清楚三件事數(shù)據(jù)集文件怎么組織、訓(xùn)練參數(shù)怎么定、數(shù)據(jù)增強(qiáng)怎么開。900張圖的數(shù)據(jù)量決定了這些配置和通用目標(biāo)檢測(cè)不一樣照搬大模型訓(xùn)練方案大概率要翻車。3.1 用dataset.yaml把訓(xùn)練和驗(yàn)證路徑寫清楚YOLO訓(xùn)練框架讀取數(shù)據(jù)集的入口就是一個(gè)yaml文件里面寫明訓(xùn)練集、驗(yàn)證集圖片路徑和類別列表。管道缺陷數(shù)據(jù)集的yaml配置長(zhǎng)這樣# pipe_defect.yaml path: /your/abs/path/pipe_defect # 數(shù)據(jù)集根目錄的絕對(duì)路徑 train: images/train # 訓(xùn)練集圖片目錄相對(duì)于path val: images/val # 驗(yàn)證集圖片目錄相對(duì)于path nc: 5 # 類別總數(shù)與classes.txt行數(shù)一致 names: 0: corrosion # 腐蝕 1: crack # 裂紋 2: weld_defect # 焊縫缺陷 3: dent # 凹陷 4: pitting # 點(diǎn)蝕path這一項(xiàng)一定要寫絕對(duì)路徑因?yàn)橛?xùn)練進(jìn)程的工作目錄可能不在數(shù)據(jù)集根目錄。train和val寫的是相對(duì)于path的路徑images/train和images/val是圖片目錄框架會(huì)自動(dòng)去同級(jí)的labels目錄里找同名txt。names的編號(hào)必須和標(biāo)簽文件里的類別編號(hào)一致否則模型訓(xùn)練時(shí)會(huì)把corrosion當(dāng)成crack來(lái)學(xué)。寫這個(gè)文件的常見(jiàn)錯(cuò)誤是類別數(shù)量跟實(shí)際不符。管道缺陷數(shù)據(jù)集有些版本會(huì)把“background”也當(dāng)作一個(gè)類別如果classes.txt里寫了6行但真正標(biāo)注的只有5類nc應(yīng)該填5還是6以實(shí)際標(biāo)注為準(zhǔn)看標(biāo)簽文件里出現(xiàn)的最大類別編號(hào)是多少而不是數(shù)classes.txt的行數(shù)。3.2 訓(xùn)練參數(shù)epochs、batch、imgsz的搭配邏輯900張圖的微調(diào)訓(xùn)練我通常推薦的命令是這樣yolo train \ modelyolov8n.pt \ datapipe_defect.yaml \ epochs100 \ batch16 \ imgsz640 \ patience15 \ lr00.001 \ augmentTrue參數(shù)含義拆開講。modelyolov8n.pt表示加載預(yù)訓(xùn)練權(quán)重用n版本是因?yàn)樗鼌?shù)少、不容易在小數(shù)據(jù)集上過(guò)擬合900張圖喂給yolov8x這種大模型訓(xùn)練集loss會(huì)很好看驗(yàn)證集mAP大概率原地踏步。epochs100不是硬性指標(biāo)配合patience15意思是連續(xù)15個(gè)epoch驗(yàn)證集mAP沒(méi)有提升就自動(dòng)停通常實(shí)際跑到50到70輪就收斂了。batch16在大多數(shù)單卡GPU上都能跑如果你顯存不夠就降到8但不要低于4否則batch normalization的統(tǒng)計(jì)量不穩(wěn)定。imgsz640是輸入分辨率管道缺陷檢測(cè)場(chǎng)景里可以適當(dāng)加大到960因?yàn)楹芏嗔鸭y和點(diǎn)蝕在原始圖片里只占幾十像素分辨率太低等于把缺陷直接抹掉了。加大imgsz的代價(jià)是顯存占用和訓(xùn)練時(shí)間翻倍所以一般先跑640驗(yàn)證流程再跑960追求精度。lr00.001是初始學(xué)習(xí)率加載預(yù)訓(xùn)練權(quán)重微調(diào)時(shí)不需要用默認(rèn)的0.01小數(shù)據(jù)集用小學(xué)習(xí)率更穩(wěn)。這個(gè)值如果發(fā)現(xiàn)訓(xùn)練loss震蕩厲害可以再降到0.0005但要給足epoch數(shù)學(xué)習(xí)率太低收斂會(huì)變慢。訓(xùn)練過(guò)程要盯兩條曲線訓(xùn)練集loss和驗(yàn)證集mAP。如果訓(xùn)練集loss持續(xù)下降但驗(yàn)證集mAP在某個(gè)數(shù)值附近不動(dòng)說(shuō)明過(guò)擬合已經(jīng)開始這時(shí)候不是加數(shù)據(jù)就是減弱增強(qiáng)而不是繼續(xù)加epoch。3.3 數(shù)據(jù)增強(qiáng)別一上來(lái)就開滿管道缺陷有自己的脾氣YOLO訓(xùn)練框架默認(rèn)開著mosaic、翻轉(zhuǎn)、縮放等增強(qiáng)手段但對(duì)管道缺陷數(shù)據(jù)集默認(rèn)配置不一定合適。原因在于管道缺陷有很強(qiáng)的方向性和尺度特征裂紋通常沿管道走向分布腐蝕區(qū)域形狀不規(guī)則但尺度相對(duì)固定。如果增強(qiáng)配置把圖片隨意旋轉(zhuǎn)90度或大幅縮放學(xué)到的特征可能與真實(shí)場(chǎng)景背離。常見(jiàn)的做法是保留基礎(chǔ)增強(qiáng)、控制強(qiáng)增強(qiáng)的程度。下面這段超參數(shù)配置代表了我在管道缺陷數(shù)據(jù)上的常用設(shè)定# 在訓(xùn)練腳本中覆蓋默認(rèn)增強(qiáng)參數(shù) mosaic: 0.5 # mosaic概率降到0.5避免大量小目標(biāo)拼接偽影 fliplr: 0.5 # 水平翻轉(zhuǎn)保留裂紋左右對(duì)稱 flipud: 0.0 # 垂直翻轉(zhuǎn)關(guān)掉管道方向感不能被破壞 hsv_h: 0.015 # 色調(diào)增強(qiáng)小幅度管道內(nèi)壁顏色不能亂變 hsv_s: 0.5 # 飽和度增強(qiáng)中等 degrees: 0.0 # 旋轉(zhuǎn)關(guān)閉缺陷方向是有物理含義的 scale: 0.5 # 縮放增強(qiáng)保留模擬不同距離拍攝mosaic降到0.5而不是默認(rèn)的1.0是因?yàn)閙osaic會(huì)把四張圖拼在一起導(dǎo)致小目標(biāo)數(shù)量暴增而管道缺陷已經(jīng)有大量小目標(biāo)了再拼接會(huì)讓模型花太多精力在異常尺度的目標(biāo)上干擾正常學(xué)習(xí)。degrees0.0是很多人的血淚教訓(xùn)換來(lái)的旋轉(zhuǎn)增強(qiáng)對(duì)通用目標(biāo)檢測(cè)是好東西但管道缺陷檢測(cè)里裂紋的水平或垂直走向本身是判別的有效特征轉(zhuǎn)個(gè)90度后這個(gè)特征就失真了模型學(xué)了等于白學(xué)。如果你非要旋轉(zhuǎn)最多只能允許正負(fù)10度而且要在后期關(guān)閉。還有一點(diǎn)容易忽略觀察驗(yàn)證集效果時(shí)不要只盯mAP還要看每類的AP。管道缺陷數(shù)據(jù)集的類別分布通常極不均衡全局mAP好但某類AP接近0的情況非常常見(jiàn)。下一章展開說(shuō)。4. 避坑指南管道缺陷數(shù)據(jù)集最常見(jiàn)的4個(gè)坑這部分寫的是我在多次管道缺陷檢測(cè)項(xiàng)目里踩過(guò)的坑每條都按“現(xiàn)象→原因→解決”來(lái)寫。你能搜到管道缺陷數(shù)據(jù)集這個(gè)關(guān)鍵詞說(shuō)明多半已經(jīng)在訓(xùn)練或準(zhǔn)備訓(xùn)練了這些坑越早知道越省錢。4.1 類別不平衡腐蝕占大頭裂紋只有幾十張現(xiàn)象訓(xùn)練結(jié)束后看驗(yàn)證結(jié)果總體mAP0.5在0.7左右但按類別翻看時(shí)發(fā)現(xiàn)corrosion的AP有0.85而crack的AP只有0.15甚至趨近于0。模型看起來(lái)“能用”實(shí)際一部署到現(xiàn)場(chǎng)真正最危險(xiǎn)的裂紋一條都測(cè)不出來(lái)。原因900多張圖里腐蝕類樣本可能占了七八百?gòu)埩鸭y類只有幾十張。模型在訓(xùn)練時(shí)見(jiàn)過(guò)的大部分正樣本都是腐蝕自然把“缺陷”這個(gè)概念的權(quán)重全部壓在了腐蝕特征上。目標(biāo)檢測(cè)損失函數(shù)里每張圖每個(gè)框都是等權(quán)重的樣本多的類別主導(dǎo)了梯度方向。解決常見(jiàn)做法是兩個(gè)方向同時(shí)走。一是數(shù)據(jù)層面對(duì)少數(shù)類做過(guò)采樣把裂紋樣本在訓(xùn)練數(shù)據(jù)中復(fù)制幾份配合隨機(jī)增強(qiáng)讓模型每次看到略有不同的裂紋圖。二是損失函數(shù)層面如果用的是可調(diào)損失權(quán)重的訓(xùn)練框架給少數(shù)類提高loss權(quán)重。我一般傾向先做數(shù)據(jù)過(guò)采樣因?yàn)樗庇^、可控、不依賴框架特性。還要記住驗(yàn)證集里每類樣本都不少于5張否則算出來(lái)的單類AP方差太大可信度不高。4.2 缺陷目標(biāo)太小幾十像素的裂紋在特征圖里消失了現(xiàn)象訓(xùn)練的loss曲線正常下降驗(yàn)證時(shí)mAP0.5和mAP0.5-95相差非常大前者0.7后者只有0.25。進(jìn)一步看檢測(cè)結(jié)果大塊腐蝕檢測(cè)出來(lái)了細(xì)裂紋完全漏檢。原因管道內(nèi)壁的裂紋寬度可能只有十幾個(gè)像素經(jīng)過(guò)模型下采樣32倍后到了特征圖里就剩下不到1個(gè)像素特征已經(jīng)丟失。imgsz設(shè)成640時(shí)這個(gè)問(wèn)題尤其嚴(yán)重。解決最直接的辦法是提高imgsz到960或1280讓小目標(biāo)在輸入層就占更多像素。顯存不夠的話可以改用tiling策略——把大圖切成若干小塊分別檢測(cè)再合并結(jié)果。tiling的代價(jià)是推理時(shí)間線性增加但對(duì)管道巡檢這種離線分析場(chǎng)景完全可接受。另一個(gè)技巧是關(guān)注小目標(biāo)的增強(qiáng)參數(shù)模型默認(rèn)可能有針對(duì)小目標(biāo)的增強(qiáng)配置不要關(guān)掉它。管道缺陷檢測(cè)的任務(wù)里小目標(biāo)不是邊緣情況而是主場(chǎng)景這點(diǎn)和通用檢測(cè)有很大區(qū)別。4.3 標(biāo)簽坐標(biāo)轉(zhuǎn)換錯(cuò)誤xy順序?qū)懛?、歸一化分母抄錯(cuò)現(xiàn)象訓(xùn)練能正常跑下去loss也在降但推理時(shí)檢測(cè)框位置明顯偏了比如缺陷在圖上方框卻框在圖中間甚至下方。原因VOC轉(zhuǎn)YOLO時(shí)x_center和y_center的計(jì)算順序?qū)懛椿蛘叻帜赣昧藞D片寬度的值去除高度方向的坐標(biāo)。這類錯(cuò)誤在代碼里非常難發(fā)現(xiàn)因?yàn)閘oss照常下降模型以為自己學(xué)的是“特征到框”的映射實(shí)際學(xué)的是“特征到錯(cuò)誤框”的映射。解決轉(zhuǎn)換后馬上做可視化驗(yàn)證。寫一個(gè)簡(jiǎn)單的腳本讀取圖片、讀取標(biāo)簽、用OpenCV或matplotlib把框畫出來(lái)人工看5到10張。下面是最簡(jiǎn)驗(yàn)證邏輯import cv2 def draw_yolo_box(img_path: str, label_path: str, save_path: str): 在圖上畫出YOLO標(biāo)注框用于轉(zhuǎn)換后的人工抽查 img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f.readlines(): cls, x, y, bw, bh line.strip().split()[:5] cls int(cls) x, y, bw, bh float(x), float(y), float(bw), float(bh) # 還原像素坐標(biāo)注意是中心點(diǎn)加減半寬高 xmin int((x - bw / 2) * w) ymin int((y - bh / 2) * h) xmax int((x bw / 2) * w) ymax int((y bh / 2) * h) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2) cv2.putText(img, str(cls), (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(save_path, img)這一步的核心不是代碼多高級(jí)而是“人眼確認(rèn)”。每批次轉(zhuǎn)換完隨機(jī)抽5張圖看一眼就能避免坐標(biāo)順序錯(cuò)誤這類低級(jí)問(wèn)題帶著你跑兩三天冤枉路。4.4 過(guò)擬合訓(xùn)練loss非常低驗(yàn)證mAP卻不再提升現(xiàn)象訓(xùn)練到第20個(gè)epoch開始訓(xùn)練集的box_loss持續(xù)下降驗(yàn)證集的mAP在0.7附近橫盤再往后train loss繼續(xù)走低、驗(yàn)證集紋絲不動(dòng)典型的過(guò)擬合信號(hào)。原因900張圖對(duì)目標(biāo)檢測(cè)模型來(lái)說(shuō)確實(shí)少尤其當(dāng)缺陷形態(tài)多樣時(shí)模型很容易把訓(xùn)練集里的背景特征也背下來(lái)。很多人以為加大數(shù)據(jù)增強(qiáng)就能解決但增強(qiáng)開太猛又會(huì)產(chǎn)生上一章說(shuō)的方向失真問(wèn)題兩頭難平衡。解決我習(xí)慣按這個(gè)順序排查。第一步換小模型yolov8n換成yolov8s通常能緩解但精度不一定會(huì)降。第二步減少訓(xùn)練輪數(shù)并開啟早停很多情況下模型在第40輪就已經(jīng)達(dá)到最優(yōu)驗(yàn)證效果再訓(xùn)就是浪費(fèi)時(shí)間。第三步檢查驗(yàn)證集是否和訓(xùn)練集太像如果val圖片來(lái)自同一管道同一批次拍攝內(nèi)容和訓(xùn)練集高度重合mAP虛高部署到新場(chǎng)景立刻打回原形。這一點(diǎn)在管道缺陷數(shù)據(jù)集里尤其常見(jiàn)因?yàn)椴杉酵辉谝粭l管道上拍了幾百?gòu)垐D隨機(jī)劃分出來(lái)的val沒(méi)有獨(dú)立性。解決方式是盡量找不同場(chǎng)景、不同光照條件下拍攝的圖片做驗(yàn)證集哪怕只有50張也比從同批數(shù)據(jù)里切出來(lái)的val更能反映真實(shí)效果。5. 剩下20%的功夫驗(yàn)證指標(biāo)、閾值校準(zhǔn)和小樣本補(bǔ)強(qiáng)訓(xùn)練跑完不要急著拿著best.pt歡呼900張圖的小樣本模型還有三件事值得做細(xì)看每類AP而不是只看全局mAP根據(jù)場(chǎng)景數(shù)據(jù)優(yōu)選置信度閾值用模型做偽標(biāo)注給數(shù)據(jù)集做一次低成本擴(kuò)充。這三件事做完模型才真正到了能交付的狀態(tài)。先看驗(yàn)證結(jié)果里的單類AP。用命令跑一遍驗(yàn)證yolo val modelruns/detect/train/weights/best.pt datapipe_defect.yaml imgsz640輸出里除了mAP0.5和mAP0.5-95還有每個(gè)類別的AP。重點(diǎn)關(guān)注樣本量少的那些類。如果某類AP比均值低超過(guò)0.3說(shuō)明這個(gè)類別學(xué)得不好回到第4章的類別不平衡處理里去補(bǔ)樣本。不要因?yàn)檎wmAP好看就跳過(guò)這一步這是小樣本數(shù)據(jù)集最容易欺騙你的地方。置信度閾值建議單獨(dú)校準(zhǔn)。默認(rèn)閾值通常是0.25但管道缺陷場(chǎng)景里漏檢的代價(jià)遠(yuǎn)高于誤檢我會(huì)把閾值下調(diào)到0.15左右讓模型多吐一些低置信度框后續(xù)人工復(fù)核再篩。反過(guò)來(lái)如果誤檢太多比如把焊縫反光也當(dāng)成缺陷就上調(diào)閾值。這個(gè)值沒(méi)有標(biāo)準(zhǔn)答案取決于你的業(yè)務(wù)容忍度我一般會(huì)在驗(yàn)證集上跑一遍不同閾值下的precision-recall曲線選曲線的拐點(diǎn)附近。偽標(biāo)注是最值得做的小樣本補(bǔ)強(qiáng)手段。用訓(xùn)練好的best.pt在無(wú)標(biāo)簽的管道圖片上推理把置信度高于0.8的檢測(cè)框提取出來(lái)人工快速?gòu)?fù)核后補(bǔ)充進(jìn)訓(xùn)練集。這個(gè)循環(huán)能幫你把數(shù)據(jù)從900張擴(kuò)到2000張以上而且新樣本來(lái)自真實(shí)場(chǎng)景比增強(qiáng)出來(lái)的虛擬樣本更有價(jià)值。代碼邏輯不復(fù)雜就是推理后按閾值過(guò)濾框再寫入txt標(biāo)簽文件。我自己的習(xí)慣是給每次訓(xùn)練寫一行備忘錄記下數(shù)據(jù)集版本、yaml內(nèi)容、訓(xùn)練參數(shù)、每類AP這樣下次拿到新的管道缺陷圖片翻備忘錄就能確定該在哪個(gè)版本上接著訓(xùn)練而不是把模型重訓(xùn)一遍。小樣本數(shù)據(jù)集的項(xiàng)目迭代效率比單次精度更重要。希望這些經(jīng)驗(yàn)?zāi)軒湍阍诠艿廊毕輽z測(cè)這條路上少走幾步彎路。本文還有配套的精品資源點(diǎn)擊獲取