☰
YOLO人脸检测实战:1853张带标签数据集的训练前体检与调参指南
2026/10/10 0:33:46 网站建设 项目流程

简介:这份资源面向从事目标检测的算法工程师、学生与研究者,提供一套可直接用于人脸检测任务训练的YOLO系列数据集,解决从零标注数据耗时、格式不统一的问题。压缩包共2000个文件,约90.78MB,其中1646个xml文件为VOC格式标注,354个txt文件为YOLO格式标注,两种标签分别存放,便于按需选用。数据集已划分好训练与验证集,并附带data.yaml配置文件,可无缝接入yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流框架。YOLO格式采用归一化的中心点与宽高比例值,类别索引从0开始,符合标准训练输入要求。目前已有357人学习下载,适合希望快速验证模型、开展人脸检测实验或进行算法对比的读者,能省去数据清洗与格式转换环节,直接投入训练与测试。

1. 拿到一份 1853 张带标签的人脸数据集,先别急着 train

很多人拿到「yolo算法-人脸检测数据集-1853张图像带标签」这类压缩包,第一反应是解压、改 data.yaml、直接yolo train。我见过太多人这么干,结果 mAP 卡在 0.6 上不去,回头怀疑模型、怀疑显卡,就是不怀疑数据本身。1853 张这个量级很微妙:它比玩具数据集大,能训出东西;又比工业数据集小,任何标注噪声、类别失衡、尺寸分布偏移都会被放大成训练曲线上的抖动。人脸检测这个任务本身也有它的特殊性——目标尺度跨度极大,近景人脸能占满半张图,远景人脸可能只有十几个像素,而 YOLO 的 anchor 和特征金字塔对这种跨度是有脾气的。

这篇文章面向的是手上已经有一份「图像 + 标签」人脸数据集、想用 YOLO 系列跑通检测的从业者。我会按「先看清数据长什么样 → 再决定怎么切分和转换 → 然后配训练参数 → 最后排错和验证」的顺序讲,中间给可直接抄的命令和脚本。不假设你用的是哪个具体仓库,只讲通用做法,你按自己环境替换路径即可。核心判断是:1853 张带标签人脸数据,能不能训出可用的检测器,取决于你在训练前对数据做了多少「体检」,而不是训练时调了多少超参。

2. 先给数据集做体检:1853 张到底够不够、脏不脏

在写任何训练命令之前,我会先花半小时把数据集的统计特征跑出来。这一步不做,后面所有调参都是盲调。人脸检测数据集常见的坑集中在三处:标注框越界或宽高为负、单图人脸数量极端不均、图像尺寸和长宽比分布和你的推理场景不匹配。1853 张这个规模,如果标注质量高、场景集中,训一个单类人脸检测器是够的;如果标注框大量偏移,那再多图也白搭。

2.1 用脚本统计标注框的宽高分布和越界情况

假设你的标签是 YOLO 格式的 txt(每行class cx cy w h,归一化到 0~1),先跑一段统计。这段代码不依赖任何训练框架,纯 Python + 标准库就能跑,目的是把「黑匣子」打开。

import os import glob # 标签目录,按你的实际路径改 label_dir = "./labels/train" img_dir = "./images/train" w_list, h_list, per_img_counts = [], [], [] bad_boxes = [] # 记录越界或非法框 for txt_path in glob.glob(os.path.join(label_dir, "*.txt")): with open(txt_path, "r") as f: lines = [l.strip() for l in f if l.strip()] per_img_counts.append(len(lines)) for idx, line in enumerate(lines): parts = line.split() if len(parts) != 5: bad_boxes.append((txt_path, idx, "字段数不对")) continue cls, cx, cy, w, h = parts cx, cy, w, h = map(float, (cx, cy, w, h)) # 归一化坐标合法范围判断 if not (0 <= cx <= 1 and 0 <= cy <= 1): bad_boxes.append((txt_path, idx, "中心点越界")) if w <= 0 or h <= 0 or w > 1 or h > 1: bad_boxes.append((txt_path, idx, "宽高非法")) w_list.append(w) h_list.append(h) print(f"图像数: {len(per_img_counts)}") print(f"总框数: {sum(per_img_counts)}") print(f"单图最多人脸: {max(per_img_counts)}, 最少: {min(per_img_counts)}") print(f"平均每图: {sum(per_img_counts)/len(per_img_counts):.2f}") print(f"宽 min/mean/max: {min(w_list):.4f}/{sum(w_list)/len(w_list):.4f}/{max(w_list):.4f}") print(f"高 min/mean/max: {min(h_list):.4f}/{sum(h_list)/len(h_list):.4f}/{max(h_list):.4f}") print(f"非法框数量: {len(bad_boxes)}") for b in bad_boxes[:10]: print(b)

