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