ARTICLE DETAIL

资讯详情

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

手机检测数据集实战:2800张YOLO格式数据从训练到部署

手机检测数据集实战:2800张YOLO格式数据从训练到部署 1. 手机检测数据集到底解决什么问题1.1 从一次产线误检说起去年帮一个做手机回收分拣的朋友看他们线上的视觉系统场景很典型传送带上跑着各种型号的旧手机摄像头拍图后端判断有没有手机手机在哪个位置。他们最初用的是自己随手标的两百来张图训出来的模型结果上线第一天就翻车——深色手机放在深色传送带上模型直接当背景忽略了屏幕反光的机器又被重复框了两次。问题不在算法在数据。这就是手机检测数据集存在的意义。2800张YOLO格式的目标检测数据集核心价值不是图片多而是它把手机这个目标在各种真实工况下的形态差异都覆盖到了不同颜色、不同角度、不同光照、有无反光、单机与多机堆叠、手持与平放。对做目标检测的人来说数据集的质量直接决定模型上限算法只是逼近这个上限的工具。这个数据集适合谁三类人最该关注一是做3C回收、质检、仓储盘点的工程同学需要快速落地一个能用的手机检测模型二是刚学YOLO想找一个目标单一、标注干净的数据集练手的新手手机这个类别比行人、车辆好收敛得多三是做模型改进研究的人需要一个轻量但真实的基准集来验证注意力机制、损失函数改动是否有效。1.2 为什么是YOLO格式而不是别的目标检测的数据集标注格式五花八门COCO、VOC、YOLO各有各的圈子。这个数据集直接给YOLO格式背后是有取舍的。YOLO格式的标注是每张图对应一个txt文件每行是类别id 中心x 中心y 宽 高全部归一化到0到1之间。这种格式最大的好处是读取极快、解析极简训练时不需要像COCO那样先加载庞大的json再建索引。对于2800张这个量级YOLO格式意味着你可以直接把图片和标签丢进目录改个yaml就能开训。格式标注文件形态坐标表示训练读取速度适合场景YOLO每图一个txt归一化中心点宽高最快单阶段检测器训练VOC每图一个xml绝对左上右下中等传统框架、可视化COCO单个大json绝对左上宽高较慢多任务、分割、评测需要提醒的是YOLO格式本身不携带图片尺寸信息坐标是归一化的所以你在做数据增强比如马赛克拼接、随机缩放时不用担心坐标越界这是它比VOC省心的地方。但反过来如果你想把它转成COCO做实例分割就得自己补上图片宽高来反归一化这一步很多人第一次转格式时会算错。1.3 2800张这个规模意味着什么有人会问2800张够吗现在动辄几十万张的数据集满天飞。我的经验是对于单一类别、场景相对可控的检测任务2800张标注良好的图配合迁移学习和合理增强完全能训出一个可用的模型。判断依据是这样的YOLO系列在COCO上预训练后已经学到了通用的边缘、纹理、形状特征。你要做的只是让它把手机这个概念和已有特征对齐。单类别检测理论上几百张高质量图就能出效果2800张属于宽裕级别足够你划分训练验证测试还留出余量做交叉验证。真正决定成败的不是数量是分布。如果2800张里2700张都是白色手机正面平放那模型遇到黑色手机侧面就废了。所以拿到数据集第一件事不是急着训练是先做数据分布统计这个后面会详细讲怎么做。2. 拿到数据集先别急着训练数据体检怎么做2.1 目录结构与标签一致性检查YOLO数据集的标准结构通常长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml但实际拿到的数据集经常不规整。我见过最常见的问题是images里有图labels里没有对应的txt或者txt是空的表示负样本但很多人误以为是漏标。所以第一步必须写脚本核对。import os img_dir dataset/images/train lbl_dir dataset/labels/train imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} print(有图无标签:, imgs - lbls) print(有标签无图:, lbls - imgs) empty [f for f in os.listdir(lbl_dir) if os.path.getsize(os.path.join(lbl_dir, f)) 0] print(空标签数量:, len(empty))这段脚本跑完你心里就有底了。有图无标签的必须处理——要么补标要么删图绝不能留着否则训练时读取会报错或者被静默跳过导致实际训练集比你以为的少。空标签文件如果是刻意标注的负样本图里确实没手机那要保留它能让模型学会什么不是手机降低误检。注意空标签和缺失标签是两回事。空txt是有效负样本缺失txt是数据错误。很多人混为一谈把负样本删了结果模型变得过度敏感背景里一点纹理就框。2.2 用统计手段摸清数据分布体检的第二步是统计。我一般会算三个东西每张图的框数量分布、框的宽高比分布、框在画面中的位置热力图。import numpy as np import glob ratios, areas, counts [], [], [] for f in glob.glob(dataset/labels/train/*.txt): with open(f) as fp: lines fp.readlines() counts.append(len(lines)) for line in lines: _, _, _, w, h map(float, line.split()) ratios.append(w / h) areas.append(w * h) print(每图平均框数:, np.mean(counts)) print(宽高比 中位数:, np.median(ratios)) print(框面积占比 中位数:, np.median(areas))宽高比中位数能告诉你手机主要是竖着还是横着出现。如果中位数在0.45左右说明大部分是竖屏手机如果在2.2左右说明横放居多。这个信息直接影响你要不要做旋转增强。框面积占比中位数如果很小比如小于0.05说明手机在画面里普遍偏小那训练时输入分辨率就不能太低否则小目标直接糊掉。位置热力图更直观把每个框的中心点画到一张空白图上颜色越深说明该区域出现越频繁。如果热力图显示框几乎都集中在画面中央那说明拍摄时手机都被摆中间了这种数据训出来的模型对边缘目标会很迟钝需要靠随机裁剪增强来补。2.3 划分训练验证测试集的正确姿势很多人划分数据集就是随机切7:2:1。对于手机检测这种场景随机切有个隐患同一部手机、同一场景连拍的多张图可能被切到不同集合导致验证集和训练集高度相似验证指标虚高。正确做法是按场景或设备分组切分。比如你有10个拍摄场景那就让8个场景进训练1个进验证1个进测试。这样验证集才能真正反映模型在没见过的场景下的表现。如果数据集没提供场景信息退而求其次按图片文件名前缀或拍摄时间聚类后再切。import random from collections import defaultdict # 假设文件名前缀代表场景如 scene01_001.jpg groups defaultdict(list) for f in all_images: groups[f.split(_)[0]].append(f) scenes list(groups.keys()) random.shuffle(scenes) n len(scenes) train_s scenes[:int(n*0.7)] val_s scenes[int(n*0.7):int(n*0.85)] test_s scenes[int(n*0.85):]这样切出来的测试集才有说服力。我踩过的坑就是早期图省事随机切模型在验证集上mAP 0.95一上真实产线掉到0.6就是因为验证集和训练集撞场景了。3. 从零跑通一次手机检测训练3.1 环境搭建与预训练权重选择环境这块我推荐直接用Ultralytics的YOLOv8或YOLOv11pip装完就能跑对新手最友好。CUDA版本和PyTorch版本要对应这个坑每年都有人踩。pip install ultralytics # 验证环境 yolo checksyolo checks会打印出你的CUDA是否可用、PyTorch版本、显卡型号。如果显示CPU only那训练会慢到怀疑人生2800张图在CPU上跑100轮可能要一整天。预训练权重怎么选这是个高频问题。我的建议是权重参数量适用场景手机检测推荐度yolov8n3.2M边缘设备、实时高速度快yolov8s11.2M平衡最高性价比好yolov8m25.9M精度优先中数据量偏小时易过拟合yolov8l/x43M服务器低2800张喂不饱2800张这个量级我实测下来yolov8s是最优解。n版本精度差一点m及以上容易过拟合因为参数量相对数据量太大了。如果你最终要部署到手机端或嵌入式设备那就用n牺牲一点精度换速度。3.2 data.yaml的正确写法data.yaml是YOLO训练的入口配置写错了训练直接起不来。标准写法path: /home/user/dataset train: images/train val: images/val test: images/test names: 0: phone几个容易出错的点path用绝对路径最稳相对路径在不同工作目录下跑会找不到train和val是相对于path的路径names的key必须从0开始连续单类别就是只有0。如果你有多个类别比如phone、case、screen那就0、1、2依次排。提示改完yaml后先跑一次yolo train datadata.yaml epochs1做个冒烟测试确认数据能正常加载再开长训练。这一步能帮你提前发现90%的路径和格式问题。3.3 训练参数怎么调才不浪费显卡直接上命令yolo train modelyolov8s.pt datadata.yaml \ epochs150 imgsz640 batch16 \ lr00.01 lrf0.01 momentum0.937 \ weight_decay0.0005 warmup_epochs3 \ mosaic1.0 mixup0.1 degrees10.0 \ fliplr0.5 hsv_h0.015 hsv_s0.7 hsv_v0.4 \ patience30 projectruns namephone_det逐个解释关键参数背后的逻辑imgsz640是YOLO的默认输入尺寸。手机如果普遍偏小可以提到960但显存占用会翻倍batch要相应减小。我一般先用640跑一版看效果小目标漏检多再提分辨率。batch16取决于显存。8G显存跑yolov8s640大概能到1612G能到32。batch太小比如4会导致BN层统计不稳定训练loss抖动大这就是热词里说的bn崩溃的常见诱因之一。lr00.01配合lrf0.01表示初始学习率0.01最终衰减到0.0001。这个余弦衰减策略对YOLO很稳。如果你用大batch学习率可以适当放大。mosaic1.0是YOLO的招牌增强把4张图拼成1张极大丰富了目标的位置和尺度多样性。对手机检测特别有用因为拼接后手机会出现在各种奇怪位置模型不会只认画面中央。但注意训练最后10轮建议关掉mosaic设0让模型在真实分布上收尾这个技巧能稳定提升最终精度。mixup0.1是图像混合轻度使用即可太高会让手机和背景糊在一起反而有害。degrees10.0是随机旋转。手机检测里这个值别开太大因为手机有明确的方向性旋转90度后竖屏变横屏如果测试场景里手机方向固定过度旋转反而引入噪声。hsv_h/s/v是色彩抖动。手机外壳颜色多样适度增强能提升泛化但hsv_h0.015已经够用调太大会让红色手机变绿色语义都变了。3.4 训练过程监控与早停判断训练启动后重点盯三个指标box_loss、cls_loss、mAP50。正常情况下box_loss和cls_loss在前20轮快速下降之后缓慢收敛。如果loss震荡剧烈八成是学习率太大或batch太小。如果loss下降很慢甚至不降检查数据标注是否有问题——我遇到过标签坐标全写成绝对像素值没归一化的情况loss就是降不下去。mAP50在验证集上应该稳步上升。如果训练集mAP一直涨、验证集mAP停滞甚至下降那就是过拟合了这时候patience30会自动早停。2800张数据yolov8s一般80到120轮就收敛了150轮是留了余量。实操心得训练时开plotsTrue默认开跑完会生成混淆矩阵、PR曲线、训练曲线。混淆矩阵对单类别检测意义不大但PR曲线能告诉你模型在什么置信度下召回和精确的平衡点在哪这个对后面定推理阈值很关键。4. 推理部署与效果验证4.1 用验证集跑一次完整评估训练完别急着上生产先在测试集上跑评估yolo val modelruns/phone_det/weights/best.pt datadata.yaml splittest输出会给你mAP50、mAP50-95、precision、recall。手机检测这种单类别任务mAP50能到0.9以上算合格0.95以上算优秀。如果低于0.85回去看是漏检多还是误检多。漏检多recall低可能是小目标问题提分辨率或加更多小目标样本。误检多precision低可能是负样本不够或者置信度阈值设太低。4.2 推理阈值不是拍脑袋定的很多人推理时直接用默认conf0.25这是偷懒。正确做法是看PR曲线找到你业务能接受的平衡点。比如手机回收场景漏检一部手机的损失是少赚一笔误检的损失是机械臂空抓一次浪费时间。如果漏检代价更高那就把conf调低到0.15宁可多框几个如果误检代价高就调到0.4。from ultralytics import YOLO model YOLO(runs/phone_det/weights/best.pt) results model.predict( sourcetest_images/, conf0.3, iou0.5, saveTrue )iou0.5是NMS的阈值控制重叠框的合并。手机堆叠场景下如果两部手机挨得很近iou设太高会把它们合并成一个框设太低又会把一部手机拆成两个框。这个值要根据实际堆叠情况调我一般从0.5起步堆叠严重就降到0.4。4.3 部署到实际设备的性能考量如果最终要部署到边缘设备模型导出很关键# 导出ONNX yolo export modelbest.pt formatonnx opset12 # 导出TensorRTNVIDIA设备 yolo export modelbest.pt formatengine halfTruehalfTrue是FP16量化速度能提升近一倍精度损失通常不到1%。在V100这类卡上yolov8s640用TensorRT FP16能跑到几百FPS完全满足实时产线需求。如果部署到手机端可以导出TFLite或NCNN。但要注意导出后的模型输入尺寸、归一化方式必须和训练时一致否则精度会莫名其妙掉一大截。我见过有人训练用RGB、部署时忘了转通道顺序结果模型把手机全认成背景。5. 常见问题排查与避坑清单5.1 训练不收敛的几种典型原因现象可能原因排查方法解决loss不降标签未归一化检查txt值是否1重新归一化loss震荡学习率过大/batch过小看loss曲线降lr或加batchmAP为0类别id不匹配核对yaml的names修正类别索引验证集无图路径错误打印val路径改绝对路径BN崩溃batch1或数据异常看报错batch≥8清洗数据BN崩溃这个热词我特意说一下。BatchNorm在训练时依赖一个batch内的统计量如果batch太小比如1或2统计量方差极大running_mean和running_var会发散表现为loss突然变NaN。解决办法要么加大batch要么改用GroupNorm要么用梯度累积模拟大batch。5.2 数据增强过度的反效果增强是把双刃剑。我早期迷信增强越多越好mosaic、mixup、旋转、色彩全拉满结果模型在验证集上表现反而变差。原因是手机这个目标有强语义约束——它必须是矩形、有屏幕、有摄像头开孔。过度旋转和混合会破坏这些特征模型学到的是扭曲的手机不是真实的手机。我的经验配比是mosaic1.0训练前期、mixup0.1、degrees10、fliplr0.5、色彩抖动中等。训练最后10轮关掉mosaic和mixup让模型回归真实分布。这套组合在手机检测上很稳。5.3 小目标漏检的针对性优化如果测试时发现远处的小手机漏检严重有几个递进的优化手段第一提高输入分辨率。640提到960或1280小目标像素变多检测率明显上升代价是速度下降。第二调整anchor或改用anchor-free。YOLOv8本身是anchor-free的如果你用的是老版本YOLOv5可以重新聚类anchor让小尺寸anchor更匹配小手机。第三在数据层面补小目标样本。把大图裁成小图让手机相对变大或者专门收集远景拍摄的样本。第四推理时用切片推理SAHI把大图切成小块分别检测再合并对小目标效果显著代价是推理时间增加。5.4 模型改进的验证方法论热词里提到很多改进方向efficient head、transformer结合、mamba、蒸馏。我的建议是任何改进都要在同一个基准上对比否则没有意义。具体做法固定数据集划分、固定训练轮数、固定增强参数只改你要验证的那个模块。跑三次取平均因为深度学习训练有随机性单次结果可能是噪声。对比指标不只看mAP还要看参数量、FLOPs、推理速度。一个改进如果mAP涨了0.5%但速度慢了一倍在产线上未必划算。我个人的排序是数据质量 训练策略 模型结构改进。2800张数据把数据清洗好、增强调对比换个花哨的head带来的提升大得多。很多人一上来就改结构结果数据里一堆错标改什么都没用。6. 这个数据集还能怎么扩展手机检测只是起点。同一套标注流程和数据组织方式可以横向扩展到很多相关任务。做手机屏幕缺陷检测把类别从phone改成scratch、crack、stain标注逻辑完全一样只是框更小、更密集需要更高分辨率。做手机型号识别在检测框基础上加一个分类头先检测再分类或者直接用YOLO的多类别把每个型号当一个类。但型号多了类别不平衡会很严重需要重采样。做手机姿态估计检测框只能告诉你手机在哪姿态估计能告诉你手机是正面朝上还是反面朝上、倾斜多少度。这对机械臂抓取很关键可以在检测框内再跑一个关键点模型。做多目标跟踪如果场景是传送带上连续运动的手机检测加跟踪能统计流量、去重计数。YOLO检测加ByteTrack是成熟方案改造成本低。我实际做下来最省力的扩展路径是检测打底其他任务叠加。因为检测框是很多下游任务的基础先把检测做扎实后面加分类、加关键点、加跟踪都是增量工作不用推倒重来。最后分享一个我在数据标注上的小技巧标注手机时框要贴着手机边缘但不要切掉边缘。有些标注员为了框紧一点把边缘切了导致模型学到的手机比真实的小一圈推理时框总是偏小。正确的做法是框刚好包住手机外轮廓留一两个像素余量。这个细节看着小但对最终框的准确度影响很直接。
返回列表