简介:面向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.28125
- y_center = (200 + 360) / 2 / 720 = 0.38889
- width = (420 - 300) / 1280 = 0.09375
- height = (360 - 200) / 720 = 0.22222
用代码还原一下更直观。下面这段脚本读一个 txt,把归一化坐标还原回像素坐标并打印出来:
import os def read_yolo_txt(txt_path, img_width, img_height): with open(txt_path, 'r', encoding='utf-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.ElementTree:
import 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", encoding="utf-8") as f: f.write("\n".join(lines)) os.makedirs("labels_convert", exist_ok=True) 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 是训练时类别 id;w和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 是否都有对应的 txt:
ls ~/datasets/phone/images/train | wc -l ls ~/datasets/phone/labels/train | wc -l两个数字应该一致,如果 labels 数量少于 images,说明有部分图没有标注,这类图留在训练集里会被当作背景样本,数量少无所谓,多了会拉低召回。配平后再看一眼每个 txt 的行数分布,正常每张图 1 到 3 个目标。
3.2 写 data.yaml:nc、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 \ model=yolov8n.pt \ data=phone.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ patience=15 \ project=runs/phone \ name=exp_n_base \ seed=42逻辑说明:yolov8n.pt是官方预训练权重,训练时会自动下载到当前目录,加载 COCO 预训练参数做迁移学习,对于这种小数据集,迁移比从头训练收敛快得多,mAP 上限也更高。patience=15表示验证集指标 15 个 epoch 没进步就早停,防止小数据集反复震荡浪费时间。seed=42固定随机种子,保证两次训练结果可对比。
参数建议如下:
| 参数 | 首次训练建议 | 说明 |
|---|---|---|
| model | yolov8n.pt | n 系列参数量最小,先建立 baseline |
| imgsz | 640 或 960 | 手机是小目标,960 能提高召回但显存翻倍 |
| batch | 16 | 根据显存调整,16 是 8G 显存的安全值 |
| epochs | 100 起 | 配合早停,实际 50-70 epoch 一般收敛 |
| mosaic | 1.0 默认 | 最后 10 个 epoch 会按 close_mosaic=10 自动关闭 |
| cache | False | 593 张图不需要缓存,内存不足反而拖慢 |
这里有个常见误用:拿到数据集就把imgsz拉到 1280,觉得分辨率越高检测越准。实际上小目标收益有限,显存占用和训练时长先翻一倍。我建议先 640 跑通流程,再单独开一个 960 的对比实验,用测试集 mAP 决定是否升级分辨率。
训练结束后重点看runs/phone/exp_n_base/results.png里的验证集 mAP@0.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_h=0.015 hsv_s=0.7 hsv_v=0.4 fliplr=0.5,让网络看到更多颜色和翻转变化。
4.2 小目标漏检:手机在画面里占比太小,模型压根没看见
现象:验证集里手拿手机的场景漏检率明显偏高,置信度阈值调到 0.1 也检不出来;检出来的框很多只框住了手或手臂的一部分。
原因:手机目标在单人全身画面里往往只有几十个像素宽,YOLO 下采样 32 倍后,特征图上一个格子对应的原图区域是 32×32,小手机框的特征被平均掉了。这份数据集里如果拍摄距离较远,这个问题会更突出。
解决:分三步走。第一步改用imgsz=960重训,相当于放大特征图;第二步检查训练集中的小目标占比,统计所有 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 是 -12,x2 超过了图像宽度。
原因:标注时框拉出了图像边界,或者 VOC 转 YOLO 时图片原始宽高读取错误。这个数据集虽然标注规范,但任何数据集都不能保证百分百干净,尤其是边缘目标比较多时。
解决:写一个遍历脚本,扫描 labels 下所有 txt,检查五个字段是否有越界:x_center 或 width 小于 0 或大于 1 的一律标记,然后自动裁剪坐标到合法范围,使用clip方式将 x1、y1 限制到不小于 0,x2、y2 限制到不大于图像宽高。注意裁剪后宽高会变,一定要重新计算中心点和宽高,不能直接截断完就写回。
4.5 验证集太小:每次评估 mAP 波动 3-5 个点,无法判断改进有没有效
现象:两次相同配置的训练,验证集 mAP 一次 0.72、一次 0.76;换个数据增强参数后 mAP 从 0.76 掉到 0.73,但无法确定是参数变差了还是随机波动。
原因:593 张按 8:1:1 分,验证集只有约 59 张,每张图的预测结果都对整体指标有近 2 个点的影响,单次评估的置信区间太大。很多人在小数据集上反复调参,调了个寂寞。
解决:固定seed=42是一方面,更实际的做法是启用 K-Fold 交叉验证,或者把训练集和验证集合并后用 5 折交叉评估得出均值。我一般先跑两遍同配置实验拿到波动范围,再比较不同配置的结果,只有差异超过波动范围时才认为改动有效。另外把评估指标从单一 mAP 换成「小目标 AP 和召回率」,这两个指标对数据增强的响应更敏感。
5. 最后一道工序:在测试集上跑一轮 badcase 检查再上生产
5.1 批量预测并导出结果,区分漏检和误检
训练结束后别急着看总 mAP,先把测试集的预测结果全部导出来,一张张看。用下面的命令把测试集预测结果保存成带标注的图片和 txt:
yolo predict \ model=runs/phone/exp_n_base/weights/best.pt \ source=datasets/phone/images/test \ conf=0.25 \ iou=0.45 \ save_txt=True \ save_conf=True \ project=runs/phone_eval \ name=test_predictsave_txt=True会把每个预测框写成 yolo 格式 txt,save_conf=True在 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。从那以后我每完成一份数据集和模型,都会强制走一遍店面视角、俯视角、暗光、遮挡四组场景抽查,而不是只信测试集指标。希望这份数据集的拆解和踩坑经验帮到你,哪怕只避开我踩过的一半坑,也值回下载和解压的时间了。
本文还有配套的精品资源,点击获取