☰
2800张手机检测YOLO数据集:从采集标注到训练实测的完整指南
2026/9/30 5:53:08 网站建设 项目流程

最近把手上的手机检测项目做完了,顺手把整理好的2800张YOLO格式数据集共享了出来。做目标检测的人应该都有体会:找公开数据集最痛苦的不是“找不到”,而是“找到了但不好用”——要么标注格式不统一,要么图片质量参差不齐,要么场景太单一跑不出效果。这套数据是我在实际项目里一标一框积累下来的,从拍摄到清洗到格式转换全程过了一遍手,所以在文章里我会把采集思路、标注标准、格式转换和训练实测都摊开讲清楚。

如果你是正在做课堂行为识别、工位监测、防沉迷提醒之类的项目,或者正在为毕业论文找视觉检测数据集,这篇文章可以帮你省下大量整理数据的时间。我把踩过的坑、反复调整过的细节也一并写出来,照着操作就能直接在自己的训练流程里跑起来。

1. 项目概述:这套数据集解决的是什么问题

1.1 核心需求:为什么做“手机检测”而不是通用目标检测

手机检测本质上属于小目标检测的范畴。在很多监控画面里,手机只占几十个像素,又要和书本、银行卡、遥控器这些长得很像的物体区分开,难度并不低。而且手机的形态高度多样化:有的带壳,有的裸机,有的屏幕亮着,有的黑屏,不同品牌、不同年份的设备外观差异极大。如果数据集里只有单一角度、单一背景的手机照片,训练出来的模型一成精就翻车,换了个环境误检漏检全来了。

这套2800张数据集的设计初衷,就是覆盖一个相对完整的“手机出现场景谱系”。我整理下来主要用在三类任务上:

  • 课堂/自习室场景的行为分析,检测学生是否在使用手机;
  • 办公室或工位场景的注意力统计,识别员工频繁查看手机的行为;
  • 无人值守场景中的违规物品识别,比如不允许带手机进入的区域。

这三类场景有一个共同点:手机不是画面中的主体,而是“需要被找出来的那个目标”。这意味着数据集必须包含大量中远景、小尺寸、部分遮挡的样本,而不能只拍手机特写。

1.2 设计方案:2800张图片如何配置才算均衡

构建数据集时我给自己定了几条硬指标。

第一,单张图片中的手机数量从0到5不等,保证模型见过“完全没有手机”的负样本,也见过“多人同时使用手机”的密集场景。负样本的目的是压制误检,这个很容易被忽略——很多人做数据集时只关注正样本有多少,结果模型训练完对背景区域胡乱输出框。

第二,图片分辨率以1920x1080和2560x1440为主,也混入部分手机实拍的较小尺寸照片。这样模型在迁移到不同分辨率的摄像头时,不至于因为尺度分布偏差而性能骤降。

第三,标注目标框的尺寸跨度大,从占据画面五分之一的大目标,到只有30x60像素的小目标都要有。小目标数量不足是很多数据集训练后mAP虚高、实际应用精度低的主要原因。

第四,场景背景覆盖教室课桌、会议室长桌、家庭沙发、咖啡厅、办公室工位、车内等常见环境。每个环境下的光线条件、桌椅颜色、桌面纹理都不一样,防止模型通过背景过拟合。

1.3 适用人群与扩展思路

这套数据最适合两类人:一是刚入门目标检测、想用一个干净数据集跑通YOLO训练全流程的新手,二是在实际业务中需要快速出效果、不想从零折腾数据整理的开发者。拿它练手YOLOv8或者结合自己的业务数据做增量训练,效果都比较好。

如果你想让它适配更特殊的场景,比如“只看手机不看其他电子设备”的特定品类识别,可以在我的数据基础上做二次标注——把共用的电子设备类别拆细,或者补充你自己场景下的图片进来。这类扩展方式比从零采集合算得多。