逻辑说明:遍历所有标签文件,把每张图的框数、每个框的归一化宽高收集起来。bad_boxes专门抓三类问题——字段数不对(说明标注工具导出格式不一致)、中心点越界(常见于标注时拖拽超出图像边界)、宽高非法(负值或大于 1)。参数上,label_dir和img_dir按你解压后的实际结构改,YOLO 常见结构是images/train配labels/train。

跑完你会得到几个关键数字。如果单图最多人脸是 50 而平均只有 2,说明存在极端密集场景,训练时 mosaic 增强会把小脸进一步缩小,需要留意。如果宽 min小于 0.01,意味着有小于图像 1% 宽度的人脸,这类目标在 640 输入下只有 6 个像素,YOLO 的 P3 特征层也救不回来,要么提高输入分辨率,要么在数据层面过滤掉。非法框数量不为零就必须先修,否则训练时 loss 会出现 NaN 或者框回归直接跑飞。

2.2 图像尺寸和长宽比分布决定输入分辨率

第二个体检项是图像本身的尺寸。人脸检测数据集经常是混合来源——一部分是监控截图(16:9),一部分是证件照(3:4),还有一部分是网络爬取的任意尺寸。YOLO 训练时默认rect=False会统一 letterbox 到正方形,长宽比差异大的图会被填充大量灰边,有效像素占比下降。

from PIL import Image import glob import os sizes = [] for img_path in glob.glob(os.path.join(img_dir, "*")): try: with Image.open(img_path) as im: sizes.append(im.size) # (宽, 高) except Exception as e: print(f"打不开: {img_path}, {e}") ratios = [w / h for w, h in sizes] print(f"图像数: {len(sizes)}") print(f"宽范围: {min(s for s, _ in sizes)} ~ {max(s for s, _ in sizes)}") print(f"高范围: {min(h for _, h in sizes)} ~ {max(h for _, h in sizes)}") print(f"长宽比 min/mean/max: {min(ratios):.2f}/{sum(ratios)/len(ratios):.2f}/{max(ratios):.2f}")

逻辑说明:用 PIL 只读文件头拿尺寸,不加载像素,1853 张几秒跑完。ratios反映长宽比离散程度。如果 mean 在 1.0 附近且 min/max 在 0.8~1.25 之间,说明数据接近正方形,imgsz=640直接训没问题。如果出现 0.3 或 3.0 这种极端值,建议开rect=True做矩形训练,减少填充浪费,但代价是 batch 内尺寸不一致会拖慢一点速度。

提示:这一步的结论直接决定你后面imgsz设 640 还是 960。人脸小且密集就往上加,但显存要跟着算。

2.3 训练集验证集切分:别用随机切分骗自己

1853 张如果随机 8:2 切分,很容易出现「同一个人、同一场景的连续帧」被分到训练和验证两边,验证集 mAP 虚高,上线就翻车。人脸数据尤其如此,如果是从视频抽帧来的,相邻帧几乎一样。我的做法是按来源或按时间切分,没有来源信息时至少做一次去重。

import hashlib import glob import os import shutil def file_md5(path): h = hashlib.md5() with open(path, "rb") as f: h.update(f.read()) return h.hexdigest() seen = {} dups = [] for p in glob.glob(os.path.join(img_dir, "*")): m = file_md5(p) if m in seen: dups.append((p, seen[m])) else: seen[m] = p print(f"重复图像对数: {len(dups)}") for d in dups[:5]: print(d)

