ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

手机检测数据集实战:YOLO格式与VOC转换、训练避坑全解析

手机检测数据集实战:YOLO格式与VOC转换、训练避坑全解析 简介面向YOLO系列算法实战的“手机—玩手机—打电话”目标检测数据集共593张带标签图像已按训练与验证需求划分好目录可直接用于yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等模型的训练和测试省去手动划分数据的步骤。压缩包内共1780个文件每张jpg图像均配套YOLO格式txt标签与VOC格式xml标签并额外提供1个yaml配置文件方便一键修改类别与路径标签坐标采用归一化的中心点与宽高表示说明清晰便于二次处理。整个压缩包仅20.46MB轻量易下载内容涵盖手机、玩手机、打电话三类常见目标场景和角度较为丰富适合目标检测入门练习、算法对比实验、课程设计以及智慧课堂、驾驶行为分析等场景的算法验证。目前已有190人浏览学习可作为手机使用行为识别等实际项目的可靠数据基础也可帮助初学者理解YOLO数据集组织与标注格式。标注框均经人工核对类别区分明确标签文件与图像一一对应能够有效支持模型训练和验证测试。1. 手机行为检测数据集593 张图、双格式标签能直接喂给 YOLO 系列手机检测这类数据集在 yolo 算法任务里一直是个需求稳定但公开资源不算多的方向这次拆的这份资源是 593 张带标签图像覆盖手机、玩手机、打电话三类目标标签同时给了 YOLO 格式 txt 和 VOC 格式 xml训练集、验证集、测试集已经划分好解压后可以直接套进 yolov5、yolov7、yolov8、yolov9、yolov10、yolo11 的训练流程。适合做课堂专注度分析、工位违规行为检测、驾驶分心提醒这类场景的人拿来当 baseline 数据省掉最耗时的标注和格式转换环节。但 593 张图毕竟是小数据集能不能训练出可用的模型、在什么条件下会翻车这篇文章会把这些边界一起讲透。2. 双格式标签目录结构、坐标字段与 VOC 转 YOLO 的换算逻辑2.1 解压后的目录长什么样train/val/test 划分和标签分区拿到 zip 后第一件事不是急着配训练环境而是先把目录结构摸清楚。这份数据集的文件名是img_0422_25.jpg、img_0255_77.jpg这种风格图片按原始采集批次和编号命名。标签按照摘要里的说明yolo 格式的 txt 文件和 voc 格式的 xml 文件分别保存在两个独立文件夹中训练集、验证集、测试集已经切好。常见做法是下到压缩包后先建一个工作根目录把 zip 解压进去然后看一眼原始目录布局mkdir -p ~/phone_dataset cd ~/phone_dataset unzip yolo算法-手机-玩手机-打电话数据集-593张图像带标签.zip -d . find . -maxdepth 2 -type d | sortfind只列到第二层是防止 images 和 labels 子目录太多把终端刷爆。正常的目录布局应该是 images 下挂着 train、val、test 三个子集对应的 yolo 标签在 labels 目录下按同样结构镜像一份这样 ultralytics 在训练时才能通过train.txt或 data.yaml 里的路径自动匹配同一 basename 的 txt。而 VOC 格式 xml 一般单独放一个annotations目录内部同样按 train/val/test 分好。这里有一个值得确认的细节有些数据集 zip 里 images 和 labels 是平铺的靠文件名前缀区分子集有些则是真正的子目录结构。平铺结构需要自己写脚本按比例二次切分子目录结构可以直接用。拿到手先跑一遍上面的find确认是哪种。目录确认之后我更习惯先把三组数据量统计出来。593 张图如果按 8:1:1 划分大概是 474 训练、59 验证、60 测试这个比例在小数据集里算合理验证集不会小到完全没法看。2.2 YOLO 格式五个字段归一化坐标的计算与校验YOLO 格式的 txt 每一行代表一个目标框五个字段分别是 class id、x_center、y_center、width、height。摘要里写得清楚中心点和宽高都是相对图像宽度和高度的比例值范围在 0 到 1 之间。这五个数字看着简单实际是整条训练链路里最容易出问题的地方。举例来说如果图像宽度是 1280 像素某个手机框左上角在 (300, 200)右下角在 (420, 360)那么换算成 YOLO 格式是这样x_center (300 420) / 2 / 1280 0.28125y_center (200 360) / 2 / 720 0.38889width (420 - 300) / 1280 0.09375height (360 - 200) / 720 0.22222用代码还原一下更直观。下面这段脚本读一个 txt把归一化坐标还原回像素坐标并打印出来import os def read_yolo_txt(txt_path, img_width, img_height): with open(txt_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: print(f[警告] 跳过异常行: {line.strip()}) continue cls_id int(parts[0]) x_c, y_c, w, h map(float, parts[1:]) # 还原像素坐标 x1 int((x_c - w / 2) * img_width) y1 int((y_c - h / 2) * img_height) x2 int((x_c w / 2) * img_width) y2 int((y_c h / 2) * img_height) print(f类别 {cls_id}: 左上({x1},{y1}) 右下({x2},{y2}) f框宽{x2-x1} 框高{y2-y1}) read_yolo_txt(labels/train/img_0255_77.txt, 1280, 720)逻辑说明先按空格切分每行前五段分别对应 class 和四个浮点坐标还原像素坐标时用中心点加减半宽半高。参数说明img_width和img_height必须和训练时imgsz设置之前的原始分辨率一致如果图片本身不是 1280×720这个脚本计算结果会对不上。我用这段脚本主要是做数据体检——如果还原出的坐标出现负数或者超出图像宽高说明原始标注越界了这类 txt 在训练时会引入噪声后面避坑章会专门讲。2.3 VOC 格式 XML 解析bndbox 像素坐标与 YOLO 的等价转换VOC 格式的 xml 核心是object节点下的bndbox里面存的是左上角和右下角的绝对像素坐标 xmin、ymin、xmax、ymax以及目标的类别名 name。两种格式本质上描述同一个框只是坐标系不同。需要互转时核心公式就是把像素坐标转成相对值。手动转一份容易出错通常直接用脚本批量处理。下面这段是把 VOC 标注转成 YOLO 格式的常用写法依赖 python 自带的 xml.etree.ElementTreeimport xml.etree.ElementTree as ET import os class_map {phone: 0, using_phone: 1, calling: 2} # 以实际类别名为准 def voc_to_yolo(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) x_c (x1 x2) / 2 / img_w y_c (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) os.makedirs(labels_convert, exist_okTrue) for xml_file in os.listdir(annotations): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(annotations, xml_file), os.path.join(labels_convert, xml_file.replace(.xml, .txt)))逻辑说明先读 xml 根节点下的 size 拿到原始图像宽高再遍历所有 object把 bndbox 的四个像素坐标换算成归一化的中心点、宽高。参数说明class_map的 key 必须与 xml 里 name 节点的实际值一致value 是训练时类别 idw和h统一保留 6 位小数够用且不会因为浮点溢出导致坐标越界。转换完建议随机抽几个 txt 和原 xml 对照检查确认框的位置一致。一句话总结这章yolo 和 voc 两种标签格式的差异只在坐标空间不同这份数据集同时提供了两份等于把「拿来即用」和「按 VOC 流程二次处理」两条路都堵死了借口。3. 一次跑通 YOLOv8 训练目录整理、yaml 配置与三组关键参数3.1 把 zip 整理成 ultralytics 标准目录YOLOv8 的训练入口ultralytics要求数据集按固定路径组织。虽然这份 zip 内部已经分好 train/val/test但很多压缩包解压出来的 images 和 labels 不在同一级或者路径带中文。我的习惯是全部拷贝到纯英文路径下再按 ultralytics 期望的结构统一放置mkdir -p ~/datasets/phone/images/{train,val,test} mkdir -p ~/datasets/phone/labels/{train,val,test} mkdir -p ~/datasets/phone/annotations/{train,val,test} cp -r 解压目录/images/train/* ~/datasets/phone/images/train/ cp -r 解压目录/images/val/* ~/datasets/phone/images/val/ cp -r 解压目录/images/test/* ~/datasets/phone/images/test/ cp -r 解压目录/labels/train/* ~/datasets/phone/labels/train/ cp -r 解压目录/labels/val/* ~/datasets/phone/labels/val/ cp -r 解压目录/labels/test/* ~/datasets/phone/labels/test/ cp -r 解压目录/annotations/* ~/datasets/phone/annotations/我一般不会直接把原目录移动过去而是用cp -r拷贝一份到干净路径原因是原始 zip 里的目录层级经常带一层多余的父目录直接移动会导致标签路径对不上。拷贝完后做一次配对检查看 images 里每个 jpg 是否都有对应的 txtls ~/datasets/phone/images/train | wc -l ls ~/datasets/phone/labels/train | wc -l两个数字应该一致如果 labels 数量少于 images说明有部分图没有标注这类图留在训练集里会被当作背景样本数量少无所谓多了会拉低召回。配平后再看一眼每个 txt 的行数分布正常每张图 1 到 3 个目标。3.2 写 data.yamlnc、names 与路径的相对关系ultralytics 通过 data.yaml 定位数据集。这份数据集具体类别名以压缩包内 classes 文件或 xml 里的 name 值为准写 yaml 时要把 id 顺序和标签文件里的 class id 严格对应上path: /root/datasets/phone train: images/train val: images/val test: images/test nc: 3 names: 0: phone 1: using_phone 2: calling参数说明path是绝对路径指向数据集根目录train、val、test写相对路径ultralytics 会拼成/root/datasets/phone/images/train去找图。nc必须和names的条目数一致如果写 3 但只列了两行 names训练时会在类别映射阶段直接报错。names的 id 顺序不能只和训练脚本一致还要和每张图对应的 txt 第一列一致——如果原数据集的 class 0 是 phone那 yaml 里 0 就必须是 phone顺序写错模型照样能跑但评估结果的混淆矩阵会全乱。测试集有没有都无所谓训练时只用到 train 和 val。test留在这里是方便训练结束后用同一个 yaml 跑val或predict不需要再改配置。3.3 从 yolov8n 起步先小模型再决定是否升级训练命令本身不复杂重要的是参数怎么选。第一次跑我建议从 n 系列开始而不是一上来就上 yolov8l 或 yolov8x因为 593 张图撑不起大模型的容量效果不好还容易过拟合cd ~/datasets/phone yolo detect train \ modelyolov8n.pt \ dataphone.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ patience15 \ projectruns/phone \ nameexp_n_base \ seed42逻辑说明yolov8n.pt是官方预训练权重训练时会自动下载到当前目录加载 COCO 预训练参数做迁移学习对于这种小数据集迁移比从头训练收敛快得多mAP 上限也更高。patience15表示验证集指标 15 个 epoch 没进步就早停防止小数据集反复震荡浪费时间。seed42固定随机种子保证两次训练结果可对比。参数建议如下参数首次训练建议说明modelyolov8n.ptn 系列参数量最小先建立 baselineimgsz640 或 960手机是小目标960 能提高召回但显存翻倍batch16根据显存调整16 是 8G 显存的安全值epochs100 起配合早停实际 50-70 epoch 一般收敛mosaic1.0 默认最后 10 个 epoch 会按 close_mosaic10 自动关闭cacheFalse593 张图不需要缓存内存不足反而拖慢这里有个常见误用拿到数据集就把imgsz拉到 1280觉得分辨率越高检测越准。实际上小目标收益有限显存占用和训练时长先翻一倍。我建议先 640 跑通流程再单独开一个 960 的对比实验用测试集 mAP 决定是否升级分辨率。训练结束后重点看runs/phone/exp_n_base/results.png里的验证集 mAP0.5 曲线以及confusion_matrix.png里的类别混淆情况。这两个文件直接决定下一步是加数据增强、换模型还是回到标注层挑错。4. 避坑与排查小数据集训练手机检测高频翻车的五个现场4.1 过拟合与 mAP 虚高训练损失降了、验证指标却纹丝不动现象训练 30 个 epoch 后 box_loss 持续下降训练集 mAP 接近 1但验证集 mAP 卡在 0.6 左右不再上升resutls 曲线里 val 分支出现明显平台期。原因593 张图撑不起模型容量网络把训练集的背景纹理和目标排列方式背下来了而不是学到通用的手机特征。最常见的诱因是数据划分时同场景的相似帧同时进了训练集和验证集导致验证集测出来的是「记忆力」而不是「泛化力」。解决先检查划分是否按场景隔离。我的做法是查看 val 里的图片文件名前缀如果和 train 大量重复说明原数据集划分时没有做场景级别的去重这时候宁可自己重新按文件名前缀分一次。其次把模型换回 yolov8n并在 data.yaml 里打开更强的在线增强hsv_h0.015 hsv_s0.7 hsv_v0.4 fliplr0.5让网络看到更多颜色和翻转变化。4.2 小目标漏检手机在画面里占比太小模型压根没看见现象验证集里手拿手机的场景漏检率明显偏高置信度阈值调到 0.1 也检不出来检出来的框很多只框住了手或手臂的一部分。原因手机目标在单人全身画面里往往只有几十个像素宽YOLO 下采样 32 倍后特征图上一个格子对应的原图区域是 32×32小手机框的特征被平均掉了。这份数据集里如果拍摄距离较远这个问题会更突出。解决分三步走。第一步改用imgsz960重训相当于放大特征图第二步检查训练集中的小目标占比统计所有 txt 里 width 和 height 小于 0.1 的框有多少如果比例不高考虑做复制粘贴增强——把小目标手机从原图抠出来贴在背景区域一张图扩出多个小目标样本第三步调低conf阈值推理但这是治标最终还是要靠训练数据解决。4.3 类别混淆玩手机和打电话在头部区域互相串现象混淆矩阵里 using_phone 和 calling 两个类别互串严重同一张图两个类别交替预测典型的是「左手举到耳边」既被预测成打电话又被预测成玩手机。原因两个类别在视觉上的差异本来就是姿态级而不是物体级的。dataset 此类标注的人工判断也容易出现不一致有的标注员把「手机贴耳」标成 calling有的把「手举着手机但没贴耳」标成 using_phone。标签噪声直接传导到模型。解决先做标注一致性审查把混淆严重的样本单独抽取出来重新对照 xml 里 name 字段和图片内容如果发现确实标错了用第 2 章的脚本定向修正后重新覆盖 labels 里对应 txt。我一般会把这类问题做成「合并类别」的备选方案——如果修正后混淆仍然严重考虑把 using_phone 和 calling 合并成一个大类「phone_in_hand」场景上完全够用mAP 立刻能提升几个点。4.4 标签坐标越界txt 里出现负数或超过 1 的比值现象训练时 regulator 日志出现警告或者预测结果里出现超出图像边界的大黑边框用第 2 章的还原脚本检查某个 txt发现 x1 是 -12x2 超过了图像宽度。原因标注时框拉出了图像边界或者 VOC 转 YOLO 时图片原始宽高读取错误。这个数据集虽然标注规范但任何数据集都不能保证百分百干净尤其是边缘目标比较多时。解决写一个遍历脚本扫描 labels 下所有 txt检查五个字段是否有越界x_center 或 width 小于 0 或大于 1 的一律标记然后自动裁剪坐标到合法范围使用clip方式将 x1、y1 限制到不小于 0x2、y2 限制到不大于图像宽高。注意裁剪后宽高会变一定要重新计算中心点和宽高不能直接截断完就写回。4.5 验证集太小每次评估 mAP 波动 3-5 个点无法判断改进有没有效现象两次相同配置的训练验证集 mAP 一次 0.72、一次 0.76换个数据增强参数后 mAP 从 0.76 掉到 0.73但无法确定是参数变差了还是随机波动。原因593 张按 8:1:1 分验证集只有约 59 张每张图的预测结果都对整体指标有近 2 个点的影响单次评估的置信区间太大。很多人在小数据集上反复调参调了个寂寞。解决固定seed42是一方面更实际的做法是启用 K-Fold 交叉验证或者把训练集和验证集合并后用 5 折交叉评估得出均值。我一般先跑两遍同配置实验拿到波动范围再比较不同配置的结果只有差异超过波动范围时才认为改动有效。另外把评估指标从单一 mAP 换成「小目标 AP 和召回率」这两个指标对数据增强的响应更敏感。5. 最后一道工序在测试集上跑一轮 badcase 检查再上生产5.1 批量预测并导出结果区分漏检和误检训练结束后别急着看总 mAP先把测试集的预测结果全部导出来一张张看。用下面的命令把测试集预测结果保存成带标注的图片和 txtyolo predict \ modelruns/phone/exp_n_base/weights/best.pt \ sourcedatasets/phone/images/test \ conf0.25 \ iou0.45 \ save_txtTrue \ save_confTrue \ projectruns/phone_eval \ nametest_predictsave_txtTrue会把每个预测框写成 yolo 格式 txtsave_confTrue在 txt 每行末尾追加置信度。跑完后把预测 txt 和真实标签放一起对比重点看两类 badcase真实标签有但预测 txt 里没有的是漏检预测 txt 有但真实标签没有的是误检。把这两类图片单独复制到两个文件夹里统计共同特征。5.2 按 badcase 类型做最小干预别上来就重训我的排查顺序是漏检集中在暗光或遮挡场景就说明训练集缺相关样本优先做针对性数据扩充而不是调参误检集中在人手、口袋、桌面等类手机纹理区域就考虑加负样本或用conf阈值卡掉低置信度框。一个很实用的技巧是用预测结果和真实标签的 IoU 分布来辅助判断阈值统计置信度在 0.1 到 0.3 之间的误检框占全部误检的比例如果超过一半说明阈值还能往上提不用动模型如果高置信度也误检那才是样本层面的问题。另一个技巧是挑 20 张 badcase 图合成一张九宫格图放到runs/phone_eval/badcase_grid.jpg方便做演示或记录。我自己卡过这个环节的坑有一版模型测试集 mAP 到了 0.83看着不错但生产环境的摄像头是俯视角度测试集里全是平视视角一上线召回直接崩到 0.4。从那以后我每完成一份数据集和模型都会强制走一遍店面视角、俯视角、暗光、遮挡四组场景抽查而不是只信测试集指标。希望这份数据集的拆解和踩坑经验帮到你哪怕只避开我踩过的一半坑也值回下载和解压的时间了。本文还有配套的精品资源点击获取
返回列表