简介基于YOLOv5与ArcFace的人脸识别项目资源面向具备深度学习基础的研究者与安防开发人员适用于监控、门禁、智能安防等需要端到端检测与身份认证的场景。压缩包共51个文件包含26个Python源码、9个预编译pyc模块、7个YAML配置、2个pth权重文件、4张人脸样例图片、Shell脚本、cfg及README说明整体仅3.41MB轻量且结构清晰便于快速部署和学习。已有1511人学习下载。工程内置yoloV5_face检测、arc_face特征识别及silentFace防伪模块目录分层组织模型、数据与工具脚本pyc文件可确保即插即用。资源覆盖数据预处理、模型训练、测试与调优全流程核心代码包括train.py训练脚本、recognition.py识别脚本、gen_data.py数据生成以及resnet、metrics、focal_loss等实现。YOLOv5采用分阶段网格预测ArcFace通过角度间距增强类间可分性两者组合兼顾速度与精度适合在有限算力下完成高鲁棒性人脸识别部署。同时附带的README和示例图片便于快速上手作为课程设计或工程落地参考均具实用价值。1. 从检测到识别YOLOv5ArcFace 这套组合为什么值得直接上手做视觉的同行应该都有体会人脸识别项目最磨人的不是模型选型而是「检测」和「识别」两段各自为战联调时经常翻车。这套基于 YOLOv5 做检测、ArcFace 做人脸特征提取的项目恰好把这两段串成了一条完整可跑的流水线。它解决的不只是「框出人脸在哪」而是「这个人是谁」的完整闭环适合刚接触深度学习人脸识别的开发者、需要快速做原型验证的算法工程师以及想在校招项目里拿出一套完整系统的同学。我拆过不少开源资源这套是少有的、能从训练数据格式一路跑到特征比对的项目值得照着复现一遍。2. 为什么是 YOLOv5 ArcFace选型逻辑与这套组合的边界2.1 YOLOv5 在检测环节的取舍速度与精度的平衡点人脸检测可选的方案不少MTCNN、RetinaFace、YOLOv5 都有人在用。MTCNN 是传统三阶段级联精度尚可但速度一般而且对遮挡和小尺度的鲁棒性比较差。RetinaFace 精度高但部署成本和训练成本都偏高。YOLOv5 的优势在于它本身是一个通用目标检测器社区生态成熟权重好找转 ONNX、TensorRT 都有现成方案而且推理速度在 GPU 上能做到实时。在这个项目里YOLOv5 扮演的角色是先把画面里的人脸位置框出来然后把裁剪后的人脸区域交给后续的特征提取模块。也就是说检测是前置环节它的质量会直接影响识别效果。2.2 ArcFace 在识别环节的优势为什么它比 FaceNet 更适合做人脸比对人脸识别的核心思路是把人脸图像映射成一个向量然后通过向量之间的距离来判断是否同一人。早期方法直接用 softmax 分类学到的特征判别力不够。FaceNet 提出了三元组损失效果不错但训练时三元组的选择非常影响收敛属于「玄学调参」。ArcFace 的做法是在 softmax 损失的基础上加了一个角度边界让类内距更紧凑、类间距更拉得开。它在 LFW、MegaFace 等公开数据集上的精度都明显优于之前的方案而且实现简单只需要在损失函数里改几行代码。这个项目选用 ArcFace等于直接站在了成熟方案的肩膀上不用自己去踩三元组损失的坑。2.3 这套组合的边界什么场景能扛住什么场景要另寻他路这套 YOLOv5 ArcFace 的组合适合的条件是光照相对稳定、人脸角度不过分夸张、摄像头画面质量过得去。如果你的场景是人脸在画面里只占几十个像素、或者有大面积遮挡、或者需要应对大规模底库比如上百万人的 1:N 检索那这套方案就需要额外处理。实际使用中我一般会在前端做一次人脸质量过滤把模糊、过小、脸不全的框直接丢掉不送进 ArcFace这样整体识别准确率能提升好几个百分点。这个细节在后面的代码里也会体现。提示这套组合适合做中小规模的人脸识别系统底库在几万到几十万量级时效果和性能比较均衡。3. 环境搭建与数据准备把依赖和训练数据理顺再动手否则后面全是坑3.1 环境依赖与项目目录结构这份资源里的代码依赖的核心库是 PyTorch、OpenCV、NumPyYOLOv5 部分用的是官方仓库的精简版本。建议用 Anaconda 建虚拟环境Python 版本锁定 3.8PyTorch 用 1.8 以上即可CUDA 版本根据你的显卡驱动来选不一定追最新。下面是环境搭建的常见做法# 创建虚拟环境锁定 Python 3.8 conda create -n face_recognition python3.8 -y conda activate face_recognition # 安装 PyTorch以 CUDA 11.3 为例实际版本请根据驱动调整 pip install torch1.10.0cu113 torchvision0.11.0cu113 -f https://download.pytorch.org/whl/torch_stable.html # 安装项目依赖 pip install opencv-python numpy scikit-learn pillow tqdm这串命令里最关键的是 PyTorch 的安装方式GPU 版和 CPU 版的性能差一个量级人脸特征提取在 GPU 上只需几毫秒在 CPU 上可能要到几十毫秒。有条件就上 GPU后面推理时你会感谢这个决定。项目目录一般会分为几个部分检测模型权重目录、ArcFace 特征提取模型目录、训练数据目录、推理脚本和工具脚本。代码包里已经预留好了对应的文件夹结构直接把下载的权重文件放进去即可。3.2 训练数据格式检测模型和识别模型各自需要什么标注格式YOLOv5 的训练数据格式是每张图片配一个同名的 txt 标注文件每一行是「类别 x_center y_center width height」其中中心坐标和宽高都是相对于图片宽高的归一化数值。人脸检测通常只有一类类别 ID 写 0 就行。ArcFace 的训练数据格式不同它是按目录组织的每个身份一个文件夹文件夹名就是该人的 ID文件夹内是该身份的多张人脸图片。训练时用 DataLoader 按目录读取每个文件夹相当于一个类别类别数量就是底库的人数。这份项目里附带的脚本可以完成两种格式的互相转换。如果你手头只有检测标注需要切出人脸图去训练识别模型常见做法是先用检测模型把人脸区域裁剪出来再按身份归类到对应文件夹。这里给出一段核心转换脚本# data_preprocess/yolo_to_arcface.py import cv2 import os def crop_faces_from_yolo(image_path, txt_path, save_dir, identity_id): img cv2.imread(image_path) h, w img.shape[:2] with open(txt_path, r, encodingutf-8) as f: for idx, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: continue cls_id, x_center, y_center, box_w, box_h parts box_w, box_h float(box_w) * w, float(box_h) * h x_center, y_center float(x_center) * w, float(y_center) * h x1, y1 int(x_center - box_w / 2), int(y_center - box_h / 2) x2, y2 int(x_center box_w / 2), int(y_center box_h / 2) # 边缘裁剪保护防止越界 x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) face img[y1:y2, x1:x2] if face.size 0: continue save_path os.path.join(save_dir, str(identity_id)) os.makedirs(save_path, exist_okTrue) cv2.imwrite(os.path.join(save_path, f{idx}.jpg), face)这段脚本逻辑上没什么花头就是把 YOLO 的归一化坐标还原成像素坐标然后按 ID 存文件夹。需要注意的两个细节一是边缘裁剪要做边界保护否则坐标越界会直接报错二是同一个身份的多张图要保证都传到同一个 identity_id否则识别模型的类别会乱掉。3.3 底库注册识别模型不是拿来直接用的先要建底库很多人拿到项目后直接跑识别脚本发现输出的相似度全是乱的原因在于没搞懂底库Gallery的构建流程。ArcFace 模型输出的只是特征向量你要先把你认识的每个人的照片都送进模型把每个人的特征向量存成一个「底库文件」识别时拍一张新脸和底库里所有特征向量比对取相似度最高的那个作为结果。这段逻辑对应的是项目里的「注册」功能代码里一般是循环读取底库目录下的图片逐张提特征存成 numpy 格式或者 json 格式。要特别注意底库的图片质量直接影响识别上限模糊的、角度偏的底库照片会让整个系统都跟着变差。提示底库照片每人大约 1-5 张即可选正脸、光线均匀的图片。超过 5 张并不会带来精度提升反而增加比对耗时。4. 核心代码走读从视频流到输出人脸 ID 的全链路实现4.1 YOLOv5 检测封装拿到人脸框的一手坐标这份项目的推理主流程是读入图像帧YOLOv5 检测出人脸框按置信度过滤后裁剪人脸ArcFace 提取特征最后做比对。第一步封装 YOLOv5 检测的常见做法是加载官方权重设置置信度阈值和 NMS 阈值# infer/detector.py import torch import cv2 class FaceDetector: def __init__(self, weights_path, devicecuda, conf_thres0.5, iou_thres0.45): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path) self.model.conf conf_thres # 置信度阈值低于此值的框直接丢弃 self.model.iou iou_thres # NMS 的 IoU 阈值抑制重叠框 self.device device if device cuda: self.model.cuda() def get_faces(self, frame_bgr): results self.model(frame_bgr) boxes results.xyxy[0].cpu().numpy() faces [] for box in boxes: x1, y1, x2, y2, conf, cls_id box if int(cls_id) ! 0: continue # 丢弃过小的人脸减少误检 if (x2 - x1) 20 or (y2 - y1) 20: continue faces.append((int(x1), int(y1), int(x2), int(y2), float(conf))) return faces这段封装里需要解释的是model.conf和model.iou这两个参数。conf建议设置在 0.4-0.6 之间设太低会出现大量误检框设太高又容易漏检侧脸和小脸。iou是 NMS 参数重叠框合并用的0.45 是常见经验值。还有一个小细节torch.hub.load首次运行会从网络拉取 YOLOv5 的模型结构代码离线环境需要先手动把仓库 clone 到本地再指定sourcelocal。4.2 ArcFace 特征提取预处理那一步马虎不得人脸识别模型对输入图片的尺寸、像素格式都很敏感。ArcFace 官方训练时用的是 112x112 的 RGB 输入推理时要做同样的预处理先把图片从 BGR 转成 RGB再 resize 到 112x112最后做归一化。很多人在这里直接用了 cv2.resize 就送进模型结果识别率大跌。# infer/feature_extractor.py import torch import cv2 import numpy as np from torchvision import transforms class ArcFaceExtractor: def __init__(self, backbone_path, devicecuda): self.device device self.model self._load_backbone(backbone_path) self.model.to(device) self.model.eval() # 预处理流程从 OpenCV 的 BGR 转为 RGB再归一化 self.transform transforms.Compose([ transforms.ToPILImage(), transforms.Resize((112, 112)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) def _load_backbone(self, path): # 项目里用的是 mobilefacenet 结构加载权重 from backbone.mobilefacenet import MobileFaceNet model MobileFaceNet(out_ch512) state_dict torch.load(path, map_locationself.device) model.load_state_dict(state_dict) return model def extract(self, face_bgr): face_rgb cv2.cvtColor(face_bgr, cv2.COLOR_BGR2RGB) tensor self.transform(face_rgb).unsqueeze(0).to(self.device) with torch.no_grad(): feature self.model(tensor) # 特征向量 L2 归一化方便后续用余弦相似度直接比对 feature torch.nn.functional.normalize(feature, p2, dim1) return feature.cpu().numpy().flatten()这里的Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))是将像素值从 [0, 1] 映射到 [-1, 1]和训练时的预处理保持一致。normalize那行是关键不做 L2 归一化后面用余弦相似度比对时就等于白白加了无关紧要的尺度信息结果会不稳定。MobileFaceNet 是我个人比较偏好的轻量骨干网络参数量小在嵌入式设备上能跑精度也够用。4.3 比对策略从欧氏距离到余弦相似度阈值怎么定特征提取完成后剩下的就是和底库特征做比对。这里常用的相似度指标是余弦相似度计算方式是矩阵乘法。底库特征通常拼成一个矩阵每行是一个人查询特征是一个向量两者做点积得到一组相似度分数取 top-1 作为识别结果。# infer/matcher.py import numpy as np class FaceMatcher: def __init__(self, gallery_path, threshold0.5): # gallery: npy 文件每行一个特征向量同时存一个 names 列表 data np.load(gallery_path, allow_pickleTrue).item() self.gallery_feats data[feats] # shape: (N, 512) self.names data[names] # list, 长度 N self.threshold threshold def match(self, query_feat): # 余弦相似度 点积因为特征已做 L2 归一化 scores np.dot(self.gallery_feats, query_feat) idx int(np.argmax(scores)) score float(scores[idx]) if score self.threshold: return self.names[idx], score else: return unknown, scorethreshold的取值直接决定系统的误识率和拒识率。阈值调高陌生人不容易混进来但认识的人也可能被拒阈值调低所有陌生人都可能被识别成某个人。我的习惯是先在验证集上把所有类内比对分数和类间比对分数画出来取两者交叉处的值作为初始阈值再按实际场景微调。5. 避坑与常见问题排查六个把新手卡到怀疑人生的点5.1 现象模型加载正常推理时显存溢出原因YOLOv5 和 ArcFace 两个模型同时加载在 GPU 上加上视频帧的数据缓冲显存直接爆掉。常见的是 4G 显存的入门显卡最容易触发。解决给检测和识别分开建两个推理进程或者把检测模型切到 CPU 上跑只把 ArcFace 放在 GPU 上。实际测试中YOLOv5s 在 CPU 上检测一帧大约 100-200ms如果视频流只需要每秒几帧这个方案完全能接受。5.2 现象识别准确率很低但单独看检测框都很准原因人脸裁剪出来后没有做对齐。ArcFace 在训练时用的是对齐后的人脸眼睛、鼻子、嘴巴的位置经过仿射变换摆正直接截框训练会引入大量角度变化拉低特征质量。解决项目里如果没带关键点检测分支建议在裁剪后至少做一次水平翻转判断或者用 OpenCV 的仿射变换把两只眼睛拉平。这个预处理步骤我之前也偷懒跳过后来在测试集上对比过对齐前后准确率差 8-12 个百分点不是小数目。5.3 现象程序跑通了但每帧推理耗时超过 300ms原因比对过程没有做批量处理。每帧都重新加载底库文件、循环算相似度这种写法最慢。解决底库在初始化时加载一次到内存比对操作全部用 numpy 矩阵运算不要写 for 循环。如果底库有几万条还能用 faiss 建索引把比对耗时的量级从 O(N) 降到近似 O(logN)。这套优化做完之后单帧推理能压到 50ms 以内。5.4 现象识别时出现过拟合训练集表现好、测试集一塌糊涂原因ArcFace 训练数据集合里同一个人的图片数量太少或者训练集和底库图片来自同一场景模型学到的是背景信息而不是人脸信息。解决训练时每个 ID 的照片至少要 10 张以上且要包含不同的角度、光照、表情另外在 DataLoader 里做随机裁剪、色彩抖动等数据增强强行让模型不依赖背景。这个就是典型的「看起来学得好实则学到背景」的坑。5.5 现象程序运行一段时间后内存持续上涨最后被杀原因OpenCV 的视频帧没有释放或者 PyTorch 的 tensor 累积在列表里没清。常见的是把检测框、特征向量都存在循环内的变量里没有及时清理。解决在循环体的最后把帧变量置为 None用del把大对象显式回收如果还不行用torch.cuda.empty_cache()在每处理完一批后清一次缓存。调内存问题花的时间往往比写功能还多建议一开始就养成这种习惯。5.6 现象换了摄像头或换了场景误检率突然升高原因YOLOv5 模型在训练时见过的人脸数据主要是自然场景如果是俯视画面、鱼眼镜头、或者黑白红外相机检测分布出现偏移。解决在检测结果后加一个人脸质量校验用传统方法检查人脸区域是否存在双眼和鼻子比如用 OpenCV 的 Haar 级联做二次校验挡掉一部分误检。再不行就得在目标场景下采集几百张图做微调训练。6. 工程化进阶把模型导出成 ONNX 做部署以及识别结果的可解释性验证当这套方案在本地跑通之后下一步大概率是被要求「把模型部署到服务端或者边缘设备上」。PyTorch 模型直接推理在服务器上没问题但到了嵌入式设备或者需要用 C 调用时导出成 ONNX 是必经之路。ONNX 格式的好处是跨框架、跨语言OpenCV DNN、TensorRT、ONNX Runtime 都能直接加载。导出流程如下# export/export_onnx.py import torch from backbone.mobilefacenet import MobileFaceNet model MobileFaceNet(out_ch512) model.load_state_dict(torch.load(weights/arcface_mobilefacenet.pth, map_locationcpu)) model.eval() dummy_input torch.randn(1, 3, 112, 112) torch.onnx.export( model, dummy_input, weights/arcface_mobilefacenet.onnx, input_names[input], output_names[embedding], dynamic_axes{input: {0: batch_size}, embedding: {0: batch_size}}, opset_version11, )导出 ONNX 时最常踩的坑是动态轴尺寸的设置。dynamic_axes把 batch 维设成动态这样部署时一次可以送入多张人脸图做批量推理。opset_version的选择也要注意太低的版本不支持某些算子太高的版本在老设备上可能不兼容11 是我目前用下来兼容性最好的折中值。导出后用 ONNX Runtime 验证一遍输出和 PyTorch 是否一致就是拿同一张图分别跑一次比较特征向量之间的误差是否在 1e-5 级别以内。部署时的性能权衡也值得提一句ArcFace 的网络本身很小MobileFaceNet 只有几百万参数和 YOLOv5 相比简直轻量。如果是嵌入式设备检测端才是瓶颈。我的做法是把 YOLOv5s 量化成 INT8精度损失大约 2%-3%但推理速度提升 2-3 倍识别端保持 FP32因为人脸特征的精度要求高INT8 会让特征一致性明显变差。最后说一个我自己的习惯每次调完阈值、修改预处理步骤或者换底库我都会拿固定的几组验证图片跑一遍全流程记录输出的 ID 和相似度分数放进一个表格里保存下来。这样下来每次改动的影响都是可追踪的不会出现「调了一下预处理自己都没注意识别率降了」的情况。这套项目本身已经具备了这个闭环验证的基础——检测、特征提取、比对三段都各有独立的输入输出改一段不影响其他段的调试。希望你在复现的时候也能用上这个方法能帮你少走不少弯路希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?