
先交代一下背景最近在做课堂专注度分析项目里面有个环节是检测学生有没有低头玩手机。网上找数据集找了一圈单张图片的、视频抽帧的都有质量参差不齐最后自己整理了一份2800张的YOLO目标检测数据集专门做手机检测。这个过程中踩了不少坑也把数据清洗、划分、训练、评估整个流程重新捋了一遍。这篇文章就把这套数据集和整套实操经验写出来供做视频监控、工位合规、骑行安全这类手机检测任务的朋友参考。这份2800张的数据集标注目标是手机使用YOLO格式的txt文件可以直接喂给YOLOv5、YOLOv8、YOLOv9等主流模型。适合三类人一是第一次接触目标检测的学生想找个干净的数据集跑通流程二是做垂直场景算法验证的工程师需要快速得到一个手机检测基线模型三是想给自己的业务数据做迁移学习的开发者拿这份数据预训练比自己从零标上万张省事得多。先说一个最实际的问题2800张到底够不够用。我的答案是够启动但不够直接上生产。如果你在教室、办公室、驾驶舱这种相对固定的场景里检测手机2800张训练出来的模型能作为可靠的Baseline后续加上你的真实场景数据用半监督或者主动学习方式迭代很快就能达到可用的精度。但是如果你要检测的是远景、强逆光、多角度极端情况那这个量级就要谨慎乐观后面我会讲怎么通过数据增强和训练策略把量小的劣势补回来。1. 手机检测数据集解决什么问题2800张够不够用1.1 手机检测的真实任务难点手机检测和通用物体检测不太一样属于典型的“小目标、强反光、多遮挡”问题。一个正常的监控画面里手机占整张图的比例通常不到百分之五甚至只有百分之二。如果摄像头是斜俯视角度手机还会变成一个扁平的矩形屏幕反射光线还会导致边缘模糊。这些因素让手机检测比检测猫、狗、汽车要麻烦得多。再加上手机作为一种随身物品出现姿态极其多样拿在手里、放在桌上、夹在胳膊下、揣在口袋里只露出一个角。检测目标不一定是完整手机可能是一个局部比如屏幕边缘、摄像头模组。所以做数据标注的时候边界框怎么打是框完整手机还是只框可见部分不同的人会给出不同答案。这种标注一致性比图片数量更影响最终模型精度。我在整理数据集时就发现一部分图片是从开源数据集里抽出来的它们的标注框习惯是“只要可见就完整框出”而自己补充标注的部分为了检测率更高倾向只框住明确可见的屏幕区域。这种不一致会导致训练时模型学到的边界框回归目标混乱。后来的解决办法是在标注规范里统一定义完整可见时框完整设备部分遮挡时框出最大可见区域且最小边不低于图片宽度的百分之一。1.2 2800张的量级定位2800张对于目标检测任务是一个什么概念一般公开的COCO数据集有十几万张标注图自动驾驶数据集则有几十万张。但那些是通用场景类别众多。手机检测基本是单类、单目标的检测问题类别只有phone一张图里平均目标数量少所以2800张图的“有效信息密度”比通用数据集高很多。如果按80/20划分训练集是2240张验证集是560张。对于YOLOv8这种数据效率高的模型2240张足以让模型学到手机的通用特征mAP50做到90以上是可以实现的。但要注意这里的前提是场景多样性足够。如果2800张全部来自同一个摄像头位置比如全是教室第一排视角模型泛化到别的视角就会明显下降。所以判断一个数据集好不好先看场景多样性再看数量。我在标注规范里对场景做了细分室内自然光、室内灯光、室外白天、傍晚、夜间屏幕亮起。夜间带屏幕光的信息尤其重要很多落地项目会遇到“手机在暗处发光才是最容易发现的时刻”这种情况但如果数据集里全是白天图像模型就学不到这个特征。1.3 这个数据集适合谁不适合谁适合想快速跑通YOLO流程的初学者。2800张图加YOLO格式标注不用自己处理COCO转YOLO的麻烦下载下来直接用。也适合做算法对比实验同一份数据跑多个模型比自己凑数据公平得多。但如果你要做的是“玩手机”行为识别而不是“手机存在”检测那这个数据集就不够用了。玩手机识别需要时序信息比如手的位置、面部朝向、手机相对人体的关系单张静态图检测只能告诉你哪里有手机无法告诉你这个人在不在玩手机。这类需求需要额外标注人体关键点或者接入行为分类模型。另外如果检测目标是手机实体键盘、蓝牙耳机这种配件也需要重新标一类数据。2. 数据集构成与YOLO标注格式拆解2.1 图片内容与场景覆盖整理这份数据集时我尽量保持了场景分布均衡。室内场景占总量的55%包括办公室、教室、会议室、客厅室外场景占30%覆盖人行道、公交站、广场其余是车内和夜间场景。视角方面既有平视摄像头拍到的手机也有从高处俯视的监控视角还有手持拍摄的画面。分辨率方面原始图片最小的是480×320最大的是1920×1080。训练目标检测模型时不建议直接使用原始分辨率而是统一在数据加载时Resize到固定尺寸YOLO内部会处理。但数据集的图片分辨率跨度大会带来一个问题小图里的手机可能只有十几个像素就算强行拉伸模型也学不到更多细节。这类图适合用来验证模型对小目标的上限而不是作为主要训练样本。图片里有一部分是带有“实体键盘”或者手机支架的周边场景。如果你做的是手机配件检测可以把这些图片单独抽出来重标。我自己的项目不需要这些类别所以这类图只作为背景样本保留没有把键盘标进去。这里的经验是如果你的正样本里出现了大量不标注的干扰物模型可能会把那个干扰物当成负样本的一部分也可能视而不见。最好的做法是宁可少一些不干净的正样本也要保证标注一致性。2.2 YOLO标注格式的细节YOLO格式的每个标注文件是txt一行对应一个目标。格式是这样的0 0.532 0.441 0.089 0.138第一个数字是类别ID单类数据集默认写0后面四个数字是归一化后的中心点x、中心点y、宽度w、高度h。归一化的意思是除以图片宽度或高度所以这四个数字都在0到1之间。比如一张1280×720的图上一个边界框左上角坐标是(500, 300)宽200高100那么中心点x(500200/2)/12800.468中心点y(300100/2)/7200.486宽w200/12800.156高h100/7200.139。打开任何一张jpg对应的txt文件如果发现数值有大于1或者小于0的情况说明标注越界了。这类数据在训练时会导致YOLO的Anchor匹配异常虽然有的版本会自动截断但最好在训练前清洗掉。我习惯用Python脚本批量检查import os label_dir labels/train problem_files [] for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), r) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: problem_files.append(f : wrong format) continue c, x, y, w, h parts x, y, w, h float(x), float(y), float(w), float(h) if not (0 x 1 and 0 y 1 and w 0 and h 1): problem_files.append(f : out of range) break print(problem_files[:50])这份2800张数据集里面有几个典型问题需要特别说明。第一极少数图片存在左右镜像翻转导致标注坐标没跟着翻转的情况。第二有些标注框把手机屏幕反光的光斑也包含了进去导致框比真实手机大一圈。这些都需要在训练前检查处理。有人会说现代模型对标注噪声有一定容忍度但既然数量不大多花半小时清洗收益非常直接。2.3 目录结构与data.yaml配置使用这份数据集时推荐目录结构如下phone_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── data.yaml └── README.mdimages和labels的train/val要保持一一对应。YOLO训练时通过data.yaml指定图片目录和类别信息它会在labels同级目录下找对应的标注文件。我的data.yaml写法是path: /your/path/phone_dataset train: images/train val: images/val nc: 1 names: [phone]这里有个坑path如果写相对路径YOLO会认为它是相对于当前执行命令的目录而不是相对于data.yaml的目录。用绝对路径最省心但如果你的项目要给别人复现就把path留空只写train和val的相对路径所有内容打包成一个文件夹。在开始训练前可以先用一行脚本验证数据有没有配对问题python -c from pathlib import Path for split in [train,val]: imgs set(Path(fimages/{split}).glob(*.jpg)) labels set(Path(flabels/{split}).glob(*.txt)) for img in imgs: if img.with_suffix(.txt).name not in labels: print(fMissing label: {img}) 跑完之后没有输出就是最好的结果。很多初学者上来直接训练等到loss异常或者mAP很低才回头看数据浪费时间。这个检查也就一分钟的事情。3. 训练前的数据准备清洗、划分与增强3.1 先做质量审查再做划分拿到数据集的第一步不是训练而是过一遍全部图片和标注。2800张一个人快速浏览大概需要40分钟。我推荐用labelImg或者Label Studio打开验证集打开自动加载标注框看到明显错标、漏标就记下来。可以接受再训练。分训练集和验证集时尽量按“场景”划分而不是按“图片”随机划分。什么意思呢如果一个场景连拍了30帧手机位置没怎么变化这30帧之间非常相似。如果30帧一部分进了训练集一部分进了验证集验证结果就会虚高因为模型见过了几乎一样的画面。正确做法是把这个场景的所有帧都放到同一个集合里。我一般按视频抽帧的时间段聚类或者按文件夹划分。2800张的常见划分比例是80%训练、20%验证。但如果数据里有一些难例比如夜间低光、模糊遮挡我建议把难例尽量放到训练集里验证集保持“中等难度”即可避免验证集太难导致模型看起来很差。这不是造假而是评估模型在作业环境中的表现作业环境本身也是难易混合的。当然最终要出报告的话还是要留一个包含全部分布的真实测试集。3.2 数据增强的取舍Mosaic和亮度扰动YOLOv8默认会开启一部分数据增强其中Mosaic增强会把4张图拼成一张对小目标检测尤其有效。为什么因为拼图之后图片整体尺寸不变但每个目标被缩小了相当于在小目标样本上做了尺度增强。这一点对手机检测非常契合。不过Mosaic需要注意一个副作用当手机目标小到极致Mosaic后可能变成一个只有4×4像素的点这时候标注框对应的特征区域几乎是纯噪声。我试过把Mosaic概率调到最大结果验证集上小目标AP反而下降。后来把增强参数调整为mosaic0.8也就是大约80%的样本做Mosaic其余保留原图效果更稳定。手机屏幕是自发光物体所以亮度扰动很重要。YOLO的HSV增强中有一项hsv_v控制亮度变化幅度。默认值是0.4我把它调到0.6并且靠近夜间场景的样本做了更多饱和度增强让模型更关注屏幕发光区域与周围暗部的对比。理论上手机在暗环境的“亮斑”特征比在明亮环境下的“矩形外观”更明显增强这个对比度有助于模型学到本质特征。3.3 预训练模型的选择与下载热词里经常看到“yolo预训练模型下载”这里说清楚。YOLOv8的预训练权重一般是在COCO数据集上训练好的模型COCO里恰好有cell phone这个类别。这意味着预训练模型已经知道手机长什么样只是没有在你自己的数据集上微调。用预训练权重做迁移学习是几千张小数据集能够训练充分的根本原因。如果你用YOLOv8n.pt模型参数量最小在3080显卡上训练非常快100轮只需要半小时左右。但手机检测对边界框精度要求高因为手机占图面积小我建议起步用YOLOv8s.pt参数比n大一些但准确率提升明显。如果显存在16G以上可以试试YOLOv8m.pt不过对2800张数据集来说m容易过拟合需要配合更强的正则化和早停策略。下载预训练模型最稳妥的方式是在命令行运行yolo训练之前YOLO会自动下载或者用下面这条命令yolo predict modelyolov8s.pt sourcetest.jpg它会自动从官方仓库拉取权重。如果网络受限也可以从GitHub Release页面手动下载pt文件放到当前目录即可。3.4 训练集与验证集的再平衡在清洗过程中我发现室内场景占比高导致模型在室外的泛化略弱。如果业务重点是室外就要考虑对室外图片做过采样。最简单的方法是复制室外图片文件到另一个文件夹但这样标注文件也要跟着复制。YOLO训练时有一个repeat参数在YOLOv5中可以使用repeat复制数据YOLOv8中可以直接在数据目录里给特定split放两份同样的图片。更优雅的办法是使用albumentations做在线增强时为室外类样本设置更高权重但实现起来稍微复杂。对于2800张数据我的建议是不要为了台账好看强行平衡。如果实际使用场景室内多就保留原有分布。模型见到更多室内样本在室内表现更好这才符合业务要求。把精力更多放在模型尺寸和训练轮次调参上。4. 基于YOLOv8的完整训练流程4.1 环境部署与显存规划用YOLOv8训练最基础的环境是Python 3.10、PyTorch 2.x、ultralytics 8.x。建议先创建一个虚拟环境避免依赖冲突conda create -n phone_yolo python3.10 conda activate phone_yolo pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118显存规划方面2800张图片、640输入尺寸用YOLOv8s加上batch_size32大概需要8G显存batch_size64需要12G。如果只有6G显存可以把batch降到16或者使用imgsz416。手机检测本身目标小降低输入尺寸会明显影响精度我更推荐保持640用小batch同时开启梯度累积。ultralytics里没有直接给gradient_accumulation参数可以通过设置batch8来模拟累积效果但训练时长会增加。4.2 修改data.yaml和超参建议前面已经写过data.yaml的完整内容。需要额外注意的是数据集的类别名names列表顺序必须和标注文件里的类别ID一一对应。单类数据集只有[phone]如果你在标注里顺手把其他类标成了1而names没加训练就会报错或者忽略。我建议在生成data.yaml后打印读出来检查一遍防止YAML解析时的缩进问题。超参方面前几轮训练可以用一个快速验证策略epochs10、imgsz640、batch16观察precise和recall是否能到0.8以上。如果10轮就有0.8说明数据质量没问题如果只有0.2先不要调参回去数据清洗。快速验证完再跑正式训练epochs设置在80到120之间。手机检测类别单一目标边界清晰100轮以内就会收敛超过150轮大概率过拟合。4.3 训练命令与关键参数讲解正式训练命令yolo detect train \ dataphone_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch32 \ workers8 \ patience15 \ device0 \ seed42 \ projectphone_train \ namerun1每个参数的意义说一下。epochs100是训练轮次imgsz640是输入尺寸batch32是批次大小影响显存和收敛平滑度workers8是数据加载线程数CPU核数够就设大点patience15是早停轮数验证指标连续15轮不提升就自动停止适合防止过拟合device0指定GPUseed42固定随机种子方便复现。训练日志里需要重点看的是val/box_loss、val/cls_loss、metrics/precision(B)、metrics/recall(B)、metrics/mAP50(B)这些指标。box_loss下降表示框越来越准recall上升表示漏检率降低。手机检测任务里我通常更关注recall因为业务上漏掉一个手机比误检一个非手机更严重。误检可以通过后处理过滤降低漏检只能靠模型自己提高。4.4 训练过程中的监控和权重选择训练完以后ultralytics会在phone_train/run1/weights/下生成best.pt和last.pt。best.pt是验证集指标最好的权重last.pt是最后一轮权重。除非特殊需求部署用best.pt。但要注意best.pt的指标是在验证集上选出来的如果你频繁做实验可能对验证集过拟合。所以最终汇报还是要留一个测试集。我一般会在训练过程中每隔10轮手动用验证集的一些难图跑一次推理看看效果和指标是否一致。有几次mAP很高但图片里手机被手挡住一半时检测不到这是因为训练集里这种遮挡样本太少了。发现问题就补充这类样本重新训练比调参更有用。训练过程中如果看到loss变成nan一般是梯度爆炸。YOLOv8新版本很少出现但如果你用自定义数据某些极端亮度图片可能会导致BN崩溃。热词里提到的“yolo训练中bn崩溃”多半是这个。解决方法是先把batch降下来然后检查是否存在全黑或全白的图片这类图片会导致BN统计量异常。我清洗数据时就剔除过两张纯黑夜间图一张是因为镜头盖没打开另一张是曝光严重不足这些图即使人眼也识别不了模型更学不会。5. 评估、部署与避坑清单5.1 读懂混淆矩阵和mAP之间的关系训练完成后ultralytics会在run目录下生成confusion_matrix.png、PR_curve.png、results.png等文件。混淆矩阵里会看到background column这是负样本误检。手机检测的混淆矩阵通常不大只有phone和background两列。看它时要注意热词里说的“混淆矩阵总合不唯一”这个现象源于YOLO绘制混淆矩阵时会按检测阈值过滤并且归一化方式可能选择行归一化或者列归一化。行相加不等于1或者列相加不等于1都很正常因为矩阵还包含未归一化的数值看趋势就好别纠结对角线之外的数字。mAP是综合指标mAP50表示IoU阈值0.5时的平均精度mAP50-95表示多个IoU阈值下精度的平均值。手机检测任务用mAP50做工程验收标准用mAP50-95衡量算法上限。原因是手机是刚性物体边界框比较规则mAP50-95较低通常是因为预测框和真实框的IoU不够高也就是边界框定位不准。手机占图面积小几个像素的偏移也会对IoU造成很大影响。想提高mAP50-95可以增大输入尺寸到768或896但训练时间会变长显存需求也更高。5.2 从best.pt到落地部署部署方面YOLOv8导出ONNX是常用操作yolo detect export modelbest.pt formatonnx imgsz640 halfTrue导出后的onnx可以用TensorRT或者OpenVINO加速。手机检测通常在边缘设备上运行比如Jetson Nano、RK3588甚至普通IPC平台。在整数推理时需要注意是否对框的坐标做了缩放补偿。很多坑不是模型精度不够而是导出时imgsz和部署时的预处理尺寸不一致导致框偏移。如果模型在白天表现好、夜间差可以通过摄像头参数调节或者图像预处理增强不一定重新训练。夜间监控通常是红外补光图像是灰度的而训练集是彩色图。这种情况下建议训练前把夜间图像统一转灰度并且在数据增强里加入灰度化。我试过在部署端直接对灰度图做三通道复制模型精度会下降3到5个点后来在训练集里额外加入30%的灰度增强样本问题就解决了。5.3 手机检测任务里最典型的五个坑第一个坑是标注框包含了手机屏幕光晕。屏幕在暗光环境下会有光晕光晕明显大于手机本体框进去后模型学到的边界不是物理边缘而是亮度过渡带。测试集里光源复杂时预测框会偏大。处理办法是标注时尽量框紧只框亮屏区域或者只框物理边缘。第二个坑是遮挡目标的漏检。手机被手挡住一半时模型预测置信度很低。解决思路之一是使用“可见部分”标注而不是完整手机这会改变任务定义但更符合检测可见物体的逻辑。另一个思路是增加遮挡合成增强把一些手机图片随机叠加到一个手部图片上。这类增强实现起来有点麻烦但对漏检帮助很大。第三个坑是正负样本不均衡。如果你的数据集中有很多背着书包的行人书包上有个金属拉链反光反光形状类似屏幕模型可能把拉链误检为手机。单纯增加负样本数量不如增加“容易混淆的负样本”数量。我在数据清洗时专门收集了一批反光物图片加入背景集但YOLO不支持直接指定负样本负样本是通过图片中没有目标、标注文件为空来体现的。你可以在一个文件夹里放背景图对应txt文件内容为空并在train列表里包含它们。第四个坑是数据量小导致的过拟合。训练集loss很低、验证集mAP波动大就是过拟合信号。可以增大Mosaic和Mixup概率、增加dropout参数、缩小模型尺寸。YOLOv8在训练命令里可以传dropout0.1之类虽然它对中间层dropout有争议但实测对稳定验证集有帮助。第五个坑是模型训练收敛后验证集指标很高但实际摄像头画面里非常差。这通常不是模型问题而是拍摄设备不同导致的图像域迁移。手机检测在室内大厅、教室这类场所不同摄像头的白平衡、锐度、压缩噪声差异很大。解决办法是在部署地址采集200张图放到原来训练集里再做一轮微调。这个操作不是数据造假而是垂直场景适配的常规做法。5.4 我使用这份数据集的心得训练过程和部署测试做下来我有两个很深的体会。第一个是手机检测的性能上限大多由数据决定而不是模型结构。拿YOLOv8n和YOLOv8s对比在2800张数据上两者验证集mAP50差距可能只有1到2个点。但把训练集里最差的200张清掉后提升幅度反而是2到3个点。清洗数据永远是最划算的投入。第二个是任何看起来合理的数据集都要自己验证一遍。哪怕是从别人那里下载的公开数据也必须过一遍可视化检查。我这次就是从几个来源合成数据时发现部分图片的标注框类别ID写错成1没有统一成0导致模型训练时类别混淆。这份2800张手机检测数据集作为起步足够扎实配合YOLOv8和合理的训练策略很快可以搭出一个能用的检测器。后续扩展方向也清晰增加不同国家的手机品牌外观数据、屏幕亮起与熄灭状态的双标注、手机与人的关系检测都是从这个基础上继续生长的枝芽。做数据集这件事没有捷径但每一步扎实走下去后面模型训练会非常顺。