☰
WiderFace小目标人脸检测:VOC/YOLO双格式7906张数据包实战调优
2026/10/7 6:19:44 网站建设 项目流程

简介:这是一份面向人脸检测算法训练与验证的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 格式的好处是通用,很多老工具链、评估脚本、可视化工具直接吃 XML;YOLO 格式的好处是训练时读取快,不用解析 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.yaml

data.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 \ data=dataset/data.yaml \ model=yolov8n.pt \ imgsz=640 \ epochs=100 \ batch=16 \ patience=20 \ seed=42

参数说明:model=yolov8n.pt用预训练权重,小目标微调不建议从零开始;imgsz=640是输入分辨率,小目标可以往上提到 960 或 1280,但显存要够;patience=20是早停,验证指标 20 轮不升就停;seed=42固定随机种子,保证切分和增强可复现。第一次跑建议先用epochs=10验证流程通不通,再拉长。

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太强会引入不真实边界,建议mosaic=1.0但配合close_mosaic=10,最后 10 轮关掉。scale增强范围别太大,0.5 到 1.5 够了,再大的人脸缩放会失真。hsv_h、hsv_s、hsv_v可以保持默认,人脸对颜色变化不敏感。

关键是copy_paste和mixup要慎用。mixup会把两张图按透明度叠加,小目标人脸叠一起容易变成鬼影,反而伤害训练。我一般mixup=0,copy_paste=0,专注把mosaic和scale调好。

4.3 学习率、anchor 与损失函数观察点

YOLOv8 是 anchor-free 的,不用手动设 anchor,但学习率还是要调。微调场景lr0=0.001起步,lrf=0.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,或者手动降到 8;imgsz从 1280 降到 960 通常能省一半显存。别用amp=False关混合精度,那会直接翻倍显存。

5.5 推理时置信度阈值不会调

现象:默认conf=0.25漏检多,调到 0.1 又满屏误检。原因:小目标人脸的分类置信度天然偏低,因为特征少。解决:先在小目标验证集上画 PR 曲线,找召回和精确率的平衡点,通常conf在 0.05 到 0.15 之间。同时开iou=0.5做 NMS,小目标密集时iou可以降到 0.4 减少重叠框。

6. 进阶:用切片推理把远处人脸捞回来

小目标检测有个绕不开的物理限制:输入分辨率再高,整图缩放到网络输入时,远处人脸还是会被压到几个像素。一个实战技巧是切片推理(SAHI 思路),把大图切成带重叠的小块,每块单独推理,再合并结果。这样远处人脸在某个切片里就变成了“大目标”。

具体做法:把原图按 640x640 切,重叠 128 像素,每块送进训练好的 YOLO,得到框后映射回原图坐标,再用 NMS 去重。代价是推理时间线性增长,一张 1080p 图切 6 块,耗时约 6 倍。所以适合离线分析或对实时性要求不高的场景。

验证切片是否有效,看两个数:切片前后小目标召回率的变化,以及误检增加量。我一般会在验证集上跑一遍切片推理,如果小目标召回提升超过 15% 且误检增加不到 5%,就值得上。如果提升不明显,说明模型本身没学好小目标特征,回去调训练参数比切片更根本。

最后说个习惯:每次拿到一个新数据集包,我第一件事不是直接训练,而是随机抽 20 张图把标注框画出来看一遍。这一步能提前发现 80% 的标注问题,比训练跑完再排查省事得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询