多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

2948张图搞懂眼镜检测:YOLOv8训练全流程与避坑指南

2948张图搞懂眼镜检测:YOLOv8训练全流程与避坑指南 简介面向采用YOLO系列目标检测算法的研究者和实战开发者这份眼镜检测数据集提供了从标注到训练的一站式支撑。资源中同时包含YOLO与VOC两种标注格式分别存放于独立目录压缩包共2000个文件以XML标注文件为主整体大小约128MB便于按需取用配套的数据集配置文件可直接适配多版本YOLO免去繁琐的转换步骤。标签内容采用归一化坐标类别索引、中心点、宽高比例清晰规范有助于快速开展模型训练与验证数据集已经完成划分省去自行切分训练集和测试集的精力对于希望快速积累特定场景数据、验证检测算法或研究标注格式转换的开发者而言这份资源具有很高的实用价值。目前已有128人学习浏览适合中初级算法工程师及高校学生上手实践。1. 眼镜检测数据集2948张图够不够用怎么把.zip变成能上线的YOLO模型门禁人脸识别在用户摘下眼镜后直接翻车这已经不是个例而是所有做安防和驾驶员监控的人都会撞上的问题。眼镜检测数据集的价值就在这里它不关心你是谁只关心你脸上有没有眼镜把这个信息作为前置工序喂给下游人脸模型能大幅降低误识率。但这个标题里的“2948张图像带标签”到底意味着什么这个规模对YOLO训练来说是刚好够用、还是会欠拟合拆开这个zip之前先说结论2948张单类目标检测图属于中小规模数据集配上YOLOv8的预训练权重和合理的数据增强完全能训练出一个可用的眼镜检测模型但前提是标签格式正确、划分干净、训练参数不吃亏。这篇笔记就是带着你把这条链路走通适合刚入手YOLO目标检测、手上正好有一批眼镜图像却不知道怎么落地的从业者。2. 拆开“玻璃.zip”目录结构、标签格式与数据规模怎么看2.1 压缩包里应该有什么images与labels的对应关系拿到一个眼镜检测数据集压缩包我一般不会急着解压就跑训练。先看目录结构这决定了后续所有命令怎么写。常见做法是压缩包内部按YOLO惯例组织成两个顶层目录images和labels。images下放所有jpg或png原图labels下放与图片同名的txt标签文件。如果你的包里还有train.txt、val.txt这类清单文件那只是把划分结果提前给你了本质不影响训练脚本的读取方式。解压后用tree命令扫一眼心里先有个底。习惯用Python的也可以直接用os.walk统计文件数顺便验证图片和标签是否一一对应。这一步很关键——数据集翻车的第一大原因就是图片有2948张但txt标签少了十几张训练时YOLO会静默跳过没标签的图你浑然不觉直到验证集mAP异常低。unzip 眼镜检测数据集-2948张图像带标签-玻璃.zip -d ./glass_dataset find ./glass_dataset -type f | sed s/.*\.// | sort | uniq -c这条命令先解压到glass_dataset目录然后用find统计所有文件的后缀名分布。正常输出应该只有jpg/png和txt两类且txt数量与图像数量一致。如果看到.json或.xml说明标签不是原生YOLO格式后面要单独转换。2.2 读一个YOLO标签文件从txt坐标反推框的位置YOLO标签不是给人看的每行都是五个数字class x_center y_center width height全部归一化到0到1之间。比如0 0.5 0.4 0.2 0.3含义是类别0框中心在图像宽度的50%处、高度的40%处框宽占整张图宽度的20%框高占整张图高度的30%。用图像像素宽高乘回去就能得到真实边界框。我习惯先随机抽几个标签文件用脚本把归一化坐标还原成像素框并画到图上肉眼确认框是不是真的套住了眼镜。这是最省事的质检方式比看坐标数字可靠得多因为坐标格式错了数字再漂亮也没意义。import cv2 import os img_path glass_dataset/images/train/00001.jpg label_path glass_dataset/labels/train/00001.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): cls, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(check_label.jpg, img)这段代码把第一个标签文件的内容画回原图。注意cv2.rectangle画出来的框如果大面积偏出图像外或者框小到只有一个点基本可以判断标注有问题。还有一种常见错误是width和height写反了画出来的框会是一条竖线或横线这种情况多见于手工标注后没按YOLO格式导出而是自己写脚本转出来的数据。2.3 2948张是什么规模对照常见检测数据集的量级2948张图听起来不少但放在目标检测领域里属于中小规模。拿几个从业者常接触的数据集做参照CCPD车牌检测数据集有5万张以上的车辆图像BDD100K自动驾驶数据集有10万张级图像HRSC2016舰船检测数据集只有约1000张图像但每张里多个目标。眼镜检测通常每张图中只有一个主体目标——人脸且眼镜区域相对较小这个任务本身并不复杂2948张单类标注足够让YOLOv8s收敛到可用的程度。规模不是唯一指标图像多样性才是。同样是2948张如果全部是同一个角度、同一个光照下的人脸模型学到的只是特定场景的眼镜外观换到侧脸或逆光就直接漏检。所以拿到数据集后别急着训练先看一下图像的平均分辨率、光照变化、眼镜类型多样性。如果发现图像基本是自拍角度自己补拍一些不同角度和场景的图再训练比依赖这一个zip更稳妥。数据集规模任务特点眼镜检测本文2948张单类小目标场景单一CCPD约15万张车牌检测多角度多光线HRSC2016约1000张舰船检测遥感俯视图BDD100K10万张级驾驶场景多任务3. 把原始标注变成可训练集数据划分、YAML配置与标签体检3.1 训练/验证集划分随机切还是按来源切很多压缩包自带划分好的train和val目录这时直接用即可。但如果你拿到的是混在一起的images和labels就需要自己切分。我一般按8:2的比例切训练集2360张验证集588张。这里有个容易被忽略的细节如果同一个人的多张照片出现在训练集和验证集里验证结果会虚高因为模型“见过”这个人。按文件所属的人物ID或拍摄批次来切分才更真实但多数公开数据集不会按人物分目录所以随机切分时要接受这个偏差并知道最终线上表现可能比验证集低几个点。import os import random import shutil base glass_dataset images os.listdir(f{base}/images) random.seed(42) random.shuffle(images) split int(len(images) * 0.8) for i, img in enumerate(images): name os.path.splitext(img)[0] sub train if i split else val src_img f{base}/images/{img} src_lbl f{base}/labels/{name}.txt os.makedirs(f{base}/images/{sub}, exist_okTrue) os.makedirs(f{base}/labels/{sub}, exist_okTrue) shutil.move(src_img, f{base}/images/{sub}/{img}) shutil.move(src_lbl, f{base}/labels/{sub}/{name}.txt)这段脚本先随机打乱图像列表再按80%比例移动图片和同名标签到train或val子目录。shutil.move在跨目录时会自动创建目标路径配合exist_okTrue。注意每次运行前要把目录恢复到未划分状态否则重复移动会覆盖之前的结果。seed固定为42是为了让划分可复现方便别人复现你的训练结果。3.2 写data.yamlclass names与路径的两个坑YOLOv8训练时不再读单独的xxx.data文件而是读一个YAML配置文件。里面最关键的三项是train路径、val路径和names列表。路径建议写绝对路径因为不同版本的YOLO对相对路径的解析规则不一样你用相对路径跑通了换个环境大概率报错。nc是类别数眼镜检测只有一个类别就写1。另一个坑是names里的类别名尽量用英文小写。这个名称会直接出现在可视化推理图和验证结果的图表里用中文虽然能跑但在终端输出和导出onnx后会遇到编码问题。别在这个地方给后面部署埋雷。train: /home/user/glass_dataset/images/train val: /home/user/glass_dataset/images/val nc: 1 names: [glasses]如果你的压缩包里自带的是train/images这种嵌套结构YAML里的路径要跟着调整。这里最容易犯的错是把images/train写成train/images训练一启动就报FileNotFoundError。确认路径最快的方式是在命令行里先ls一下目标目录能列出来再写进YAML。3.3 训练前做一轮标签体检越界、空文件与重复标签标签文件不是存在就行里面内容可能藏着各种问题。我每次训练前都会跑一个体检脚本检查三件事坐标是否全部在0到1之间、有没有空文件、有没有同一张图对应多个标签文件。坐标越界通常是标注工具导出时没裁剪产生的空文件是标注中断留下的多标签文件则是解压或脚本copy时生成的重复产物。这些问题不清理轻则模型学歪重则训练直接报错。import os label_dirs [glass_dataset/labels/train, glass_dataset/labels/val] for d in label_dirs: for f in os.listdir(d): path os.path.join(d, f) with open(path, r) as fr: lines [l.strip() for l in fr.readlines() if l.strip()] if not lines: print(f空标签: {path}) continue for l in lines: parts l.split() if len(parts) ! 5: print(f字段数错误: {path} - {l}) continue try: vals list(map(float, parts)) except ValueError: print(f非数字内容: {path} - {l}) continue if vals[1] 0 or vals[1] 1 or vals[2] 0 or vals[2] 1: print(f中心点越界: {path} - {l})这段脚本遍历train和val目录下的所有标签逐行检查五个字段的完整性和数值范围。字段数不对说明标签行里多了逗号或缺少类别号中心点越界说明归一化出错。发现后直接用编辑器修正或删除对应文件不要写脚本“顺手修”先手工看几条搞清楚问题根源再批量处理否则可能把好数据也改了。4. 用YOLOv8跑通眼镜检测训练命令、参数与训练日志判读4.1 最小训练命令与三个必调参数YOLOv8是当前把自定义数据集跑起来最顺的版本没有之一。安装ultralytics之后一条命令就能开始训练。我第一次跑眼镜检测用的就是下面这条能跑通但不一定是最优先让整个链路转起来最重要。pip install ultralytics yolo detect train dataglass_dataset/data.yaml modelyolov8s.pt epochs100 imgsz640 batch16这条命令的四个核心参数分别是data指定上面写好的YAMLmodel指定预训练权重epochs是训练轮数imgsz是输入图像尺寸。batch16要根据显存调整显存只有8GB就把batch降到8或者4。训练开始后看到Github字样的进度条不要慌那是ultralytics在自动检查更新不影响本地训练。三个必调参数里第一个是model。别选yolov8l或yolov8x2948张图配大模型会严重过拟合训练集loss降到0.01验证集mAP却上不去。yolov8s是平衡点。第二个是imgsz。眼镜相对人脸是小目标但缩到640后仍然有足够的像素容纳镜框特征用640起步如果你的原图普遍大于2000像素可以试1280但显存和时间成本会翻几倍。第三个是patience它控制早停轮数默认100对于小数据集建议改成30避免训练到最后几十轮一直在原地打转浪费算力。4.2 训练日志里看什么loss下降和mAP曲线的真实含义训练开始后终端会实时打印box_loss、cls_loss、dfl_loss和mAP50。不要被loss的绝对值吓住YOLOv8的loss值本身就很小0.02和0.01之间的差距在视觉上可能毫无区别。更该关注的是曲线趋势前20轮loss快速下降后面缓慢收敛这是正常的如果loss前几轮不降反升大概率是学习率过高或标签有问题。训练结束后runs/detect/train目录下会生成results.png、confusion_matrix.png和val_batch*.jpg。我只看三样东西一是results.png里mAP50曲线是否在后期还能缓慢上升能说明模型还在学新东西二是val_batch图里框有没有明显错位框大了一圈或偏到鼻梁上都说明标注本身有误差三是confusion_matrix.png里的background占比如果背景被误检成眼镜的比例过高说明置信度阈值需要调高。4.3 显存不够时的降级配置很多开发者的机器只有一张8GB显存的显卡。这种情况不必换机器也不需要买云GPU调整几个参数就能跑起来。首先把batch降到8代价是训练速度变慢但收敛效果基本不变。其次用yolov8n.pt替代s版本模型参数量约为s版的三分之一显存占用直接减半。最后把imgsz降到544或480推理分辨率降低会损失一些小目标的检测精度但眼镜这种尺寸的目标在480下仍然可辨。yolo detect train dataglass_dataset/data.yaml modelyolov8n.pt epochs100 imgsz512 batch8 patience30这套组合拳下来4GB显存也能完成眼镜检测训练。注意imgsz建议选32的倍数YOLOv8在模型内部会把输入缩放到最近的stride倍数非32倍数虽然不会报错但会引入额外的resize开销。另外设了patience30后训练可能会在80轮就提前结束这是早停机制在起作用不是报错别等不到100轮就以为训练中断了。5. 眼镜检测训练常见的坑从loss不降到测试框全乱5.1 标签文件与图像文件名对不上现象训练能跑完但验证集mAP50只有0.1不到可视化里框的位置与眼镜毫无关系。检查标签目录发现txt文件名与jpg文件名不匹配比如图片叫00001.jpg标签叫00001.txt.txt。原因很多标注工具导出时会在原文件名后追加.txt再次打包时如果脚本对后缀处理不当就会变成.txt.txt。YOLO读取标签时按图片名去找对应txt找不到就跳过这张图的训练导致实际有效样本远低于2948张。解决写一个脚本批量重命名把*.txt.txt统一改回.txt顺手统计一下有效标签对的数量确认是否等于2948。find glass_dataset/labels -name *.txt.txt -exec bash -c mv $0 ${0%.txt} {} \;这条命令只去掉多余的.txt后缀保留主文件名。执行后重新数一遍labels目录文件数和images目录对比。如果还有缺失检查是不是每张图都有标签缺失的就从训练集里剔除不要留着一张没标签的图让YOLO静默跳过。5.2 小目标眼镜框被过滤掉现象训练时mAP50始终在0.2到0.3之间徘徊查看val_batch发现远处的脸根本没被检测到近处的脸框出来了但框明显偏大把整个上半脸都框了进去。原因YOLOv8在训练阶段会对标签框做尺寸过滤small_object相关参数会丢弃过小的标注框。如果原图是1080p的多人合照单个人脸宽度只占全图的5%左右镜框更小训练时这些标签很可能直接被当成噪声丢弃。另一方面模型本身对密集小目标的学习能力有限imgsz640下眼镜区域可能只有十几个像素。解决把imgsz提高到960或1280同时检查标签中是否有宽高小于0.03的框有的话手动确认是否真的标注了微小眼镜。5.3 类别不均衡戴眼镜远多于不戴眼镜现象模型训练完成后对戴眼镜的测试图表现优异但连拍几组摘掉眼镜的照片模型仍然在鼻梁位置画框误检率高得离谱。原因如果这个数据集的标注语义是“脸上有没有眼镜”的二分类那戴眼镜和不戴眼镜的比例可能严重失衡。我见过一个包里戴眼镜样本占95%不戴眼镜只占5%模型学到的是“人脸中心区域就是眼镜”而不是真正区分眼镜外观。解决先看类别分布如果比例低于1比5需要补充不戴眼镜的图像并保证这些图像也有标注文件且标注为空文件或单独标记为1类别。另一种做法是只把“眼镜”作为单类目标检测不做戴与不戴的二分类把判断交给下游逻辑这样就不存在类别不平衡问题。5.4 数据增强过度把镜框特征弄丢现象训练集loss正常下降但验证集mAP50在第30轮后开始回落出现过拟合迹象。查看增强后的训练样本发现镜框在旋转和透视变换后已严重变形。原因YOLOv8默认开启hsv_h、hsv_s、hsv_v和degrees等增强参数对自然场景目标有效但眼镜是精细的小目标过大的旋转角度和色域扭曲会让镜框边缘特征被破坏。尤其当原图本身就带角度时增强后的图可能已看不出是眼镜。解决训练时显式调低增强强度关闭或减小与几何变换相关的参数。yolo detect train dataglass_dataset/data.yaml modelyolov8s.pt \ epochs100 imgsz640 batch16 \ degrees10 hsv_h0.01 hsv_s0.5 hsv_v0.3 fliplr0.5这里把旋转角度限制在正负10度色相偏移降到0.01饱和度和明度偏移也压制到较低水平。翻转保留0.5因为眼镜左右对称水平翻转是安全且有效的增强。如果发现增强后的效果仍不理想可以再把mosaic关掉因为四张图拼接产生的马赛克边界容易切断镜框轮廓。5.5 预训练权重与目标分布不匹配现象从头训练yolov8s.pt却把model参数误设成yolov8s.yaml训练速度极慢前50轮mAP还是0。原因.yaml是模型结构定义不带预训练权重.pt才是COCO上预训练好的权重。用.yaml启动相当于从随机初始化开始训练2948张图完全不足以让模型从头收敛。COCO数据集本身包含眼镜这个类别虽然检测效果不佳但特征提取网络已经学好了边缘和纹理信息迁移学习能在几十轮内收敛。解决确认命令里写的是yolov8s.pt或yolov8n.pt不要写.yaml。如果你已经用.yaml训练到一半别继续等立刻中断换成预训练权重重新开始早停会帮你省下时间。6. 用验证集算出模型真实水平mAP、单图推理与置信度阈值选取训练结束后下一步不是直接上线而是把模型喊出来“考试”。跑一遍验证集拿到mAP50和mAP50-95两个数字。mAP50要求预测框与真实框的IoU大于0.5才算命中眼镜检测这类小目标任务mAP50达到0.85以上基本够用mAP50-95是更严格的指标从0.5到0.95每隔0.05算一次取平均它更看重框的定位精度。小目标检测里mAP50-95通常会比mAP50低20个点以上不要因此怀疑模型坏了。yolo detect val dataglass_dataset/data.yaml modelruns/detect/train/weights/best.pt这个命令输出的mAP50和mAP50-95就是模型的最终成绩单。接着用predict跑几张训练时没见过、但有代表性的测试图比如侧脸、逆光、墨镜混入的图看看框的稳定度。这里有个我踩过多次的坑置信度阈值默认是0.25如果测试时发现很多误检框把阈值调到0.45误检会明显减少但真正的难例也可能漏掉。调阈值没有统一标准关键看你的业务场景是容忍漏检还是容忍误检。yolo detect predict modelruns/detect/train/weights/best.pt \ sourcetest_images/ conf0.4 saveTrue给conf0.4是让模型只输出置信度超过0.4的框如果保存的图片里框仍然零散再继续往上加。我习惯训练完先挑十张最难的推理图看一遍而不是只看mAP数字。数字再漂亮一张逆光下的漏检就足以让业务方炸毛。把推理结果和原图叠在一起看比盯着终端输出有效得多。眼镜检测这个方向本身不复杂难的是把数据整理干净、把参数调到匹配场景这几步走通了2948张图也能成为上线模型的底子。希望这些步骤对你有用。本文还有配套的精品资源点击获取
返回列表