news 2026/10/7 1:34:21

YOLOv5扑克牌检测实战:从数据集格式到训练避坑全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5扑克牌检测实战:从数据集格式到训练避坑全指南

简介:这是一份专为目标检测任务设计的扑克牌图像数据集,采用YOLOV5标准目录结构,适合需要训练检测模型的开发者、研究者以及入门深度学习的目标检测实战。数据集中包含52个类别,即四种花色的A至K全部牌型,图像尺寸统一为720×720的RGB图片,训练集16000张、验证集4000张,分别配有对应的txt标注文件,可直接用于模型训练与验证,无需额外格式转换。资源压缩包共有2000个文件,其中1999个为txt标注及类别字典文件,1个为可运行的可视化脚本,整体大小约947.74MB。内置的Python脚本会随机读取一张图片并绘制边界框,运行后自动保存在当前目录,方便快速检查标注效果。目前已有134人学习下载,适合用于扑克牌识别、棋牌场景目标检测等实际项目的前期数据准备。

1. 这个数据集解决的不只是“有没有图”的问题:52类别扑克牌检测的硬骨头在哪

很多人拿到一个 YOLOv5 格式的目标检测数据集,第一反应是“解压、开训、看 mAP”,扑克牌这个场景却最容易在这套流程里翻车。一张牌面上同时存在“点数”和“花色”两类信息,52 个类别意味着模型既要分清数字 3 和 5,又要辨明红心和方块,细粒度差异比区分“猫和狗”难得多。再加上牌面经常被旋转、重叠、反光,公开的扑克牌检测数据集基本都是 YOLOv5 目录格式,正好拿来做迁移学习和超参数调优的练手对象。这篇文章沿着“目录结构 → 标签含义 → 训练命令 → 踩坑 → 盲测验证”这条路,把这个数据集从解压到出指标讲透,适合准备用 YOLOv5 训练自己数据集、又不想在格式转换上浪费时间的从业者,也适合刚入门目标检测、需要一个可控的小型数据集跑通全流程的新手。

2. 先拆目录和标签:YOLOv5 格式的数据集到底长什么样

2.1 目录结构与类别文件:先看清楚五个文件夹和一组映射

YOLOv5 官方仓库对数据集的目录约定非常固定,扑克牌数据集既然声明是 YOLOv5 目录格式,解压后你看到的应该是这样的骨架:

poker_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ # 可选,部分版本只有 train 和 val ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── classes.txt # 52 行,每行一个类别名 └── data.yaml # 有的数据集直接给好了,有的要自己写

这里有个容易被忽略的点:YOLOv5 训练时读取的是images/train和labels/train这对目录,靠的是“同名不同后缀”来匹配图片和标签。也就是说images/train/A_of_Spades_001.jpg对应的标签文件必须是labels/train/A_of_Spades_001.txt,多一个字符、少一个后缀,图片就会被当成无标签样本跳过。这个数据集里每张图通常是一张或多张扑克牌的照片,一张图对应一个 txt,txt 里的行数就是这张图里标注的牌的数量。

classes.txt 的排列顺序直接决定标签数值。常见的排列方式是按花色分组:黑桃 A、黑桃 2……黑桃 K、红心 A、红心 2……,52 行的顺序就是模型训练时类别 ID 的顺序。你训练时用的 data.yaml 里names列表必须和这个文件逐行对应,否则会出现模型输出了class_id=0,你按自己的理解去解析成“黑桃 A”,实际标注却指向另一种牌的错位事故。

2.2 归一化坐标:txt 标签里那五个数,每个都不能马虎

打开任意一个 labels 下的 txt 文件,你会看到类似这样的内容:

12 0.453125 0.671875 0.240625 0.312500 38 0.781250 0.515625 0.187500 0.250000

每行五个数,空格分隔,含义依次是:类别 ID、归一化中心点 x、归一化中心点 y、归一化宽度 w、归一化高度 h。归一化的基准是图片的原始宽高,也就是说真实框的像素坐标除以图片宽高后,四个数值全部落在 0 到 1 之间。这样做的好处是标签不随输入分辨率变化,训练时无论img=640还是img=1280,标签都无需重新计算。

