基于YOLOv5与YOLOv8的骰子点数检测:从数据集到部署全流程
骰子点数检测乍一听是个挺玩具向的项目真正上手把它从数据集跑到能用的YOLOv5/YOLOv8模型才发现里面全是目标检测的硬骨头小目标、细粒度分类、多目标密集排布、光照干扰。这次拿到的是编号第46的骰子数据集图片和标注都整理齐了连v5和v8两套训练好的权重都一并附上等于把最费功夫的数据采集和训练流程全跑完了。这篇文章我按自己的实操顺序来写把数据集构成、模型选型思路、完整训练参数、推理环节的误判排查都摊开讲基础弱的朋友照着配置和代码走一遍也能复现一套能用的骰子点数检测模型。1. 骰子数据集底细任务难点与标注规范1.1 骰子点数检测为什么是个“不简单的简单任务”骰子点数检测在任务类型上属于目标检测叠加细粒度分类。模型要输出的不是“这里有一颗骰子”而是要直接给出每颗骰子对应的点数类别从1点到6点一共六类。听起来比检测一百类物体简单多了但实际训练过就知道点数识别的难度恰恰藏在“只有六类”这件事里。第一个难点是目标尺度小。点数区域在整颗骰子里只占一小块骰子在画面里再一缩点数特征可能就只剩十几个像素。目标检测里有个基本规律目标越小提取到的有效特征越少分类置信度就越低漏检率也跟着上升。第二个难点是类别之间高度相似2点和3点在骰子面上的排布都是斜向一条线倾斜视角下非常容易混淆6点三个点排成两列在部分遮挡或强反光时也可能被误判成别的点数。第三个难点是同一个画面里往往有多颗骰子同时出现互相堆叠遮挡对模型的召回能力提出了更高要求。这也是我为什么觉得这个项目适合反复练手它会逼着你去关心输入分辨率、特征提取网络结构、数据增强、阈值选择这些东西而不是像常规大目标检测那样把模型喂饱了随便跑跑就有个不错的结果。1.2 数据集内容与标注规范拿到手先检查什么这份编号46的骰子数据集采用目标检测最常见的组织方式图片和标签分开放置目录结构大致是这样的dice_dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 └── labels/ ├── train/ # 训练标签 └── val/ # 验证标签标签内容使用YOLO格式的txt文件每一行对应一个骰子目标包含类别编号和归一化后的中心点坐标、宽高。举个例子打开某个label文件内容长这样3 0.78671875 0.421875 0.203125 0.21875 0 0.21679688 0.71875000 0.18750000 0.20312500第一列是类别编号这里0到5分别代表点数1到6后面四列依次是x_center、y_center、width、height全部除以了图片宽高做了归一化。这种格式在YOLOv5和YOLOv8里通用不用额外转换。数据拿回来之后我习惯先写一段脚本检查几类常见问题标注框坐标加宽高后是否超过了1.0也就是越界类别编号是否在0到5范围内是否存在空标签文件。越界和空标签最常出现尤其是人工标注漏标时某个txt里可能完全没有内容训练时YOLO会跳过这张图表面看不出问题实际上白白浪费了一个训练样本。顺带还要看一眼类别分布。如果某个点数在数据集里出现的次数明显少于其他点数训练时模型会对这个类别的召回率偏低。骰子点数在理论上随机掷出的概率是均匀的但实际拍摄数据集时不一定能做到均匀采样这一块必须用脚本统计确认。1.3 数据增强策略与样本均衡处理骰子这类物体非常适合做比较激进的数据增强因为它在现实中被观察的角度本身就非常多样。我常用的增强组合包括水平翻转、垂直翻转、90度旋转以及HSV色域扰动、高斯噪声和轻微模糊。前几种几何增强对骰子点数检测来说不会破坏语义点数排列在旋转后依然是有效样本可以放心用。YOLOv5和YOLOv8都内置了Mosaic增强把四张图随机拼接成一张能显著提升小目标检测效果。训练时Mosaic默认开启训练后期会自动降低使用概率避免模型过度适应拼接图。这个默认策略我一般保持不动只有当训练集图片数量很少时才考虑手动关掉Mosaic改用普通增强。这里有一个我自己踩过的细节骰子本体颜色很多红色、白色、黑色、绿色都有但点数孔通常是白色或黑色与底色的对比很强烈。如果HSV增强的饱和度和亮度扰动范围拉得太大模型可能偷偷学会“靠颜色判断点数”而不是靠形状布局。这样一旦遇到和训练集色差很大的骰子表现就会崩。为了压制这个倾向我通常会额外准备一部分灰度图参与训练强迫模型依赖几何结构而不是颜色纹理。样本均衡方面统计完类别数量后对数量明显偏少的点数类别做针对性补充把对应图片复制一份配合随机旋转、透视变换生成新样本。这样做比单纯靠在线增强的随机性更可控测试下来少数类的召回率能提升几个百分点。2. YOLOv5与YOLOv8模型选型两套权重到底怎么用2.1 两代YOLO的核心差异这个项目同时提供YOLOv5和YOLOv8两套权重刚看到的时候我也觉得有点冗余但实际跑完就理解了这两代模型在工程落地里各有不可替代的位置。YOLOv5是典型的Anchor-based方法结构上使用C3模块和SPPF特征提取经过多年的迭代训练和部署生态非常成熟。很多老项目的推理框架只兼容YOLOv5的导出版本或者团队内部已经沉淀了针对v5的优化算子这时候v5模型就是最省事的选择。YOLOv8在结构上做了比较大的调整骨干网络换成C2f模块检测头改成Anchor-free设计输出层使用解耦结构标签分配改成了TaskAlignedAssigner动态分配。直接感受是v8对目标尺度的适应性更强在目标偏小且数量较多的画面里召回率和定位精度都领先于v5。具体到骰子检测这种场景如果画面里通常只有两三颗骰子、位置比较疏朗v5完全够用。但如果要做一把骰子撒下去、一次统计十几颗的场景密集目标互相靠近v8的动态标签分配机制会更从容。2.2 为什么同一份数据要保留两套模型我在实际项目里养成了一个习惯只要硬件资源允许一份数据集至少跑通两个不同版本的模型再定方案。这么做不是折腾而是给后面的部署留后路。第一是兼容性兜底。旧版推理框架或者嵌入式设备上v5模型往往更好导入ONNX导出和量化工具链都很成熟。新项目里v8的接口更简洁从训练到部署一条龙但有些旧环境不一定支持。第二是精度和速度的权衡。v5n或v5s模型体量小CPU推理有明显优势v8在GPU上精度更高速度差距不明显。第三是方便横向对比用同一批验证图片分别跑两套模型能直接看出数据增强和检测头设计带来的影响。我这套骰子数据上v8s在验证集的mAP50比v5s大约高了0.8到1.2个百分点主要差异集中在多颗骰子互相靠近的图片上v8的漏检更少。差距不算大但在真实桌游计分场景里多漏一颗骰子就可能导致总点数差好几分属于必须控制的错误。2.3 模型规模与预训练权重的选择模型规模我建议优先试n和s两个尺寸。骰子数据集本身就那几百张图片六分类任务也不算复杂强行上m或l很容易过拟合。参数量上去了但数据喂不够训练集loss继续降、验证集mAP原地不动甚至往下掉典型的过拟合信号。我这次主力用的v5s和v8s训练前都下载了COCO预训练权重做初始化。使用预训练权重的收益非常直接模型已经学会了通用的边缘、纹理、局部特征不需要从零开始学“什么是物体”在小型数据集上可以少用很多epoch就收敛到可用精度。如果显存非常紧张也可以退一步用n系列或者把输入分辨率降到416或480。点数虽然小但n系列配合Mosaic增强能跑到能用的水平只是对极端倾斜、遮挡的抵抗力会弱一些。我的建议是先跑一个s模型的baseline拿到精度的参考值之后再判断要不要往上试m。注意不要一开始就盲目上大模型。类别只有6个的检测任务s系列已经覆盖绝大多数需要优先把数据和调参做扎实比堆参数更有效。3. 训练实操从数据集到可用模型的完整流程3.1 环境准备与数据文件组织训练之前先准备环境。YOLOv8的安装非常省事ultralytics这个包把训练、验证、预测、导出一把包完pip install ultralyticsYOLOv5官方仓库的方式稍繁琐一些需要先克隆仓库再安装依赖git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里有个非常容易踩的坑Windows系统下路径问题。数据集的yaml配置文件如果写在项目根目录里面使用的是相对路径而数据集放在另一个盘符训练时会一直提示找不到图片。我第一次跑就栽在这里检查了半天才发现是路径拼接问题。解决方案很简单yaml里path字段直接写绝对路径train和val字段保持相对路径两者组合起来指向正确的数据目录。数据文件夹建议统一放在项目根目录的datasets下面完整结构如下project/ ├── datasets/ │ └── dice/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ └── runs/训练输出默认会写到runs目录下包括权重、曲线、单张验证结果图这样每次训练完检查起来都很顺手。3.2 数据配置文件与训练命令YOLO系列的配置逻辑一脉相承yaml文件描述数据路径和类别名称。骰子数据集的yaml基本长这样# dice.yaml path: ./datasets/dice train: images/train val: images/val test: images/val names: 0: one 1: two 2: three 3: four 4: five 5: six类别名称建议用英文。原因是可视化时直接拿标签名渲染中文在部分环境下会出现字体缺失或乱码影响排查效率。训练命令需要区分两个版本。YOLOv5在克隆的仓库目录里执行python train.py --data dice.yaml --weights yolov5s.pt --img 640 --epochs 150 --batch 16YOLOv8直接在命令行调用yolo detect train datadice.yaml modelyolov8s.pt imgsz640 epochs150 batch16batch size的选择主要看显存。单卡的话8G显存跑batch 16比较稳妥6G显存建议batch 812G以上可以尝试batch 32。遇到CUDA out of memory时优先降低batch而不是降低分辨率。对于骰子这种小目标物体分辨率直接决定点数区域的像素大小从640降到416会让本就模糊的小点数更加难认。3.3 训练过程监控与指标解读训练开始之后我重点看四件事box_loss和cls_loss是否平稳下降训练集和验证集mAP差距是否过大超过5个百分点就要警惕过拟合混淆矩阵里哪些点数互相误判小尺寸目标在验证集上的召回表现。以我这轮150个epoch的训练为例前20轮mAP上升非常快从0直接冲到0.85左右说明预训练权重和数据集都配合得不错。接着40轮慢慢爬到0.9以上到130轮附近基本稳定。最终v8s在验证集上的mAP50大约在0.97mAP50-95在0.85上下。v5s略低但也在可用范围内。混淆矩阵是整个训练过程里最值得盯的一张图。我这次遇到的主要问题集中在2点和3点的互相误判统计一下训练集中倾斜视角的2点、3点图片数量果然偏少。之后我对这两类做了针对性补样专门拍了一些大角度倾斜的照片丢进训练集重训后混淆率下降明显。还有个小技巧数据集拆分一定要固定随机种子。YOLO系列在划分验证集时可以传入seed参数如果不固定两次训练的验证集可能不同指标没有可比性。我习惯把随机种子固定为固定值并在每次训练前记录数据集的hash值方便后续回溯。3.4 训练后的验证与泛化检查训练结束后我不会只看验证集指标而是额外准备一组没有参与训练的照片做泛化测试。这组照片故意挑选不同背景、不同桌面材质、不同光照条件甚至包含一些轻微模糊的画面用来模拟真实部署时可能遇到的恶劣情况。实测下来俯拍加上正常室内光的条件下模型基本能做到接近全对。一旦骰子出现在强反光的亚克力材质表面个别点数类别的置信度会掉到0.6以下偶尔出现把3点看成2点的误判。原因是反光把点数孔洞的高光抹平了视觉上相当于一个点被遮挡模型拿到的输入并不完整。还有一个典型场景是骰子离相机很远、在画面里只占二三十个像素。这时检测框本身都不够稳定点数类别更是在几个候选之间飘忽不定。这属于小目标检测的固有限制单靠换模型解决不了要么提升输入分辨率要么把画面中的骰子区域先放大再检测。4. 推理部署与点数误判排查实测经验全记录4.1 单张图片与实时视频推理模型训练完推理环节最重要的是能不能干净地接到业务代码里。YOLOv8的推理接口很顺手加载权重后调用predict即可from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(test_images/dice_01.jpg, conf0.35, imgsz640) for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 box.xyxy[0].tolist() print(f点数 {cls_id 1} 置信度 {conf:.2f} 坐标 {x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f})如果需要自动统计总点数直接把所有检测框的类别编号加一之后求和即可。多骰子场景下求和逻辑几行就写完这个模型立刻就能变成一个简单的自动计分工具。视频流推理同样简单predict方法直接传视频路径或者摄像头编号ultralytics内部会处理帧循环。实时应用里记得控制推理频率比如每两帧检测一次避免连续检测把CPU或者GPU占满导致画面卡顿。YOLOv5的推理则需要走仓库里的detect.py脚本或torch.hub加载接口风格不同但思路一致。如果只是日常用我推荐优先用v8做推理省心不少。4.2 实测效果与典型误判场景我在各种条件下跑了几轮实测把现象汇总成一张表方便后来人对号入座实测场景整体表现主要问题桌面俯拍、自然光点数识别几乎全对无明显问题室内暖光、侧面光置信度略降阴影遮挡部分点数强反光材质表面偶尔2/3混淆高光掩盖点数多颗骰子堆叠个别骰子漏检互相遮挡画面中骰子过小漏检率明显升高特征像素太少反复出现的问题集中在两类2点/3点混淆以及5点/6点在部分点数被遮住时误判。前者来自视角形变后者来自局部遮挡。这两种情况靠调阈值都很难根治只能从数据补样和增强策略上入手。实际优化中有一个立竿见影的手段就是提高conf阈值。默认的conf0.25会放进来很多低置信度框点数为类的场景里低置信度框往往就是误判的来源。我把阈值提到0.35到0.45之间牺牲了少数不确定骰子的召回换来了误判率的大幅下降综合体验反而更好。4.3 常见问题排查表与优化手段把几天折腾的经验整理成速查表遇到问题先对号排查问题现象可能原因解决办法训练提示图片路径找不到yaml路径配置错误或标签缺失使用绝对路径检查目录结构训练loss不降或震荡学习率过大、标注错误降低初始学习率检查标注文件训练集mAP高、验证集差过拟合背景单一增加背景多样性调大增强强度小骰子漏检严重输入分辨率偏低提高imgsz到960或1280强反光时误判高光抹掉点数数据增强中模拟光照变化或采集时避免反光点数类别互相混淆视角样本不足针对性补充倾斜视图做角度增强除了表格里的常规手段有个思路值得尝试把检测和分类拆成两段。检测网络只负责找到骰子并框出来之后把所有骰子区域裁剪下来单独训练一个小分类网络判断点数。这种“检测分类”的分离架构对细粒度特征的建模更充分点数误判率通常比端到端六类检测更低代价是多维护一个模型和一套流水线。4.4 应用场景延展与后续改进方向骰子点数检测能落地的场景比想象中广。最直接的是桌游计分游戏结束后手机拍一下桌面模型自动把每颗骰子的点数识别出来并求和省去手工算分的繁琐。另一个方向是棋牌类游戏自动化测试模拟对局时需要用大量骰子画面作为输入这个模型可以直接承担自动标注的功能。如果要追求更高的鲁棒性可以把方案升级成关键点检测。将骰子的每个点数孔洞当作关键点用类似姿态估计的结构输出每个孔的位置再根据孔点之间的空间排布关系推断点数。这种思路在遮挡场景下优势明显只要能看到三个孔中的两个仍有一定依据推断出这是一颗3点骰子而不是完全依赖整体框的特征。还有一条折中路线检测所有点数小圆孔拿到全部点中心之后做聚类再利用聚类内点的数量判断点数。这个方法在点数孔洞清晰的标准骰子上效果很好但遇到花骰子或者点数孔有花纹遮挡时会失效。各条路线各有适用边界按自己的实际场景去选就好。我在实际跑这套数据与模型的过程中最大的体会是类别少并不意味着任务简单。骰子点数只有六类但把精度做到稳定可用从数据统计、增强策略、模型对比到阈值调优每个环节都得过一遍。如果你正准备入门目标检测用这个小项目练手非常合适如果你要落地一个真实的点数统计工具建议从v8s的权重开始跑先拿到baseline再根据你的具体使用环境补充数据。说到底采集端的质量永远是第一位的固定机位、均匀光照、避开反光比后续堆任何训练技巧都更管用。