玩手机检测实战:从YOLO数据集构建到落地告警

📅 2026/8/26 6:16:49
玩手机检测实战:从YOLO数据集构建到落地告警
简介目标检测是计算机视觉的核心任务之一YOLO系列算法凭借实时性与精度优势成为工程应用的首选。然而实际项目效果深度依赖数据质量与场景匹配。以监控摄像头下的行为识别为例小目标、遮挡、多角度等因素导致通用模型表现不佳。如何构建贴近真实场景的数据集、设计合理的类别体系、结合跟踪与业务规则完成行为判定是落地的关键。以玩手机检测为切入点详解从数据标注、YOLO训练调参到端侧部署的完整链路并分享多个实测经验。 前段时间好几个人找我开口都是同一句话“我想做个玩手机检测用YOLO算法是不是很简单的”第一次听到这个需求我也觉得简单——成熟的检测框架、现成的预训练权重、找个公开数据集训练两个晚上不就能把手机框出来了吗等真把教室、办公室、工位的监控画面拉到桌面上看我才明白问题没那么轻巧。画面里的手机可能只有几十个像素被手挡着、被桌面挡着还要跟书本、遥控器、平板电脑区分开。真正难的从来不是YOLO本身而是怎么定义“玩手机”、怎么造一份能真实还原监控视角的数据集、怎么把单帧检测结果变成可用的业务告警。这篇文章我就把这套完整思路写下来从数据集构建到训练调参再到最后的落地判定一次说清楚。1. 玩手机检测难点从来不在“检测”1.1 你想象的监控画面和真实画面的差距很多人第一次做玩手机检测脑子里浮现的是这样的画面摄像头正对着人光线充足手机举在胸前屏幕发亮占画面比例很大像手机厂商宣传片一样。真实场景完全不是这样。以最常见的教室和办公区监控为例摄像头通常装在墙角或者天花板以斜俯视角度往下看画面里可能同时出现十几个人。一部手机在1080P画面里常常只有30×60像素左右也就是成年人拇指指甲盖的大小。更麻烦的是这个角度的手机看起来不是方方正正的矩形而是一个被透视压缩、可能被手部遮挡的倾斜四边形。早上阳光照进来屏幕反光直接过曝手机轮廓和桌面融为一体。我从之前测试过的公开模型效果说起。用COCO预训练的YOLOv8模型直接去跑现场监控画面里正常放在桌上的手机能检出但一旦手机被人拿起来、举起来、侧着看检出率就会明显下降。因为公开数据集里的“cell phone”类绝大多数都是水平视角、目标较大、遮挡较少的图片跟你实际面对的斜俯视监控画面属于两个分布。模型学到的特征在那种干净图片上很有效到了监控画面里就开始“水土不服”。这就是领域偏移domain shift最直白的体现。你以为在做一个检测任务其实在做一个场景迁移任务而迁移成本就是你需要自己的数据集。1.2 目标定义才是第一个坑玩手机不是物体这里要先捅破一个窗户纸物体检测算法只能输出“某个位置存在某个物体”它没有办法直接告诉你“这个人在玩手机”。玩手机是一个行为至少包含两层信息手机在手上且正在被使用。这两层信息单靠一帧静态图像都没法完全确认。一个人拿着手机可能是在打电话可能是在看时间也可能只是顺手拿起来一个人低头看着桌面可能是看书也可能是看手机。所以检测阶段的目标要拆开定义。我的经验是把检测目标定为“手持且正在使用的手机”而不是“玩手机的人”。什么意思呢手机放在桌上不标这是负样本。手机拿在手里屏幕亮着或者手指在滑动标为“手持使用中手机”。手机贴在耳边打电话这个看业务需求。如果场景是开车分心检测打电话也是需要告警的如果是教室防沉迷手机贴耳边可以视为违规一般也标。人在低着头看放在桌面上的手机这种驾驶场景常见教室场景也有要不要单独一个类别取决于你的告警逻辑。检测目标定成“手机”之后模型要学的是手机外观特征矩形边框、屏幕发光、人手握持的轮廓。这种特征在监控画面里虽然有难度但比直接检测“玩手机的人”要可靠得多。如果你把类别设成“playing_phone”并去标整个人体框模型会非常困惑因为同样坐着、低头、手部有动作的人绝大多数时候不是在玩手机模型等于在猜精度自然上不去。这也就是为什么数据集设计必须从业务目标反推。先把“什么情况算玩手机”写成规则再决定标注哪些目标最后才轮到算法和模型。1.3 为什么公开数据集救不了这个任务我知道肯定有人会问COCO、Open Images、VisDrone这些数据集里不是有手机类别吗直接用不就行了我帮大家实际对比过公开数据集在这个任务上的问题集中在三方面。第一是视角问题。主流公开数据集的手机图像基本是平视或轻微俯视拍摄的目标尺度和角度都正常。监控摄像头常见的斜俯视大广角几乎找不到。第二是场景问题。COCO里的手机图片是什么场景咖啡馆、卧室、街拍。教室、办公室、生产车间这类固定机位监控场景非常少更何况监控本身还有固定的视角、固定的光照变化模式。第三是目标尺度问题。监控画面里的手机属于小目标乃至极小目标而公开数据集里的手机往往占画面的5%以上。直接用公开数据训练完去跑监控漏检率会高到没法用。这里不是说公开数据集没用它可以作为预训练基础也可以在自建数据不足时作为补充增强来源但主力还得是贴合自己场景的数据。别指望“白嫖”一份现成数据集就把产品交付了。2. 数据集怎么造采集、标注与类别方案2.1 数据来源自采为主公开数据做补充玩手机检测数据集的核心思想就一句话你的摄像头长什么样、装在哪里、看什么场景数据集就该长什么样。自采数据是绝对主力。需要覆盖你实际部署场景的多个维度多个点位不同摄像头高度、角度、距离。同样是教室前排讲台视角和后墙摄像头视角差异很大。多个时段白天自然光、晚上开灯、拉窗帘后的逆光、屏幕亮光在暗环境的对比都要覆盖。多人状态单人单机、双人交谈、多人同时玩手机、左手拿、右手拿、横屏、竖屏、戴手机壳、不戴壳、大屏手机、小屏手机。干扰物平板电脑、电子书阅读器、遥控器、书本、鼠标、卡片。这些是误报的主要来源必须当作背景负样本收录。我建议采集节奏按“场景数、人数、时段”三个维度做矩阵。一个比较小的单场景项目最少拍500~1000张原始图片如果场景多每个场景至少300张宁多勿少。视频采集后可以按固定帧率抽帧比如每秒抽1~2帧这样同类画面不会太多又保留了连续动作变化。公开数据集的作用是补类别内差异。我会从COCO、Open Images里专门挑出“人拿手机”“手机放在桌上”“手持通话”这三类图片筛选后和自采数据混合。筛选时注意只保留和你的部署视角相近的图。这些图主要用来增加正样本的形态多样性数量一般控制在总量的20%~30%即可。还要提醒一下授权问题。公开数据集各有各的LicenseCOCO是CC BY 4.0Open Images是CC BY 2.0Kaggle上第三方整理的数据集有的带有附加限制。如果项目只是自己测试怎么用都行如果要做商业产品建议把所有发布用素材换成自采数据免得后面处理授权纠纷。2.2 类别设计单类、双类还是细分标注前先把类别定死不然后面返工非常痛苦。我接触过的玩手机检测数据集常见的有三种类别方案。方案类别设置适用场景优缺点A单类只设一个类handheld_phone专门标“手持使用中的手机”摄像头近距离、画面里人数不多、只需要简单告警实现最简单但无法区分玩手机和打电话误报率偏高B双类phone_handheld手持使用、phone_on_desk放在桌上需要排除“桌上手机”误报的办公/教室场景能通过后处理过滤掉桌上手机效果比A更稳定C细分phone_handheld、phone_ear贴耳通话、phone_desk桌上、person人体框需要做行为判定、需要统计“使用手机”还是“打电话”信息最丰富但标注成本高、训练难度也大我的建议是普通项目起步选B。把“手持使用中的手机”和“放在桌上的手机”分成两个类既不会显著增加标注压力又能在推理阶段直接过滤掉桌上手机这个最大干扰源。很多误报其实都来自桌面上亮着的屏幕被当成手机双类方案本质上把这个难题前置到了标注环节。如果项目还要做“人机关联”——也就是判断哪个手机属于哪个人——那就要在方案C里加人体框person。人体框类的作用不是给算法当主检测目标而是给后处理提供参照手机框中心是否落在某个人体框内、是否贴近人体关键点区域。这样就能把“他拿着手机”和“桌上放了个手机”清晰地分开。有一点需要提前说明类别不是越细越好。类别越多每个类别的标注一致性越难保证模型要学到的判别边界越复杂对数据量的需求也越大。先能用再细分是更务实的路线。2.3 标注规范遮挡、小目标、边界处的处理标注规范是数据集质量的生命线尤其是遮挡和小目标这两个情况标错了模型就学了错误信号。遮挡怎么标手机被手挡住一半露出一部分屏幕和边框这个目标要不要标要标而且框要尽可能贴合可见部分的外接矩形。如果手机被完全盖住肉眼完全看不到那就不标。这一条规则看起来很基础但多人协作时特别容易出分歧必须写进标注文档。小目标标不标我见过不少数据集小目标漏标率非常高。规则最好量化目标框最长边小于图片最长边的1%时不标介于1%~3%之间时可以标但要保证肉眼能确认。比如1080P的图小于约19像素的手机基本没法确认建议忽略20~60像素之间尽量标。别小看这些“小得看不清”的目标监控场景里大量手机就是这么大的如果全部漏标模型就永远学不会检测这个尺度的目标。截断目标手机在画面边缘被切掉一半要标。只要还能看出是手机就框可见部分。这和通用检测数据集的做法一致。不要把人手一起框进去有人习惯把“手机手”整体框成一个矩形觉得这样检测更容易。这是个很危险的坏习惯。模型学到的是“手手机”这个组合的外观换一个人戴不同手套、拿不同姿势就检测不到了泛化会很差。标注工具方面个人标注可以用LabelImg、X-AnyLabeling多人协作建议用CVAT或Roboflow可以在线分任务、做审核避免一个人标了一周发现数据格式不对。标注完成后必须抽检。抽检比例至少10%重点看三个指标漏标率高不高、框贴合度好不好、类别是否混淆。数据集在精确率上翻车往往不是因为模型不行而是标注阶段就带着歪的。2.4 数据增强、划分与规模控制玩手机检测离不开数据增强但增强不是越猛越好。YOLO系列自带的增强里Mosaic四图拼接和HSV色彩增强建议默认开启它们的收益在这个任务上非常明显。要注意的是翻转策略水平翻转左右翻转可以用但垂直翻转上下翻转要谨慎关闭。监控摄像头是固定的画面里的人是站着的上下翻转会生成现实中不存在的构图模型学到的特征会失真。旋转增强建议限制在±15°以内扫描式全景画面里手机不会出现90度旋转。如果训练时给模型看了大量歪七扭八的手机方向推理时反而会误判。数据集划分是另一个容易踩坑的点如果按图片随机划分train/val同一个场景、同一个人的连续帧会同时出现在训练集和验证集里验证集loss会非常好看但上线就崩。正确做法是按person或按场景划分同一个人的所有图片进同一个集合场景A、B进训练集场景C进验证集。这样才能验证模型真正的位置泛化能力。规模上方案B这种双类单场景项目训练集有1200~2000张就可以看到不错的检测效果如果场景跨度大建议目标个数——注意是目标个数不是图片张数——达到5000个以上。目标数比图片数更能反映数据的信息量。3. YOLO选型、工程化与训练调参3.1 版本与模型大小选择YOLOv5、YOLOv8、YOLOv10、YOLO11这些版本我都跑过玩手机检测这块选型优先级我建议这样排。如果你用的是NVIDIA显卡推理、或者Jetson这类边缘GPU设备YOLOv8是我的首选。它在精度和部署方便度之间平衡得最好Ultralytics官方提供的导出工具链非常成熟ONNX、TensorRT、NCNN一键导出省去很多适配时间。项目里如果没有特殊的硬件约束直接用v8。如果你的部署平台是瑞芯微RK3588、地平线J5这类国产芯片YOLOv5也有不错的基础生态。很多国产AI芯片厂商的适配文档就是以v5为主用v5能减少踩底层的坑。YOLOv10和YOLO11理论上效率更高但生态和支持面还不够广量产项目我会保守一些。模型大小方面监控场景通常用yolov8s起步yolov8n虽然快但小目标表现偏弱如果设备算力充足直接上yolov8m。大模型在小目标检测上的优势是实打实的因为模型的感受野和特征表达能力更强。3.2 把数据变成模型能吃的格式目录、标签与yaml数据集要整理成YOLO标准目录结构phone-dataset/ ├── images/ │ ├── train/ │ │ ├── sceneA_0001.jpg │ │ ├── sceneA_0002.jpg │ │ └── ... │ └── val/ │ ├── sceneC_0001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── sceneA_0001.txt │ │ └── ... │ └── val/ │ └── ... └── data.yaml每张图片对应一个同名txt标签文件每行表示一个目标class_id x_center y_center width height例如标注了一个手持手机框中心点在图中的归一化坐标是(0.512, 0.386)宽高是(0.053, 0.091)类别id是0对应phone_handheld那这一行就是0 0.512 0.386 0.053 0.091坐标全是相对图片宽高的0~1之间的浮点数。用CVAT、Roboflow导出YOLO格式时会自动帮你算好但如果你用LabelImg需要确认输出格式是YOLO而不是Pascal VOC两个格式很容易搞混。data.yaml内容如下path: /path/to/phone-dataset train: images/train val: images/val names: 0: phone_handheld 1: phone_desk写完可以先用下面的命令检查标签格式是否正确python -c from ultralytics import YOLO; YOLO(yolov8s.pt)或者直接用Ultralytics的数据集检查接口跑一个快速验证。3.3 训练参数与命令行实操数据准备好之后训练命令其实很简短yolo detect train \ modelyolov8s.pt \ datadata.yaml \ imgsz640 \ epochs100 \ batch16 \ device0这里有几个参数要单独说。imgsz训练尺寸。如果标注里小目标多建议直接提到imgsz1280。代价是显存占用明显上升训练速度变慢但对小目标召回率的提升是肉眼可见的。先跑一个640的版本看baseline再跑1280对比一下是有价值的。epochs预训练权重微调的情况下100~150轮基本收敛。如果数据量特别小几百张可以减到80轮并及时观察val loss防止过拟合。过拟合信号是训练集损失持续下降而验证集损失不再下降甚至回升。batch在不爆显存的前提下尽量大。batch太小BatchNorm统计不稳定小目标检测更容易抖动。一般24GB显存跑yolov8s1280大概可以放到16~24。优化器Ultralytics默认的AdamW已经够用。lr0保持默认0.01不用刻意调。真正需要反复调的是imgsz和模型的scale这两个对最终效果的影响比花哨的优化器大得多。训练结束后看runs/detect/train目录下的results.png、confusion_matrix.png和val_batch*.jpg。我会重点看验证集预测图里的漏检和误检比看数字更直观。3.4 小目标专项优化大分辨率、P2头与切图推理如果训练完之后模型在监控画面上漏检远处的小手机先别急着换模型架构按顺序做下面三件事。第一步推理时提高输入分辨率。检测速度允许的情况下训练和推理统一用imgsz1280。同一个模型640和1280输入下的小目标召回率可以相差20个百分点。原因很好理解输入分辨率决定原始目标的特征像素数。第二步增加P2检测头。YOLOv8标准结构有三个检测头分别输出在80×80、40×40、20×20的特征图上。P2头会额外增加一个160×160的高分辨率检测头专门处理小目标。Ultralytics在v8.1之后支持在yaml里为模型配置文件添加额外的P2层改起来也不复杂但对小目标很有效。第三步SAHI切图推理。如果目标实在太小——比如画面里手机只有20像素——单靠加大分辨率收益有限可以考虑把大图切成若干小块分别推理再合并结果。SAHISlicing Aided Hyper Inference这个库就是干这个的支持Ultralytics模型。切图推理的代价是耗时倍数上升适合离线分析或者算力充足的服务器端部署。这三步按成本从低到高排。大多数监控场景做到第一步就够用了特别严苛的场景再把后两步加上。4. 从“框出手机”到“识别玩手机”跟踪与业务判定4.1 单帧检测的局限模型训练完毕你得到了一个能把手机框出来的检测器。但如果只在单帧上做检测然后直接拿来告警你会发现两个很现实的问题。第一是闪烁。模型偶尔会把某个目标丢一帧、下一帧又捡回来直接导致告警反复触发。第二是行为判定缺失。检测到手机在手只是检测到了“有手机”不是“在玩手机”后端的判定逻辑才是真正把检测结果变成业务价值的部分。我的做法是把单帧检测提升为基于视频流的跟踪判定。核心模块是目标跟踪器常见选ByteTrack它在遮挡、静止场景下比DeepSORT这类依赖外观特征的跟踪器更稳固而且不依赖额外的ReID模型性能开销小。流程大概是视频流每帧送入检测器输出手机框跟踪器把连续帧中的同一个手机框关联成一条轨迹分配一个ID后端拿到这个ID对应的轨迹长度、位置变化、移动轨迹再做业务判定。有ID的好处是你可以说“ID 7这手机已经连续出现在桌上3分钟了不用管”或者“ID 12这个手机在手部区域出现超过5秒触发提醒”。4.2 人体框、ROI与时序规则降低误报的关键要有效减少误报不能只依赖检测器还得加业务规则。人体框约束如果你在标注阶段加了person类推理时让检测器同时输出人体框和手机框然后判断手机框是否落在人手附近。具体做法可以先用人体关键点或人体框的上半部分区域粗筛出“手的候选区域”再检查手机框中心是否落在这个区域。比一个个框对框计算简单但效果往往够用。ROI区域约束很多场景里我们只关心某个固定区域比如工位、课桌、驾驶席。部署时可以在算法配置里画一个多边形ROI只处理落在ROI内的检测结果落在ROI外的手机一律忽略。这一个规则能干掉大量来自走道、相邻工位的误报。时长判定玩手机是一个连续性行为建议设定“持续N秒才告警”的规则。默认N可以设1秒配合检测帧率。如果视频是25fps但检测只跑5fps那N可以改成3~4帧连续命中即告警。这个规则能过滤掉大量转瞬即逝的误检。动作模式过滤手机在手上静止摆放比如拿着看书时顺便夹着手机和手指滑动的动态在时序特征上是有差异的。手机中心点的微运动、框面积的变化率、屏幕朝向上的变化都可以提取出来做简易规则。这个属于进阶技巧但实现成本不高值得试。4.3 部署性能与端侧优化思路检测模型的推理速度直接决定后端规则怎么设计。不同部署平台差异很大我列一下常用的配置组合。部署方式设备模型/精度参考速度备注服务器GPURTX 3060以上yolov8s FP163~8ms/帧可同时接多路视频边缘GPUJetson Orin Nanoyolov8n/s TensorRT FP168~15ms/帧注意内存带宽AI ISP盒子RK3588yolov8s RKNN INT820~40ms/帧需走量化流程CPU服务器志强E5yolov8n ONNX INT850~120ms/帧仅适合低帧率轮询如果算力吃紧最简单的优化是“跳帧检测”每3帧取1帧送检测中间几帧直接用跟踪器预测位置这个方案在目标运动不剧烈的监控场景表现很好。模型导出时优先TensorRT/ONNX Runtime能明显提速。TensorRT的FP16比PyTorch原生CUDA推理快2~4倍INT8量化又能再翻一倍但对小目标精度有损耗建议导出前先小批量测试一下召回率下降幅度。如果INT8会漏检就退回去用FP16。5. 实测中的翻车现场与排查经验5.1 案例一小目标几乎全漏检问题出在哪有次项目上线客户反馈说“远处那几排工位完全检测不到手机”我把测试视频拉出来一帧帧看发现确实如此。画面里一位员工坐在离摄像头大约15米的位置手机在1080P画面里只有不到25像素宽人眼不经提示基本察觉不到。排查思路是这样的第一步先用标注工具量了一下测试集里各目标框的尺寸分布发现小于32×32像素的目标占了40%。这个数字说明数据集里绝对数量不少但模型没学会。第二步用模型推理打印每个预测框的置信度。发现这些远距离小目标其实也出了框但置信度普遍只有0.1~0.2被置信度阈值拦掉了。那不是模型没看见而是它自信不起来。第三步把阈值从0.25降到0.05能检测出一些但带来的误报又不可接受。说明特征层面学得不够不是单纯调阈值能解决的。最终方案训练尺寸从640提到1280模型从yolov8s换成yolov8m同时在推理端用小图切块。三者叠加后远距离召回率从不到50%提到了80%以上。这个排查链路每步都很简单关键是顺序——先量化问题、再确认模型输出、最后针对性地提分辨率、换模型、切图而不是一上来就重训一个模型买彩票。5.2 案例二平板、遥控器全被识别成手机另一个项目上线后告警里出现大量“拿平板电脑在玩手机”的荒诞记录。查了误报样本画面里确实有明显平板、遥控器、黑色皮面手机壳被判成phone_handheld。原因很直接训练集里的负样本种类太少。我把自采数据翻了一遍桌上的干扰物主要是书本和水杯平板和遥控器几乎为零。模型只见过“手机是什么样”对“哪些东西长得像手机但不是手机”没有任何概念。修复方案分两步。第一步专门去采集一批“不是手机但容易混淆”的数据平板电脑正反面、遥控器、电子书阅读器、计算器、香皂盒、黑色充电宝。这些图片不标注任何目标类别作为纯背景图片混进训练集。第二步给它们分配一个“背景”标签或者直接不放任何标签文件代表无目标混合训练两三轮。这个办法的核心思想是给模型补充“负样本知识”。检测模型不只是从正样本里学目标长什么样也要从负样本里学“什么不该框出来”。很多新手训练集里负样本严重缺失导致模型胆子太大什么方形物体都敢框。5.3 案例三指标好但现场效果差同一个人泄露到两个集合还有一个让我记忆深刻的坑训练集验证集是纯随机划分的验证集mAP达到0.94现场实测却崩得很厉害。排查时偶然打开一张验证集图片发现里面的人脸跟某一张训练集里的是同一个人、同一个位置、几乎同一个角度。再一细查原来视频抽帧时把同一个场景连续几十帧按顺序拆进了不同集合同一个人的动作变化出现在训练集和验证集里几乎是同一时刻。模型等于变相“记住了”这个人的特征验证时成绩自然好换个人就不灵了。修复方式是按Person/场景而不是按帧号划分。具体做法给每个人员或每个座位编号把同一编号的所有图片放进同一个集合里不同场景单独切分。这样才能保证验证集测试的是“没见过的组合”。这也是很多自建数据集项目性能虚高的头号原因比调参影响大得多。5.4 其他几个值得提前注意的细节夜间/低光表现屏幕发光在暗光环境其实更容易被检测到但手机黑色边框会隐入暗色衣服。建议在数据采集时加入夜间时段并进行一次专门的亮度/对比度增强不然夜班场景会有明显掉点。视频编码问题监控视频有H.264/H.265之分解码后的画质如果用了很激进的压缩小目标细节会被抹掉。部署时建议把视频流分辨率设置为4MP以上码率不要太低。追踪ID的稳定性ByteTrack在目标相互靠近、遮挡时会丢失ID。如果业务强烈依赖“持续时间”统计建议在无ID告警附近加一个平滑机制如果目标短暂丢失但很快又出现且位置接近沿用原ID。标注一致性抽检多人标注时一定指定一个人做终审。否则不同标注者对“遮挡率多少才算可见”的理解不一致模型会学到非常混乱的边界。这些细节单独看都不大但叠加起来就是线上效果和Demo效果的差距。最后再分享一个我的习惯每做一次数据迭代就把之前版本的数据集和模型结果保存一份跑完对比再决定要不要换。别扔在一个目录里反复训练不然出了问题根本不知道是哪轮数据把模型带偏了。玩手机检测这个项目表面上是模型工程根子上是数据工程。把数据集这关过了后面反而顺风顺水。本文还有配套的精品资源点击获取