我一般拿到数据集后做的第一件事不是直接训练,而是写一段脚本把这五个数还原回像素坐标,把框画在图上肉眼检查一轮。常见做法是随机抽 20 张训练图,叠加画框后看标注是否贴合牌面边缘。扑克牌检测对框的质量极其敏感,框画得比牌面大一圈,模型学到的特征里就会混入背景;框小了,角上的点数和花色可能被截掉一半,这类误差在网络深层特征里会被放大。

2.3 为什么这种格式值得直接训:省掉的是转换脚本和坐标系换算的坑

目标检测领域里 VOC 格式(xml)和 COCO 格式(json)是另两大主流,但它们都不能直接喂给 YOLOv5 训练器。VOC 的坐标是绝对的左上右下像素值,COCO 的坐标是绝对左上角加宽高,而 YOLO 系列统一要求归一化的中心点加宽高。这个数据集直接用 YOLOv5 格式给出,等于帮你跳过了最容易被写错的转换环节。

自己写过 VOC 转 YOLO 脚本的人都有体会:最容易出错的是宽高换算是除以原图尺寸还是除以输入尺寸,以及类别索引是从 0 还是从 1 开始。YOLOv5 的类别 ID 严格从 0 开始,52 个类别的 ID 范围是 0 到 51,classes.txt 第 1 行对应 ID 0,第 52 行对应 ID 51。这个数据集既然直接给好了格式,你就没有这些转换负担,剩下的核心工作就是配置 data.yaml 和调超参数。

3. 跑通训练:写 data.yaml、摆放数据集、调前三个超参数

3.1 最小可用的 data.yaml:路径、类别数、names 三件事

把数据集放在工程目录下后,第一步是配 data.yaml。如果你解压后发现目录里自带了这个文件,也要打开检查一遍,确认绝对路径或相对路径指向正确。常见的写法是:

# poker.yaml path: ./poker_dataset # 数据集根目录,相对当前工作目录或绝对路径 train: images/train # 训练图片目录,相对于 path val: images/val # 验证图片目录,相对于 path test: images/test # 测试目录,可选;没有就注释掉 nc: 52 # 类别数量,扑克牌 52 张,不含大小王 names: [ 'A_of_Spades', '2_of_Spades', ..., 'K_of_Spades', 'A_of_Hearts', '2_of_Hearts', ..., 'K_of_Hearts', 'A_of_Clubs', '2_of_Clubs', ..., 'K_of_Clubs', 'A_of_Diamonds', '2_of_Diamonds', ..., 'K_of_Diamonds' ]

path字段在 YOLOv5 v7.0 之后的版本里是推荐写法,它决定了 train 和 val 是相对谁解析的。这里最容易犯的错是把train写成完整路径,又把path也写上,结果路径拼接重复导致找不到图片。如果你在跑train.py时报了 Image not found 之类的错误,先回来看这三行。

nc必须和 names 列表长度一致,模型输出层的通道数由它决定。如果数据集实际是 52 类,你写成 53,训练不会立即报错,但最终导出的模型在解析时会出现一个永远没有样本的“幽灵类别”。反过来写成 51,训练过程会出现类别 ID 越界的错误,因为标签里最大 ID 是 51,而模型只分配了 51 个输出通道(0 到 50)。

3.2 训练命令与三个关键超参数:img、batch、epochs 怎么定

配置好后,进入 YOLOv5 仓库目录执行训练。命令本身很简洁,但参数怎么定直接关系到这张扑克牌数据集能不能收敛:

python train.py \ --data poker.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml

--img是输入分辨率。扑克牌属于中等尺寸目标,牌面细节集中在点数和花色上,640 是起点。如果你的图片里牌在画面中占比很小,比如桌上摆了一整副牌,需要把 img 提到 960 或 1280,代价是显存占用和训练时间明显上升。--batch受显存约束,16 在 8GB 显存上跑 640 分辨率基本是极限;显存小就降到 8,但 batch 过小会导致 BN 层的统计量抖动,损失曲线会呈现明显的锯齿。--epochs我一般先给 100,然后看验证集 mAP 曲线是否在 60 到 80 epoch 之间进入平台期,这个数据集的体量和类别数决定了它不需要像 COCO 那样训 300 epoch。

