尧图精选

扑克牌识别数据集实战:YOLO v11训练与98.7%识别率调优指南

🕒 发布时间:2026/10/1 10:35:17 📁 来源:尧图网络
简介这份扑克牌识别数据集面向计算机视觉学习者、目标检测开发者及棋牌类应用研发人员用于训练可识别A至K全部牌面字母的检测模型官方标注正确识别率可达98.7%。资源包共2000个文件包含1850个txt标注文件、149张jpg原始图像和1个yaml配置文件压缩包约109.76MBtxt对应YOLO v11格式的边界框与类别标签jpg为实拍牌面样本yaml用于声明数据集路径与类别名称可直接接入YOLO v11训练流程。目前已有241人学习下载。数据集覆盖A到K各点数牌面样本包含不同拍摄角度与光照条件适合作为目标检测课程实验、模型微调或棋牌识别项目的训练与验证素材帮助读者省去从零采集与标注的成本快速复现高精度识别效果并对比不同模型性能。1. 扑克牌识别数据集1850 张原始图与 98.7% 识别率背后的落地价值做牌类识别项目的工程师大多经历过这个阶段模型结构调了半天数据增强加了一堆mAP 就是卡在 0.7 上不去最后发现瓶颈根本不在网络而在标注质量。扑克牌识别这个场景尤其典型——A 到 K 十三个点数四种花色牌面在光照、角度、遮挡下差异极大自己从零标 1850 张图按熟练工一天标 300 张算光标注就得搭进去一周还不算返工。这份扑克牌识别数据集直接给了 1850 张原始图和 YOLO v11 格式的标注文件覆盖 A 到 K 全部牌字母官方给出的正确识别率能到 98.7%。它解决的不是有没有数据的问题而是数据能不能直接进训练管线的问题。适合正在做牌类检测、卡牌识别、桌面游戏视觉的从业者也适合想拿一个干净数据集跑通 YOLO v11 全流程的新手。下面从数据组织、格式转换、训练配置到踩坑排查把这份资源拆开讲透。2. 数据集结构与 YOLO v11 标注格式先搞懂文件怎么组织2.1 目录布局与文件命名规律拿到压缩包解压后常见的目录结构是这样组织的根目录下分images和labels两个文件夹分别存放原始图和标注文件两边文件名一一对应只是扩展名不同。从项目正文给出的文件名能看出命名规律比如IMG_20220316_165709_jpg.rf.9388e4bb5d674a1000f6d73abfb11c2f.jpg这种前半段是原始拍摄编号中间_jpg表示源格式后面.rf.加一长串哈希是标注平台导出时自动追加的唯一标识。另一类像k4_jpg.rf.a158d105967e7ec645d2a2e014c241d6.jpg前缀k4直接对应牌面信息。这种命名方式有个好处哈希后缀保证了即使原始文件名重复也不会覆盖但坏处是可读性差排查问题时没法一眼看出某张图对应哪张牌。我一般会先跑一个脚本把文件名和标注内容做个映射表方便后续抽查。# 查看数据集目录结构确认 images 和 labels 是否配对 find ./poker_dataset -maxdepth 2 -type d # 统计图片数量和标注数量两者应该相等 ls ./poker_dataset/images | wc -l ls ./poker_dataset/labels | wc -l上面两条统计命令是进项目后的第一道检查。如果图片数和标注数对不上说明有文件在传输或解压过程中丢失后面训练必然报错。常见做法是先补齐再往下走别抱着少几张无所谓的心态YOLO 训练时找不到对应 label 会直接跳过该图等于白标。2.2 YOLO v11 标注格式逐字段拆解YOLO 系列的标注格式从 v5 到 v11 在文件层面是兼容的每张图对应一个.txt文件每行代表一个目标框格式为class_id x_center y_center width height五个字段全部用空格分隔后四个是归一化到 0 到 1 之间的浮点数。class_id从 0 开始编号具体哪个数字对应哪张牌得看数据集附带的classes.txt或data.yaml。这份数据集覆盖 A 到 K如果按点数分 13 类那 class_id 0 到 12 分别对应 A、2、3……K如果还区分花色类别数会更多。拿到手第一件事就是确认类别映射别想当然。# 读取一张标注文件打印类别分布确认 class_id 映射 import os from collections import Counter label_dir ./poker_dataset/labels class_counter Counter() for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) 5: class_counter[int(parts[0])] 1 # 按类别编号排序输出看每个类别有多少个框 for cid in sorted(class_counter.keys()): print(fclass {cid}: {class_counter[cid]} boxes)这段脚本的作用是统计每个类别在全部标注里出现了多少次。正常情况各类别数量应该大致均衡如果某个 class_id 的框数明显偏少比如只有个位数那要么是这类牌拍得少要么是标注时漏标了。扑克牌数据集里 A 和 K 这种端点牌有时会被漏因为牌面图案和数字牌差异大标注员容易犹豫。发现不均衡先别急着训练回去补标比训完再返工划算得多。2.3 从原始图到可训练集的划分策略1850 张图不算多划分训练集、验证集、测试集时不能随手 8:1:1 切。扑克牌识别有个坑同一副牌在同一光线下连拍的多张图如果随机划分很可能训练集和验证集里出现几乎一样的图验证指标虚高实际部署就翻车。正确做法是按拍摄批次或牌组划分同一批次的图要么全进训练集要么全进验证集。# 按文件名前缀分组划分避免同批次图片泄漏到验证集 import os import random import shutil random.seed(42) src_img ./poker_dataset/images src_lbl ./poker_dataset/labels dst_root ./dataset_split # 以文件名中 _jpg 之前的部分作为批次标识 def get_batch_key(fname): return fname.split(_jpg)[0] files [f for f in os.listdir(src_img) if f.endswith(.jpg)] batches {} for f in files: key get_batch_key(f) batches.setdefault(key, []).append(f) batch_keys list(batches.keys()) random.shuffle(batch_keys) # 按批次 8:1:1 分配 n len(batch_keys) train_keys batch_keys[:int(n * 0.8)] val_keys batch_keys[int(n * 0.8):int(n * 0.9)] test_keys batch_keys[int(n * 0.9):] for split, keys in [(train, train_keys), (val, val_keys), (test, test_keys)]: for sub in [images, labels]: os.makedirs(os.path.join(dst_root, split, sub), exist_okTrue) for k in keys: for f in batches[k]: shutil.copy(os.path.join(src_img, f), os.path.join(dst_root, split, images, f)) lbl f.rsplit(., 1)[0] .txt shutil.copy(os.path.join(src_lbl, lbl), os.path.join(dst_root, split, labels, lbl))这段划分脚本的关键在get_batch_key这个函数它把同一批次拍摄的图绑在一起保证不会跨集泄漏。random.seed(42)是为了让划分可复现团队协作时大家拿到一样的切分。参数上 8:1:1 是常规比例如果后续要做模型对比实验可以把验证集比例提到 0.15测试集留 0.05 就够因为测试集只用来做最终一次性评估。3. 用 YOLO v11 训练扑克牌检测模型配置、命令与参数调优3.1 环境准备与 data.yaml 配置YOLO v11 通过 ultralytics 包调用环境准备不复杂但版本要对齐。常见做法是建一个干净的虚拟环境装 ultralytics 和 torch别和系统里其他项目的依赖混在一起否则 CUDA 版本冲突能折腾一下午。# 创建虚拟环境并安装依赖 python -m venv venv_poker source venv_poker/bin/activate pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu121 # 验证安装和 GPU 可用性 yolo checksyolo checks会打印出当前环境、CUDA 是否可用、GPU 型号等信息。如果这里显示 CPU only后面训练会慢到怀疑人生先解决驱动和 CUDA 匹配问题再往下走。接着写data.yaml这是 YOLO 训练的数据入口配置# data.yaml path: ./dataset_split train: train/images val: val/images test: test/images # 类别数A 到 K 共 13 类 nc: 13 names: 0: A 1: 2 2: 3 3: 4 4: 5 5: 6 6: 7 7: 8 8: 9 9: 10 10: J 11: Q 12: Kpath是数据集根目录train、val、test是相对路径。nc必须和实际类别数一致names的顺序必须和标注文件里的 class_id 严格对应错一个位置模型学出来的就是错位的类别。如果这份数据集还区分花色那nc要改成 52names也要相应扩展成黑桃A、红桃A这种。拿到数据集先确认这一点别照抄。3.2 训练命令与关键超参设置配置好之后训练命令本身很短但参数选择决定了能不能复现 98.7% 这个指标。# 用 YOLO v11n 预训练权重在扑克牌数据集上微调 yolo detect train \ modelyolo11n.pt \ data./data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ project./runs_poker \ nameexp_v11n逐项说下参数。modelyolo11n.pt用的是 nano 版本1850 张图这个量级n 版足够用 l 或 x 版反而容易过拟合。epochs100配合patience20意思是 20 轮验证指标不提升就早停避免无效训练。imgsz640是 YOLO 默认输入尺寸扑克牌在图中占比通常不小640 够用如果牌在画面里很小比如监控远景可以提到 1280但显存占用会翻倍。batch16是 8G 显存下的稳妥值显存够可以往上加。lr00.01是初始学习率微调场景下这个值偏大如果发现 loss 震荡降到 0.001 再试。device0指定第一块 GPU。训练启动后重点盯三个输出box_loss、cls_loss和mAP50。box_loss 管定位准不准cls_loss 管分类对不对。正常情况两个 loss 都该稳步下降如果 cls_loss 降不下去多半是类别映射错了或者某类样本太少。3.3 训练过程监控与指标解读训练日志里mAP50和mAP50-95是两个核心指标。mAP50 是 IoU 阈值 0.5 时的平均精度扑克牌这种形状规整的目标mAP50 上 0.95 不难mAP50-95 更严格能到 0.8 以上说明定位很准。官方说的 98.7% 正确识别率大概率指的是分类准确率或者 mAP50 对应的指标具体口径得看数据集说明别直接拿自己的 mAP50-95 去对标。# 训练结束后加载最佳权重在测试集上跑评估 from ultralytics import YOLO model YOLO(./runs_poker/exp_v11n/weights/best.pt) metrics model.val(data./data.yaml, splittest) print(fmAP50: {metrics.box.map50:.4f}) print(fmAP50-95: {metrics.box.map:.4f}) print(f每类 AP50: {metrics.box.ap50})metrics.box.ap50会返回每个类别的 AP这个比总体 mAP 更有诊断价值。如果总体 mAP50 有 0.97但某一类比如 K 只有 0.85说明 K 的样本可能有问题回去查这类图的标注。splittest确保用的是没参与训练和调参的测试集指标才可信。3.4 推理验证与置信度阈值调整训练完拿几张实际图跑推理看框和类别对不对。置信度阈值conf默认 0.25扑克牌场景可以适当调高到 0.4 到 0.5减少误检。# 对单张图推理并保存结果 from ultralytics import YOLO model YOLO(./runs_poker/exp_v11n/weights/best.pt) results model.predict( source./test_samples/IMG_20220316_165709.jpg, conf0.45, iou0.5, saveTrue, project./infer_out ) for r in results: for box in r.boxes: cls_id int(box.cls) conf float(box.conf) print(f类别: {model.names[cls_id]}, 置信度: {conf:.3f})conf0.45是过滤低置信度框iou0.5是 NMS 的 IoU 阈值控制重叠框合并。扑克牌如果叠在一起NMS 阈值调太低会把相邻的牌误合并调太高又会留重复框0.5 是个平衡点。推理输出里如果发现某张牌反复出现两个框把 iou 降到 0.4 试试如果漏检把 conf 降到 0.3。4. 避坑与排查标注、训练、推理里最容易翻车的五件事4.1 类别映射错位导致全盘皆错现象训练 loss 正常下降mAP 也能到 0.9 以上但推理时发现模型把 A 认成 K把 7 认成 9类别整体错位。原因data.yaml里names的顺序和标注文件里 class_id 的实际含义不一致。比如标注时 0 是 A但 yaml 里写成了 0 是 2模型学到的就是错的映射。解决拿一张标注文件手动对照图片确认 class_id 对应的牌面再改data.yaml。改完重新训练别指望在推理阶段用后处理纠正错位是系统性的后处理救不回来。4.2 图片与标注文件名不匹配现象训练启动后日志里出现大量WARNING: No labels found实际参与训练的图远少于 1850 张。原因图片是.jpg标注是.txt但两边文件名主体不一致。比如图片叫k4_jpg.rf.a158.jpg标注却叫k4.txtYOLO 按同名匹配找不到就跳过。解决写脚本批量重命名把标注文件名改成和图片一致只改扩展名。重命名前先备份改错了还能回退。# 批量把 labels 里的 txt 文件名对齐 images 里的 jpg 文件名 cd ./poker_dataset for img in images/*.jpg; do base$(basename $img .jpg) if [ -f labels/${base}.txt ]; then continue fi # 尝试用前缀匹配找回对应标注 prefix$(echo $base | cut -d_ -f1) match$(ls labels/${prefix}*.txt 2/dev/null | head -1) if [ -n $match ]; then cp $match labels/${base}.txt fi done4.3 验证集指标虚高部署就掉点现象验证集 mAP50 有 0.98实际拿手机拍几张牌去测识别率掉到 0.7。原因划分数据集时随机切分同一批次连拍的图同时进了训练集和验证集模型见过几乎一样的图验证指标是背答案背出来的。解决按拍摄批次或牌组划分同一批次的图只进一个集合。前面 2.3 节的划分脚本就是干这个的。如果数据集没提供批次信息至少按文件名前缀粗分。4.4 小目标漏检与置信度阈值误设现象远景拍摄的扑克牌模型一个框都不出或者只出大牌漏小牌。原因imgsz640下如果牌在图中只占几十个像素特征太弱。另外conf默认 0.25 对远景小目标偏高低置信度的正确框被滤掉了。解决把imgsz提到 1280 重新训练同时推理时conf降到 0.2。如果显存不够用切片推理把大图切成小块分别检测再合并。4.5 过拟合训练集完美验证集拉胯现象训练集 mAP 到 0.99验证集卡在 0.85 上不去loss 曲线训练集还在降验证集已经平了甚至回升。原因1850 张图对 13 类目标来说偏少模型容量大就容易记住训练样本。另外数据增强开得太弱模型没见过足够的变化。解决换更小的模型n 版换 s 版反而更糟应该加正则开强数据增强mosaic1.0、mixup0.2、degrees15、hsv_h0.015。同时把dropout调高或者加weight_decay0.0005。如果还不行说明数据量确实不够考虑用这份数据集做基础再自己补拍一些不同光照和角度的图。5. 进阶技巧用混淆矩阵定位弱类并做定向补强训练完只看总体 mAP 是不够的真正能提升模型的是找到哪几类容易混。YOLO 训练结束后会在runs_poker/exp_v11n/下生成confusion_matrix.png这张图是排查弱类的黑匣子。横轴是预测类别纵轴是真实类别对角线越深越好非对角线的亮块就是混淆高发区。# 用验证集结果生成归一化混淆矩阵便于对比各类 from ultralytics import YOLO import numpy as np model YOLO(./runs_poker/exp_v11n/weights/best.pt) metrics model.val(data./data.yaml, splitval, plotsTrue) # 直接读取 ultralytics 生成的混淆矩阵数据 cm metrics.confusion_matrix.matrix cm_norm cm / cm.sum(axis1, keepdimsTrue) names list(model.names.values()) print(混淆最严重的类别对) for i in range(len(names)): for j in range(len(names)): if i ! j and cm_norm[i][j] 0.05: print(f真实 {names[i]} 被预测为 {names[j]}: {cm_norm[i][j]:.2%})这段代码把混淆矩阵按行归一化输出所有超过 5% 的误判对。扑克牌里常见的混淆是 6 和 9因为牌面旋转 180 度后几乎一样还有 10 和 J某些字体下 10 的1和 J 的竖笔容易混。找到这些对之后针对性补数据把 6 和 9 的图各挑出来做 180 度旋转增强让模型学会区分方向10 和 J 则补拍不同字体的牌。补强之后重新训练重点看这几类的 AP 有没有提升。我一般会做两轮第一轮全量训练拿到基线第二轮只对弱类做过采样把弱类样本复制到和最多类一样的数量再训一次。两轮对比如果弱类 AP 涨了 5 个点以上说明方向对了。还有个技巧是调整推理时的类别阈值。YOLO 支持给每个类别单独设conf对容易误检的类调高对容易漏检的类调低。比如 6 和 9 容易混就把这两类的 conf 都提到 0.6宁可漏检也不误判因为牌类应用里误判比漏检代价大。# 推理时对特定类别单独设阈值 from ultralytics import YOLO model YOLO(./runs_poker/exp_v11n/weights/best.pt) # 假设 6 和 9 的 class_id 是 5 和 8单独提高阈值 results model.predict( source./test_samples/, conf0.35, iou0.5, classesNone, # 用 agnostic_nms 避免跨类合并 agnostic_nmsFalse )agnostic_nmsFalse表示 NMS 按类别独立做不同类的框不会互相抑制。如果发现 6 和 9 的框总是被合并成一个检查这个参数是不是被设成了 True。从那以后我每次拿到新数据集都强制先跑一遍类别分布统计和混淆矩阵基线再动手调模型。数据的问题不解决网络怎么改都是白费。希望这份扑克牌识别数据集和上面的流程能帮你少走几天弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →