
简介这是一份面向人脸检测算法训练与验证的WiderFace子集资源尤其适合小目标人脸检测场景如远距离监控、人群密集场景下的微小人脸识别。数据集共7906张jpg图片每张均配有对应的Pascal VOC格式xml标注与YOLO格式txt标注标注类别仅含face一类累计标注框达180744个全部由labelImg工具按矩形框规则完成标注准确合理。压缩包为7z格式内含2000个文件其中1999个xml标注文件与1个使用说明txt整体约872.3MB。该数据集的核心特点是每张图片至少包含一个面积小于3500像素的边界框能有效检验模型对小尺度人脸的召回能力同时也可用于分析小脸易误检的难点。目前已有840人学习下载适合从事人脸检测研究、模型训练与算法对比的开发者及学生使用可作为小目标检测任务的训练与评测素材。1. 从一张 7906 张的 widerface 小目标人脸包说起如果你正在做人脸检测尤其是监控、门禁、课堂考勤这类画面里人脸只占几十个像素的场景那你大概率绕不开 widerface 这个数据集。这次要拆的是一个很具体的包widerface 人脸检测数据集A 小目标子集VOC 和 YOLO 两种标注格式7906 张图1 个类别。它解决的不是“从零训练一个通用人脸模型”而是“我手上有个 YOLO 训练流程想快速喂进一批小目标人脸数据验证我的 anchor、输入分辨率和数据增强到底行不行”。适合谁适合已经跑通过一次 YOLO 训练、知道data.yaml怎么写、但被 widerface 原始标注格式劝退的人也适合做边缘端人脸检测、需要评估小目标召回率的工程师。7906 张不算大但胜在格式已经转好省掉最烦的标注转换环节。下面我按“先搞懂它是什么 → 怎么接进 YOLO → 参数怎么调 → 坑在哪”的顺序讲透。2. widerface 的 A 子集与 VOC/YOLO 双格式到底意味着什么2.1 widerface 的划分逻辑与 A 子集定位widerface 原始数据集按难度分成 Easy、Medium、Hard 三个子集划分依据是人脸尺寸、遮挡程度、姿态变化这些因素。A 子集通常对应的是较难的那一档人脸普遍偏小、遮挡多、姿态杂。这一点很关键你拿到的 7906 张不是随机抽样而是偏向小目标和困难样本的集合。这意味着用它训练出来的模型在常规正脸场景下可能表现平平但在远景、侧脸、部分遮挡场景下召回会明显好于用 Easy 子集训出来的模型。为什么小目标人脸这么难因为检测器在浅层特征图上感受野小小目标经过多次下采样后信息几乎丢光。widerface 的 A 子集里大量人脸标注框的宽高在 10 到 50 像素之间这对 YOLO 的 P3 检测头是直接考验。所以这个包的价值不在“数据量大”而在“难度集中”适合做小目标专项调优。2.2 VOC 与 YOLO 两种格式的差异和选用VOC 格式的标注是 XML每个图对应一个 XML 文件里面用bndbox记录xmin/ymin/xmax/ymax坐标是绝对像素值。YOLO 格式是每张图一个 txt每行class_id cx cy w h坐标是归一化到 0 到 1 的相对值。两种格式描述的是同一批框但用途不同。VOC 格式的好处是通用很多老工具链、评估脚本、可视化工具直接吃 XMLYOLO 格式的好处是训练时读取快不用解析 XML直接按行读数字。这个包同时给了两种意味着你可以用 VOC 做数据审查和可视化用 YOLO 直接喂训练。常见做法是先用 VOC 的 XML 做一轮框的合理性检查确认没有越界框、零面积框再转成 YOLO 训练。对比项VOC XMLYOLO txt坐标形式绝对像素 xmin/ymin/xmax/ymax归一化 cx/cy/w/h文件粒度每图一个 XML每图一个 txt类别表达标签名文本数字 class_id训练读取速度慢需解析快按行读适合场景审查、可视化、评估直接训练2.3 7906 张 1 类别的实际训练意义1 个类别就是 face没有别的。这简化了分类头但也意味着你不能拿它做多类检测。7906 张对于从零训练一个 YOLO 来说偏少容易过拟合但对于微调一个已经在 COCO 或人脸数据上预训练过的模型这个量级足够让模型适应小目标分布。我一般会把它当作“小目标专项微调集”而不是“主训练集”。训练集和验证集的划分要自己动手。常见做法是按 8:2 或 9:1 切注意要保证验证集里也有足够多的小目标样本否则验证指标会虚高。切分时用固定随机种子别每次跑都重新切不然指标没法对比。3. 把 VOC 标注转成 YOLO 并接进训练流程3.1 从 XML 到 txt 的转换脚本与边界处理虽然包里已经给了 YOLO 格式但你还是要知道转换逻辑因为一旦你要增删类别或修正框就得自己转。下面这个脚本处理 VOC XML 到 YOLO txt重点处理了越界和零面积框。import os import xml.etree.ElementTree as ET # 类别映射这个包只有 face 一类 classes [face] def convert_bbox(size, box): 把 VOC 的绝对坐标转成 YOLO 归一化坐标 dw 1.0 / size[0] dh 1.0 / size[1] xmin, ymin, xmax, ymax box # 裁剪到图像边界内防止越界框 xmin max(0, min(xmin, size[0] - 1)) xmax max(0, min(xmax, size[0] - 1)) ymin max(0, min(ymin, size[1] - 1)) ymax max(0, min(ymax, size[1] - 1)) w xmax - xmin h ymax - ymin if w 1 or h 1: return None # 零面积或过小框直接丢弃 cx xmin w / 2.0 cy ymin h / 2.0 return (cx * dw, cy * dh, w * dw, h * dh) def convert_xml(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue cls_id classes.index(cls) bndbox obj.find(bndbox) box ( float(bndbox.find(xmin).text), float(bndbox.find(ymin).text), float(bndbox.find(xmax).text), float(bndbox.find(ymax).text), ) bb convert_bbox((w, h), box) if bb is None: continue lines.append(f{cls_id} .join(f{v:.6f} for v in bb)) with open(out_txt_path, w) as f: f.write(\n.join(lines))逻辑说明convert_bbox先把坐标裁到图像范围内避免 widerface 里偶尔出现的越界标注然后算宽高小于等于 1 像素的框直接丢因为这种框训练时是噪声。归一化用图像宽高做分母保留 6 位小数足够。参数上classes列表必须和你的data.yaml里names顺序一致否则类别 id 会错位。3.2 data.yaml 与目录结构的正确摆法YOLO 训练对目录结构有固定要求。常见做法是dataset/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml内容path: /abs/path/to/dataset train: images/train val: images/val nc: 1 names: [face]注意path用绝对路径train和val是相对path的路径。nc是类别数这里是 1。names顺序必须和转换脚本里的classes一致。很多人翻车在names写成[Face]而脚本里是[face]大小写不一致导致类别名对不上训练能跑但推理时标签显示错。3.3 用 YOLOv8 跑通第一次训练的最小命令假设你已经装好 ultralytics最小训练命令yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ imgsz640 \ epochs100 \ batch16 \ patience20 \ seed42参数说明modelyolov8n.pt用预训练权重小目标微调不建议从零开始imgsz640是输入分辨率小目标可以往上提到 960 或 1280但显存要够patience20是早停验证指标 20 轮不升就停seed42固定随机种子保证切分和增强可复现。第一次跑建议先用epochs10验证流程通不通再拉长。4. 小目标人脸的训练参数与增强策略怎么定4.1 输入分辨率与 P3 检测头的关系小目标检测的核心矛盾是输入分辨率越高小目标占的像素越多但显存和计算量也越大。YOLO 默认三个检测头 P3、P4、P5分别对应 8、16、32 倍下采样。一张 640 输入的图P3 特征图是 80x80一个 20 像素的人脸在 P3 上只有 2 到 3 个格子信息很勉强。提到 1280 输入P3 变成 160x160同样的人脸有 5 到 6 个格子召回会明显改善。但别盲目拉高。我一般先跑 640 看基线如果验证集小目标召回低于 0.5再试 960。显存不够就降 batch别降分辨率。另外可以开multi_scale训练让模型适应不同尺度但对小目标专项固定高分辨率往往比多尺度更稳。4.2 针对小目标的增强参数设置YOLO 默认增强里mosaic对小目标有帮助因为它把四张图拼一起变相增加了小目标出现的密度。但mosaic太强会引入不真实边界建议mosaic1.0但配合close_mosaic10最后 10 轮关掉。scale增强范围别太大0.5 到 1.5 够了再大的人脸缩放会失真。hsv_h、hsv_s、hsv_v可以保持默认人脸对颜色变化不敏感。关键是copy_paste和mixup要慎用。mixup会把两张图按透明度叠加小目标人脸叠一起容易变成鬼影反而伤害训练。我一般mixup0copy_paste0专注把mosaic和scale调好。4.3 学习率、anchor 与损失函数观察点YOLOv8 是 anchor-free 的不用手动设 anchor但学习率还是要调。微调场景lr00.001起步lrf0.01做余弦退火。如果 loss 在前几轮就爆 NaN多半是学习率太大或数据里有坏框回去查转换脚本有没有丢零面积框。观察损失时重点看box_loss和dfl_loss。小目标训练里box_loss下降慢是正常的因为小框的回归梯度本身就小。如果cls_loss很快降到接近 0 但box_loss不降说明分类太容易、定位没学好这时候要检查标注框质量而不是继续加轮数。5. 避坑与排查小目标人脸训练里最容易翻车的几件事5.1 验证集指标虚高实际推理一塌糊涂现象训练日志里 mAP50 到 0.9但拿真实监控截图推理远处人脸全漏。原因验证集和训练集来自同一分布且切分时没保证小目标比例验证集里小目标偏少指标被大脸拉高。解决切分时按人脸尺寸分层抽样确保验证集里小目标占比和整体一致另外单独统计小目标宽高小于 32 像素的召回率别只看总体 mAP。5.2 训练报错 “No labels found”现象启动训练后提示找不到标签或训练 loss 一直是 0。原因data.yaml里train路径写错或者 images 和 labels 目录名不匹配。YOLO 默认把images替换成labels找标签如果你目录叫imgs和labs它就找不到。解决严格用images和labels命名或者用train: images/train这种相对路径并确认path正确。5.3 小目标框在增强后消失现象训练时可视化增强后的图发现很多人脸框不见了。原因mosaic拼接后原本靠近边缘的小目标被裁掉或者scale缩小后框小于 1 像素被过滤。解决降低mosaic概率到 0.5或者用close_mosaic提前关闭scale下限别低于 0.5。另外检查转换脚本里丢弃小于 1 像素框的逻辑别把正常小目标也丢了。5.4 显存溢出导致训练中断现象跑几百轮后突然 OOM。原因imgsz设太高加batch太大或者mosaic拼接后单图尺寸波动。解决用batch-1让 YOLO 自动找最大 batch或者手动降到 8imgsz从 1280 降到 960 通常能省一半显存。别用ampFalse关混合精度那会直接翻倍显存。5.5 推理时置信度阈值不会调现象默认conf0.25漏检多调到 0.1 又满屏误检。原因小目标人脸的分类置信度天然偏低因为特征少。解决先在小目标验证集上画 PR 曲线找召回和精确率的平衡点通常conf在 0.05 到 0.15 之间。同时开iou0.5做 NMS小目标密集时iou可以降到 0.4 减少重叠框。6. 进阶用切片推理把远处人脸捞回来小目标检测有个绕不开的物理限制输入分辨率再高整图缩放到网络输入时远处人脸还是会被压到几个像素。一个实战技巧是切片推理SAHI 思路把大图切成带重叠的小块每块单独推理再合并结果。这样远处人脸在某个切片里就变成了“大目标”。具体做法把原图按 640x640 切重叠 128 像素每块送进训练好的 YOLO得到框后映射回原图坐标再用 NMS 去重。代价是推理时间线性增长一张 1080p 图切 6 块耗时约 6 倍。所以适合离线分析或对实时性要求不高的场景。验证切片是否有效看两个数切片前后小目标召回率的变化以及误检增加量。我一般会在验证集上跑一遍切片推理如果小目标召回提升超过 15% 且误检增加不到 5%就值得上。如果提升不明显说明模型本身没学好小目标特征回去调训练参数比切片更根本。最后说个习惯每次拿到一个新数据集包我第一件事不是直接训练而是随机抽 20 张图把标注框画出来看一遍。这一步能提前发现 80% 的标注问题比训练跑完再排查省事得多。希望帮到你。本文还有配套的精品资源点击获取