--hyp指定超参数文件。YOLOv5 自带hyp.scratch-low.yaml和hyp.scratch-high.yaml,区别主要在数据增强强度上。扑克牌场景我建议先用 low 版本,因为牌面是平面物体,过强的 HSV 扰动会把红色方块和红色红心的色相拉得过于接近,反而增加混淆。

3.3 验证与指标:mAP 之外还要看每一类的 AP

训练结束后,验证命令单独跑:

python val.py \ --data poker.yaml \ --weights runs/train/exp/weights/best.pt \ --img 640 \ --save-json --save-txt

--save-txt会把每张验证图的预测结果写到 txt 里,--save-json在装了 pycocotools 的情况下会额外输出 COCO 格式的评估文件。控制台会打印 mAP@0.5 和 mAP@0.5:0.95 两个主要指标。对扑克牌来说,mAP@0.5 达到 0.95 以上不算稀奇,因为牌面相对规整;真正值得盯的是 52 个类各自的 AP。你大概率会发现红心 A、方块 3 这类“纯红色牌面”的 AP 明显低于黑桃和梅花,这不是网络结构的问题,而是数据分布和颜色混淆叠加的结果。把results.csv里每类的 AP 拉出来排序,底部那几类就是后续补数据或调增强方向的风向标。

4. 扑克牌训练避坑:从标注翻车到推理失效的五个常见问题

4.1 牌面旋转 180 度:A 和 8 对称,6 和 9 谁是谁

现象:训练 loss 降得很漂亮,验证 mAP 也不错,但实测时把一张“红心 6”旋转 180 度喂进去,模型死活识别成“红心 9”。

原因:公开数据集中部分图像里的牌是倒置的,标注时按牌面正向语义标了类别,但 6 和 9 这类对称翻转后会变成另一张牌的图案,旋转增强(--hyp里的degrees)会把这些写进训练集,模型被迫在“6 的正向特征”和“6 旋转后像 9”之间做折中。

解决:扑克牌场景建议把degrees这个增强参数设成 0,或者只保留 90 度的倍数旋转(degrees: 90.0)。同时检查数据集里是否给同一张牌的正反两面都做了标注,如果只标了正向,就不要开旋转增强。这类混淆在混淆矩阵热力图上会非常刺眼:6 和 9 的交叉格子里会有一片亮色。

4.2 类别不平衡:边角牌样本少,A/K/Q 占了大头

现象:训练完看每类 AP,A、K、Q、J 这些牌的 AP 都在 0.95 以上,2、3、4、5 这些数字牌普遍掉到 0.88 左右。

原因:扑克牌数据集的采集天然偏向“展示面”,带 A/K/Q 的牌图拍得多,数字牌尤其是小点数的牌图数量少。YOLOv5 默认的损失函数对尾部类别没有特殊加权,样本少的类别梯度贡献小。

解决:先统计每类的样本数,如果最大类比最小类超过 5 倍,就用--cls参数调大分类损失的权重,比如从默认的 0.5 提到 1.0。另一个更实用的做法是直接在数据层面动手:把样本少的类别做 mosaic 增强时的复制粘贴,或者额外找几十张这些牌的单牌图补进训练集。补数据优先于调损失权重,因为损失权重只能在现有特征里做权衡,补图才能引入新特征。

4.3 小目标检测:整副牌场景里的小框不稳定

现象:单张牌的大图检测很准,一换成“桌面平铺多张牌”的图片,边角处的小牌开始漏检,置信度在 0.25 附近抖动。

原因:牌在整图里占比小,经过 YOLOv5 的 stride 32 下采样后,小尺寸牌在特征图上的有效像素可能只剩十几个,点数和花色的纹理细节基本被池化抹掉了。

解决:先确认标注框面积占整图的比例。如果大量框的归一化面积小于 0.01,考虑把--img提到 960,让每张牌在输入图上占据更多像素。同时打开--multi-scale让训练过程随机缩放输入尺寸,逼迫模型适应不同尺度。注意 mAP 会因此有小幅下降,但对实际场景的泛化能力是提升的。

4.4 标注框质量问题:贴边框把花色切掉一半

