多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

YOLOv8玩手机检测实战:593张数据集训练与部署全攻略

YOLOv8玩手机检测实战:593张数据集训练与部署全攻略 简介这是一份用于手机使用行为检测的YOLO系列数据集聚焦玩手机、打电话等常见场景面向目标检测入门者与算法研究人员可直接在YOLOv5、v7、v8、v9、v10、v11等框架中训练和验证。压缩包共收录1780个文件包括593张JPG原图、593个TXT标注YOLO格式与593个XML标注VOC格式另有1个YAML配置文件整包约20.46MB数据集已完成划分无需额外预处理。目前已有190人浏览学习。标注内容采用归一化坐标TXT每一行依次记录类别索引、中心点X、中心点Y、框宽、框高数值均在0到1之间VOC格式则便于标注工具或跨框架迁移时使用。该数据集规模适中标注信息完整既适合初学者理解目标框归一化表示也能满足中小型项目的训练需求无论是快速跑通检测流程还是开展算法对比实验都能显著降低数据准备门槛。1. 玩手机检测数据集593张带标签图像能做什么适合谁YOLO算法做目标检测最怕的不是模型不够强而是标签和场景对不上。手头这份「手机-玩手机-打电话数据集-593张图像带标签」我拿到后先扫了一遍类别和图像尺寸结论是它适合做两件事一是快速验证玩手机检测这个任务在你的业务场景里到底能不能收敛二是作为预标注底座配合你自己拍的现场图做增量训练。不适合直接拿上线593张对单一姿态够用对光照、摄像头角度稍微一换就露馅。这个数据集的典型用途集中在工位行为识别、车间安全监督、课堂专注度分析这几类场景。你会发现它同时覆盖了「手机在手里」「手机在耳边」和「正常拿手机看屏幕」三种状态正好对应业务里最难定性的两个标签玩手机和打电话。后面所有训练、评估、踩坑都围绕这份数据的标签语义展开。2. 拆开数据包看门道目录结构、YOLO标签格式与类别映射2.1 数据包里通常有什么先看目录再谈训练拿到zip先别急着解压丢进训练脚本先list一下。我见过太多人把压缩包直接拖进datasets/目录结果路径嵌套两层训练时报AssertionError: Label class X exceeds ncY才回头看文件长什么样。解开之后一般长这样. ├── images/ │ ├── train/ │ ├── val/ │ └── test/ # 有的包不拆分test只有train和val ├── labels/ │ ├── train/ │ └── val/ ├── classes.txt # 或叫 names.txt / obj.names ├── train.txt # 部分老版本YOLO需要图像路径清单 ├── val.txt └── data.yaml # 有的包给的是yaml有的要自己写先做三件事第一wc -l数一下train.txt里的图像路径数量是否和images/train下的文件数一致第二抽样打开三五个txt标签看坐标范围第三确认classes.txt里类别顺序和标签txt第一列数字能否对上这一步能避免后面80%的翻车。常见做法是python scripts/check_labels.py跑一遍没有脚本就自己数。这里有个容易被忽略的点解压路径不要带中文。YOLO的data.yaml里path字段虽然支持相对路径但OpenCV读中文路径偶尔会静默失败表现为训练loss正常但验证集mAP常年为0。血泪经验先把目录改成纯英文。2.2 为什么YOLO标签是txt而不是xml归一化坐标的约定这份数据集大概率给的是YOLO格式也就是每个.txt文件对应一张图文件名叫000001.txt对应000001.jpg。每行表示一个目标框五个值class_id x_center y_center width height坐标全部除以图像宽高做了归一化。所以无论原图是1080p还是720p标签值都在0到1之间。写个脚本快速验证一下标签是否有问题# check_labels.py # 检查单个label文件的坐标是否越界、目标是否过小 import os def check_label(label_path, img_w, img_h): bad [] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: bad.append(f字段数错误: {line}) continue cid, cx, cy, w, h (float(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4])) if cx 0 or cy 0 or w 0 or h 0: bad.append(f非正值: {line}) continue # 越界检查允许小幅度溢出但超过5%就要报警 if cx - w / 2 -0.05 or cx w / 2 1.05: bad.append(fx越界: {line}) if cy - h / 2 -0.05 or cy h / 2 1.05: bad.append(fy越界: {line}) return bad # 用法遍历labels目录里所有txt收集异常输出这个脚本只做一件事把坏的样本挑出来。值得说明的是YOLOv8训练本身对轻微越界有容错它会在load_mosaic等增强阶段自动裁剪但超过5%的越界往往意味着标注师画框时把边界画出了图像边缘这类样本在rect模式下会严重拉偏anchor匹配。另外有些数据集会把「低头看手机」和「手机拿在手里但没看」标成同一个类这个属于语义问题脚本查不出来要人工看。2.3 玩手机和打电话这两个类业务语义比框的位置更关键这份数据集里「打电话」和「玩手机」的区别不要只看手部位置。我打开标注可视化后有一个明显感受很多打电话的样本里手机实际上被手挡住了一大半标注框更多是框住「手手机」这个整体而玩手机的样本手机屏幕朝人脸框得相对完整。训练时模型学到的是两种姿态的整体视觉特征而不是手机本身。所以如果你在业务里要求「必须区分手持手机看屏幕 vs 手持手机贴耳朵」光靠这份数据是不够的。它更合适的标签语义是「使用手机-通话」和「使用手机-操作」不要硬抠边界。这也意味着后面推理时两个类之间成绩只差0.1阈值或者出现频繁抖动不要慌这是正常现象需要加时序过滤而不是调阈值。下文第5章会具体说。3. 从0到1训练YOLOv8复现的最小命令集3.1 环境准备Anaconda里装YOLOv8的推荐姿势yolo环境配置这个热搜词常年有人搜但报错大多出在torch和CUDA版本匹配上。我一般用Anaconda管理环境装YOLOv8的标准流程如下conda create -n yolo python3.10 -y conda activate yolo # 先装CUDA版pytorch再装ultralytics顺序不能反 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics # 验证 python -c import torch; print(torch.cuda.is_available())说明python3.10是YOLOv8/v11的稳妥选择3.12某些时候ultralytics依赖的numba等库会出现兼容性问题。--index-url这里指定了CUDA 11.8的wheel源如果你的显卡驱动只支持CUDA 12把cu118换成cu121。看到torch.cuda.is_available()返回True再继续否则后面训练时所有tensor都在CPU上跑593张图也要跑上半小时一个epoch。至于要不要用YOLOv5还是YOLOv8我的建议是直接上v8。v5的anchor机制对anchor-free的改进虽然稳定但v8的自适应anchor在玩手机这种目标尺寸波动大的场景下省去很多调参步骤。你如果后续要对照网上大量yolo算法讲解ppt和教程v8的资料也比v5更接近当前主流。3.2 数据目录改造把zip变成YOLO能认出来的结构很多开源数据包的目录结构和YOLO期望的images/、labels/平级结构不一样常见的是JPEGImages和Annotations这种VOC风格目录。这种情况下需要写个脚本转换。mkdir -p datasets/phone_use/images/train mkdir -p datasets/phone_use/images/val mkdir -p datasets/phone_use/labels/train mkdir -p datasets/phone_use/labels/val # 移动图像文件按比例划分这里用9:1 python split_dataset.py --source images/train --label-source labels/train --train-ratio 0.9对应的数据集配置文件直接放到datasets/phone_use/phone_use.yaml# 数据集配置文件 path: 手机打电话检测数据集 # 数据集根目录的父级路径通常填绝对路径更省心 train: images/train val: images/val nc: 3 # 类别数根据你打开classes.txt确认 names: [phone, playing, calling] # 和classes.txt里的顺序必须完全一致参数说明path里如果填的是相对路径那么它默认相对ultralytics设置的DATASETS_DIR我第一次用漏配path报Dataset phone_use.yaml images not found检查了十分钟才反应过来。改成绝对路径问题消失。names的顺序是硬约束不能用视觉直觉去猜必须以classes.txt里的顺序为准——之前有个朋友把names写成了[playing, calling, phone]训练倒是正常跑完了但推理时每一个框的类别名全部错位。这就是典型的「标签名和类别数不一致」坑后面第5章还会展开。3.3 训练命令与关键参数batch、imgsz、epochs怎么定数据集小就靠参数找补。593张图我一般不会直接全量丢进去先裁剪出一个子集做10个epoch的smoke test确认loss在降、验证集有框出来再跑完整训练。具体命令如下yolo detect train \ datadatasets/phone_use/phone_use.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectrun/runs \ namephone_use_v8s \ lr00.01这里几个参数的具体取舍值得展开。yolov8s.pt而不是yolov8n.pt因为玩手机检测的目标手机本身非常小nano模型的特征图分辨率有限小目标会丢。如果机子显存有限只有6Gbatch从16降到8但不要换更小的模型优先保证模型容量。imgsz640是默认值但实际很多手机检测场景里手机在画面中的像素高度往往不到50像素640输入下小目标特征极其有限。建议先跑一版640看mAP如果小目标检测拉胯再用imgsz960或者1280重训一版。代价是训练时间会翻倍。patience20是早停策略如果连续20个epoch验证集mAP没有提升就自动停止——我习惯开着省电省时间尤其这种小数据集极容易过拟合往往50个epoch就到天花板了。训练完之后看三个数字mAP50、mAP50-95和loss。593张带标签数据YOLOv8s正常跑完mAP50应该能做到0.85以上mAP50-95在0.55到0.7之间。低于这个区间优先怀疑标签问题而不是模型问题跳到第4章去做标签体检。4. 数据质量检查与增强如何避免「训练曲线漂亮、实测全废」4.1 标签体检三件套空文件、超界框、类别分布593张图像带标签听起来不少但要先检查它是不是「看起来多、实际不平衡」。我每拿到一个数据集都会写脚本统计三个指标# analyze_labels.py # 统计每类别目标数、空标签文件数、目标框面积分布 import os from collections import Counter label_dir labels/train class_count Counter() empty_files [] area_distribution [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue fpath os.path.join(label_dir, fname) with open(fpath, r) as f: lines [line.strip() for line in f if line.strip()] if len(lines) 0: empty_files.append(fname) # 空标签文件训练时会被跳过 continue for line in lines: parts line.split() cls_id int(parts[0]) w float(parts[3]) h float(parts[4]) class_count[cls_id] 1 area_distribution.append(w * h) print(类别统计:, class_count) print(空标签文件数:, len(empty_files)) for f in empty_files[:5]: print(空文件示例:, f)三个维度分别看。类别统计用来确认三个类别之间的比例正常数据集里「玩手机」会比「打电话」多两倍以上如果两类数量几乎持平要小心标注方把「拿着手机」这种中性状态也标进了「玩手机」这个后面细说。空标签文件会导致DataLoader在训练时直接跳过这张图相当于你的有效训练样本缩水数量多的话要把它们删掉或补标。面积分布则直接决定你要不要调整anchor或使用更大imgsz如果大多数框面积都小于整图的5%那你需要特别留意小目标检测的评估结果。4.2 增强手段怎么选旋转、翻转、亮度哪些有效小数据集要增广但增广不是越多越好。对玩手机检测来说有些增强手段反而有害增强手段对手机检测的有效性原因水平翻转强烈推荐人左右手习惯不同翻转让模型学到对称性亮度/对比度扰动推荐不同环境光线差异大但幅度别超0.3小角度旋转(/-15°)可加超过±30°会把人头转出正常姿态Mosaic推荐增加小目标出现频率缓解那个5%面积问题随机裁剪慎用容易裁掉手机本身色彩空间HSV扰动视场景定如果现场有和皮肤颜色相近的桌子要模拟这种干扰否则别加特别说下Mosaic四图拼接它会让一个小目标在一张大图里占比更小看起来像「更难检测」但实际效果恰恰相反——它在数据层面增加了小目标的背景多样性模型被迫更关注目标本身特征而不是依赖背景。YOLOv8默认开着Mosaic所以我一般不会再额外开RandomPerspective这类会透射变形的增强手机变形后和正常形态差异太大等于给模型引入噪声。4.3 视觉复核不可省打开图片是检查标注的唯一方式脚本只能查「格式错误」查不出「语义错误」。labelimg打开images下一张图你会立刻发现不少标得不清不楚的框有些框把两只手全装进去了有些把手机屏幕和手掌下边缘连在一起有些在远处人群里漏标了还没怎么画。不用全部重标抽样30张就够。重点看三点一是「打电话」类的框是不是把整个头都框进去了二是远处小目标手机是否漏标三是「玩手机」类是否把「拿在手里但没看屏幕」也算进去了。这三个问题分别导致误检率偏高、小目标recall偏低、业务语义不匹配。发现漏标别一张张改用下面第6章说的方法做一次预标注自己检查修正比手动从零标快得多。5. 玩手机/打电话检测避坑清单从标注到部署的5个翻车点5.1 摆拍图和真实监控视角混在一起模型学到的和你以为的不一样现象训练集mAP很高但一接现场摄像头画面就大量误报把拿笔、拿烟的动作都识别成打电话。原因很多公开的玩手机数据集是摆拍或手机自拍视角人脸占画面比例大模型学到的是「手贴近脸的姿态」而不是「手机靠近耳朵」。一旦监控视角下角度稍有变化姿态特征对不上模型开始瞎猜。解决拿到数据集先看拍摄视角和你要部署的角度差多少。如果你的摄像头是俯视工位而数据集多是平视视角那必须在自己的视角下额外拍几百张补进训练集。补完再做一次fine-tune不要从头训modelyolov8s.pt加载预训练权重接着训就行。5.2 593张里有大量「手机在桌上」的框背景特征污染现象训练时loss降得很快但test阶段拿一张「桌面有手机但人没碰手机」的图模型误报成「玩手机」。原因找一下数据集里有多少照片是「人手远离手机但手机放在桌上」——如果标注方把这种场景也画框标了playing模型会学成「只要有手机桌子就报警」。解决统计标签框的位置是不是集中在图片下方的桌面区域是的话检查原图确认标注语义。如果确实是标注把「手机在视野内」等同于「玩手机」最简单粗暴的办法是删掉这些样本的框或者把它们单独划出来做负样本一张图不包含任何目标但参与训练让模型见见反例。5.3 部署时框全偏移或消失输入尺寸和归一化坐标不匹配现象训练时验证集mAP挺好导出ONNX后在真机上推框要么偏了几十像素要么直接空结果。原因推理时letterbox处理不当。YOLO要求输入图像等比缩放并填充灰边到640x640如果你直接cv2.resize到640x640而不保持纵横比坐标全乱。另一个常见原因是训练时用了imgsz960但导出时用默认640模型输入尺寸不统一推理结果差很多。解决导出和推理时固定同一imgsz并用ultralytics自带的预处理不要手写resize。手写的话记得把坐标映射回原图尺寸时要减掉padding偏移# 推理后坐标还原到原图的公式 # 假设letterbox后图像为640x640原图为WxH # 缩放比例 640 / max(W, H) # 还原: x_orig (x_letterbox - pad_x) / scale5.4 类别标签和业务定义对不上你想要的「分心行为」不是数据集的「玩手机」现象推理结果里「calling」类频繁闪烁同一段视频里一个框时而calling时而playing。原因数据集里「打电话」类往往只标了「手机贴近耳边」有些人习惯把手机拿在胸口说话姿态上更接近「玩手机」模型在两个类别之间摇摆。解决业务侧别用两个独立类别做判定改用「phone出现在手部区域且保持N秒以上」的时序逻辑。常见做法是把两个类合并成一个phone_active类别或用tracking加帧间过滤连续10帧、超过3帧判为打电话才算打电话。单纯调置信度阈值治标不治本因为低阈值时误报多了高阈值时真实正例也丢了。5.5 数据量虚胖重复图像多有效信息不足现象数据包解压后593张看起来够用但训练到50个epoch后val loss开始反弹而且confusion_matrix显示打电话和玩手机混淆严重。原因很多数据包存在相同或轻微变化图像的「复制粘贴」如图中连续截帧。此时有效图像可能只有200张信息冗余模型陷入过拟合。解决去重——对图像计算感知哈希去掉相似度大于0.9的重复帧。做法是pip install imagededup跑一遍去重观察去重后剩多少。如果剩不到150张那就得认真考虑补充自己的现场数据而不是在这个压缩包上无止尽地调参593张已经给了你最好的东西剩下的要靠真实场景补齐。6. 往业务走模型导出、ROI过滤与二次标注技巧模型训练完只完成了一半工作。落地到工位监控或校园场景还需要解决推理速度和误报率这两个问题。推理速度方面671张图的训练产物导出成ONNX或TensorRT后在Jetson Orin或普通PC上能达到10~20ms/帧。导出命令很简单yolo export modelrun/runs/phone_use_v8s/weights/best.pt \ formatonnx \ imgsz640 \ dynamicFalse \ simplifyTrue导出后先用onnxruntime在几张验证图上对比输出确认和PyTorch推理结果一致再部署。特别注意导出时的imgsz必须和推理前处理尺寸一致。误报过滤方面工位场景有个利器ROI区域。摄像头拍到的桌面区域如果本身放手机不影响判定就定义一个多边形ROI把人手活动区域框住只统计ROI内的目标。我一般会在推理代码里加一个cv2.pointPolygonTest的判断在目标框中心进入ROI才触发报警一步过滤掉大量「手机放桌上」的干扰。再加一个帧间判定同一目标连续5帧以上被检测到才输出事件瞬时误报全部消掉。最后说下593张后续怎么用。我自己的习惯是拿这个模型去做伪标签——拍一批新场景图用当前模型跑一遍推理把置信度0.7以上的框置信度低于0.3的图自动筛掉只保留0.3~0.7的样本手动修正。伪标签方式比完全人工标注省一半时间也比边缘置信度全丢弃更稳。修正后的样本并回train集再训一轮通常每加300张真实场景图mAP50会往上走1到2个点。这个数据集的真正价值不在「593张够不够大」而在于它给了你一套可以持续迭代的起点。先把类别语义摸清楚再决定要不要合并「玩手机」和「打电话」然后把你部署场景的摄像头角度、光线条件主动地补进去模型才算是真正长在你自己的业务上。做完一版模型后我习惯余把validation结果里每张图的置信度和类别打印出来过一遍所有误报截图把问题归成硬件摄像头角度畸变、数据背景相似、逻辑时序判定三类分别解决。这比对着评测指标空想管用得多也希望帮到你。本文还有配套的精品资源点击获取
返回列表