逻辑说明:用 MD5 做精确去重,能抓出完全相同的文件。更严格的做法是感知哈希(pHash)抓近似重复,但 MD5 先跑一遍成本低。如果重复对超过总数的 5%,说明数据集有采集冗余,切分前先去重,否则验证集里混入训练集副本,指标没有参考意义。参数上img_dir指向全部图像目录,去重后再做切分。

3. 标签格式转换与 data.yaml:把 1853 张喂给 YOLO 的正确姿势

体检做完,数据干净了,接下来是格式对齐。YOLO 系列(v5/v8/v11 及衍生)吃的是「图像 + 同名 txt」结构,txt 每行class cx cy w h。如果你拿到的标签是 VOC XML 或 COCO JSON,就得转。人脸检测通常只有一类,class恒为 0,但有些数据集会把「人脸」和「人脸关键点」混在一起,转换时要过滤。

3.1 VOC XML 转 YOLO txt 的脚本与四个边界坑

VOC 格式的 XML 里是绝对坐标xmin ymin xmax ymax,转 YOLO 要归一化。这段脚本处理单类人脸,顺手把越界框裁到图像范围内。

import xml.etree.ElementTree as ET import os import glob from PIL import Image xml_dir = "./annotations" out_dir = "./labels_all" os.makedirs(out_dir, exist_ok=True) for xml_path in glob.glob(os.path.join(xml_dir, "*.xml")): tree = ET.parse(xml_path) root = tree.getroot() # 从 XML 里读图像尺寸 size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip().lower() # 只保留人脸类,按你数据集的实际类名改 if name not in ("face", "human_face", "人脸"): continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 坑1:坐标裁到图像范围内 xmin = max(0, min(xmin, img_w - 1)) ymin = max(0, min(ymin, img_h - 1)) xmax = max(0, min(xmax, img_w - 1)) ymax = max(0, min(ymax, img_h - 1)) # 坑2:裁完可能宽高为 0,跳过 if xmax <= xmin or ymax <= ymin: continue cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h # 坑3:归一化后仍可能因浮点误差略超 1,夹一下 cx, cy = min(max(cx, 0), 1), min(max(cy, 0), 1) w, h = min(max(w, 0), 1), min(max(h, 0), 1) lines.append(f"0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") # 坑4:没有人脸框的图也要生成空 txt,否则训练时找不到标签会报错 out_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines))

逻辑说明:四个坑分别是坐标越界、裁完零面积、归一化浮点溢出、空标签文件缺失。参数上xml_dir是 VOC 标注目录,out_dir是输出 txt 目录。类名过滤那行按你数据集实际写,如果 XML 里类名五花八门,先跑一遍统计所有name值再决定保留哪些。空 txt 这点很多人忽略,YOLO 训练时如果某张图没有对应标签文件,部分实现会直接跳过该图,等于白白浪费了负样本。

3.2 data.yaml 的字段含义与路径写法

转换完,写data.yaml。这个文件看着简单,路径写错是最常见的翻车点。

# data.yaml path: /abs/path/to/dataset # 数据集根目录,绝对路径最稳 train: images/train # 相对 path 的训练图像目录 val: images/val # 相对 path 的验证图像目录 nc: 1 # 类别数,人脸检测就是 1 names: 0: face # 类别名,和 txt 里的 class 0 对应

逻辑说明:path用绝对路径,避免训练时工作目录变化导致找不到文件。train和val是相对path的路径,指向图像目录,YOLO 会自动去找同级的labels目录(把images替换成labels)。nc和names必须和标签里的 class 索引一致,人脸单类就是nc: 1。如果你的目录结构不是images/labels平行,而是所有图在一个文件夹、所有标签在另一个文件夹,那就得在 yaml 里写绝对路径,或者提前用脚本整理成标准结构。

注意:names的键是整数,YAML 里写0: face没问题,但别写成字符串"0",某些解析器会报类型错误。

3.3 用一行命令验证标签和图像是否一一对应

转换和 yaml 都就绪后,跑一个对应性检查,避免训练到一半才发现有图无标签或有标签无图。