现象:训练 50 个 epoch 后 loss 降不下去,验证集上同一种牌一会儿正一会儿误检,损失曲线像心电图。

原因:扑克牌的标注框如果压得太紧,会把左上角的点数和右下角的花色符号切掉一部分;如果框得太松,又把桌面纹理收进来。同一个数据集里标注尺度不统一,模型学到的框回归目标自相矛盾。

解决:写一个可视化脚本,对每个类别随机抽 10 张图叠加真实标注框,直接看框边缘和牌面的贴合情况。发现系统性偏差(比如所有框都比牌面小一圈),用批量脚本统一外扩 3% 到 5%。这类问题不需要重新训练太多轮,修正标注后从原权重继续训 30 个 epoch 就够。

4.5 过拟合:模型背答案而不是认牌

现象:训练 loss 降到接近 0,验证 mAP 也好看,但把训练时没见过的牌面照片(换个角度、换张桌子)喂进去,置信度直接崩到 0.5 以下。

原因:数据集里每张牌的出现次数少,且拍摄背景单一——都是同一张桌面、同一光照。模型记住了“黑桃 A 出现的那个角落的纹理”,而不是“黑桃 A 本身的图案”。

解决:观察验证集 mAP 和训练集 mAP 的差值,如果超过 5 个百分点就要干预。打开更强的 mosaic 和 mixup 增强,或者把背景替换成随机纹理(常见做法是用--hyp里的mosaic: 1.0, mixup: 0.2)。这一步做完通常训练 loss 会回升一点,但验证集上的表现会变得更可信。这个数据集如果本身包含多种场景的照片,过拟合风险会低很多;如果场景单一,务必在增强上做足文章。

5. 进阶验证:用一批新牌面做盲测,别只信 val 指标

训练完模型,val 集指标再漂亮也只是“见过面的考试”,扑克牌这类细粒度任务必须做一次盲测。盲测的做法是:从数据集里抽出训练和验证都没出现过的一批牌面照片,最好是不同拍摄角度、不同背景的,单独建一个目录,写一个推理脚本跑一遍输出每张图的预测类别和置信度。

实际操作中我会把模型导出成 ONNX,用下面这段脚本做快速盲测:

import cv2 import onnxruntime as ort import numpy as np session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name img = cv2.imread("blind_test/unknown_poker.jpg") img = cv2.resize(img, (640, 640)) img = img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB,HWC转CHW img = np.ascontiguousarray(img, dtype=np.float32) / 255.0 img = np.expand_dims(img, axis=0) outputs = session.run(None, {input_name: img}) # output[0]: 检测框和置信度,output[1]: 52类分类概率

这段代码不依赖 YOLOv5 的推理封装,直接走 ONNX Runtime,适合快速验证导出后的模型行为,也方便写进自动化测试流程。盲测时两个指标要同时看:一是类别判对没有,二是置信度是否在 0.7 以上。如果判对了但置信度只有 0.5,说明模型虽然赢了 val 指标,但在真实场景里仍然是犹豫的,这种牌面应该被加入训练集做增量训练。

我的习惯是每训完一版模型,就攒 30 张没见过的牌面照片做一次盲测,发现哪类牌经常被误判,就回去看这类的训练样本数和标注质量。这个迭代闭环比反复调超参数更管用,我早期在扑克牌数据集上吃过“val mAP 0.96,实拍识别稀烂”的亏,后来检查发现是数据增强开太大把红心染成了紫色。多做几轮盲测,很多训练时注意不到的隐患会在推理阶段现形。希望这套流程帮你少走几步弯路,拿到这个数据集后第一轮训练就能跑到该有的水平。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 1:33:06

随机森林鸢尾花分类实战:从原理到调参完整教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:32:50

JavaWeb个人网上银行系统:MVC架构、数据库设计与部署避坑全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:32:47

C++飞机大战源码解析:从版本演进学EasyX游戏开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:32:38

NFC标签双端App唤起与未安装兜底:Android/iOS全链路配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:31:32

南大计院夏令营机试与笔试题型破解:刷题路线与算法模板全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:30:58

ESP32隐藏射频通路:寄存器级IQ采样与嵌入式SDR实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华