2. 数据采集与标注标准:决定模型上限的关键环节

2.1 采集策略:怎么拍出“真实世界”的手机样本

很多公开数据集看起来精美、干净,但实际落地效果一般,就是因为拍摄环境中没有噪声——没有反光、没有暗角、没有运动模糊。我自己采集时故意制造了一些“不理想但真实”的情况。

拍摄器材用了三台不同年代的手机和一台单反,分辨率交错。场景布置上,我尽量还原真实生活:桌上会有水杯、笔记本、眼镜、笔筒,背景会有窗户、白板、书架。手机摆放姿态包括平放、斜靠、竖立、使用者手持等,角度覆盖俯视、平视和侧面视角。

光线这块我特别做了差异化处理:白天自然光、夜晚台灯、日光灯管照明、逆光、暗光各占一定比例。实际项目中夜间的手机屏幕亮光往往是最好找的特征,但白天黑屏手机必须靠轮廓和边缘识别,所以两类样本都不能少。

还有一个值得注意的点:运动模糊。我会在拍摄时轻微移动镜头或让手机做小幅滑动,模拟真实监控画面中目标短暂模糊的帧。模型见过模糊样本后,在视频流检测时就不会因为某一帧突然变模糊而丢失目标。

2.2 标注标准:统一边界是数据集质量的生命线

标注阶段最大的坑不是“累”,而是“标准不统一”。如果标注的人一会儿把手机壳也框进去,一会儿只框屏幕区域,模型学到的特征就乱了。我采用的具体标准是这样:

  • 手机完全可见时,标注框沿机身外缘贴合,留2到3个像素的边距;
  • 手机部分被手、书本、数据线遮挡时,按可见部分外接矩形标注;
  • 手机只露出极小一部分、可见面积小于20%时不标,避免引入过大的不确定性;
  • 画面中有多部手机重叠时,每一部都单独标注,哪怕两个框有较大重叠;
  • 手机在画面边缘只露出一部分时,按实际可见区域标注,不能因为“看着像半个”就不标。

标注工具我用的是LabelImg和CVAT。CVAT适合多人协作标注,LabelImg则适合单机快速处理。如果数据量在几百张级别,LabelImg完全够用,不需要折腾复杂的标注平台。

标注格式上,我建议用Pascal VOC的XML格式作为中间存储,之后再统一转成YOLO的txt格式。这样做的原因是XML格式可以任意增删字段、便于脚本化检查,而YOLO的txt一旦标错很难批量修复。

2.3 质量校验:肉眼审一遍远不够,还要写脚本查

全量人工审核是必须的,但人眼在连续标注几百张后会出现疲劳,漏框的情况并不少见。我额外写了一个Python脚本做自动交叉检查。

首先是坐标越界检查:如果标注框的x、y坐标出现负值,或者x2、y2超出了图片宽高,直接标记为错误候选。其次是框面积下限检查:单张图片中如果出现面积小于30像素的框,很可能是在图片边缘截断的半截目标,需要人工重新确认。最后是高宽比检查:手机目标的高宽比通常在1.5到3.0之间,如果出现极端比例(比如1:5或5:1),大概率是误标注。

我实测发现,光靠脚本检查就能找出大约5%的问题样本,这些样本在训练时如果不去除,会直接拉低mAP值1到2个百分点。单张问题图片的负面影响比你想的大得多,因为YOLO在训练时会对每个标注框计算损失,错误框会误导模型学“错误的长相”,尤其是那种坐标偏移一半以上的严重错误框。

3. 从原始图片到YOLO格式:完整的数据处理流水线

3.1 目录结构和命名规范

数据集的目录结构我建议按下面这样组织:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── data.yaml └── README.md

图片和标签分开存放,而不是放在同一个目录下,这是YOLO官方推荐的布局,也方便后续做二次划分或迁移学习时统一操作。文件命名用8位数字编号,图片和对应标签文件名保持一致,例如00001234.jpg对应00001234.txt。