# 统计图像和标签数量,按你的路径改 ls images/train | sed 's/\.[^.]*$//' | sort > /tmp/img_names.txt ls labels/train | sed 's/\.txt$//' | sort > /tmp/lbl_names.txt # 找出有图无标签的 comm -23 /tmp/img_names.txt /tmp/lbl_names.txt | head -20 # 找出有标签无图的 comm -13 /tmp/img_names.txt /tmp/lbl_names.txt | head -20

逻辑说明:sed去掉扩展名,sort后comm做集合差。第一组输出是「有图无标签」,这些图训练时会被当负样本或跳过,取决于实现;第二组是「有标签无图」,属于脏数据,直接删标签。参数上把images/train和labels/train换成你的实际路径,验证集同理再跑一遍。

4. 训练参数怎么设:1853 张人脸数据的 epoch、imgsz 与增强

数据就绪,进入训练。1853 张这个量级,我的经验是 epoch 不宜太少,因为单 epoch 迭代次数有限,模型还没看够就停了;但也不宜盲目堆到 300,容易过拟合。关键参数是imgsz、batch、epochs和增强开关,下面逐个说清楚为什么这么设。

4.1 imgsz 的选择:人脸像素尺寸说了算

imgsz决定输入网络的分辨率,直接影响小脸能不能被检测到。判断依据是你在 2.1 里统计的宽 min。如果最小人脸归一化宽度是 0.02,在imgsz=640下就是 12.8 像素,YOLO 的 P3 层 stride 8,特征图上约 1.6 个格子,勉强能学;如果最小是 0.01,640 下只有 6.4 像素,基本学不动,得把imgsz提到 960 或 1280。

# 以 640 为基准训练,路径按实际改 yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ imgsz=640 \ epochs=150 \ batch=16 \ workers=4 \ project=./runs_face \ name=exp_640

逻辑说明:model用预训练权重起步,人脸检测和通用目标检测有共性,预训练能省不少 epoch。batch=16是 8G 显存的保守值,显存够可以往上加,但要注意 batch 变大时学习率通常也要跟着调。workers是数据加载线程,设成 CPU 核数的 1/4 到 1/2。project和name决定输出目录,方便对比不同实验。

如果 640 训完验证集小脸漏检严重,换 960 再训一版对比:

yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ imgsz=960 \ epochs=150 \ batch=8 \ workers=4 \ project=./runs_face \ name=exp_960

逻辑说明:imgsz翻倍,显存占用大约翻四倍,所以batch从 16 降到 8。两版跑完对比验证集上小脸(按框面积分档)的召回率,再决定上线用哪个。参数上没有绝对最优,只有和你的推理场景匹配。

4.2 增强开关:mosaic 和 mixup 在人脸数据上的取舍

YOLO 默认开 mosaic(四图拼接)和 mixup。mosaic 对通用检测很有效,但人脸数据要小心:四图拼接后,原本中等大小的人脸可能被缩到很小,如果数据集本身小脸就多,mosaic 会加剧尺度失衡。我的做法是前期开 mosaic 提升泛化,后期关掉做微调。

# 前 120 epoch 开 mosaic,后 30 epoch 关闭做微调 yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ imgsz=640 \ epochs=150 \ batch=16 \ mosaic=1.0 \ close_mosaic=30 \ mixup=0.0 \ project=./runs_face \ name=exp_mosaic

逻辑说明:close_mosaic=30表示最后 30 个 epoch 自动关闭 mosaic,让模型在真实分布上收敛。mixup=0.0是我在人脸数据上的保守选择,mixup 把两张图按透明度叠加,人脸会变成半透明鬼影,对检测框回归不友好,除非你的数据量实在太小需要强正则。参数上mosaic=1.0是开启概率,想减弱可以设 0.5。

4.3 学习率和优化器:小数据集的稳定优先策略

1853 张属于小数据集,学习率不宜太大,否则 loss 震荡。YOLO 默认 SGD 的lr0=0.01,在小数据集上我一般降到 0.005 或换 AdamW。

yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ imgsz=640 \ epochs=150 \ batch=16 \ optimizer=AdamW \ lr0=0.001 \ lrf=0.01 \ weight_decay=0.0005 \ warmup_epochs=3 \ project=./runs_face \ name=exp_adamw

