你有没有遇到过这类需求:教室后排想自动统计学生是不是在低头刷手机,办公区想看看下午有多少人摸鱼,停车场出口想提醒司机别在开车时碰屏幕。真动手做的时候你会发现,卡住你的往往不是YOLO怎么调参,也不是模型选v8还是v5,而是手里根本没有一份能直接用来训练的手机检测数据集。我整理的这套2800张YOLO目标检测数据集,就是为这类场景准备的。图片全部完成人工标注,标签统一为phone,格式可以直接接入YOLOv5/YOLOv8训练流程。下面我会把整个数据集的构建逻辑、标注约定、训练实测和踩坑记录都摊开来讲,打算做课堂玩手机检测、工位监察、驾驶分心提醒这类项目的朋友,可以直接照着来。
1. 为什么非要做一套专门的手机检测数据集
1.1 通用数据集里的手机根本不够用
很多人第一反应是“用COCO不就行了”,COCO里面确实有cell phone这个类,但当你单独去检测手机时,就知道它有多不靠谱。COCO是80类的大杂烩,手机在所有标注里占比很小,而且绝大多数是近景、大目标、干净背景的样张。真实场景里的手机是什么样的?可能只有画面里十几个像素,可能握在手里只露出三分之一,可能在夜间被屏幕照得一片惨白。拿通用权重直接去测,最常见的结果是:人倒是框得挺准,手机要么没框出来,要么把平板、笔记本适配器都框成了手机。专门做一套手机检测数据集,就是为了把这些通用数据集留下的坑全部填上。
我印象很深的一次测试是,直接用COCO预训练权重跑办公室摄像头画面,画面上同事手里拿着黑色手机从工位走过,模型全程没有给出一个手机框,反而把桌面上的黑色笔记本电源误检成了手机,置信度还高达0.7。这种结果其实不意外,COCO里的手机样本一般出现在近景的人像照片里,角度正、遮挡少、背景干净,模型学的特征偏理想化。一旦场景变成中远距离的室内监控视角,目标小、角度杂、光照乱,原来那套特征就全对不上了。
1.2 手机小目标的检测难点到底卡在哪
这套数据集设计时,我重点思考的就是“为什么手机检测比想象中难”。我把它拆成四类问题:
- 小目标问题:监控摄像头位置高,手机在画面里经常只有几十个像素,YOLO默认的640输入下小目标特征很容易被池化层吃掉。
- 反光与屏幕干扰:屏幕点亮时,整个矩形区域过曝,边缘被光晕糊掉,模型容易把亮斑当特征,换个角度就不认识了。
- 形变与遮挡:手持、揣兜、贴耳、横屏打游戏,手机的形状变化很大,加上手部的遮挡,一个完整的矩形框经常装不下。
- 背景混淆:黑色手机放在黑色桌面上,银色手机放在不锈钢设备旁边,边框和背景融为一体,模型很容易漏掉或者把类似色块误检出来。
这些问题直接影响数据采集和标注策略。比如同一个场景我至少要保证三个时间段的样本(白天、黄昏、夜间),同一个目标要覆盖“拿在手里”“放在桌上”“贴在耳边”三种姿态,目的就是让模型在训练阶段就把这些变化都见过。下面这个表是难点和数据集层面对策的对照,后面所有操作基本都围绕着这张表来展开:
| 难点 | 对检测的影响 | 数据集层面的对策 |
|---|---|---|
| 小目标 | 漏检率高 | 保留大量远景样本,配合Copy-Paste增强 |
| 屏幕反光 | 特征漂移 | 覆盖亮屏/息屏两种状态,避免只学亮斑 |
| 形态遮挡 | 框不完整 | 标注遮挡可见部分,加入手持、贴耳姿态 |
| 背景混淆 | 误检 | 补充黑色桌面、深色衣物等难负样本 |
2. 2800张图是怎么攒出来的
2.1 数据来源分三路,但每一路都要过脑子
攒数据我用了三条路同时走。
第一条是公开合规来源。像Open Images、AI Challenger这类目标检测数据集里本身含有手机相关图片,我从里面筛出手机占比较高的样张,按许可协议确认可用后拿来作为基础样本。这里提醒一句:从公开数据集筛图时一定要看授权条款,商用项目和实验室项目对版权的要求不一样,别踩了坑。
第二条是自己拍摄。我找了几部不同颜色、不同尺寸的手机当道具,分别在办公室、教室、走廊、户外、夜间街道等环境里拍摄,手机屏幕设置为亮屏、息屏、半亮三种状态。拍摄时不要只拍“好看的构图”,要刻意模拟真实监控视角——俯拍、远拍、隔一个工位侧拍。
第三条是视频抽帧。自己录几段自然场景的视频,按每3到5帧抽一帧,能得到大量背景略有变化但目标姿态连续的图片。抽帧的好处是后续训练时,模型能看到同一个手机在不同瞬间的位置变化,对小目标跟踪类任务特别友好。
我做了一个简单的配比实验:第一版只有公开筛选样本,测试集漏检率明显偏高;加入自拍和抽帧样本后,漏检率下降很明显。原因是公开样本里“摆拍感”太强,让模型学偏了。
我按下面这个场景清单来控制数据配比,最终凑齐2800张:
| 场景 | 光线条件 | 手机状态 | 张数 |
|---|---|---|---|
| 办公室工位 | 日光灯 | 亮屏/息屏/桌面/手持 | 600 |
| 教室后排 | 混合光 | 桌面/手持/遮挡 | 500 |
| 走廊过道 | 自然光 | 手持/贴耳 | 400 |
| 户外街道 | 白天/黄昏 | 手持/揣兜/贴耳 | 500 |
| 夜间室内 | 弱光 | 屏幕亮起/息屏 | 400 |
| 车内驾驶舱 | 混合光 | 支架/手持 | 400 |
2.2 清洗筛选:宁缺毋滥
图片攒回来粗算有4000多张,最后只留了2800张,剩下的全删了。删掉的主要是这三类:
- 手机在画面中占比小于1%的远景图。这种图不是不能学,而是在640分辨率下目标只有十几个像素,对训练YOLO的性价比太低。
- 严重运动模糊到人眼都认不出的图。手机被快速拿起放下时很容易拍出这种帧,视觉上有个形状但完全没有边缘信息,标注了也是给模型喂噪声。
- 目标被遮挡超过70%的图。只露出一个角或者只剩耳机线连着手机,这种标注框会给损失函数传递错误信号。
这个筛选过程不要心疼。数据量从来不是第一优先级,数据质量才是。2800张干净样本的训练效果,经常比4000张混入大量低质量样本的效果还要好。我当时把每一张被淘汰的图都记了个原因,回头再看,运动模糊占了四成,小目标占了三成,遮挡占了两成多,真正有价值却不小心删掉的图其实很少。
2.3 划分数据集时的“分层”技巧
训练、验证、测试我按2300/300/200来分,但这一步有个容易被忽略的细节:不要直接随机打乱划分,而是按场景先分层,再在各层里随机抽。也就是说,办公室场景、教室场景、夜间场景、户外场景各占多少比例,在划分时保持一致。如果直接随机划分,很可能验证集全落在白天办公室场景上,测试集却全是夜间场景,最后跑出来的指标虚高,一到真实环境就露馅。
另外还有个数据泄露问题要留意:同一个视频抽帧出来的图片,内容高度相似,如果一部分进了训练集,另一部分进了测试集,测试指标会被大幅高估。我的做法是按“视频片段”而不是按“单帧”来划分,保证同一个视频片段要么全在训练集,要么全在测试集。这个细节做目标检测的人容易忽视,但对于视频抽帧类型的数据它影响非常大。
3. 标注规范:一个框也是有讲究的
3.1 标注工具选择与流程
2800张图如果纯手工框,一个人大概要连续标两天半,很容易标到后面手抖。我的做法是先用X-AnyLabeling做半自动预标注,加载一个通用YOLO权重(比如YOLOv8s)先跑一遍预测,生成一批初始框,然后人工在工具里逐个修正。这样直接把标注时间压到一天以内。小批量数据或者你只是临时补几百张图,用LabelImg手动框也完全够,不用过度工具化。
这里有一个操作上的顺序建议:先把预标注模型调整到一个偏低的置信度,比如0.15,宁可多框出来一些错框,也不要在预标注阶段漏掉目标。漏掉的目标如果你没注意到,就会被直接跳过,比错框更麻烦。错框至少还在画面上,能被你一眼看到然后修正;漏框就彻底隐身了,检查的时候也很难发现。
3.2 标注边界约定:什么时候框,框哪里
拿手机检测这种单类标注来说,最容易出现分歧的就是边界。我定了一套自己的规则,也建议你提前定好,否则两个人标出来的框风格完全不同:
- 只框肉眼可以明确识别为手机完整轮廓的可见部分,哪怕只有50%可见,有条件判断是手机就框。
- 屏幕点亮时产生的光晕不算手机边界,按照机身轮廓来框,光晕属于背景。
- 手机被手握住时,框可见的机身部分,不要把整个拳头框进去。
- 画面里出现多个手机全部标注,不因为距离远、看不清就不标——除非目标确实小到人眼无法判断。
这些规则看着琐碎,但直接影响模型学到的边界回归能力。我第一版标注时没管光晕问题,结果模型把屏幕上的一片白光学成了手机特征,亮屏手机大量误检,后来花了一整天把亮屏图片重新过了一遍标注,再训练才恢复正常。
3.3 两轮校验机制
全部标注完成后,我做两轮检查。第一轮是脚本检查:框坐标是否越界、是否出现负值、同一目标是否有重复框、类别ID是否越界。第二轮是人工抽检:按场景各抽10%到20%,重点看难例标注是否一致。抽检不合规的标注比例超过5%,就让标注者统一重看一遍规则再修。这一步建议不要省,标注质量差一个等级,训练出来的模型就差一个等级,后面再去调loss曲线浪费时间,不如老老实实把框标好。
脚本检查我有几个可以直接用的规则:x_center、y_center必须在0到1之间,width、height必须在0到1之间且不为0,单个框面积不能超过全图面积的0.9,同一张图里两个框的IoU超过0.8时人工复查。这些规则写成一个几十行的Python脚本就行,跑一遍输出可疑样本列表,比肉眼扫几千张图靠谱得多。
4. YOLO训练实录:参数和坑
4.1 为什么先用YOLOv8n打底
选型我建议从YOLOv8n开始,而不是一上来就上YOLOv9或YOLOv10。原因有三:第一,手机检测属于单类目标检测,任务本身不复杂,n级的容量已经足够学出一个不错的baseline;第二,n参数量小,训练和推理都快,方便快速迭代数据质量;第三,后续如果用TensorRT部署到边缘设备,n级模型在Jetson这类平台上跑起来更现实。如果你想追求更高精度,可以在同一套数据集上直接升到YOLOv8s或m,数据不需要重新标注。
4.2 一份可以直接抄的训练配置
我用的是Ultralytics的YOLOv8训练流程,数据文件按YOLO格式写好,标签是txt文件,每行“class x_center y_center width height”,目标类别只有phone一类。数据集YAML长这样:
# phone_dataset.yaml path: /data/phone_dataset train: images/train val: images/val test: images/test names: 0: phone下面这组参数是我实验下来比较稳的起点配置:
| 参数 | 取值 | 说明 |
|---|---|---|
| model | yolov8n.pt | 用预训练权重做初始化,不是从零训 |
| imgsz | 640 | 后续可以试1280看小目标增益 |
| batch | 16 | 单卡显存16G以内建议别超过16 |
| epochs | 300 | 配合早停,实际跑不到300 |
| optimizer | SGD | 配合warmup,稳定性和精度都还行 |
| lr0 | 0.01 | 学习率,bn异常时降到0.005 |
| mosaic | 1.0 | 前200轮开启,后期可以关闭 |
| hsv_h/hsv_s/hsv_v | 0.015/0.7/0.4 | 色彩增强,对暗光场景很有用 |
训练命令大概是这样:
yolo train data=/path/to/phone_dataset.yaml \ model=yolov8n.pt \ epochs=300 \ imgsz=640 \ batch=16 \ optimizer=SGD \ lr0=0.01 \ patience=60这里说下mosaic这个参数。mosaic增强对小目标检测很有帮助,因为它把小图拼接成大图,让模型在训练时更多看到相对小尺寸的目标。但它有个副作用:如果目标跨拼接边界被切断,标签框其实是不准的。所以我通常让mosaic一直开着跑到最后几十轮再关掉,让模型最后在干净分布里稳定一下。
4.3 训练中我踩过的两个坑
第一个坑是BN崩溃。这个词看起来吓人,其实就是训练没跑几步,loss突然飙升到几十上百,然后输出全是NaN。我在实际训练中遇到过两次:一次是显存不够把batch调成8之后出现的,一次是手滑把lr0设成了0.05。原因无非是学习率在warmup阶段之后设置得太激进,或者batch过小导致BN统计量不稳定。解决办法很简单:把lr0从0.01降到0.005,或者把batch从8提到16,重新跑一次基本就正常了。如果你用了自动找到的lr,也要人工看一眼曲线再往下训。
第二个坑是验证集loss在200个epoch附近开始回升,训练集loss还在往下降。这是典型的过拟合信号,说明模型开始背训练样本了。我的处理方式不是调模型,而是回数据:把HSV增强拉大一点,加入平移和轻微旋转增强;同时把早停patience设为60,让它自己在指标不再涨的时候停下来。手机检测本身类内差异很大,如果数据增强到位,300轮epoch通常到150到200轮就收敛了。
训练过程中我还会盯着三个曲线:box_loss稳步下降说明定位在学;cls_loss有波动是正常的,但整体趋势必须向下;如果val曲线出现“掉下去又拉上来”的锯齿状,大概率是学习率太大,把lr减少一半再看。
5. 实测效果和边界分析
5.1 测试集表现和真实场景感受
在这套2800张数据集的测试集上,我跑了几组实验,最终YOLOv8n的mAP50能稳定在0.94到0.96附近,mAP50-95在0.75到0.8之间。这个指标对单类检测任务来说属于正常偏好的水平。不同场景的表现也有差异,我简单记录了一下:
| 场景类别 | 表现情况 |
|---|---|
| 室内日光灯 | 很稳定,误检少 |
| 夜间屏幕亮起 | 置信度很高,特征明显 |
| 黑色手机配黑色桌面 | 容易漏检,对比度不够 |
| 隔着玻璃反射面 | 偶尔出现重影框 |
| 快速拿起手机瞬间 | 拖影导致模糊漏检 |
不过指标只能说明一部分问题。真正部署到实际场景后,我感受到的是“场景变了效果变化很大”:夜间屏幕亮起的帧,检测置信度很高,因为屏幕亮斑特征太明显了;但黑色手机放在黑色桌面上的背影,经常被漏掉。这说明测试集里没有完全覆盖极端低对比场景,后面要补的正是这类难负样本。
5.2 最容易漏检和误检的三种情况
- 黑色手机加黑色桌面加俯拍:目标和背景几乎同色,模型很难区分边线。
- 隔着玻璃或反光面拍摄:手机轮廓被倒影干扰,容易出现重影框。
- 运动过程中的短时拖影:比如快速拿起手机时,画面边缘模糊到只剩一个虚影。
误检方面,最常见的是把黑色钱包、充电宝、手机壳色块、书脊误检成手机。这些误检来源都有一个共同点:尺寸和长宽比跟手机相似,纹理又被模糊掉了。想压制这种误检,最好的办法不是调NMS,而是把这些负样本收集起来,单独作为背景图混进训练集,让模型见过“长得像手机但不是手机”的东西。
5.3 提升真实场景召回率的三招
第一招:把Copy-Paste增强打开。将小目标手机随机粘贴到其他背景图中,可以在不增加人工标注的前提下,让模型看到更多小尺寸样本。这个增强对手机检测尤其有效,因为手机是刚性物体,粘贴后不会像猫狗那样变形失真。
第二招:测试时用TTA。把图片做水平和轻微旋转增强后多次推理再融合结果,模糊、低光场景的召回能提升,代价是推理时间翻倍。如果你做的是离线视频分析,TTA完全可以用;做实时检测的话就要慎重。
第三招:后处理阶段把置信度阈值调到0.2到0.25,配合更高IoU的NMS保留更多候选框;对误检较高的场景再单独调回来。这里要说明的是,阈值调低会带来更多误检框,所以一定要结合具体场景试验,没有一劳永逸的设定。
6. 后续迭代:从2800张到一个闭环
数据集的真正价值不在于一次性训练,而在于能不能滚起来。我现在用的迭代方式是半自动标注闭环:先用第一版模型对海量未标注视频抽帧做预测,产生一堆“预标注”,人工只修正那些置信度低于0.5的框,一天时间就能把数据集从2800张扩到5000张以上。扩完后再训练一版,新模型又能在更难的数据上工作,就这样滚雪球。
想把这个数据集往产品方向推的朋友,我建议下一步把“手机”拆成“手机普通状态”和“手机屏幕点亮”两类。这一点在实际项目中很重要,比如课堂玩手机检测,屏幕亮起时抓拍更有说服力,也可以减少误报。再加一个“耳机”类也不错,很多场景下戴耳机本身就是需要重点关注的行为特征。
部署层面的经验是:不要拿着nano模型直接上线,先在服务器上用高精度模型做离线评测,确认好误检率可接受,再导出ONNX用TensorRT或OpenVINO加速。精度和速度之间怎么权衡,一定要拿真实监控视频测,别拿测试集指标自嗨。我见过不少项目死在“测试集很漂亮、现场一塌糊涂”这一步,根源多半是数据场景覆盖不够,而不是模型结构不行。
这套数据集我自己迭代过程中最大的体会是:真正花时间的不是标注本身,而是定标准、筛数据、按场景分层、反复测边界。数据干净了,YOLO训练反而很快。你如果也在做类似的手机检测项目,建议先把这套流程走一遍,哪怕只凑几百张图,把数据和标注规范立住,后面所有调参和模型迭代都会顺手很多。