ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

2800张图训练YOLO手机检测模型:数据集构建与部署实战

2800张图训练YOLO手机检测模型:数据集构建与部署实战 前阵子接了个边缘终端上的违规行为识别项目要求从摄像头画面里判断有没有人正在使用手机。我花了两周时间整理出一套2800张的YOLO目标检测数据集把“手机”当作唯一检测类别跑通了从采集、标注、训练到部署的完整链路。这篇文章就是记录我在这套手机检测数据集上的真实操作包括数据怎么筛、标注怎么标、YOLO怎么调参、踩了哪些坑给后面做同类检测任务的朋友留一份能直接抄作业的参考。1. 项目背景与数据集定位1.1 这个数据集到底解决什么问题手机检测并不是一个新课题但它的应用场景非常分散驾驶分心检测、考场违规行为分析、厂房安全生产、银行柜台操作合规检查甚至店面收银时有没有偷偷看手机这些需求都会落到同一个基础能力上——从画面里把“手机”这个物体准确框出来。很多人一上来就想着做人脸识别、做行为分析实际上一个稳定的手机检测器反而是后续所有业务逻辑的底座。先检测到手机再去判断是“拿着打电话”还是“放在桌面上”这样把问题分层解决每一步都会简单很多。2800张图像属于典型的小规模目标检测数据集。对单个类别来说如果场景足够集中这个量级完全够用。我这次的目标也很明确单类手机检测实时性要求高部署设备是边缘盒子最终选择YOLO系列模型。YOLO在工业界生态成熟预训练权重丰富训练和部署工具链完整是这类项目的优选。1.2 数据集结构与标注规范数据集遵循YOLO标准的目录组织方式。这是网上开源的通用习惯也是我自己的记录方式mobile_phone_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── phone.yaml图像和标签一一对应标签文件名与图像文件名保持一致后缀由.jpg换成.txt。每张图像配套的txt文件内容每行代表一个标注框0 0.5325 0.4812 0.1187 0.1650这里五个数字分别是类别ID因为是单类所以都是0、归一化后的中心点x坐标、中心点y坐标、归一化后的宽和高。所谓归一化就是除以图像自身的宽度和高度。这个格式是从YOLOv5开始就一直沿用的YOLOv8也完全兼容。我切分数据集时用了2200张做训练400张做验证200张做测试比例大约在8比1.5比0.5附近。单类检测任务这个切分足够不需要过度留测试集重要的是验证集的分布要跟真实场景接近。如果后面继续迭代还可以用这200张测试图作为回归基线保证每次训练改参数后不会出现“验证集涨点实测变差”的假象。标注范围也是一开始就要定义清楚的。我的规则是标注手机机身完整的外接矩形如果手掌大面积遮挡只标可见部分的框手机屏幕朝向镜头时按屏幕区域标侧边朝向镜头时按整个机身轮廓标。这样既保证统一性又避免模型被手指干扰。所有标注框都用矩形不用多边形因为YOLO原生矩形输出最简单也足够下游任务使用。2. 数据集的构建与处理2.1 图像来源与筛选规则数据集的素材来源我大概分了三类自己采集的监控场景截图室内为主有自然光和灯光。合作方提供的脱敏视频帧覆盖不同摄像头角度、高度、距离。公开发布的数据集和自己拍摄的补拍图用于补充极端角度和暗光条件。整个采集过程中最重要的一条规则就是“宁缺毋滥”。刚开始我拉了几千张素材但直接能用的不到一半。筛选时我会按这几个标准做初筛模糊的图像直接剔除。监控画面如果带着动态模糊哪怕人眼能看出是手机训练模型也不容易学。手机在画面中如果小于20×20像素先不做标注留到后面单独做小目标增强再补充。反过来说如果所有图像都是手机占据半张画面那模型只能学会检测“大的手机”一旦部署到真正的监控画面小目标的漏检率会非常难看。我最终保留的2800张图里手机框宽或高小于64像素的样本占比大约25%中尺寸占55%大尺寸占20%。实际训练下来这个比例对监控场景很友好。筛选时还要注意场景多样性。同一个办公桌的截图放几千张毫无意义模型很快就过拟合了。我尽量保证光源变化、背景变化、肤色和衣着变化都覆盖到。尤其是肤色人手和手机在颜色上有时很接近如果训练集里全是同一肤色模型很容易把肤色区域一并召回产生误检。2.2 标注工具与格式转换标注工具我用过LabelImg和CVAT。像2800张单类数据LabelImg完全够界面简单按着框就行。CVAT适合多人协作和视频标注如果是团队做项目建议直接用CVAT。工具不重要重要的是标注质量。我自己的标注流程是先由一个人把全部图上完标再由第二个人抽查20%重点看三类问题——遮挡边界是否规范、小目标是否漏标、框是否明显过大或过小。单类检测的标注一致性比类别数量更影响训练效果。因为模型本质上是在回归标签给出的边界如果同样的手机在不同图上框得忽大忽小loss很容易震荡。标注完通常导出成VOC XML格式再转成YOLO txt。这个转换脚本网上很多我贴一下自己常用的版本import os import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height 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] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) class_map {phone: 0}转换完我会写一个检查脚本把所有txt读一遍看有没有类别ID越界、归一化坐标超出[0,1]范围、框宽高为负数等问题。这类低级错误会在训练时直接导致数据加载异常或者让loss曲线突然乱跳。2.3 数据增强策略单类检测任务的数据增强重点应放在让模型适应真实场景的干扰上。我用了很多YOLO训练器自带的多尺度增强在YOLOv8里就是那些hsv_h、hsv_s、degrees、translate、scale、mosaic之类的参数。实测下来最有用的是mosaic增强把4张图拼成1张有效增加了每张图像内的背景复杂度也让模型学会了在一张图里处理多个目标尤其适合手机这种经常出现“一屏多手机”的场景。随机上下翻转监控视角偶尔有俯仰差异翻转能缓解视点过拟合。轻微旋转和缩放摄像头安装角度偏差在几度范围内实测超过15度的旋转容易把手机长条形特征破坏损失很大。光照扰动配合hsv_h和hsv_s的参数模拟不同屏幕亮度和环境暖冷色差。我没有直接用外部图像做离线增强而是在训练时开启在线增强。原因是离线增强会让硬盘膨胀好几倍而且在线增强每个epoch都会产生新组合相当于变相获得了更多有效样本。实际调参时我用过一组相对保守的增强参数hsv_h: 0.015 hsv_s: 0.6 hsv_v: 0.5 degrees: 8.0 translate: 0.1 scale: 0.4 shear: 2.0 perspective: 0.0005 flipud: 0.5 fliplr: 0.5 mosaic: 1.0 mixup: 0.1这组参数的目的不是把图像变得花里胡哨而是让模型在室内光照和摄像头小幅度偏移下依然稳定。如果场景里有明显的鱼眼镜头或者超广角畸变这些参数还不够可能需要额外做畸变矫正。3. 基于YOLO的训练实战3.1 模型选型与预训练权重手机检测任务不是比赛刷榜不需要上YOLOv8x或YOLOv5x这种大模型。边缘盒子的算力有限我选择的是YOLOv8s。如果你的部署环境更吃紧YOLOv8n也能跑数据量小的时候训练起来更稳如果算力相对充裕YOLOv8m的精度比s高不少但推理时间也会相应上升。预训练权重是绕不开的。最好直接用YOLO官方在COCO上训练好的权重做迁移学习。这里有个核心逻辑COCO数据集中本身有“cell phone”类别模型已经见过手机的大致特征即使养老模型只认识其他物体它的浅层特征边缘、纹理、颜色也能迁移过来。我训练的时候就是用了YOLOv8s在COCO上预训练的yolov8s.pt作为起点这样即使只有2800张图收敛也很快。如果下载比较慢可以找国内的开源镜像站只要确保权重文件的哈希值和官方一致就行不要随便下来源不明的“预训练模型”那种文件被加入广告插件或错误参数的风险非常高甚至可能无法正常加载。数据集配置文件phone.yaml也是训练前必须准备好的path: /path/to/mobile_phone_dataset train: images/train val: images/val test: images/test names: 0: phone写好这个文件之后用写绝对路径的方式。相对路径在单机训练时容易出问题尤其是如果项目目录被移动过YOLO按相对路径找不到图片会静默跳过该样本最后训练集实际数量少了一截你还不一定察觉。3.2 超参数设置与训练过程我自己的训练命令用的是ultralytics接口方便做实验记录from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( dataphone.yaml, epochs150, imgsz640, batch16, lr00.01, lrf0.01, warmup_epochs3, patience20, augmentTrue, seed42, )这几个参数并不是拍脑袋定的。imgsz640是精度和速度的平衡点再抬高到960对手机小目标有帮助但推理耗时也接近翻倍边缘盒子上不太划算。batch16取决于显存如果你只有12G显存这个值可能要降到8甚至4。lr00.01是YOLOv8的默认学习率也是COCO预训练权重继续迁移微调的一个常见起点。如果是从头训练自己的数据集学习率适度降低到0.005会更稳。warmup_epochs3保证了前几个epoch用较小的学习率让预训练权重先适应新数据集的标签分布避免一开始出现NaN或梯度爆炸。训练过程中loss曲线的形态比绝对数值更值得关注。我观察到box_loss在前30个epoch下降明显之后进入平台期而cls_loss下降比较平滑。如果看到cls_loss出现很大的尖峰大概率是某个batch里包含了大量错标样本这时我会暂停训练检查对应图像。YOLOv8里我也开启了ampTrue混合精度训练在部分显卡上可以明显加快训练注意如果你的卡是旧架构可能会出现“损失不降”的现象那就把amp关掉。3.3 训练中的两个坑BN崩溃和混淆矩阵异常训练过程中最让我头大的是“BN崩溃”。所谓BN崩溃指的是Batch Normalization层的滑动均值和方差在某个batch上被极端值搅乱表现为loss突然跳到NaN整个模型的权重直接废掉。触发BN崩溃的原因通常有两个。一是学习率太大尤其是训练初期warmup不足二是某个batch里混入了一整批异常样本比如连续多张图都是同一片纯色背景没有手机。我经历过一次后解决方案是降低初始学习率到0.005。调大warmup_epochs到5。在数据加载器中把shuffleTrue保持开启避免同一个文件夹里的连续帧扎堆出现。如果已经NaN不要尝试加载上次的checkpoint继续跑因为BN参数已经污染最好的方法是回到崩溃前的epoch重新开始。另一个很多人问的“混淆矩阵总合不唯一”也很典型。训练完做验证时YOLO给出的混淆矩阵每一行加起来的数字跟对应类别的样本数不对。这个问题不是矩阵计算出错而是标签文件和图像出现了错位。我在排查时发现自动标注脚本把某些txt文件写在了同名但不同尺寸的图像上导致归一化坐标虽然在[0,1]范围内但实际目标位置错得离谱。另外如果标签文件里有多余的空行或空格某些解析逻辑会把行首空字符串当成一个不存在的目标也会让混淆矩阵的统计出现偏差。修复方式很简单写一个小脚本清理所有标签文件去掉空行按空格切分后校验必须是5个浮点数任何一行异常就打印出文件名。跑完一遍之后混淆矩阵就恢复正常了。4. 模型评估与效果对比4.1 评价指标怎么看目标检测的常规评价指标是precision、recall、mAP50和mAP50-95。手机检测是单类任务所以这些指标都只对“phone”这一个类别计算。我一般先看验证集上的mAP50它衡量的是预测框跟真实框的IoU超过0.5时是否算对。在这个数据上训练150个epoch之后mAP50稳定在0.925左右mAP50-95在0.66附近。mAP50容易高因为手机这种物体特征明确边界清晰只要不严重遮挡框得都比较准。mAP50-95对定位精度更敏感提升难度大0.66对单类小目标数据集来说已经不错了。还要对照precision和recall一起看。手机检测业务里recall低会导致漏报比如有人确实在用手机但模型没框出来precision低会导致误报比如把充电宝和遥控器当成手机。我调参时会把confidence阈值尽量留在默认的0.25附近然后看PR曲线。如果曲线整体往右上角凸模型可用性就高。用一张表格记录我的验证结果指标数值mAP500.925mAP50-950.661precision0.934recall0.906这个pipeline的单类性能已经能满足轻提醒类的应用不需要继续堆模型规模。4.2 手机小目标的专项优化手机检测最容易翻车的场景是“小目标”监控画面里手机可能只有几十个像素。我第一版训练完测了25%小目标样本召回率只有0.5出头非常惨。后来做了几个针对性优化。第一个是提高输入分辨率。训练和推理时把imgsz从640提高到960小目标的像素数量相当于增加了1.5倍召回率立刻上涨。代价是推理时长增加约60%在边缘盒子上需要权衡。另一个常用手段是SAHI切片推理把大图切成若干小块每个小块分别检测再合并结果。这个方案能做但部署复杂度高我最终只是在测试时用来对标。第二个是重新聚类anchor。YOLO默认anchor是基于COCO统计的COCO图像里的手机通常占画面比例偏大。我用自己的训练集跑k-means重新聚类统计出适合2800张图的anchor尺寸手动写进模型配置里。这个操作对YOLOv8的anchor-free模式不一定有明显收益但对YOLOv5这种anchor-based模型是真的有效。第三是针对性补数据。我在测试集的漏检样本里发现大部分漏检手机处于暗光或者强逆光状态于是专门补拍了一批暗光图加入了约300张比例控制在10%左右。补数据之后小目标的漏检率降幅很大遇到复杂光照时的稳定性也好了很多。5. 常见问题与排查实录5.1 漏检与误检处理漏检的主要成因是目标太小、遮挡太严重、光照太极端。处理漏检一方面要增加对应难例样本在数据集里的占比另一方面要结合推理时的手段比如稍微降低置信度阈值让模型更激进地预测。但阈值降太多会带来误检增多所以我会在PR曲线上找一个平衡点。误检则更有意思。手机误报的高频对象是遥控器、充电宝、对讲机这类电子产品在形状、颜色、甚至屏幕反光上都像手机。对策不是单纯增加“非手机”负样本而是把负样本以背景图的方式硬加到训练集中。YOLO数据加载时会从背景图里随机裁剪但不会产生梯度去惩罚“框出背景”的行为。更有效的办法是给负样本图标注一个“ignore”区域或者干脆把图放在验证集里用confidence校准来压掉误检。我踩过的坑是训练时把所有负样本直接丢进训练集而不标任何框结果模型不但没学好反而把无目标的图片视为“背景类”干扰了正样本学习。正确做法是背景图只参与数据加载的随机抽样混合比例控制在10%以内不要单独大量堆。5.2 数据隐私与伦理边线做监控下的手机检测数据隐私是一个必须反复强调的问题。我用的素材都经过授权和脱敏处理人脸区域统一打码只保留手部和手机区域。涉及真实客户现场的视频全部在本地处理不能上传到外部服务器。部署端到端时也要在算法流程里加入区域屏蔽功能可以设置只对指定ROI区域检测其他区域不做推理从技术上限制隐私采集范围。手机检测本身只是一个工具不能用来做侵犯个人隐私的事。我建议开发者在做类似数据集时提前确认数据授权范围并在最终产品中明确用途。5.3 迁移到其他检测场景的扩展这套2800张的手机检测数据集同样可以作为基础迁移到手部动作识别、行人使用手机行为分析等场景。如果有更丰富的算力也可以尝试YOLO结合transformer的多模态方案但不是所有场景都需要。我做这套数据集最大的收获是小规模单类检测项目关键不在于模型多花哨而在于数据和标注策略跟真实部署场景匹配。最后分享一个自己踩过的小技巧做手机检测数据集时不要全用中大型手机图一定要留出20%到30%的样本是画面里很小的手机。我第一版就是因为小目标太少训练出来的精确率看着很高一部署到现场漏检全暴露了。后来把训练集中小目标占比提到30%召回率立刻改善了。这个比例直接决定落地效果这个坑我已经替你踩过了。
返回列表