:501張圖達到99.3%準確率)
簡介這份撲克牌識別數(shù)據(jù)集面向計算機視覺學習者與目標檢測開發(fā)者用于訓練可同時識別牌面數(shù)字與花色的模型適用于YOLO系列實戰(zhàn)、課程設計或小型競賽場景。資源包共1003個文件包含501張原始jpg圖像、501個同名txt標注文件及1個yaml配置文件壓縮包約11.82MBtxt為YOLO v8格式的邊界框標注yaml用于定義數(shù)據(jù)集路徑與類別jpg即原始牌面圖像整體結構規(guī)整可直接接入訓練流程。據(jù)描述該數(shù)據(jù)集在數(shù)字與花色識別任務上正確識別率可達99.3%樣本覆蓋多種牌面組合便于快速驗證模型效果。目前已有112人學習下載適合希望以較小數(shù)據(jù)量完成端到端檢測實驗、對比不同YOLO版本性能或作為數(shù)據(jù)增強與遷移學習起點的讀者參考使用。1. 撲克牌識別數(shù)據(jù)集501 張原始圖怎么撐起 99.3% 的識別率手里有一批撲克牌照片想讓模型同時認出「數(shù)字」和「花色」這件事聽起來簡單做起來處處是坑。數(shù)字只有 13 類花色只有 4 類可一旦把兩者組合起來就是 52 類目標再加上不同角度、光照、遮擋、牌面反光難度立刻上一個臺階。這個撲克牌識別數(shù)據(jù)集給了一個很具體的起點501 張原始圖按 yolo v8 格式標注官方口徑的正確識別率可達 99.3%。對做目標檢測落地的人來說這不是一個玩具數(shù)據(jù)集而是一個能快速驗證「小樣本 多類別 細粒度」方案的試驗田。它適合誰一是想跑通 yolo v8 訓練自己數(shù)據(jù)集全流程的新手501 張圖規(guī)模不大單卡幾十分鐘就能出第一版權重二是做牌類游戲輔助、發(fā)牌機器人、桌面視覺統(tǒng)計的工程師需要一套能直接復現(xiàn)的基線三是研究細粒度檢測的人撲克牌的數(shù)字和花色在視覺上高度相似正好用來壓榨模型的特征分辨能力。接下來我會按「數(shù)據(jù)長什么樣 → 怎么轉成 yolo v8 能吃的格式 → 訓練參數(shù)怎么設 → 翻車點在哪 → 怎么把 99.3% 穩(wěn)住」這條線把能抄的步驟和參數(shù)都攤開。2. 先看清數(shù)據(jù)501 張原始圖的標注結構與類別設計2.1 撲克牌識別的兩類標注思路單標簽 52 類 vs 雙標簽組合拿到撲克牌數(shù)據(jù)第一件事不是急著訓練而是決定標注體系。常見做法有兩種一種是把「紅桃 A」當成一個獨立類別總共 52 類另一種是拆成數(shù)字和花色兩個維度檢測框只標牌面再用兩個分類頭分別預測數(shù)字和花色。這個數(shù)據(jù)集走的是前一種思路標注文件里直接給出 52 類中的某一類yolo v8 格式一行一個目標格式是class_id x_center y_center width height坐標全部歸一化到 0 到 1。為什么小樣本更適合單標簽 52 類因為 501 張圖分攤到 52 類平均每類不到 10 個樣本如果再做雙頭結構每個頭的正樣本更少訓練時容易某一維塌縮。單標簽把數(shù)字和花色的組合當成一個整體模型學的是「這張牌長什么樣」而不是「先認數(shù)字再認花色」在數(shù)據(jù)量不足時反而更穩(wěn)。代價是類別數(shù)多分類層參數(shù)量上升但對 yolo v8 這種 anchor-free 結構來說52 類并不算負擔。提示如果你的場景里撲克牌種類固定且每類樣本能到 50 張以上雙標簽組合會更靈活因為新增一種花色或數(shù)字時不用重新標全部數(shù)據(jù)。501 張這個量級建議先按單標簽 52 類跑通。2.2 yolo v8 標注格式逐字段拆解與目錄組織yolo v8 的標注文件是純文本每行代表一個目標字段之間用空格分隔。以一張包含「黑桃 10」的圖為例標注行可能是47 0.512 0.634 0.180 0.260。第一個數(shù)字 47 是類別索引對應你data.yaml里 names 列表的第 48 個名字后面四個數(shù)分別是框中心 x、中心 y、框寬、框高全部除以圖像寬高做歸一化。這里最容易翻車的是類別索引和 names 順序對不上訓練時 loss 能降但推理出來全是錯類。目錄組織上我一般會保持這樣的結構讓 yolo v8 的默認 dataloader 直接認poker_dataset/ ├── images/ │ ├── train/ # 訓練圖約 400 張 │ └── val/ # 驗證圖約 101 張 ├── labels/ │ ├── train/ # 與 train 圖同名的 .txt │ └── val/ └── data.yamldata.yaml里寫清路徑和類別名注意path用絕對路徑或相對于訓練腳本的路徑train和val指向 images 下的子目錄yolo v8 會自動去找同級的 labels。path: /home/user/poker_dataset train: images/train val: images/val nc: 52 names: 0: spade_A 1: spade_2 # ... 依次補齊 52 類 51: heart_K字段說明nc必須等于 names 的長度少一個都會在訓練啟動時報索引越界names 的順序必須和標注文件里的 class_id 嚴格一致建議用腳本生成而不是手寫。501 張圖按 8:2 切分訓練集 400 張、驗證集 101 張這個比例在小樣本下比較穩(wěn)驗證集太小會導致 mAP 波動大太大又浪費訓練樣本。2.3 用腳本檢查標注完整性三個必查項在訓練之前我習慣跑一個檢查腳本把三類問題提前揪出來標注文件缺失、坐標越界、類別索引超出 nc。這三類問題在 501 張規(guī)模的數(shù)據(jù)里很常見尤其是從其他格式轉過來的時候。import os from pathlib import Path img_dir Path(poker_dataset/images/train) lbl_dir Path(poker_dataset/labels/train) nc 52 errors [] for img in img_dir.glob(*.jpg): lbl lbl_dir / (img.stem .txt) if not lbl.exists(): errors.append(f缺標注: {img.name}) continue for line in lbl.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: errors.append(f字段數(shù)錯: {lbl.name} - {line}) continue cid int(parts[0]) x, y, w, h map(float, parts[1:]) if cid 0 or cid nc: errors.append(f類別越界: {lbl.name} cid{cid}) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): errors.append(f坐標異常: {lbl.name} - {line}) print(f共發(fā)現(xiàn) {len(errors)} 個問題) for e in errors[:20]: print(e)邏輯說明遍歷訓練圖逐個找同名 txt檢查每行字段數(shù)、類別索引范圍、歸一化坐標是否落在合法區(qū)間。參數(shù)上nc要和 data.yaml 一致坐標檢查里寬高必須大于 0中心點允許等于 0 或 1貼邊目標。跑完如果輸出 0 個問題說明標注基本干凈如果有越界優(yōu)先懷疑轉換腳本把像素坐標直接寫進去了沒有除以圖像寬高。3. 從原始圖到 yolo v8 可訓練轉換、劃分與增強配置3.1 把 VOC 或 COCO 標注轉成 yolo v8 格式的腳本很多撲克牌數(shù)據(jù)最初是 VOC 的 XML 或 COCO 的 JSON要喂給 yolo v8 必須先轉。轉換的核心就一件事把絕對像素坐標變成歸一化中心點加寬高。下面這個腳本處理 VOC 格式COCO 只需把讀取部分換成 json 解析邏輯一樣。import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image classes [spade_A, spade_2, ...] # 52 類順序固定 cls2id {c: i for i, c in enumerate(classes)} def voc_to_yolo(xml_path, img_path, out_path): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in cls2id: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) xc (xmin xmax) / 2 / w yc (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls2id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) out_path.write_text(\n.join(lines)) for xml in Path(voc_annotations).glob(*.xml): img Path(images) / (xml.stem .jpg) voc_to_yolo(xml, img, Path(labels) / (xml.stem .txt))邏輯說明先讀 XML 里的圖像寬高再對每個 object 取 bbox 四個角點算中心點和寬高后除以寬高歸一化。參數(shù)上classes列表順序就是最終 class_id必須和 data.yaml 完全一致保留 6 位小數(shù)足夠yolo v8 內(nèi)部會再處理。轉換完務必用 2.3 的檢查腳本過一遍我見過太多因為 XML 里 name 拼寫不一致導致某類直接消失的情況。3.2 訓練集驗證集劃分別讓同一張牌的兩個角度分到兩邊501 張圖如果隨機切分很容易出現(xiàn)「同一張牌的不同拍攝角度」被分到訓練和驗證兩邊驗證集 mAP 虛高實際部署就翻車。我的做法是按牌面組合分層抽樣先統(tǒng)計每類出現(xiàn)的圖片保證每個類別在驗證集里至少有一張同時同一張物理牌的不同角度盡量只進一邊。如果數(shù)據(jù)里同一張牌有多個角度建議按「牌」分組后再切分而不是按圖隨機。import random from pathlib import Path from collections import defaultdict random.seed(42) img_dir Path(poker_dataset/images/all) imgs list(img_dir.glob(*.jpg)) # 按類別分組這里簡化為按文件名前綴分組實際按標注統(tǒng)計 groups defaultdict(list) for img in imgs: key img.stem.split(_)[0] # 假設文件名含牌面標識 groups[key].append(img) train, val [], [] for key, items in groups.items(): random.shuffle(items) split max(1, int(len(items) * 0.2)) val.extend(items[:split]) train.extend(items[split:]) print(ftrain{len(train)} val{len(val)})邏輯說明用文件名前綴模擬「同一張牌」的分組實際項目里應該根據(jù)標注內(nèi)容聚類。參數(shù)0.2是驗證集比例seed42保證可復現(xiàn)。切分后把圖復制到 images/train 和 images/val標注同步復制再核對一遍兩邊類別分布避免某類只在訓練集出現(xiàn)。3.3 yolo v8 訓練參數(shù)501 張圖該設多少 epoch 和 batch數(shù)據(jù)準備好后用 ultralytics 的 yolo v8 訓練命令行一行就能起。501 張圖屬于小樣本參數(shù)不能照搬 COCO 那套。yolo detect train \ datapoker_dataset/data.yaml \ modelyolov8n.pt \ epochs300 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience50 \ augmentTrue \ mosaic1.0 \ degrees10.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ projectruns/poker \ namev8n_501參數(shù)說明modelyolov8n.pt用 nano 版501 張圖不需要大模型n 版在單卡上幾分鐘一個 epochepochs300配合patience50驗證集 50 輪不漲就早停避免過擬合imgsz640是 yolo v8 默認撲克牌在圖中占比不小640 夠用如果牌很小可以提到 960batch16在 8G 顯存上穩(wěn)顯存大可加到 32lr00.01是初始學習率小樣本別設太大否則 loss 震蕩mosaic1.0開啟馬賽克增強對小樣本很關鍵能顯著提升泛化degrees10.0做小角度旋轉撲克牌擺放角度多變這個增強很值HSV 三參數(shù)控制顏色抖動應對不同光照。訓練啟動后重點看三個指標box_loss是否穩(wěn)定下降、mAP50是否在 100 輪后進入平臺、驗證集cls_loss是否明顯高于訓練集過擬合信號。如果 mAP50 卡在 0.8 上不去先別調(diào)模型回頭查標注里有沒有類別混淆尤其是數(shù)字 6 和 9、方塊和紅桃這種視覺相近的。4. 99.3% 識別率背后的避坑與排查清單4.1 現(xiàn)象訓練 mAP 很高推理卻把紅桃認成方塊原因驗證集和訓練集來自同一批拍攝條件模型學到了背景或光照的捷徑而不是牌面本身的特征?;ㄉ锛t桃和方塊都是紅色形狀差異在低分辨率下被抹平模型靠顏色區(qū)分一旦換光照就崩。解決在增強里加大hsv_h和hsv_s讓顏色通道抖動更劇烈逼模型看形狀同時把驗證集換成不同光照下拍的圖哪怕只有幾十張也能暴露問題。如果條件允許把紅桃和方塊的樣本單獨抽出來做混淆矩陣看錯分集中在哪幾類。4.2 現(xiàn)象某些類別 mAP 為 0訓練日志里也沒報錯原因這些類別的標注 class_id 超出了nc或者 names 列表里名字拼寫和標注不一致yolo v8 在加載時靜默跳過不報錯。501 張圖里如果某類只有兩三個樣本更容易被忽略。解決跑 2.3 的檢查腳本重點看類別索引分布再用yolo detect val輸出每類 APAP 為 0 的類逐個核對標注。我一般會統(tǒng)計每個 class_id 的標注框數(shù)量少于 5 個的類要么補數(shù)據(jù)要么合并到相近類。4.3 現(xiàn)象訓練到 200 輪后驗證 loss 開始上升mAP 反而降原因小樣本過擬合。501 張圖對 52 類來說平均每類不到 10 個樣本模型在 150 輪左右就記住了訓練集之后開始背噪聲。解決把patience從 50 降到 30早停更敏感開啟dropoutyolo v8 里通過dropout0.1設置減小模型從 yolov8s 換回 yolov8n增強里把mosaic保持 1.0再加mixup0.1。如果還壓不住說明數(shù)據(jù)量確實是瓶頸考慮用同一批牌多拍幾個角度補到 800 張以上。4.4 現(xiàn)象推理時一張圖里多張牌漏檢靠近邊緣的牌原因yolo v8 對貼邊目標的回歸能力弱標注時如果框貼邊歸一化坐標接近 0 或 1訓練時容易被裁掉。另外imgsz太小邊緣目標下采樣后特征消失。解決標注時給貼邊目標留 2 到 3 像素余量訓練時把imgsz提到 960推理時降低conf閾值到 0.15 觀察是否召回如果召回但誤檢多再用 NMS 的iou參數(shù)壓。我一般會在推理腳本里把conf0.25, iou0.45作為起點根據(jù)實際漏檢情況微調(diào)。4.5 現(xiàn)象換一臺機器或換一批牌識別率從 99.3% 掉到 80%原因99.3% 是在特定數(shù)據(jù)集分布下測的牌面印刷、相機白平衡、桌面背景一變特征分布就漂移。這不是模型的問題是數(shù)據(jù)覆蓋度的問題。解決部署前用目標場景的圖做一次小樣本微調(diào)哪怕只有 50 張凍結 backbone 只訓 head學習率設 0.00120 輪就能拉回來。同時把推理時的預處理對齊訓練配置比如訓練用了 letterbox推理也要 letterbox別直接 resize否則長寬比一變框就偏了。5. 把 99.3% 復現(xiàn)出來驗證腳本與一個提點技巧訓練完拿到best.pt別急著信日志里的 mAP自己寫一個驗證腳本按類別統(tǒng)計準確率才能知道 99.3% 到底落在哪。下面這個腳本用 ultralytics 的 API 跑驗證集輸出每類 AP 和整體混淆矩陣。from ultralytics import YOLO import numpy as np model YOLO(runs/poker/v8n_501/weights/best.pt) metrics model.val(datapoker_dataset/data.yaml, imgsz640, conf0.25, iou0.45) print(fmAP50: {metrics.box.map50:.4f}) print(fmAP50-95: {metrics.box.map:.4f}) # 每類 AP for i, ap in enumerate(metrics.box.ap50): if ap 0.9: print(f類別 {i} AP50{ap:.3f} 偏低需補數(shù)據(jù))邏輯說明model.val會按 data.yaml 的 val 路徑跑一遍返回的metrics.box.ap50是每類 AP 數(shù)組。參數(shù)conf0.25和iou0.45要和部署時一致否則驗證結果沒有參考意義。跑完重點看 AP 低于 0.9 的類這些就是拉低整體 99.3% 的短板優(yōu)先補它們的樣本。一個提點技巧撲克牌的數(shù)字和花色在牌面上下兩端是對稱的如果模型對旋轉敏感可以在推理前做一次 TTA測試時增強把原圖和旋轉 180 度的圖各跑一次框合并后再 NMS。yolo v8 自帶augmentTrue參數(shù)推理時加上就能開啟 TTA代價是速度慢一倍但對小樣本模型通常能提 1 到 2 個點。我自己的習慣是只要部署端算力夠推理默認開 TTA尤其是牌面可能倒著放的時候。最后說個血淚教訓別在驗證集上反復調(diào)參調(diào)到 99.9%然后拿這個數(shù)去匯報。501 張圖的驗證集只有 101 張mAP 波動一兩個點是常事真正靠譜的做法是留一批完全沒參與訓練的圖做最終測試哪怕只有 30 張。我一般會把數(shù)據(jù)切成 train/val/test 三份test 只在最后跑一次跑完就封存這樣得到的數(shù)字才敢寫進文檔。希望幫到你。本文還有配套的精品資源點擊獲取