3.2 格式转换脚本:从XML到YOLO的归一化坐标

YOLO格式的标注是一行一个目标,每行包含类别ID和归一化后的中心坐标及宽高。计算公式很简单:

  • x_center = (x1 + x2) / 2 / image_width
  • y_center = (y1 + y2) / 2 / image_height
  • width = (x2 - x1) / image_width
  • height = (y2 - y1) / image_height

归一化的好处是标注不随图片分辨率变化,训练时YOLO无论把输入缩放到640还是1280,标注框都能按比例正确对应。

下面是我用的转换脚本:

import xml.etree.ElementTree as ET import os from PIL import Image def convert_xml_to_yolo(xml_path, txt_path, class_list): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find('size').find('width').text) img_h = int(root.find('size').find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_list: continue class_id = class_list.index(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) # 越界修正 x1 = max(0, min(x1, img_w - 1)) x2 = max(0, min(x2, img_w - 1)) y1 = max(0, min(y1, img_h - 1)) y2 = max(0, min(y2, img_h - 1)) x_center = (x1 + x2) / 2 / img_w y_center = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(txt_path, 'w') as f: f.write('\n'.join(lines)) class_list = ['phone']

注意越界修正这一步,XML里偶尔会因为标注手滑出现x2比图片宽度还大的情况,如果不做clip,训练时会报错或者计算出负数坐标。

3.3 数据划分:7:2:1的比例怎么切才科学

我按7:2:1的比例将数据集划分成训练集、验证集和测试集。看起来是最常规的做法,但有一个细节必须强调:同场景的连续帧图片不能同时出现在训练集和验证集中。

我之前吃过一次亏,用监控视频抽帧得到的数据没做场景隔离就随机划分,结果模型在验证集上的mAP非常高,一上真实场景就掉链子。原因是连续帧高度相似,模型相当于“见过”验证集的画面了。所以在划分脚本里,我按图片的拍摄场景ID做分组,同一个场景的所有图片要么全进训练集,要么全进验证集,杜绝数据泄漏。

import random import os from collections import defaultdict scene_map = defaultdict(list) for img_name in os.listdir('images'): scene_id = img_name.split('_')[0] # 场景ID前缀 scene_map[scene_id].append(img_name) train_imgs, val_imgs, test_imgs = [], [], [] for scene_imgs in scene_map.values(): random.shuffle(scene_imgs) n = len(scene_imgs) train_imgs.extend(scene_imgs[:int(n * 0.7)]) val_imgs.extend(scene_imgs[int(n * 0.7):int(n * 0.9)]) test_imgs.extend(scene_imgs[int(n * 0.9):])

3.4 数据增强策略:先别急着做,留着训练时再动态增强

很多刚接触目标检测的人会先把图片在数据集层面做翻转、旋转、亮度调整,把数据量翻好几倍。我的经验是:尽量别这么做。

在数据集层面做增强有几个问题:一是硬盘空间和复制时间浪费严重,二是这些增强后的图片本质上高度相似,对模型泛化能力的提升帮助有限,三是如果增强操作不当(比如翻转后标注框没跟着变),反而会让模型学到错误信息。

正确做法是用YOLO训练框架自带的在线增强。YOLOv5和YOLOv8都在训练过程中对每张图片做随机翻转、缩放、色调变化和马赛克增强,这些操作会配合标注框自动同步变换,安全高效。我跑训练时只用了在线增强,没有对原始数据做任何扩充,效果完全够用。

4. 基于YOLOv8的模型训练与实测效果分析

4.1 模型选型:YOLOv8n在手机检测任务上的优势

YOLO系列是目标检测领域绕不开的模型族。从YOLOv5到YOLOv8,结构上持续有优化,到了YOLOv8引入anchor-free检测头后,对密集小目标的适应性更好。选YOLOv8n作为默认配置,是因为它的参数量小、推理速度快,即便在CPU上也能跑到可用的速度,这在部署到教室摄像头或办公监控场景时非常关键。

如果你手头有GPU资源,也可以从nano模型起步,验证数据集没问题后再用YOLOv8s或者YOLOv8m提升精度。这里不建议一上来就训练最大的模型,因为你还需要快速迭代数据标注质量和超参数配置,用小模型跑通流程的成本要低得多。

4.2 训练配置:模型文件、超参数和训练命令

在Ultralytics YOLOv8目录下,先准备data.yaml:

path: /path/to/dataset train: images/train val: images/val test: images/test nc: 1 names: ['phone']

nc是类别数,这里只有手机一个类。names里的顺序必须和标注txt中的类别ID对应,0对应phone。

训练命令示例:

yolo detect train data=data.yaml model=yolov8n.pt epochs=150 imgsz=640 batch=16 device=0 patience=20

几个参数的说明:

  • epochs=150:对2800张的小数据集来说,150轮足够收敛,再多容易过拟合;
  • imgsz=640:YOLOv8默认输入尺寸,手机目标如果整体偏小,可以尝试768甚至1024,但推理速度会下降;
  • batch=16:根据显存调整,显存12G以上可以尝试32,batch越大收敛越稳定;
  • patience=20:连续20轮验证集指标不提升就提前停止,防止无意义的训练时间浪费。

训练过程中重点关注三个指标:box_loss、cls_loss和dfl_loss。这三个损失曲线应该平稳下降,如果出现反弹再持续上升,说明学习率过大了,需要调低初始学习率。

4.3 实测结果和数据指标解读

用上面的配置训练150轮后,我这套数据的实测结果如下:

  • mAP50:0.937
  • mAP50-95:0.712
  • Precision:0.921
  • Recall:0.885

这个结果在单类别小目标检测任务中属于比较理想的状态。mAP50接近0.94说明模型能稳定检测出绝大多数手机,mAP50-95偏低则说明小目标和大目标之间的检测精度差距还是明显存在——这是小目标检测的通病,不是数据集的问题。

再看Precision和Recall的关系:Precision偏高、Recall偏低,说明模型倾向于“少报但不报错”,这在实际应用中其实比“宁错勿漏”更让我满意。因为误检会造成大量无效报警,而漏检通过多帧确认机制可以弥补。

4.4 部署验证:从PyTorch导出到推理的全流程

训练好的模型要落到实际应用,需要先做导出。YOLOv8支持导出ONNX格式,这样就算目标设备上没有PyTorch环境也能跑推理。

yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640

导出后可以用ONNX Runtime做推理。CPU上640x640输入的单张推理耗时大约在30到80毫秒之间,取决于设备CPU型号。如果部署在NVIDIA的Jetson设备上,可以导出TensorRT引擎,使用FP16精度推理,速度能跑到15到25毫秒每帧,基本满足实时检测需求。

视频流检测时,我还会额外做一个简单的帧间去重逻辑:同一个目标框在连续3帧中都被检测到才触发一次报警,交叠率超过0.6就认为是同一个目标。这样可以有效避免手机屏幕闪烁或手抖动带来的瞬时误检。

5. 常见问题与排查技巧实录

5.1 训练时Loss为NaN(数值异常发散)怎么办

遇到NaN最直接的原因是学习率过大或者batch内存在极端的标注框。先尝试降低学习率,从默认的0.01降到0.001,再看Loss是否恢复正常。如果还是NaN,就要检查标注数据。我排查一次问题时发现,某张图片的标注txt里有一行坐标为负数,由于这个坐标没有做过clip处理,导致损失计算中出现无效值。跑数据校验脚本后把非法样本删除,问题就消失了。

5.2 手机和背景混淆:误检来源与解决方法

手机误检的重灾区通常是这两种物体:一类是带有光滑反光面的书本或平板,被模型误认为手机屏幕;另一类是室内灯光下的金属笔筒或桌面装饰物,因为高光区域和手机壳特征接近而被误报。

解决误检的方法三条路并行:一是增加人工标注为该类误检物体单独建类。比如我把“平板”单独拉出来标了一批,模型区分度和准确率立刻提升了。二是修改NMS阈值,将置信度阈值从0.25提高到0.35,误检数量能下降不少,但会有轻微的召回损失。三是加大负样本数量,把完全没有手机的图片再增加200张左右,模型对“没有手机”的背景特征学习会更充分。

5.3 小目标检测效果差,有哪些可靠的提升策略

小目标检测效果不理想,优先尝试提高输入分辨率。imgsz=640升级到imgsz=1024对小尺寸手机的提升非常明显,mAP50-95普遍能涨3到5个百分点。代价是显存占用翻倍,推理速度变慢。

第二个策略是使用SAHI这类切图工具,将大图切成带重叠的多个小图分块推理后再合并结果。这个方法对监控画面的适配度很高,因为大分辨率图中手机占比小,切图后手机在子图中的占比变大,检测器能提取到更多特征。实测切图后小目标的Recall从不到0.8提升到0.9以上,但推理时间也会线性增加。

第三个策略是调整YOLO本身的结构,比如在backbone中增加对小目标的检测层。YOLOv8的head已经针对不同尺度设计了多个输出层,但P2层(高分辨率特征图)对小目标的响应还是偏弱。可以将输入尺寸提升并配合tune模式搜索合适的超参数组合,看看是否有更好的结果。

5.4 需要规避的训练误区(强经验向)

最后强调几条容易踩坑的经验。第一,数据集中“手机数量少但都很大”的样本打多了,模型会偏科,对远处小手机视而不见。我建议在采集时故意压小目标比例不低于30%,让模型在训练时频繁接触小目标。第二,不要过度清理“脏数据”。带运动模糊、轻微抖动、部分遮挡的样本不要删,它们反而是提升泛化能力的关键。第三,在线增强参数里的mosaic=1.0在数据量较小的时候可能会引入过多的合成噪声,可以考虑把mosaic的启用概率调到0.5,保留一部分原生样本来稳定训练过程。

在实际训练中,我分别用纯原生数据和增加在线增强的配置跑过对比,最终采用增强配置后mAP50提高了约2个百分点,但这个提升的前提是标注质量已经足够扎实。如果标注本身问题很多,任何增强都救不回来。

5.5 问题和解决方式速查

现象原因解决方式
Loss变成NaN标注坐标越界或学习率过大校验数据坐标,调低学习率到0.001
验证集mAP高但真实场景差训练/验证集场景未隔离按场景划分数据,严禁同场景图像跨集合
手机被平板、书本误检相似目标特征未学习新增平板、书本独立类别或增加负样本
小手机完全检不到输入分辨率过低imgsz升到1024,或使用SAHI切图推理
手机一旦移动就丢目标缺少运动模糊样本采集时加入轻微运动模糊帧
多目标重叠互相干扰标注框重叠混乱每个目标单独标注,不合并、不忽略重叠框

数据集的整理工作看起来不起眼,但这套2800张的积累从采集到标注到训练成稿,前后花了大约三周时间。回头总结,我最深的体会是:好的数据集不是“数量堆出来的”,而是“标准、质量和场景覆盖共同撑起来的”。标注标准统一,比多标500张图更有效。如果你想拿这套数据跑实验,或者在此基础上做二次标注,建议先从拍摄场景丰富度入手,补一些你自己目标环境下的真实照片,再迁移模型,泛化效果会明显好过直接拿通用权重硬跑。后续我打算把数据做成支持多类别版本,新增平板、手机壳、耳机等常见混淆物,再配合开放词汇检测模块做联合训练,到时再写一篇更详细的扩展方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询