搞深度学习的人应该都有同感:跑通别人的开源项目很容易,但从零开始准备数据、训练一个自己的模型,每一步都可能踩坑。这篇文章是“零基础入门人脸识别”系列的第二篇,继续用 YOLOv11 这条主线,在一张 RTX 5060 显卡上,把配置、训练、验证、测试、预测这五步全流程代码完整跑一遍,每条命令都给可复制的版本,并解释为什么这样写。适合刚装好显卡驱动、还没正经训过模型的新手,也适合被各种版本问题折磨过、想系统捋一遍流程的老玩家。
1. 开始之前:YOLOv11 做的是“人脸检测”,不是完整意义的“人脸识别”
先把这个概念掰清楚,不然后面的代码你可能会理解偏。YOLOv11 输出的是边界框和类别置信度,它解决的是“图片里哪里有脸”这个问题,属于检测任务。而真正的“人脸识别”还包含“这张脸是谁”——那需要提取人脸特征向量,再做比对,通常由 ArcFace 这类专用模型完成。
所以严格来说,本文标题里的“人脸识别”指的是识别系统的第一步:人脸检测。几乎所有成熟的识别系统都是“先检测、后识别”,检测框不准,后面提特征、比对全部白搭。把这一步做扎实,第三篇再讲特征提取和检索比对就顺理成章了。
顺带说一个命名细节:Ultralytics 官方把这代模型叫 YOLO11,没有 “v”,社区里写 YOLOv11 也很常见,两者指的是同一个东西,不用纠结。
再明确一下硬件预期。RTX 5060 属于“入门很舒服、重活不太行”的定位:跑 YOLOv11 的人脸检测训练完全够用,yolo11n/s/m 都能顺利训完;但显存只有 8GB,batch 和输入分辨率不能像顶级卡那样放开。本文的所有参数都按 8GB 显存来设定,换卡以后你可以在这个基础上等比调整。
2. 环境配置:RTX 5060 的新架构,逼着我们把版本选对
2.1 为什么“能跑起来”和“能用上显卡”是两回事
很多新手拿到新显卡,第一件事是装最新驱动,然后开箱即用旧版 PyTorch。结果torch.cuda.is_available()返回 True,一跑训练却报错,或者干脆返回 False。原因出在架构上:RTX 50 系列桌面卡的计算能力是 12.0(架构代号 sm_120),而 PyTorch 2.6 及更早的官方预编译包主要覆盖到 sm_90 之前的架构,不认识新卡的指令集。
解决办法是选择 PyTorch 2.7 及以上、基于 CUDA 12.8(cu128)构建的 wheel 版本。这类 wheel 自带 CUDA 运行库,不需要你再单独安装完整的 CUDA Toolkit,对新手省了很多事。
2.2 环境创建与安装命令
我用 Miniconda 管理的环境,命令如下:
conda create -n yolo11 python=3.11 -y conda activate yolo11 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128 pip install ultralyticsPython 3.11 目前是兼容性最稳的选择,别在 conda 默认的 3.13 上折腾。顺序也有讲究:先装 torch 再装 ultralytics,否则 Ultralytics 可能会把你刚装好的 GPU 版 torch 覆盖成 CPU 版,这是新人最容易踩的暗坑。国内网络环境如果觉得官方源慢,可以把 pip 换成常见的镜像源,效果一样。
2.3 装完必须做的三个验证
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"正常输出会看到类似2.7.0+cu128、True、NVIDIA GeForce RTX 5060。如果is_available()是 False,先运行nvidia-smi确认驱动能看到显卡,再确认你安装的 torch 是不是 cu128 版本,这两个检查能解决八成环境问题。
再验证一下模型能正常加载:
python -c "from ultralytics import YOLO; YOLO('yolo11n.pt')"第一次运行会自动下载权重文件,网络慢就手动下载放到当前目录,文件名对得上即可。最后可以跑一下yolo checks,它会汇总输出 Python、GPU、依赖库版本,一眼看清环境全貌。
3. 数据准备:从零到一份能训练的 YOLO 人脸数据集
3.1 先理解 YOLO 格式的标签到底长什么样
YOLO 的标签不是单独一个 json 文件,而是每张图片对应一个同名 txt 文件。每行描述一个目标,格式是五个数字:
类别ID 中心点x 中心点y 框宽 框高注意 x、y、w、h 全部是归一化到 0~1 的相对值,也就是除以图片的宽高。比如一张 1920x1080 的图,人脸框左上角在 (810, 560),右下角在 (1110, 960),那么中心点是 (960, 760),宽 300、高 400。归一化后就是:
0 0.5 0.70556 0.15625 0.37037人脸检测一般就一个类别,记为 0,名字叫 face。如果你想区分戴没戴口罩,再定义 1: no_mask 之类,但入门阶段先只做一个类别,把流程跑通。
3.2 目录结构:直接复制这份模板
my_face_data/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── face.yaml图片放在 images 下,标签放在 labels 下,两个目录的子目录结构必须一致。图片和标签靠文件名关联,所以000001.jpg必须对应000001.txt,后缀可以不同,主名必须相同。强烈建议整个项目路径用纯英文,不要出现“人脸数据”这种中文目录,Windows 下编码问题会让你报出完全看不懂的错误。
3.3 标注工具与数据划分
小数据量用 LabelImg 最直接,打开图片画框,保存格式选 YOLO 就可以,生成的 txt 能直接被训练代码使用。追求效率的话用 X-AnyLabeling,它支持模型辅助预标注,标了一半框就能帮你猜剩下的,人工只需要修正边界,几百张图也不会太累。
标注人脸有个细节:框要尽量贴合人脸,包含下巴但不留大块背景,宽度正好卡住两侧脸颊。侧脸、低头、戴帽子都要标,这些难例决定了你模型的上限。
数据划分原则是“按场景和人物去分”,不是随机分。比如你拍了 10 段视频,按 7:2:1 应该切 7 段进 train、2 段进 val、1 段进 test,而不是把所有视频的每一帧混在一起随机分——否则同一段视频的连续帧同时出现在训练集和验证集,mAP 虚高,一换真实场景就现原形。
如果暂时没数据,可以先拿公开的人脸检测数据集练手,比如 WIDER FACE 这类常见基准数据,网上有现成整理好的 YOLO 格式版本可以下载。
3.4 data.yaml 写法
path: F:/datasets/my_face_data # 数据集的绝对路径 train: images/train val: images/val test: images/test names: 0: facepath建议写绝对路径,Ultralytics 虽然支持相对路径,但新手在项目目录上反复切换很容易搞混。train/val/test是相对于path的路径。names里 0 对应 face,类别顺序必须和标签 txt 里的类别 ID 一致。
4. 训练环节:一条命令的事,但参数决定了你能不能跑到 best.pt
4.1 训练命令
环境配好、数据就位之后,训练本身只有一条命令:
yolo detect train data=face.yaml model=yolo11s.pt epochs=120 imgsz=640 batch=16 device=0 workers=4 patience=20 cache=True project=runs/face_detect name=train_v1用 Python API 写也是一样的意思:
from ultralytics import YOLO model = YOLO("yolo11s.pt") # 自动加载 COCO 预训练权重 model.train( data="face.yaml", epochs=120, imgsz=640, batch=16, device=0, workers=4, patience=20, cache=True, optimizer="SGD", cos_lr=True, project="runs/face_detect", name="train_v1", )model="yolo11s.pt"这行很关键,它加载的是在 COCO 数据集上预训练过的权重。虽然 COCO 里没有专门的人脸这个类,但预训练学到的低层特征(边缘、纹理、轮廓)对任何检测任务都有用,这就是迁移学习。我测试过,从预训练权重开始训,五百张人脸图就能收敛到能用的程度;从零开始训,数据量没有两三千张很难看。
4.2 参数逐条拆解(以 RTX 5060 的 8GB 显存为基准)
| 参数 | 含义 | 我的建议 |
|---|---|---|
| model | 用什么规模的模型 | 入门选 yolo11s,速度和精度平衡;yolo11n 更快但精度一般,yolo11m 在 8GB 显存也能跑但要把 batch 调小 |
| epochs | 训练轮数 | 120 起步,多数数据集在 60~100 轮时 mAP 就不再明显提升 |
| imgsz | 输入分辨率 | 640 默认够用;小脸多可以试 960,但显存占用随分辨率平方增长,batch 要减半 |
| batch | 每批图片数 | 8GB 显存下 yolo11s + 640 建议 16;报 OOM 就先 8 或 4 |
| workers | 数据加载线程数 | Windows 上 4 比较稳,设 8 有时会卡死 |
| patience | 早停耐心值 | 20 表示 val 指标连续 20 轮不提升就自动停止,省电省时间 |
| device | 用哪块 GPU | 单卡写 0,没 GPU 写 cpu(会很慢,不推荐) |
| cache | 是否缓存数据 | True 会把图片缓存到内存,第二次训练明显变快;内存小于 16GB 建议 False |
batch 是最容易劝退新人的参数。显存不够报CUDA out of memory时,第一反应应该是降 batch,而不是降模型。yolo11s 在 640 分辨率下一张图大概要占 350MB 左右显存,加上中间激活值,16 的 batch 在 8GB 上是安全线。想开更大 batch 但显存不够,可以研究梯度累积,不过新手阶段没必要。
4.3 训练过程中看什么
训练开始后,终端会不断打印box_loss、cls_loss、dfl_loss、Time、GPU这些字段。loss 总体应该在波动中下降,不用管每一轮的小抖动,看趋势就行。同时在runs/face_detect/train_v1/目录下会生成results.csv,里面有每个 epoch 的精确率、召回率、mAP50、mAP50-95 和各类 loss,这才是你判断训练是否健康的核心依据。
训练结束后目录里会出现best.pt和last.pt。best.pt是验证集上 mAP 最高的那个模型,last.pt是最后一个 epoch 的模型,一般选 best。
两个常见判断:loss 还在降但 val 的 mAP 不再涨,说明模型过拟合,要么加数据、要么调低 epochs;mAP50 很高但 mAP50-95 偏低,说明框位置不够准,可以适当提高输入分辨率。
速度方面可以给个参考:五千张人脸图,yolo11s,RTX 5060,640 分辨率,batch 16,一个 epoch 大概 2~4 分钟,100 轮半天内跑完。要是你发现一个 epoch 要十几分钟,先检查是不是 batch 太小或者 workers 没设置好。
5. 验证与测试:指标怎么读,测试集为什么必须单独留
5.1 验证命令与输出文件
训练完先用验证集做一次评估:
yolo detect val model=runs/face_detect/train_v1/weights/best.pt data=face.yaml split=val batch=16 device=0Python 写法:
model = YOLO("runs/face_detect/train_v1/weights/best.pt") metrics = model.val(data="face.yaml", split="val", device=0, batch=16) print(metrics.box.map50, metrics.box.map)验证跑完会在runs/detect/val下生成一堆图:混淆矩阵、P-R 曲线、F1 曲线,还有每张验证图上预测结果的可视化。先用眼睛过一遍可视化结果,再去看数值指标,两者结合才完整。
5.2 指标表:看懂这四个数就够入门了
| 指标 | 含义 | 人脸检测的参考值 |
|---|---|---|
| Precision 精确率 | 预测为“人脸”的框里,真正是脸的比例 | > 0.9 |
| Recall 召回率 | 所有真实人脸中,被找到的比例 | > 0.85 |
| mAP50 | IoU 阈值 0.5 时的平均精度 | > 0.9 |
| mAP50-95 | 多个 IoU 阈值的综合平均 | > 0.6 |
人脸检测的特点是“框偏一点问题不大”——后续识别阶段通常会把检测结果裁剪出来再缩放,所以 mAP50 是主要参考指标,mAP50-95 是更严格的综合参考。如果 mAP50 高但 mAP50-95 低,说明你的框经常不够贴合人脸,在密集场景里容易互相遮挡。
5.3 测试集单独评估,这是很多人漏掉的一步
训练过程中你一直在用 val 集选模型,相当于已经“看过” val 上的表现,再用 val 报最终成绩是不公平的。正确做法是留出一部分从训练到选模型全程没碰过的数据,等一切定稿后,最后跑一次 test 集,得到的结果才是这个模型的真实水平。
命令和验 val 一样,把split=val换成split=test即可。如果 test 的 mAP50 比 val 掉 5 个点以上,基本可以断定数据划分出了问题——要么是泄漏,要么是 val 规模太小不能代表真实分布。
还有一个我每次必做的检查:把测试集中最难的那几张图单独拿出来——密集人群、逆光、侧脸、模糊——跑预测看结果。指标是数字,面对真实场景的感受是另一回事,两者都要过关。
5.4 置信度阈值怎么定
验证报告里的 Precision 和 Recall 是某个阈值下的值,mAP 则是把所有阈值下的表现积分得到的。实际预测时你要通过conf参数选择阈值。
阈值高,漏检会变多;阈值低,误检会变多。人脸业务一般取 0.25~0.4。做安防监控,宁可多框几个背景也不能漏掉人脸,阈值调低;做考勤闸机,误检一次就开一次门,阈值就得调高。这个选择没有绝对的标准,全看你的业务容忍度。
6. 预测环节:命令行、Python API 与摄像头实时检测一次讲清
6.1 命令行预测
训练一切顺利,看一下新图片上的效果:
yolo detect predict model=runs/face_detect/train_v1/weights/best.pt source=test.jpg conf=0.35 iou=0.45 imgsz=640 save=Truesource参数非常灵活,可以传单张图片、整个文件夹、视频文件路径,也可以传0表示调用第一个摄像头。save=True会把标注框的图片保存到runs/detect/predict目录,show=True是实时弹窗显示,注意服务器没有桌面环境时不要开 show。
6.2 Python API:拿到坐标才算真正开始
命令行适合快速看效果,实际项目里还是要用 Python API 操作结果:
from ultralytics import YOLO model = YOLO("runs/face_detect/train_v1/weights/best.pt") results = model.predict(source="test.jpg", conf=0.35, iou=0.45, save=True, imgsz=640, device=0) r = results[0] for box in r.boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() conf = round(float(box.conf[0]), 3) cls = int(box.cls[0]) print(f"face {conf:.3f} ({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f})")box.xyxy返回的是像素坐标,左上角 (x1,y1) 到右下角 (x2,y2),方便直接在图片上画框或裁剪。box.xywhn才是归一化的中心点格式,别混淆。
如果要为后续的人脸识别准备数据,可以直接裁剪出人脸区域:
import cv2 img = cv2.imread("test.jpg") for i, box in enumerate(r.boxes): x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) face = img[y1:y2, x1:x2] cv2.imwrite(f"face_{i}.jpg", face)这几行代码就是整套识别系统里“检测”模块的标准输出形式,后续接特征提取模型完全够用。
6.3 摄像头实时检测
yolo detect predict model=runs/face_detect/train_v1/weights/best.pt source=0 show=True conf=0.35RTX 5060 跑 yolo11s 的摄像头实时检测,帧率非常宽裕,完全感受不到卡顿。如果觉得框线太粗影响观感,加一个line_width=2参数。
6.4 导出与加速:ONNX 和 TensorRT
模型训好只是第一步,落地部署通常要导出成其他格式:
yolo export model=runs/face_detect/train_v1/weights/best.pt format=onnx opset=12 yolo export model=runs/face_detect/train_v1/weights/best.pt format=engine device=0ONNX 是通用格式,跨平台、跨代码框架,后续可以接 ONNX Runtime,CPU 都能跑,新手首选。TensorRT(engine 格式)是英伟达专属的高性能推理引擎,在 50 系列显卡上能进一步压榨性能,但要在 NVIDIA 环境下使用。先学会 ONNX,再考虑 TensorRT,别一上来就搞最复杂的。
7. 训练人脸检测模型最容易踩的坑,以及我现在的默认参数
7.1 六个高频坑
| 问题 | 原因 | 解决办法 |
|---|---|---|
torch.cuda.is_available()为 False | PyTorch 版本太老,不支持 RTX 50 系列 | 安装 2.7+ 的 cu128 版本 |
训练报CUDA out of memory | batch 或 imgsz 太大 | 先降 batch 到 8 或 4,再考虑降 imgsz |
| mAP 始终为 0 | 标签 txt 里坐标可能超出 0~1 范围 | 抽样检查 labels 目录下的文件,确认归一化正确 |
| val 指标虚高,新场景一塌糊涂 | 同段视频的连续帧被分到了 train 和 val | 按场景而不是按帧切分数据 |
| 训练时报无法读取图片 | 路径里有中文或特殊字符 | 数据集路径全部用英文 |
| 小脸密集场景 mAP 上不去 | 分辨率不够,特征太小 | imgsz 提到 960 或 1280,batch 减半 |
7.2 我现在的默认训练习惯
先小规模试跑再全量跑,这是最重要的习惯。不管是新数据集还是新环境,先用 30 个 epoch、batch 8,确认没有任何报错、loss 能正常下降,再放开了跑完整训练。一上来直接跑 300 个 epoch,结果 20 分钟后才发现数据路径配错了,时间和电费都浪费了。
我最近几次人脸检测项目,默认参数基本就是 yolo11s、120 epochs、SGD + cos_lr、patience=20,imgsz 根据场景选 640 或 960。先以 val 的 mAP50 过 0.9 作为基线,达不到就检查数据而不是盲目换大模型。在 8GB 显存上,yolo11m 训练体验明显比 s 难受,精度提升却很有限,数据质量的收益永远比模型规模的收益来得快。
最后分享一个文档习惯:每次实验把命令连同 data.yaml 一起记录到项目笔记里,命名用train_v1、train_v2这种清晰递增的编号。你永远不知道自己会在哪一天需要回滚到三周前的某个配置,到时候没有记录就只能从头再跑一遍。折腾几次之后你就明白,训练人脸检测模型真正难的从来不是敲命令,而是管理好你的数据和实验痕迹。