實(shí)戰(zhàn):數(shù)據(jù)集、標(biāo)簽格式與訓(xùn)練避坑指南)
簡(jiǎn)介YOLO鋼表面缺陷檢測(cè)數(shù)據(jù)集面向鋼表面缺陷識(shí)別與工業(yè)質(zhì)檢場(chǎng)景包含5000張真實(shí)場(chǎng)景高質(zhì)量圖片數(shù)據(jù)場(chǎng)景豐富標(biāo)注框質(zhì)量較高適合目標(biāo)檢測(cè)入門、課程實(shí)訓(xùn)和YOLO系列模型調(diào)優(yōu)。資源提供VOC、COCO、YOLO三種格式標(biāo)簽按文件夾分類存放可直接切換到不同檢測(cè)框架使用。壓縮包共2000個(gè)文件約98.51MB主體為xml標(biāo)注文件同時(shí)包含txt標(biāo)簽列表、py數(shù)據(jù)集劃分腳本和html環(huán)境搭建/訓(xùn)練說明結(jié)構(gòu)清晰便于檢索。目前已有321人瀏覽學(xué)習(xí)。配套的YOLO環(huán)境搭建和訓(xùn)練教程覆蓋Linux與Windows兩個(gè)版本可從零配置環(huán)境并修改案例訓(xùn)練自己的數(shù)據(jù)三個(gè)劃分腳本支持將圖片和標(biāo)簽按需切分為訓(xùn)練集、驗(yàn)證集、測(cè)試集并寫入新文件夾也可生成ImageSets下的txt文件為后續(xù)批量訓(xùn)練和驗(yàn)證提供便利。1. YOLO鋼表面缺陷檢測(cè)5000張圖和三種標(biāo)簽格式拿到手就能開跑在我接觸過的工業(yè)視覺資源里YOLO鋼表面缺陷檢測(cè)數(shù)據(jù)集是少有的“拿到手就能開跑”的一份。鋼卷表面的裂紋、麻點(diǎn)、夾雜每類缺陷正樣本都靠產(chǎn)線長(zhǎng)期積累人工標(biāo)注更是按框計(jì)價(jià)光是把標(biāo)簽從XML轉(zhuǎn)成txt就能耗掉大半天。這份資源打包了5000張圖片同時(shí)給出VOC、COCO、YOLO三種標(biāo)簽格式外加劃分腳本和訓(xùn)練教程等于把“裸圖→可訓(xùn)練狀態(tài)”這一整段臟活提前干完了。適合剛接觸YOLO的入門者也適合想快速驗(yàn)證缺陷檢測(cè)方案的工程師。我會(huì)按實(shí)際拆包的順序展開先看數(shù)據(jù)結(jié)構(gòu)再跑劃分腳本接著配訓(xùn)練最后聊幾個(gè)容易翻車的細(xì)節(jié)。2. 解剖數(shù)據(jù)集三種標(biāo)注格式的差異、目錄結(jié)構(gòu)與類別分布2.1 VOC、COCO、YOLO三種格式到底在分別保存什么第一次接觸這份資源的人容易把三種標(biāo)簽格式理解成“同一件事做了三遍”。其實(shí)它們只是同一批標(biāo)注框的三種序列化方式分別服務(wù)不同的訓(xùn)練框架和工具鏈。VOC格式是每張圖片對(duì)應(yīng)一個(gè)XML文件文件里用object節(jié)點(diǎn)保存每個(gè)目標(biāo)的類別名和bndbox坐標(biāo)COCO格式是把整個(gè)數(shù)據(jù)集的標(biāo)注集中到一個(gè)JSON文件每條annotation都帶著image_id、category_id和bbox四個(gè)數(shù)字YOLO格式則是每張圖片對(duì)應(yīng)一個(gè)txt文件一行一個(gè)目標(biāo)五個(gè)數(shù)字依次是“類別id、歸一化中心x、歸一化中心y、歸一化寬、歸一化高”。三個(gè)格式的差異集中體現(xiàn)在坐標(biāo)表示上格式文件形態(tài)坐標(biāo)表示典型適用框架VOC每圖一個(gè)XMLxmin/ymin/xmax/ymax絕對(duì)像素值Pascal VOC系、標(biāo)注工具導(dǎo)出COCO整個(gè)數(shù)據(jù)集一個(gè)JSON左上角x、y和寬w、高h(yuǎn)絕對(duì)像素值Detectron2、MMDetectionYOLO每圖一個(gè)TXTcx、cy、w、h全部除以圖片寬高做歸一化YOLOv5、YOLOv8、Ultralytics系YOLO格式的歸一化坐標(biāo)是新手最容易栽跟頭的地方。標(biāo)簽txt里出現(xiàn)的0.512345 0.345678 0.045678 0.023456前面的0.51是目標(biāo)中心點(diǎn)在水平方向的位置比例后面0.34是中心點(diǎn)在垂直方向的比例再后面兩項(xiàng)是寬和高的比例。所有這些數(shù)字都被限制在0到1之間一旦在格式轉(zhuǎn)換時(shí)忘了除以圖片實(shí)際寬高就會(huì)出現(xiàn)坐標(biāo)越界大于1或小于0訓(xùn)練時(shí)要么直接報(bào)錯(cuò)要么loss異常波動(dòng)。提示拿到壓縮包后先用文本編輯器打開一個(gè)XML、一段JSON和一個(gè)txt對(duì)照著看同一張圖的同一個(gè)框在三種格式下分別長(zhǎng)什么樣。這比看任何格式文檔都更快。這份資源把三種格式并列給出省掉的不止是轉(zhuǎn)換步驟。實(shí)際項(xiàng)目中標(biāo)注團(tuán)隊(duì)用labelImg導(dǎo)出VOC算法組用COCO格式喂給MMDetection線上推理用YOLO格式做數(shù)據(jù)組織三者之間來回轉(zhuǎn)換是常態(tài)。直接給齊三份意味著無論你接下來用哪個(gè)框架都不用先寫一遍轉(zhuǎn)換腳本也不必?fù)?dān)心轉(zhuǎn)換過程中坐標(biāo)精度丟失。2.2 目錄結(jié)構(gòu)先確認(rèn)圖片和標(biāo)簽的對(duì)應(yīng)關(guān)系解壓之后先別急著訓(xùn)練。我的第一個(gè)動(dòng)作是執(zhí)行tree命令把目錄結(jié)構(gòu)完整打出來確認(rèn)圖片目錄和標(biāo)簽?zāi)夸浀膶?duì)應(yīng)方式。常見組織是這樣的steel_defect_dataset/ ├── images/ │ ├── train/ │ │ ├── 00001.jpg │ │ ├── 00002.jpg │ │ └── ... │ ├── val/ │ │ ├── 00101.jpg │ │ └── ... │ └── test/ │ ├── 00201.jpg │ └── ... ├── voc_labels/ │ ├── train/ │ │ ├── 00001.xml │ │ ├── 00002.xml │ │ └── ... │ ├── val/ │ └── test/ ├── coco_labels/ │ ├── train.json │ ├── val.json │ └── test.json ├── yolo_labels/ │ ├── train/ │ │ ├── 00001.txt │ │ ├── 00002.txt │ │ └── ... │ ├── val/ │ └── test/ ├── split_dataset.py └── train_tutorial.md這種“圖片一個(gè)目錄、標(biāo)簽一個(gè)目錄、文件名一一對(duì)應(yīng)”的結(jié)構(gòu)是最穩(wěn)的。YOLO訓(xùn)練時(shí)只需要遍歷圖片目錄然后按文件名去另一側(cè)找同名txt兩邊都不會(huì)出現(xiàn)找不到配對(duì)的問題。檢查的時(shí)候我重點(diǎn)看三處一是圖片文件有沒有損壞、能不能正常解碼二是標(biāo)簽文件有沒有0字節(jié)的空文件三是同名圖片和標(biāo)簽是否一一對(duì)應(yīng)。壓縮包解壓和網(wǎng)絡(luò)傳輸過程中偶爾會(huì)出現(xiàn)某個(gè)文件損壞或丟失如果帶著臟數(shù)據(jù)直接開訓(xùn)輕則個(gè)別圖片被跳過重則訓(xùn)練中斷。我的習(xí)慣是寫一個(gè)十幾行的小腳本遍歷圖片目錄逐個(gè)檢查同名標(biāo)簽是否存在、是否非空有問題的先撈出來再?zèng)Q定是刪掉對(duì)應(yīng)圖片還是重新補(bǔ)標(biāo)。from pathlib import Path img_dir Path(steel_defect_dataset/images/train) label_dir Path(steel_defect_dataset/yolo_labels/train) missing [] empty [] for img_path in sorted(img_dir.glob(*.jpg)): label_path label_dir / (img_path.stem .txt) if not label_path.exists(): missing.append(img_path.name) elif label_path.stat().st_size 0: empty.append(img_path.name) print(f缺失標(biāo)簽: {len(missing)} 個(gè)) print(f空標(biāo)簽文件: {len(empty)} 個(gè))這段腳本的邏輯不復(fù)雜遍歷train目錄下所有jpg拼出同名txt路徑先檢查文件存不存在再檢查文件大小是否為0。跑完之后如果缺失數(shù)不為0說明數(shù)據(jù)集本身不完整需要從其他備份里補(bǔ)回來而不是直接進(jìn)訓(xùn)練流程。2.3 鋼表面缺陷檢測(cè)的特殊性小目標(biāo)、細(xì)長(zhǎng)目標(biāo)與類別不平衡鋼表面缺陷檢測(cè)和通用目標(biāo)檢測(cè)有本質(zhì)區(qū)別最明顯的是目標(biāo)尺寸分布。crazing裂紋這類缺陷在圖片里往往只有幾十個(gè)像素寬形狀細(xì)長(zhǎng)且方向隨機(jī)矩形框很難緊密貼合patches麻點(diǎn)則是成片出現(xiàn)邊界模糊標(biāo)注框和真實(shí)缺陷區(qū)域之間總有偏差rolled-in_scale氧化鐵皮壓入的顏色紋理與背景高度相似人眼辨認(rèn)都費(fèi)勁。這種數(shù)據(jù)特性決定了兩個(gè)訓(xùn)練方向一是輸入分辨率不能太低默認(rèn)的640×640會(huì)把細(xì)長(zhǎng)裂紋縮成幾個(gè)像素特征根本提不出來二是損失函數(shù)里框回歸部分需要格外關(guān)注因?yàn)榫匦慰驅(qū)?xì)長(zhǎng)目標(biāo)的覆蓋本身就不精確如果標(biāo)注框再帶有隨機(jī)誤差訓(xùn)練時(shí)回歸損失會(huì)一直降不下去。還有類別不平衡問題。六類缺陷里scratches劃傷和patches往往樣本數(shù)量占大頭crazing和inclusion數(shù)量偏少。訓(xùn)練時(shí)如果不對(duì)小樣本類別做任何處理模型會(huì)傾向于把不確定目標(biāo)預(yù)測(cè)成樣本量大的類別導(dǎo)致crazing的召回率慘不忍睹。拿到數(shù)據(jù)集后我一般會(huì)先統(tǒng)計(jì)每個(gè)類別的目標(biāo)數(shù)量確認(rèn)哪些類別是少數(shù)派后面訓(xùn)練時(shí)再看要不要調(diào)整類別權(quán)重。3. 劃分腳本實(shí)戰(zhàn)train/val/test的生成邏輯與參數(shù)修改3.1 劃分腳本解決的是什么問題壓縮包里帶的split_dataset.py核心任務(wù)只有一個(gè)把圖片列表隨機(jī)打亂按比例切成train、val、test三份同時(shí)保證同一張圖的標(biāo)簽跟著圖片走。這個(gè)看似簡(jiǎn)單的邏輯手動(dòng)操作時(shí)最容易出錯(cuò)。很多人習(xí)慣直接按文件管理器里的順序拖拽移動(dòng)結(jié)果圖片移走了、標(biāo)簽沒移走訓(xùn)練時(shí)反復(fù)報(bào)“l(fā)abel file missing”。腳本的價(jià)值在于把圖片和標(biāo)簽的劃分結(jié)果綁定在一起。常見的實(shí)現(xiàn)是遍歷圖片得到完整列表用random.shuffle打亂再按比例切片然后同時(shí)寫出圖片路徑和對(duì)應(yīng)標(biāo)簽路徑。為了避免每次跑出來的劃分結(jié)果都不一樣腳本里會(huì)固定一個(gè)隨機(jī)種子比如random.seed(42)。這樣不管是自己隔天重跑還是發(fā)給同事復(fù)現(xiàn)得到的train/val/test組合完全一致。固定種子這件事容易被忽略但實(shí)際排查問題時(shí)作用很大。模型在驗(yàn)證集上指標(biāo)忽高忽低不一定是模型問題可能是每次劃分出來的驗(yàn)證集不同。把種子固定住相當(dāng)于鎖定了實(shí)驗(yàn)變量之后所有對(duì)比都建立在同一套數(shù)據(jù)集劃分上。3.2 運(yùn)行腳本與參數(shù)說明我習(xí)慣把腳本里的路徑和比例參數(shù)提到命令行里傳避免每次修改Python源文件。用argparse組織的典型版本是這樣的import argparse import random from pathlib import Path def parse_args(): parser argparse.ArgumentParser(descriptionSteel defect dataset split) parser.add_argument(--img_dir, typestr, requiredTrue, help原始圖片目錄腳本從這里讀所有jpg) parser.add_argument(--label_dir, typestr, requiredTrue, help標(biāo)簽?zāi)夸浗Y(jié)構(gòu)和img_dir保持對(duì)應(yīng)) parser.add_argument(--train_ratio, typefloat, default0.8, help訓(xùn)練集比例默認(rèn)0.8) parser.add_argument(--val_ratio, typefloat, default0.1, help驗(yàn)證集比例默認(rèn)0.1剩余的為測(cè)試集) parser.add_argument(--seed, typeint, default42, help隨機(jī)種子固定后可復(fù)現(xiàn)劃分結(jié)果) parser.add_argument(--out_dir, typestr, requiredTrue, help輸出目錄生成train.txt/val.txt/test.txt) return parser.parse_args() def main(): args parse_args() random.seed(args.seed) img_dir Path(args.img_dir) all_imgs sorted(img_dir.glob(*.jpg)) random.shuffle(all_imgs) n_train int(len(all_imgs) * args.train_ratio) n_val int(len(all_imgs) * args.val_ratio) train_imgs all_imgs[:n_train] val_imgs all_imgs[n_train:n_train n_val] test_imgs all_imgs[n_train n_val:] out_dir Path(args.out_dir) out_dir.mkdir(parentsTrue, exist_okTrue) for name, img_list in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: with open(out_dir / f{name}.txt, w, encodingutf-8) as f: for img in img_list: f.write(str(img.resolve()) \n) if __name__ __main__: main()邏輯分四步先把所有jpg路徑讀進(jìn)來并排序保證每次運(yùn)行初始順序穩(wěn)定再用種子打亂隨后按比例切片得到三份圖片列表最后把絕對(duì)路徑寫入三個(gè)txt清單文件。注意這里n_val用的是int()向下取整如果圖片總數(shù)不是10的整數(shù)倍余數(shù)會(huì)全部歸到test集三個(gè)集合比例不會(huì)嚴(yán)格等于0.8/0.1/0.1。運(yùn)行命令python split_dataset.py \ --img_dir steel_defect_dataset/images \ --label_dir steel_defect_dataset/yolo_labels \ --train_ratio 0.8 \ --val_ratio 0.1 \ --seed 42 \ --out_dir steel_defect_dataset/splits生成三個(gè)txt清單只是第一步。YOLO訓(xùn)練代碼通常并不直接讀txt清單而是要求圖片按train/val/test子目錄存放。所以我一般再寫一段復(fù)制邏輯讀清單把圖片和對(duì)應(yīng)的標(biāo)簽一起復(fù)制到images/train、yolo_labels/train這類目錄。復(fù)制而不是移動(dòng)是為了保留原始數(shù)據(jù)萬一劃分有問題還能重來不至于把原始數(shù)據(jù)折騰亂。這個(gè)后悔藥我每次都會(huì)準(zhǔn)備。3.3 數(shù)據(jù)泄漏最隱蔽的劃分陷阱劃分腳本本身簡(jiǎn)單真正的坑在于“同類圖片泄漏”。鋼卷表面圖片經(jīng)常是連續(xù)采集的同一個(gè)卷號(hào)下相鄰幀的紋理、光照、缺陷分布高度相似幾乎可以看作同一張圖的輕微變化。如果直接按文件級(jí)隨機(jī)劃分訓(xùn)練集和驗(yàn)證集里會(huì)出現(xiàn)大量這種“近親圖片”模型在驗(yàn)證集上的表現(xiàn)會(huì)虛高一到真實(shí)產(chǎn)線數(shù)據(jù)就垮掉。合理做法是按“卷號(hào)”或“采集批次”分組再把整組劃入同一個(gè)子集保證同一個(gè)卷的圖片不會(huì)同時(shí)出現(xiàn)在train和val里。壓縮包里的劃分腳本如果只是隨機(jī)打亂沒有做分組邏輯我會(huì)先根據(jù)文件名前綴提取卷號(hào)把圖片按組聚合后再劃分。判斷泄漏是否存在的辦法是訓(xùn)練完成后看驗(yàn)證集mAP和實(shí)際產(chǎn)線測(cè)試的差距如果差距超過15%大概率是劃分時(shí)存在泄漏。4. YOLO訓(xùn)練配置數(shù)據(jù)yaml、預(yù)訓(xùn)練權(quán)重與關(guān)鍵訓(xùn)練參數(shù)4.1 數(shù)據(jù)yaml路徑、類別數(shù)量與類別名YOLO系列框架訓(xùn)練前需要一個(gè)數(shù)據(jù)配置文件告訴框架去哪里找圖片、一共有幾類。以Ultralytics YOLOv8為例# steel_defect.yaml path: ../steel_defect_dataset train: images/train val: images/val test: images/test nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled-in_scale 5: scratchespath字段指定數(shù)據(jù)集根目錄train、val、test是相對(duì)根目錄的子路徑nc是類別數(shù)量names是類別名列表。注意names的排列順序必須和YOLO標(biāo)簽txt里的類別id完全一致。txt里第一個(gè)數(shù)字為0對(duì)應(yīng)的就是names里第0個(gè)元素crazing如果txt里的類別編號(hào)在轉(zhuǎn)換時(shí)被重新排過序而names還是按原數(shù)據(jù)集順序?qū)懙哪P陀?xùn)練時(shí)不會(huì)報(bào)錯(cuò)但預(yù)測(cè)結(jié)果會(huì)整體錯(cuò)位把crazing識(shí)別成inclusion這種事就是這么來的。注意寫yaml前務(wù)必全量掃描所有txt標(biāo)簽統(tǒng)計(jì)實(shí)際出現(xiàn)的類別id集合再與yaml里的names逐項(xiàng)比對(duì)。這個(gè)步驟花不了三十秒但能省掉一整天的排錯(cuò)時(shí)間。4.2 預(yù)訓(xùn)練權(quán)重與模型規(guī)模n/s/m怎么選鋼表面缺陷數(shù)據(jù)是典型的工業(yè)特定場(chǎng)景類別和COCO相差很大但訓(xùn)練時(shí)仍然建議從COCO預(yù)訓(xùn)練權(quán)重起步而不是隨機(jī)初始化。原因很直接COCO預(yù)訓(xùn)練已經(jīng)把低層特征提取器練得夠好遷移到鋼材表面時(shí)收斂更快最終精度也更高。YOLOv8的yolov8n.pt、yolov8s.pt這些預(yù)訓(xùn)練權(quán)重框架會(huì)自動(dòng)從官方地址下載也可以手動(dòng)下載后放到項(xiàng)目目錄里用model路徑指定。模型規(guī)模的選擇取決于顯存和任務(wù)復(fù)雜度。我的經(jīng)驗(yàn)值如下模型顯存參考適用場(chǎng)景YOLOv8n4~6GB邊緣設(shè)備部署、快速驗(yàn)證流程YOLOv8s8~12GB大多數(shù)缺陷檢測(cè)項(xiàng)目精度/速度平衡YOLOv8m16GB以上小目標(biāo)占比高、對(duì)精度要求苛刻的產(chǎn)線方案鋼材缺陷圖片分辨率通常偏高小目標(biāo)多直接上大模型加高分辨率輸入很容易把顯存撐爆。我見過有人在V100上跑YOLOv8m輸入尺寸調(diào)到1280之后顯存不足折騰半天分辨率降回640最終精度也沒比s模型強(qiáng)多少。結(jié)論是先在s模型上把數(shù)據(jù)流程跑通再根據(jù)bad case決定要不要換更大的模型。4.3 訓(xùn)練命令、損失函數(shù)與常見崩潰以YOLOv8s為例一條完整的訓(xùn)練命令yolo train \ modelyolov8s.pt \ datasteel_defect.yaml \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30參數(shù)含義epochs是訓(xùn)練輪數(shù)缺陷檢測(cè)一般120到200輪足夠收斂imgsz是輸入圖像縮放尺寸小目標(biāo)多時(shí)建議提到768batch是每批圖片數(shù)顯存不夠就調(diào)小lr0是初始學(xué)習(xí)率類別不平衡明顯時(shí)我習(xí)慣降到0.005patience是早停輪數(shù)連續(xù)30輪驗(yàn)證集指標(biāo)沒提升就自動(dòng)停止省得空跑。損失函數(shù)方面YOLOv8的損失由分類損失、框回歸損失和置信度損失三部分組成。框回歸部分使用的CIoU類損失理論上對(duì)小目標(biāo)足夠友好但前提是標(biāo)簽框坐標(biāo)本身是準(zhǔn)的。如果標(biāo)注框和真實(shí)缺陷區(qū)域貼合度很差損失函數(shù)的優(yōu)化空間就很有限。訓(xùn)練中最常見的翻車是loss變成NaN元兇通常是三個(gè)學(xué)習(xí)率過大、標(biāo)簽坐標(biāo)越界、圖片損壞。排查順序先掃描所有txt標(biāo)簽確認(rèn)每個(gè)坐標(biāo)都被限制在0到1之間再降低學(xué)習(xí)率到0.001試試最后看是不是某張損壞圖片導(dǎo)致的。另外有項(xiàng)目里遇到BN層訓(xùn)練中崩潰特征圖在某個(gè)batch之后全是NaN多半是batch太小或預(yù)訓(xùn)練權(quán)重沒有正確加載。把batch從4提到8以上并把預(yù)訓(xùn)練權(quán)重路徑確認(rèn)一遍問題基本能緩解。5. 鋼表面缺陷檢測(cè)避坑我踩過的五個(gè)問題5.1 類別編號(hào)錯(cuò)位訓(xùn)練時(shí)loss不降現(xiàn)象訓(xùn)練loss正常下降驗(yàn)證集mAP卻極低預(yù)測(cè)出來的類別完全對(duì)不上。原因VOC轉(zhuǎn)YOLO時(shí)類別id被重新排列過而yaml里的names仍按原始數(shù)據(jù)集順序?qū)憙蛇厡?duì)不上。框架不檢查這一層模型就在錯(cuò)誤的監(jiān)督信號(hào)里完成了訓(xùn)練。解決訓(xùn)練前統(tǒng)計(jì)所有txt標(biāo)簽里的類別id按從小到大排列和yaml的names逐項(xiàng)對(duì)照。我后來每次換數(shù)據(jù)集都會(huì)強(qiáng)制走這一步不再憑感覺。5.2 密集小缺陷導(dǎo)致NMS后漏檢現(xiàn)象一張圖里同時(shí)出現(xiàn)幾十個(gè)裂紋和麻點(diǎn)模型預(yù)測(cè)出大量重疊框NMS之后只剩零星幾個(gè)漏檢率極高。原因NMS默認(rèn)閾值對(duì)密集目標(biāo)過于激進(jìn)多個(gè)真實(shí)目標(biāo)如果重疊度高會(huì)被當(dāng)成同一個(gè)目標(biāo)抑制掉。解決推理時(shí)把NMS的IoU閾值從默認(rèn)的0.45調(diào)到0.3或者改用Soft-NMS訓(xùn)練階段把imgsz提升到768并開啟多尺度訓(xùn)練。這兩個(gè)措施對(duì)密集小目標(biāo)的召回率改善最明顯。5.3 混淆矩陣各列總和不為1現(xiàn)象驗(yàn)證集混淆矩陣看起來“對(duì)不上賬”橫著豎著加起來都不是100%。原因混淆矩陣每個(gè)單元格除以的是該行對(duì)應(yīng)的真實(shí)類別總樣本數(shù)行內(nèi)總和應(yīng)為1但最后多出來的background列統(tǒng)計(jì)的是被誤檢為缺陷的背景區(qū)域它不屬于任何真實(shí)類別所以從整個(gè)矩陣看總和不是1是正常的。解決讀矩陣時(shí)按行看忽略全局總和。如果background列數(shù)值偏高說明模型對(duì)背景紋理過于敏感需要提高置信度閾值或補(bǔ)充負(fù)樣本。5.4 背景紋理干擾導(dǎo)致誤檢偏高現(xiàn)象鋼表面的水漬、氧化紋理被識(shí)別成缺陷false positive數(shù)量居高不下。原因鋼板表面非缺陷紋理在灰度特征上和缺陷高度相似模型學(xué)到的可能只是紋理統(tǒng)計(jì)特征而不是真正的缺陷語義。解決訓(xùn)練配置里打開隨機(jī)擦除和色彩抖動(dòng)增強(qiáng)削弱模型對(duì)顏色的依賴再收集一批無缺陷的純背景圖片加入訓(xùn)練集讓模型見過真正的負(fù)樣本。5.5 劃分腳本重復(fù)運(yùn)行導(dǎo)致數(shù)據(jù)重疊現(xiàn)象重新跑一遍劃分腳本后train.txt和val.txt里出現(xiàn)同一張圖片。原因腳本用追加模式寫文件上一次運(yùn)行的結(jié)果沒有被清空新舊清單拼在了一起。解決寫文件時(shí)強(qiáng)制使用覆蓋模式open(path, w)并在劃分完成后用集合差集檢查兩個(gè)清單是否有重復(fù)項(xiàng)。從那以后我每次跑完劃分腳本都會(huì)先做一次交集校驗(yàn)確保train和val完全隔離。6. 訓(xùn)練完成的進(jìn)階驗(yàn)證指標(biāo)分析、可視化推理與模型導(dǎo)出6.1 結(jié)果目錄里哪些文件值得看YOLO訓(xùn)練完成后會(huì)在runs/detect/train下生成一批結(jié)果文件重點(diǎn)看三個(gè)results.png、confusion_matrix.png和val_batch.jpg。results.png里包含box_loss、cls_loss、mAP50、mAP50-95四條曲線。判斷收斂的標(biāo)準(zhǔn)不是loss降到0而是mAP曲線進(jìn)入平臺(tái)期且不再明顯上升。如果mAP50很高但mAP50-95偏低說明預(yù)測(cè)框和真實(shí)框的重合精度不夠問題集中在框回歸部分。混淆矩陣則用來定位具體類的短板。矩陣對(duì)角線是每個(gè)類別的正確檢出率那些對(duì)角線明顯偏低的類別就是后續(xù)需要針對(duì)性優(yōu)化的對(duì)象。6.2 用測(cè)試集做可視化推理訓(xùn)練完成后用best.pt跑一遍測(cè)試集做bad case分析。一個(gè)簡(jiǎn)單的推理腳本from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcesteel_defect_dataset/images/test/, conf0.25, saveTrue, save_txtTrue, imgsz640 )conf0.25是置信度閾值低于這個(gè)值的預(yù)測(cè)框會(huì)被丟棄saveTrue把畫好框的結(jié)果圖保存下來save_txtTrue額外輸出每個(gè)框的坐標(biāo)和類別。跑完之后挨張翻結(jié)果圖重點(diǎn)找兩類bad case實(shí)際有缺陷但沒被框出來的以及框在了錯(cuò)誤位置上的。這些bad case的共性特征決定了下一步是調(diào)閾值還是補(bǔ)數(shù)據(jù)。6.3 導(dǎo)出ONNX做邊緣部署工業(yè)產(chǎn)線部署通常不住PyTorch環(huán)境。導(dǎo)出成ONNX是常見做法yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640導(dǎo)出的onnx文件可以用ONNXRuntime加載推理速度比PyTorch快也不需要裝整套深度學(xué)習(xí)訓(xùn)練環(huán)境。部署時(shí)還需要把歸一化坐標(biāo)映射回原圖坐標(biāo)系再關(guān)聯(lián)產(chǎn)線的鋼卷批次信息。之前有一次我圖省事直接用PyTorch模型做在線推理顯存占用高還頻繁被OOM打斷導(dǎo)出ONNX之后才穩(wěn)定下來。從那以后我每次訓(xùn)練完都強(qiáng)制走一遍“指標(biāo)分析→bad case可視化→ONNX導(dǎo)出”流程能提前發(fā)現(xiàn)的問題絕不留到產(chǎn)線希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取