逻辑说明:optimizer=AdamW对学习率不那么敏感,适合不想反复调参的场景。lr0=0.001是 AdamW 的常见起点,lrf=0.01表示最终学习率是初始的 1%,余弦衰减到底。warmup_epochs=3让学习率从很小线性升到lr0,避免一开始就大步长破坏预训练权重。weight_decay做 L2 正则,小数据集上防过拟合。

提示:如果训练 loss 在前 10 个 epoch 就降到接近 0 而验证 mAP 不涨,基本是过拟合了,回头检查数据增强是不是太弱、或者训练验证切分有泄漏。

5. 训练过程排查:loss 不降、mAP 抖动、显存爆的常见原因

训练跑起来不等于跑对了。这一章列几个我实际踩过的坑,按「现象 → 原因 → 解决」写,你对号入座。

5.1 现象:box_loss 一直不降,cls_loss 正常

原因通常是标签坐标有问题。最常见的是归一化时用了错误的图像尺寸——比如 XML 里写的width/height和实际图像尺寸不一致,或者转换脚本读的是缩略图尺寸。另一个原因是标签里混入了非人脸类但没过滤,模型在学一些莫名其妙的框。

解决:回到 2.1 的统计脚本,重点看宽 min/mean/max是否合理。正常人脸框归一化宽度应该在 0.05~0.8 之间,如果出现大量 0.001 级别的框,就是坐标算错了。再抽查几张图,用画框脚本把标签可视化出来,肉眼比对。

import cv2 import os img_path = "./images/train/000001.jpg" label_path = "./labels/train/000001.txt" img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: 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.imwrite("./check_vis.jpg", img)

逻辑说明:把归一化坐标还原成像素坐标画框,输出一张可视化图。参数上换几张不同来源的图各跑一次,重点看框有没有整体偏移、有没有框到背景。这个脚本不依赖训练框架,纯 OpenCV。

5.2 现象:验证集 mAP 上下抖动超过 5 个点

原因一般是验证集太小或分布不均。1853 张按 8:2 切,验证集约 370 张,如果里面某类场景(比如侧脸、遮挡)样本很少,每轮评估时这批样本的检测结果波动就会放大整体 mAP。

解决:要么扩大验证集比例到 7:3,要么做交叉验证。更实际的做法是固定验证集后,看每个类别的 AP 而不是只看 mAP,定位到底是哪类样本在抖。如果抖动来自少量难样本,可以在训练后期针对性补充这类数据,而不是盲目调参。

5.3 现象:训练到一半 CUDA out of memory

原因可能是imgsz或batch设大了,也可能是数据加载时某些超大图没被正确 resize。YOLO 的 letterbox 会按imgsz缩放,但如果某张图尺寸异常大(比如 8000x6000),解码时内存峰值会很高。

解决:先降batch到 8 试,还爆就降imgsz。同时在数据加载前用脚本把超大图筛出来,超过 4000 像素的单独处理或直接排除。另外workers设太大也会因为多进程各自缓存数据而吃内存,设成 2~4 更稳。

5.4 现象:训练完推理时框位置整体偏移

原因通常是训练时的 letterbox 填充和推理时的预处理不一致。YOLO 官方推理接口会自动处理,但如果你自己写预处理,很容易在缩放比例和 padding 上算错。

解决:优先用官方predict接口,别自己造轮子。如果必须自己写,记住 letterbox 是「等比缩放 + 居中填充」,还原坐标时要先减去 padding 再除以缩放比例。这个顺序错了,框就会整体偏移。

6. 验证与进阶:用混淆矩阵和分档召回定位真实短板

训练跑完,runs_face/exp_*/下会有一堆曲线和指标。多数人只看 mAP50,但人脸检测上线前,我更关注两件事:不同尺度人脸的召回率,以及误检集中在什么背景上。这一章讲怎么把验证做细,以及一个提升小脸召回的具体技巧。

6.1 用验证集跑混淆矩阵和 PR 曲线

yolo detect val \ data=./data.yaml \ model=./runs_face/exp_640/weights/best.pt \ imgsz=640 \ conf=0.001 \ iou=0.6 \ plots=True \ project=./runs_face_val \ name=val_640

