戰(zhàn):可口可樂(lè)產(chǎn)品識(shí)別數(shù)據(jù)集訓(xùn)練與避坑指南)
簡(jiǎn)介這是一份面向YOLO系列算法學(xué)習(xí)者的產(chǎn)品識(shí)別目標(biāo)檢測(cè)數(shù)據(jù)集內(nèi)含5166張帶標(biāo)簽圖像覆蓋可口可樂(lè)等產(chǎn)品的識(shí)別場(chǎng)景。數(shù)據(jù)集已按訓(xùn)練/驗(yàn)證需求劃分并配備data.yaml配置文件可直接用于yolov5、yolov8、yolov9、yolov10、yolo11等主流版本的模型訓(xùn)練與測(cè)試。標(biāo)簽同時(shí)提供YOLO格式txt與VOC格式xml分別保存便于不同工具鏈調(diào)用YOLO格式采用class、歸一化中心坐標(biāo)與寬高的標(biāo)準(zhǔn)寫法上手簡(jiǎn)單。壓縮包文件總數(shù)2000個(gè)以xml標(biāo)簽文件為主要類型整體大小約143.6MB標(biāo)簽類別可在資源詳情頁(yè)查看。目前已有95人學(xué)習(xí)下載適合需要產(chǎn)品識(shí)別數(shù)據(jù)集進(jìn)行算法實(shí)戰(zhàn)、模型評(píng)估與對(duì)比實(shí)驗(yàn)的開發(fā)者。1. 先別急著解壓這份可口可樂(lè)產(chǎn)品識(shí)別數(shù)據(jù)集是什么、能解決什么拿到Y(jié)OLO算法-產(chǎn)品識(shí)別數(shù)據(jù)集-5166張圖像帶標(biāo)簽-可口可樂(lè).zip這類壓縮包第一反應(yīng)通常是解壓、丟進(jìn)訓(xùn)練腳本、等著看 mAP。我建議先花半小時(shí)把這 5166 張圖的標(biāo)簽格式、類別編號(hào)和圖像來(lái)源看清楚。這是一份面向目標(biāo)檢測(cè)任務(wù)的產(chǎn)品識(shí)別數(shù)據(jù)集標(biāo)注對(duì)象是可口可樂(lè)的瓶、罐、箱等商品實(shí)例典型用在零售貨架盤點(diǎn)、無(wú)人貨柜和生產(chǎn)線質(zhì)檢。標(biāo)簽統(tǒng)一、場(chǎng)景相對(duì)集中適合新手練手也適合做遷移學(xué)習(xí)的底料。但直接拿來(lái)訓(xùn)練大概率會(huì)在驗(yàn)證集上自嗨部署到真實(shí)門店就翻車。這篇筆記從數(shù)據(jù)集本身拆起講清 YOLO 標(biāo)簽格式跑通 YOLOv8 訓(xùn)練再交代產(chǎn)品識(shí)別里幾個(gè)容易踩的坑。2. 拆開 YOLO 格式5166 張圖像的目錄結(jié)構(gòu)、標(biāo)簽文件與類別定義2.1 典型的 YOLO 檢測(cè)數(shù)據(jù)集目錄長(zhǎng)什么樣解壓后先別急著看圖把頂層目錄結(jié)構(gòu)打一遍。絕大多數(shù) YOLO 檢測(cè)數(shù)據(jù)集體例是images/和labels/平級(jí)圖像和標(biāo)簽同名不同后綴000001.jpg對(duì)應(yīng)000001.txt。需要先分清這是檢測(cè)框標(biāo)簽不是 yolo 實(shí)例分割那種掩碼多邊形——后者標(biāo)簽里是一串多邊形頂點(diǎn)坐標(biāo)格式完全是另一套東西。實(shí)際拿到的數(shù)據(jù)集可能有幾種擺法我的處理經(jīng)驗(yàn)如下已經(jīng)分好train/、val/子目錄直接進(jìn) 3.2 寫 yaml圖像全部平鋪在images/里需要自己劃分文件名帶著采集批次或相機(jī)編號(hào)例如checkout_01_000123.jpg。第三種最容易被忽略。文件名里的批次信息往往是劃分訓(xùn)練集和驗(yàn)證集的重要依據(jù)按文件名隨機(jī)洗牌同一批次的連拍幀會(huì)同時(shí)落到訓(xùn)練集和驗(yàn)證集訓(xùn)練時(shí)看著正常驗(yàn)證集 mAP 虛高。后面第 4 章專門講數(shù)據(jù)劃分泄漏這件事。另外如果打開 txt 發(fā)現(xiàn)一行不止 5 個(gè)數(shù)字比如出現(xiàn)一串浮點(diǎn)數(shù)坐標(biāo)說(shuō)明這份標(biāo)簽可能是分割標(biāo)注或者多邊形框不是 YOLO 檢測(cè)格式。不要硬套訓(xùn)練腳本否則解析時(shí)直接報(bào)錯(cuò)。2.2 讀懂 label txt歸一化坐標(biāo)是 YOLO 格式的靈魂YOLO 的一行標(biāo)注是 5 個(gè)值class_id x_center y_center width height。坐標(biāo)全部除以圖像寬高做歸一化換句話說(shuō)不管是 640×640 小圖還是 3024×4032 手機(jī)原圖框的坐標(biāo)都在 0 到 1 之間。這個(gè)設(shè)計(jì)和 COCO json 的左上角 x、y 加寬高不一樣混用會(huì)讓框整體偏移訓(xùn)練出來(lái)的模型自己騙自己。打開一個(gè)標(biāo)簽文件用下面這段腳本把歸一化坐標(biāo)反算回像素坐標(biāo)疊在原圖上檢查from pathlib import Path from PIL import Image label_path Path(labels/000001.txt) image_path Path(images/000001.jpg) w, h Image.open(image_path).size for line in label_path.read_text(encodingutf-8).splitlines(): line line.strip() if not line: continue cls_id int(line.split()[0]) x_c, y_c, bw, bh map(float, line.split()[1:]) # 反算左上角和右下角的像素坐標(biāo) x1 (x_c - bw / 2) * w y1 (y_c - bh / 2) * h x2 (x_c bw / 2) * w y2 (y_c bh / 2) * h print(cls_id, round(x1), round(y1), round(x2), round(y2))邏輯是先拿 PIL 讀出圖像的寬和高再逐行取類別編號(hào)、中心點(diǎn)坐標(biāo)和寬高最后反算成左上右下兩個(gè)角點(diǎn)。這里map(float, ...)是把字符串批量轉(zhuǎn)浮點(diǎn)數(shù)line.split()[0]是類別編號(hào)必須是整數(shù)。跑出來(lái)的像素坐標(biāo)如果明顯超出圖像邊界比如x2比w大很多說(shuō)明標(biāo)簽越界了。打印的四個(gè)值可以直接用 OpenCV 畫框和圖像疊在一起看比盯數(shù)字直觀。字段含義整理成一張表核對(duì)標(biāo)簽時(shí)心里有底字段含義取值范圍class_id類別編號(hào)從 0 開始0 ~ 類別數(shù)-1x_center框中心點(diǎn)的 x 除以圖像寬0~1y_center框中心點(diǎn)的 y 除以圖像高0~1width框?qū)挸詧D像寬0~1height框高除以圖像高0~1大多數(shù)翻車現(xiàn)場(chǎng)出在 class_id 上。標(biāo)簽寫的是 2data.yaml 的 names 列表第二位卻不是可樂(lè)模型就永遠(yuǎn)學(xué)不對(duì)。這是整個(gè) YOLO 數(shù)據(jù)集環(huán)節(jié)最容易黑匣子化的地方3.2 再?gòu)?qiáng)調(diào)一次。2.3 沒(méi)有 classes.txt 時(shí)怎么確認(rèn)類別到底有幾個(gè)訓(xùn)練必須要有 names 類別列表但很多壓縮包里只有 txt。這時(shí)先跑一個(gè)統(tǒng)計(jì)腳本把 class_id 分布打出來(lái)from collections import Counter from pathlib import Path counter Counter() empty_files 0 for txt in Path(labels).glob(*.txt): ids [] for line in txt.read_text(encodingutf-8).splitlines(): line line.strip() if line: ids.append(int(line.split()[0])) if not ids: empty_files 1 else: counter.update(ids) print(class_id 分布:, sorted(counter.items())) print(空標(biāo)簽文件數(shù):, empty_files)這個(gè)腳本同時(shí)確認(rèn)兩件事類別編號(hào)有沒(méi)有斷檔以及有多少空標(biāo)簽文件。如果分布是{0: 2000, 1: 1800, 2: 1300}說(shuō)明 3 個(gè)連續(xù)類別names 照0、1、2寫如果分布是{0: 2000, 2: 3000}沒(méi)有 1那要么采集時(shí)漏了類要么標(biāo)注工具跳過(guò)了某類直接按最大編號(hào)加一猜類別數(shù)會(huì)出問(wèn)題。類別拆多細(xì)也直接影響訓(xùn)練難度。以可口可樂(lè)這類產(chǎn)品識(shí)別數(shù)據(jù)集為例常見標(biāo)注方案是按包裝形態(tài)分瓶、罐、箱也有按配方分經(jīng)典、零度、健怡。兩種都合理關(guān)鍵是 names 順序和 txt 里的 class_id 一一對(duì)應(yīng)。很多人問(wèn)「xml 格式文件沒(méi)有標(biāo)簽怎么辦」大多不是 xml 真沒(méi)標(biāo)簽而是 VOC 格式的 object 轉(zhuǎn) YOLO txt 時(shí)classes 列表順序沒(méi)和轉(zhuǎn)換工具對(duì)齊導(dǎo)致生成的標(biāo)簽全錯(cuò)位。2.4 標(biāo)注質(zhì)量體檢越界框、空標(biāo)簽與框面積分布class_id 看完了再看框本身。除了 2.2 說(shuō)的越界檢查我一般還會(huì)統(tǒng)計(jì)框面積占整張圖的比例。產(chǎn)品識(shí)別數(shù)據(jù)集里一瓶可樂(lè)和一箱可樂(lè)的框面積能差幾十倍如果訓(xùn)練時(shí)只在某一個(gè)尺度上見過(guò)目標(biāo)換個(gè)視角就漏檢。from pathlib import Path samples [] for txt in Path(labels).glob(*.txt): for line in txt.read_text(encodingutf-8).splitlines(): line line.strip() if not line: continue parts line.split() if len(parts) ! 5: continue # 格式不對(duì)的直接跳過(guò) w float(parts[3]) h float(parts[4]) samples.append(w * h) # 歸一化面積占比 samples.sort() p5 samples[len(samples) // 20] p50 samples[len(samples) // 2] p95 samples[int(len(samples) * 0.95)] print(框面積占比 p5 / p50 / p95:, p5, p50, p95)w和h是歸一化寬高乘起來(lái)就是框面積占比。p5 和 p95 的差距能一眼看出尺度跨度??缍瘸^(guò) 20 倍訓(xùn)練時(shí)就要把 imgsz 加大或者走切片推理的路線而不是默認(rèn) 640 一路到底。這一步做完數(shù)據(jù)集才算真正打開看過(guò)了可以進(jìn)訓(xùn)練環(huán)節(jié)。3. 用這份數(shù)據(jù)集跑通 YOLOv8 訓(xùn)練劃分腳本、data.yaml 與訓(xùn)練命令3.1 數(shù)據(jù)劃分腳本按類別分層別把同一場(chǎng)景漏到驗(yàn)證集壓縮包里如果沒(méi)分 train/val就得自己動(dòng)手。最省事但最不該做的是random.shuffle一把梭。同一場(chǎng)景的連續(xù)幀高度相似隨機(jī)切會(huì)讓同一瓶可樂(lè)同時(shí)出現(xiàn)在訓(xùn)練集和驗(yàn)證集最后驗(yàn)證 mAP 虛高一到現(xiàn)場(chǎng)就現(xiàn)原形。常見做法是按「主類別」分層每個(gè)類別抽 10% 做驗(yàn)證集空標(biāo)簽文件提前踢掉。我用的劃分腳本長(zhǎng)這樣from pathlib import Path import random import shutil from collections import Counter, defaultdict random.seed(42) root Path(coke_dataset) images_src root / images labels_src root / labels out_dirs [ root / train / images, root / train / labels, root / val / images, root / val / labels, ] for d in out_dirs: d.mkdir(parentsTrue, exist_okTrue) def main_class(label_path: Path) - str: counts Counter() for line in label_path.read_text(encodingutf-8).splitlines(): line line.strip() if line: counts[int(line.split()[0])] 1 return str(counts.most_common(1)[0][0]) if counts else empty groups defaultdict(list) for label_path in labels_src.glob(*.txt): cls main_class(label_path) if cls empty: continue # 空標(biāo)簽不參與劃分 groups[cls].append(label_path) for paths in groups.values(): paths.sort() # 排序后再 shuffle保證隨機(jī)種子穩(wěn)定生效 random.shuffle(paths) val_n max(1, round(len(paths) * 0.1)) for label_path in paths[:val_n]: img images_src / (label_path.stem .jpg) if not img.exists(): img images_src / (label_path.stem .png) shutil.copy2(label_path, root / val / labels / label_path.name) shutil.copy2(img, root / val / images / img.name) for label_path in paths[val_n:]: img images_src / (label_path.stem .jpg) if not img.exists(): img images_src / (label_path.stem .png) shutil.copy2(label_path, root / train / labels / label_path.name) shutil.copy2(img, root / train / images / img.name) print(train/val 劃分完成請(qǐng)檢查目錄數(shù)量)邏輯是每個(gè)樣本先統(tǒng)計(jì)自己包含的所有類別取數(shù)量最多的那個(gè)作為主類別再按主類別分組組內(nèi)打亂后前 10% 進(jìn)驗(yàn)證集。這樣能保證每個(gè)類別在驗(yàn)證集里都留有一定比例不會(huì)出現(xiàn)「驗(yàn)證集全是可樂(lè)瓶、訓(xùn)練集全是可樂(lè)罐」的極端情況。參數(shù)說(shuō)明random.seed(42)讓劃分過(guò)程可復(fù)現(xiàn)round(len(paths) * 0.1)對(duì)每個(gè)類至少留 1 個(gè)驗(yàn)證樣本用copy2而不是move是給自己留后悔藥訓(xùn)練集確認(rèn)沒(méi)問(wèn)題再刪源目錄不遲。如果圖像后綴混著.jpg、.jpeg、.png建議把后綴列表寫成一個(gè)循環(huán)逐個(gè)嘗試不要只寫死.jpg。要真正做到同場(chǎng)景不跨集合還得按拍攝批次目錄分組這個(gè)腳本是類別分層的近似方案更嚴(yán)格的泄漏控制在第 4 章講。3.2 寫 data.yaml路徑、類別編號(hào)和名稱必須對(duì)齊YOLOv8 靠一個(gè) data.yaml 找到數(shù)據(jù)和類別。注意train和val指向的是 images 目錄YOLOv8 會(huì)按同名規(guī)則自動(dòng)去 labels 目錄找標(biāo)注不需要在 yaml 里寫 labels 路徑。path: /home/you/coke_dataset # 改成你解壓后的絕對(duì)路徑 train: train/images val: val/images names: 0: coca-cola 1: coke-zero 2: diet-cokenames必須和 txt 里的 class_id 完全一致一個(gè)蘿卜一個(gè)坑。假設(shè)標(biāo)簽文件里第二類寫的是1而 names 里1對(duì)應(yīng)經(jīng)典款模型就會(huì)把零度學(xué)成經(jīng)典款這類錯(cuò)誤在驗(yàn)證集上要看到混淆矩陣才能發(fā)現(xiàn)。常見做法是先跑一遍 2.3 的統(tǒng)計(jì)腳本把類別編號(hào)和 names 擺在一起校準(zhǔn)完再開訓(xùn)。path建議寫絕對(duì)路徑規(guī)避不同機(jī)器上相對(duì)路徑解析差異names的條目數(shù)必須等于標(biāo)簽最大編號(hào)加一。訓(xùn)練中出現(xiàn)類別數(shù)量對(duì)不上優(yōu)先改 yaml不要去批量改 txt。注意批量改 txt 里的 class_id 很容易把同類別編號(hào)改亂而且通常不可逆。先備份再用腳本統(tǒng)一改改完重新跑一遍類別統(tǒng)計(jì)確認(rèn)分布。3.3 訓(xùn)練命令與參數(shù)baseline 先用 nano別一上來(lái)就上大模型裝 ultralytics 很簡(jiǎn)單pip install ultralytics不管用 PyCharm 還是純命令行先確認(rèn)解釋器是同一個(gè)虛擬環(huán)境否則裝完卻 import 不到是新手最容易卡住的一步。第一次跑 baseline 用 nano 權(quán)重不做高級(jí)調(diào)參。下面這組參數(shù)適合 5166 張中等規(guī)模的產(chǎn)品識(shí)別數(shù)據(jù)集yolo detect train \ data/home/you/coke_dataset/data.yaml \ modelyolov8n.pt \ epochs100 imgsz640 batch16 \ patience20 device0 \ project./runs namecoke_product參數(shù)說(shuō)明modelyolov8n.pt是在 COCO 上預(yù)訓(xùn)練過(guò)的權(quán)重拿產(chǎn)品識(shí)別數(shù)據(jù)集做 fine-tune圖量不大也有底子imgsz640是顯存和精度之間的平衡點(diǎn)batch16大約需要 8~10 GB 顯存顯卡緊張就降到 8patience20表示驗(yàn)證指標(biāo) 20 輪不漲就早停對(duì)中等規(guī)模數(shù)據(jù)集很實(shí)用能省掉后半段無(wú)效訓(xùn)練時(shí)間。project./runs namecoke_product把日志和權(quán)重單獨(dú)放一個(gè)目錄方便后續(xù)對(duì)比實(shí)驗(yàn)。有人問(wèn)損失曲線為什么不降我的排查順序是data.yaml 路徑、圖像是否正常讀取、空標(biāo)簽是否混入最后才是學(xué)習(xí)率。YOLOv8 默認(rèn)學(xué)習(xí)率對(duì) fine-tune 夠用真正讓損失不動(dòng)的原因十有八九是數(shù)據(jù)路徑錯(cuò)了或者標(biāo)簽文件空了。3.4 訓(xùn)練完先看三張圖損失曲線、混淆矩陣和驗(yàn)證集預(yù)測(cè)圖訓(xùn)練結(jié)束先別只盯 mAP 數(shù)字打開 runs 目錄里的圖。第一張是results.png里面是 yolo 損失函數(shù)在訓(xùn)練過(guò)程中的實(shí)際表現(xiàn)包括box_loss和cls_loss的訓(xùn)練與驗(yàn)證兩條曲線。驗(yàn)證損失尾段明顯回升是過(guò)擬合信號(hào)驗(yàn)證損失橫盤不降大概率是標(biāo)注不一致或者類別太難。第二張看confusion_matrix_normalized.png重點(diǎn)看哪些類別互相混淆。產(chǎn)品識(shí)別場(chǎng)景里最常見的是零度可樂(lè)和經(jīng)典可樂(lè)互認(rèn)或者透明瓶被漏檢導(dǎo)致背景占比偏高。第三張是val_batchN_pred.jpg直接看驗(yàn)證集上的預(yù)測(cè)框位置框偏了半個(gè)瓶身、漏了后排的小瓶都比 mAP 數(shù)字更早暴露問(wèn)題。看完這三張圖再?zèng)Q定要不要調(diào)參。數(shù)據(jù)本身有毛病時(shí)調(diào) anchor 和損失權(quán)重都是隔靴搔癢先把標(biāo)簽體檢和劃分修好再回來(lái)跑第二輪。4. 產(chǎn)品識(shí)別數(shù)據(jù)集避坑反光、小目標(biāo)、場(chǎng)景單一與標(biāo)簽噪聲產(chǎn)品識(shí)別數(shù)據(jù)集看起來(lái)比行人檢測(cè)、車輛檢測(cè)簡(jiǎn)單——目標(biāo)就是貨架上的商品類別少、背景固定。但 5166 張圖對(duì)工業(yè)落地來(lái)說(shuō)并不算多真正持續(xù)讓人翻車的是幾個(gè)看起來(lái)不嚴(yán)重的細(xì)節(jié)。下面按我踩過(guò)的順序?qū)憽?.1 透明瓶反光可樂(lè)瓶被認(rèn)成礦泉水瓶透明瓶反光在驗(yàn)證集上最容易暴露紅色可樂(lè)瓶要么直接漏檢要么框縮到瓶蓋以下還有一種情況是被認(rèn)成礦泉水瓶。透明 PET 瓶在高光下輪廓斷裂深色液體隔著瓶身反光看又和背景攪在一起。根子在于訓(xùn)練圖大多是白底棚拍瓶身反光區(qū)域在顏色空間里和白色背景幾乎沒(méi)有邊界模型學(xué)到的是「紅色瓶標(biāo)」而不是完整瓶型。同一瓶可樂(lè)換兩個(gè)拍攝角度反光形態(tài)完全不同模型就認(rèn)不出來(lái)了。解決優(yōu)先級(jí)是補(bǔ)圖、調(diào)增強(qiáng)、改推理。往訓(xùn)練集補(bǔ)一兩百?gòu)堊匀还庹盏拈T店圖最有效增強(qiáng)里加大hsv_v抖動(dòng)讓瓶身亮度在訓(xùn)練中學(xué)會(huì)變化推理端對(duì)高光區(qū)域做 CLAHE 局部均衡有一點(diǎn)改善但治標(biāo)不治本。4.2 小目標(biāo)與密集陳列一整面貨架怎么數(shù)都數(shù)不對(duì)小目標(biāo)問(wèn)題在遠(yuǎn)景貨架圖上非常明顯一瓶可樂(lè)只有二三十像素寬漏檢一片中景密集陳列時(shí)幾個(gè)緊貼的瓶框被 NMS 合并成一個(gè)最后數(shù)量永遠(yuǎn)對(duì)不上。原因不在參數(shù)而在分辨率。imgsz640 時(shí)小目標(biāo)占像素太少特征圖下采樣幾輪之后幾乎消失anchor 匹配不到正樣本損失函數(shù)在這一類上學(xué)不到東西。這是 yolo 系列在小目標(biāo)上的老問(wèn)題。預(yù)算夠就imgsz1280訓(xùn)練和驗(yàn)證分辨率翻倍對(duì) 20 像素的小瓶是質(zhì)變代價(jià)是顯存翻倍顯存不夠就保持 640推理時(shí)用 SAHI 把大圖切成小圖分別檢測(cè)再拼回坐標(biāo)。接了深度相機(jī)想做測(cè)距的話先等檢測(cè)框穩(wěn)定再談距離框歪了測(cè)出來(lái)的距離沒(méi)有任何意義。4.3 場(chǎng)景單一導(dǎo)致過(guò)擬合棚拍訓(xùn)練集門店部署直接翻車場(chǎng)景過(guò)擬合的典型表現(xiàn)是訓(xùn)練集 mAP 做到 0.95拿到真實(shí)門店攝像頭一測(cè)只有 0.7還伴隨大量誤檢。5166 張圖里很多是同一批棚拍或同一貨架的重復(fù)角度有效場(chǎng)景可能只有幾百個(gè)。模型把背景紋理、燈光色溫當(dāng)成了特征。白底棚拍訓(xùn)出來(lái)的模型一旦遇到木紋貨架、日光燈色溫、手指遮擋置信度立刻崩。最有效的辦法是帶上一版模型去目標(biāo)門店采 200~500 張圖人工框幾十張做 fine-tune這是產(chǎn)品識(shí)別落地的常規(guī)路徑。不想采圖就做背景替換增強(qiáng)把目標(biāo) ROI 摳出來(lái)隨機(jī)貼到真實(shí)貨架背景上再合成訓(xùn)練樣本。只做翻轉(zhuǎn)、加高斯噪聲這類通用增強(qiáng)對(duì)場(chǎng)景過(guò)擬合幫助很小。4.4 標(biāo)簽噪聲與類別不平衡5166 張里的分布可能和你想的不一樣標(biāo)簽噪聲的表現(xiàn)是訓(xùn)練曲線正常但某個(gè)類別 recall 明顯低于其他類打開驗(yàn)證集預(yù)測(cè)圖該框的沒(méi)框框了的類別不對(duì)。根源是標(biāo)注標(biāo)準(zhǔn)不統(tǒng)一?!缚煽诳蓸?lè)」到底指經(jīng)典款還是包含零度瓶、罐、箱要不要分開多人協(xié)作標(biāo)注時(shí)這兩類標(biāo)準(zhǔn)混在一起模型就學(xué)出一個(gè)四不像類別。另一個(gè)常見問(wèn)題是類別不平衡經(jīng)典款可能占了一半的框小瓶裝只有幾百個(gè)模型自然會(huì)犧牲少數(shù)類。訓(xùn)練前跑一遍類別頻次和框面積統(tǒng)計(jì)。頻次差 5 倍以上要么合并成一個(gè)大類要么在訓(xùn)練時(shí)提高少數(shù)類的損失權(quán)重??蛎娣e兩極分化嚴(yán)重時(shí)按面積過(guò)濾掉極端大框或小框比硬調(diào) anchor 更直接。4.5 數(shù)據(jù)劃分泄漏驗(yàn)證集指標(biāo)看著漂亮現(xiàn)場(chǎng)一測(cè)就崩劃分泄漏最氣人val mAP50 有 0.95換一個(gè)隨機(jī)種子重新劃分再訓(xùn)結(jié)果差了 5 個(gè)點(diǎn)部署到現(xiàn)場(chǎng)效果遠(yuǎn)不如驗(yàn)證集。原因就是劃分的最小單位錯(cuò)了。同一瓶可樂(lè)、同一個(gè)貨架的連續(xù)幀在隨機(jī)劃分時(shí)被拆進(jìn)了訓(xùn)練集和驗(yàn)證集驗(yàn)證集變成了「半夢(mèng)見過(guò)」的圖。5166 張里連拍幀占比越高泄漏越嚴(yán)重。解決方式是按拍攝批次或場(chǎng)景目錄分組整個(gè)組只進(jìn)訓(xùn)練集或只進(jìn)驗(yàn)證集。也可以先用感知哈希對(duì)全部圖像算相似度高度重復(fù)的幀去掉一部分再做劃分。判斷泄漏的土辦法是看訓(xùn)練 mAP 和驗(yàn)證 mAP 的差距小得異常比如兩個(gè)都到 0.99基本就是泄漏了。5. 從訓(xùn)練到部署算 mAP、導(dǎo)出 ONNX 與實(shí)時(shí)識(shí)別的三個(gè)檢查點(diǎn)模型訓(xùn)練完還要在驗(yàn)證集上正式評(píng)估一次再導(dǎo)出成部署格式。這一步能讓你從「訓(xùn)練腳本跑完了」過(guò)渡到「模型真的能用了」。5.1 在驗(yàn)證集上算 mAP50 和 mAP50-95yolo detect val \ data/home/you/coke_dataset/data.yaml \ model./runs/coke_product/weights/best.pt \ imgsz640 batch32結(jié)果里重點(diǎn)看 mAP50 和 mAP50-95 兩個(gè)數(shù)。產(chǎn)品識(shí)別更看重 mAP50貨架計(jì)數(shù)和質(zhì)檢對(duì)「框大概在不在」敏感mAP50-95 更嚴(yán)苛框位置偏差一點(diǎn)就掉分。驗(yàn)證集指標(biāo)這時(shí)候有點(diǎn)玄學(xué)別只看數(shù)字配合 3.4 的預(yù)測(cè)圖一起判斷。5.2 導(dǎo)出 ONNX讓模型脫離 PyTorch 環(huán)境跑起來(lái)yolo export model./runs/coke_product/weights/best.pt formatonnx導(dǎo)出后可以用 ONNX Runtime 在 CPU 上推理也可以在 Jetson AGX Orin 這類設(shè)備上繼續(xù)轉(zhuǎn) TensorRT engine這是邊緣盒子上搭 YOLO 環(huán)境的常見做法。默認(rèn)導(dǎo)出參數(shù)夠用真要多 batch 推理再開dynamicTrue。5.3 一瓶可樂(lè)都認(rèn)不準(zhǔn)時(shí)先回來(lái)查這三件事第一部署代碼里的 names 順序和訓(xùn)練 data.yaml 是否一致順序不一致可樂(lè)會(huì)變零度第二預(yù)處理是否和訓(xùn)練一致letterbox 保持寬高比后再歸一化BGR/RGB 通道順序反了顏色全偏第三置信度閾值別設(shè)太低產(chǎn)品識(shí)別現(xiàn)場(chǎng)調(diào)到 0.35 到 0.5比在訓(xùn)練端加正則更省事。我第一次拿這種帶標(biāo)簽的現(xiàn)成數(shù)據(jù)集跳過(guò)體檢直接開訓(xùn)結(jié)果在客戶現(xiàn)場(chǎng)被攝像頭底下的透明瓶身教做人。后來(lái)凡是從網(wǎng)上下數(shù)據(jù)集我都先做標(biāo)簽統(tǒng)計(jì)、分層劃分、跑 baseline折騰半小時(shí)省的是后面調(diào)參的一整天。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取