逻辑说明:conf=0.001把置信度阈值压到很低,让 PR 曲线覆盖完整召回范围,这样算出来的 AP 才准。iou=0.6是 COCO 风格的匹配阈值,人脸检测也可以用 0.5 看宽松指标。plots=True会输出混淆矩阵、PR 曲线、F1 曲线到输出目录。参数上model指向你训练出的best.pt。

看混淆矩阵时,人脸单类任务只有「face」和「background」两行两列。重点看 background 被误判成 face 的数量(误检),以及 face 被漏成 background 的数量(漏检)。如果误检多,提高推理时的conf阈值;如果漏检多,说明模型对某些人脸不敏感,回到数据层面找原因。

6.2 按人脸像素面积分档统计召回率

mAP 是一个平均数,会掩盖小脸漏检。我一般写个脚本,把验证集标注框按面积分成小(<32²)、中(32²~96²)、大(>96²)三档,分别统计召回。

import glob import os from PIL import Image # 这里用标注框的像素面积分档,实际召回需要模型预测结果,简化演示分档逻辑 label_dir = "./labels/val" img_dir = "./images/val" buckets = {"small": 0, "medium": 0, "large": 0} for txt in glob.glob(os.path.join(label_dir, "*.txt")): stem = os.path.splitext(os.path.basename(txt))[0] # 找对应图像拿尺寸 img_path = None for ext in (".jpg", ".png", ".jpeg"): p = os.path.join(img_dir, stem + ext) if os.path.exists(p): img_path = p break if not img_path: continue with Image.open(img_path) as im: w, h = im.size with open(txt) as f: for line in f: parts = line.split() if len(parts) != 5: continue _, cx, cy, bw, bh = map(float, parts) area = (bw * w) * (bh * h) if area < 32 * 32: buckets["small"] += 1 elif area < 96 * 96: buckets["medium"] += 1 else: buckets["large"] += 1 print(buckets)

逻辑说明:这段先统计验证集里各档人脸的数量分布,让你知道小脸占比。如果 small 占比超过 30%,而你的模型 mAP 又不高,基本可以断定瓶颈在小脸。真正的分档召回需要把模型预测结果和标注做匹配,逻辑更长,但思路是:对每个标注框,找 IoU 最大的预测框,若 IoU 达标且类别正确则算召回。参数上label_dir和img_dir指向验证集。

6.3 提升小脸召回的一个具体技巧:切片推理

如果小脸占比高且imgsz已经提到 960 还不行,可以试切片推理(SAHI 思路)。把一张大图切成有重叠的小块,每块单独推理,再合并结果。这样小脸在切片里相对变大,更容易被检测到。

# 切片推理的简化逻辑,实际用现成库更稳 import cv2 def slice_infer(img, model, slice_size=640, overlap=0.2): h, w = img.shape[:2] step = int(slice_size * (1 - overlap)) boxes = [] for y in range(0, h, step): for x in range(0, w, step): patch = img[y:y+slice_size, x:x+slice_size] if patch.shape[0] < 32 or patch.shape[1] < 32: continue # model.predict 返回该切片的框,坐标要加回 (x, y) 偏移 results = model.predict(patch, verbose=False) for r in results: for box in r.boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() boxes.append([x1 + x, y1 + y, x2 + x, y2 + y, box.conf[0].item()]) # 合并重叠框用 NMS,这里省略具体实现 return boxes

逻辑说明:slice_size是切片边长,overlap是重叠比例,0.2 表示相邻切片有 20% 重叠,避免目标被切在边界上漏掉。每个切片推理后坐标要加回切片在原图的偏移。最后所有切片的框合并做一次 NMS 去重。这个方法的代价是推理时间成倍增加,适合对召回要求高、能接受延迟的场景。参数上slice_size一般设成和训练imgsz一致,overlap在 0.2~0.3 之间。

我自己在这类 1800 张量级的人脸数据上折腾过好几轮,最大的教训是:别在训练参数上反复横跳,先把数据体检做扎实。标注框的坐标错一位,你调一百次学习率也补不回来。现在我的习惯是,拿到任何带标签数据集,先跑统计脚本、画几张可视化、做一次去重,这三步做完再碰训练命令。希望帮到你。

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

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

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

立即咨询