简介:基于Yolov5+Python实现的人脸识别、细粒度表情识别与异常行为检测源码,面向准备毕业设计、期末大作业或课程设计的高校学生,也适合希望快速上手YOLOv5视觉应用、开展人脸与行为分析的开发者。压缩包内共107个文件,以37个py源码文件、18个yaml模型配置、21个txt文本说明为核心,辅以10张jpg示例图、4张png图片,以及pt权重、ui界面、xml、mat和Dockerfile等文件,整体大小24.81MB,目录结构清晰,便于按功能模块查阅和二次修改。目前已有269人学习下载,属于导师认可的高分项目,各模块代码附有详细注释与关键说明,新手也能理解核心逻辑。系统涵盖人脸识别、细粒度表情识别和异常行为检测三大模块,功能完善、界面美观、操作简单,配置好依赖后即可直接运行,既能作为完整项目提交,也适合二次开发与算法学习,并配有基础UI便于交互操作。
1. 基于Yolov5+Python的人脸识别项目:三合一系统到底怎么落地
如果你正在找毕业设计或者课程设计的源码,大概率看过不少号称“人脸识别+表情识别+异常行为检测”的项目,但真正下载下来能跑通的没几个。这套基于Yolov5+Python实现的源码是我拆过的同类项目里比较省心的一套——三个核心功能打包在一个系统里,人脸识别负责“是谁”,细粒度表情识别负责“什么情绪”,异常行为检测负责“在干什么”,互相独立又能联动。代码里注释写得比较全,新手照着部署能跑起来,老手也能直接改检测头或者换backbone做二次开发。我先把整体结论放这儿:这项目不是那种PPT级别的demo,检测精度和界面完成度都够得上答辩演示,但前提是你得把环境配对、权重文件放对位置,否则翻车概率不小。下面我从选型逻辑到部署细节,一步步拆给你看。
2. 为什么是Yolov5而不是其他检测框架:选型逻辑与系统架构
2.1 Yolov5在轻量级视觉任务里的真实定位
先说一个很多人纠结的问题:人脸识别、表情识别、行为检测,这三个任务各有一堆专用算法,为什么偏偏用Yolov5统一做?我的看法是,对于需要同时跑三个任务的单机系统,Yolov5的性价比最高。人脸检测用MTCNN或者RetinaFace确实精度高,但你要在同一个进程里再跑表情分类和行为识别,显存和CPU占用会打架。Yolov5的检测头是解耦的,一个模型权重里可以同时输出多个类别的bbox,你只需要准备好不同任务的标注数据,训练成多类别检测即可。
这套源码的做法是:人脸识别和表情识别复用同一个检测模型,检测到人脸框之后,再通过分类分支输出身份ID和表情类别;行为检测单独用一个模型,检测目标是摔倒、挥手、奔跑这些异常动作。两个模型可以串行推理,也可以分两个线程并行处理,源码里默认是串行,方便新手理解流程。
2.2 整体架构与数据流
整个系统分成三层。底层是OpenCV负责视频流读取和帧预处理,中间层是Yolov5的推理模块,负责输出检测框、置信度和类别,顶层是业务逻辑层,把检测结果映射成“某某人在什么时间做出了什么表情,触发了什么异常预警”。这里有一个关键设计:异常行为检测的判定不是单纯看单帧的检测结果,而是结合了时序信息——连续N帧检测到同一行为才触发报警,避免单帧误检。
视频流输入 → 帧解码 → Yolov5推理 → 后处理(NMS) → 业务判定 → 结果可视化/报警后处理部分用的是Yolov5自带的NMS,置信度阈值默认设0.5,IOU阈值设0.45,这两个参数在异常行为检测里建议调低到0.4和0.4,因为行为检测的bbox通常比较大,目标重叠率低,适当放宽阈值能减少漏检。
3. 环境部署与依赖安装:从零到能跑通推理
3.1 Python环境与依赖版本对照
这是整个项目最容易翻车的一步,我直接给你一组能跑通的版本组合。Python建议3.8或者3.9,不要用3.10以上,因为有些老版本torch的算子不兼容。PyTorch用1.10到1.12之间都行,CUDA用11.3配套的版本。如果你机器没有NVIDIA显卡,CPU也能跑推理,只是速度会慢很多——Yolov5s模型CPU推理一帧大概200-400毫秒,做演示够用,做实时视频流就会明显卡顿。
# 创建虚拟环境,避免污染系统Python conda create -n yoloface python=3.8 -y conda activate yoloface # 安装PyTorch(以CUDA 11.3为例) pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 --extra-index-url https://download.pytorch.org/whl/cu113 # 安装项目依赖 pip install -r requirements.txtrequirements.txt里主要包含opencv-python、numpy、pandas、matplotlib、seaborn、tqdm、pyyaml这些常规库。注意一点:不要直接pip install opencv-python-headless,这个版本不带GUI模块,项目里的可视化窗口会起不来。装的时候用conda install opencv会更省心,会自动帮你解决ffmpeg的依赖。
3.2 目录结构与权重文件放置
源码下载解压之后,目录结构大概是这样的:detect.py是推理入口,train.py是训练脚本,models文件夹放的是Yolov5的模型定义文件,weights文件夹放预训练权重。第一次运行之前,你需要在weights文件夹里放入对应的.pt文件——人脸检测模型一个,行为检测模型一个。如果下载的压缩包里没带权重(因为文件太大经常被网盘砍掉),你就需要自己训练或者从官方Yolov5模型转换。
# 目录结构参考 project/ ├── detect.py ├── train.py ├── models/ │ ├── yolo5s.yaml │ └── common.py ├── weights/ │ ├── face_detect.pt │ └── action_detect.pt ├── utils/ ├── data/ └── runs/权重缺失是下载类项目最常见的坑,如果运行报错提示找不到.pt文件,先去确认weights目录有没有东西。如果确实缺权重,有两个补救方案:一是用Yolov5官方提供的yolov5s.pt做迁移学习,用自己的数据集微调;二是直接用开源的人脸检测权重(比如WIDER Face数据集上训练的模型)顶替。我个人建议先跑通推理流程再谈训练,不然排查问题的时候你分不清是代码问题还是权重问题。
3.3 首次运行与常见启动报错
代码跑通第一个里程碑是能弹出视频窗口看到检测框。运行命令很简单:
python detect.py --source 0 --weights weights/face_detect.pt --conf-thres 0.5--source 0代表读取摄像头,也可以传视频文件路径。如果你看到窗口弹出来但检测框没画上,先查cv2.imshow的显示逻辑——有些版本的OpenCV在高DPI屏幕上会把窗口放大导致看不见框,这个可以在代码里把窗口分辨率固定住解决。如果直接报错ModuleNotFoundError,大概率是依赖没装全,把requirements.txt逐行检查一遍。
4. 三大核心任务的代码实现:识别、表情与行为检测
4.1 人脸识别模块:检测框到身份映射的实现
人脸识别这块,源码用的是“检测+特征比对”的两段式思路。Yolov5只负责把人脸框出来,然后裁剪出人脸区域,送入特征提取网络得到128维特征向量,再和底库里的特征做余弦相似度比对。底库用pickle文件存储,格式是字典,key是人名,value是特征向量列表。为什么不用分类头直接输出人名?因为人脸识别是开放集问题——你不可能预先定义所有可能的人,而特征比对天然支持新增人脸的场景。
# 人脸特征提取与比对的核心逻辑 import cv2 import numpy as np import pickle from models.experimental import attempt_load from utils.general import non_max_suppression def extract_face_feature(face_img, feature_model): """提取人脸区域的特征向量""" # 预处理:缩放、归一化 face_img = cv2.resize(face_img, (112, 112)) face_img = face_img.astype(np.float32) / 255.0 face_img = (face_img - 0.5) / 0.5 # 转成模型输入格式:NCHW input_tensor = np.transpose(face_img, (2, 0, 1))[np.newaxis, ...] input_tensor = torch.from_numpy(input_tensor).float() with torch.no_grad(): feature = feature_model(input_tensor) # L2归一化,便于后续余弦相似度计算 feature = feature / np.linalg.norm(feature) return feature.flatten()这段代码里有两个参数需要注意:resize到112x112是人脸特征网络的固定输入尺寸,改成别的尺寸会导致特征质量下降;归一化用0.5均值0.5方差的方式,这个是和训练时保持一致的,不能改成ImageNet那套[0.485, 0.456, 0.406]的均值方差。余弦相似度阈值源码里设的是0.6,低于这个值判定为陌生人,实际使用的话建议在0.55到0.65之间调,阈值调低漏检少但误识别多,调高则反过来。
4.2 细粒度表情识别:多标签分类的工程处理
细粒度表情识别是这套源码里我觉得做得比较用心的地方。它不是简单分成高兴、难过那种七大类,而是引入了多个维度的描述——比如“开心-大笑”“开心-微笑”“惊讶-微张嘴巴”,这种细粒度标签的好处是能捕捉更多交互细节,但在工程实现上有个麻烦:一个表情可能同时属于多个维度,不能简单用softmax做单标签分类。
源码的处理方式是把输出层改成多个二分类头,每个头对应一个表情属性维度,损失函数用BCEWithLogitsLoss而不是CrossEntropyLoss。这样训练的时候,一张“开心大笑”的图,在“是否开心”和“是否大笑”两个头上都标1,在“是否生气”的头上标0。推理的时候,每个头独立sigmoid输出概率,超过0.5就认为是该属性命中。
# 多标签表情分类的推理逻辑 import torch import torch.nn.functional as F def predict_expression(face_crops, model): """ 输入: 多张人脸裁剪图 输出: 每张人脸的细粒度表情标签 """ labels_map = { 0: '开心', 1: '微笑', 2: '正常', 3: '惊讶', 4: '难过', 5: '愤怒' } results = [] with torch.no_grad(): logits = model(face_crops) # sigmoid将logits转为概率 probs = torch.sigmoid(logits) # 阈值0.5判定该属性是否命中 preds = (probs > 0.5).int() for i in range(preds.shape[0]): hit_indices = torch.nonzero(preds[i]).squeeze(1).tolist() face_labels = [labels_map[idx] for idx in hit_indices] results.append({ 'labels': face_labels, 'probs': probs[i][preds[i] == 1].tolist() }) return results细粒度表情识别这里有一个经验:阈值0.5是起步值,实际用的时候建议把“正常”类的阈值提高到0.6,把其他表情的阈值保持在0.5。原因是训练数据里“正常”表情占了绝大多数,模型对这类样本的置信度天然偏高,不提高阈值会出现所有表情都被预测成“正常”的情况。
4.3 异常行为检测:时序判定与报警触发机制
异常行为检测是整个系统里唯一涉及时序逻辑的模块。单纯的Yolov5检测是逐帧的,但“摔倒”这个行为,单帧看可能只是一个人躺在地上,需要结合前后帧的姿态变化才能判断是不是摔倒。源码的实现思路并不复杂:维护一个长度为10的滑动窗口,窗口内同一目标的行为类别变化满足特定模式才触发报警。
# 基于滑动窗口的行为判定 from collections import deque class BehaviorDetector: def __init__(self, window_size=10, alert_threshold=5): self.window = deque(maxlen=window_size) self.alert_threshold = alert_threshold def update_and_check(self, behavior_id, confidence): """ 每帧调用一次,传入当前帧检测到的行为ID 返回: 是否触发报警 """ self.window.append({ 'behavior_id': behavior_id, 'confidence': confidence }) # 统计窗口内同一种异常行为出现的次数 if len(self.window) < self.window.maxlen: return False abnormal_count = sum(1 for item in self.window if item['behavior_id'] == behavior_id and item['confidence'] > 0.5) # 连续超过一半帧数检测到异常行为则报警 if abnormal_count >= self.alert_threshold: self.window.clear() # 报警后清空窗口,防止连续重复报警 return True return False这个时序判定有几个参数决定了系统的灵敏度和误报率。window_size是滑动窗口长度,越长越稳定但响应越慢,10帧在30fps的视频流里是0.33秒的观察窗口,比较合理。alert_threshold是报警阈值,默认5代表窗口内一半以上的帧都检测到异常才报警。如果实际场景里误报频繁,可以把阈值调高到7或者8,代价是报警有延迟。
还有一个细节值得注意:self.window.clear()这个操作。报警之后清空窗口是为了防止同一个行为连续触发多次报警,但这也意味着如果异常行为一直持续,系统每10帧会报警一次。如果需求是“持续异常只要报一次”,你需要额外维护一个报警状态标志位,源码里没有做这个去重,你可以自己加上。
5. 避坑指南:训练与部署中的五个高频翻车点
5.1 现象:推理速度极慢,CPU占用100%但帧率不到5fps
原因:没有启用CUDA加速,PyTorch默认在CPU上跑推理。检查一下torch.cuda.is_available()返回的是不是True,如果环境装的是CPU版本的PyTorch,即使有显卡也用不上。
解决:重装CUDA版本的PyTorch,注意版本必须和你的显卡驱动匹配。可以用nvidia-smi查看驱动支持的CUDA版本,然后去PyTorch官网选对应的安装命令。装完之后在代码里加上torch.device('cuda' if torch.cuda.is_available() else 'cpu'),并把模型和输入tensor都to(device)。
5.2 现象:检测模型对特定角度的脸漏检严重,侧脸几乎检测不到
原因:训练数据里正脸样本占绝大多数,模型没有学到足够多的侧脸特征。这个在开源数据集里很常见,WIDER Face虽然数据量大,但侧脸样本比例依然偏低。
解决:如果你只是做演示,可以在数据增强里加上random_rotate和random_perspective,把角度变化范围加大。用Yolov5自带的超参数文件,在hyp.scratch.yaml里把hsv_h、hsv_s这些颜色增强参数适当调大,让模型见过更多样的输入。如果是实际项目,老老实实补充侧脸数据重新标注。
5.3 现象:表情识别在真实摄像头下准确率暴跌,和测试集表现差距巨大
原因:训练数据来自网络图片,和摄像头采集的人脸在光照、角度、分辨率上有分布偏移。这是一个典型的“域差异”问题。
解决:我一般会在部署前做一步简单的图像预处理统一——把摄像头帧里的人脸区域做直方图均衡化,让光照分布更接近训练集。另外把confidence阈值适当调低,因为域偏移会让模型输出的概率整体偏低,阈值太高会导致大量表情被判成“正常”。
5.4 现象:模型能检测到人的位置,但边界框抖动剧烈,行为判定不稳定
原因:视频流帧间检测框的位置和大小波动大,导致行为判定的输入不连贯。Yolov5本身的输出就对小目标比较敏感,稍微模糊一帧,框就飘了。
解决:在业务判断层之前加一个简单的卡尔曼滤波或者EMA平滑。EMA的做法是当前帧检测框中心和上一帧平滑结果做加权平均,权重取0.7的新帧比例。这个方法实现只要三行代码,但能显著减少抖动,代价是检测框会稍微滞后于真实位置,对人脸识别无影响,对行为检测影响可接受。
5.5 现象:视频流推理占用显存持续增长,跑十几分钟后程序被OOM杀掉
原因:视频处理循环里没有释放中间tensor,梯度计算图累积。推理模式必须包在torch.no_grad()里,很多人训练代码写惯了会忘记加。
解决:在detect.py的推理循环外面加上torch.no_grad(),循环内部每次处理的帧如果用到了numpy数组转tensor,记得在不用之后del掉。另外opencv的视频捕获对象VideoCapture用完之后要调用release()释放,否则摄像头资源一直被占着。如果是Docker部署,记得给容器加--shm-size参数,默认的64MB共享内存经常不够用。
6. 进阶调优:把自己的数据集接进来并加速推理
6.1 自定义数据集的格式转换与训练
你肯定不想一直用自带的模型,接自己的数据集是迟早的事。Yolov5训练自己的数据只需要两个东西:标注文件和配置文件。标注文件用YOLO格式——每个bbox一行,格式是class_id x_center y_center width height,坐标归一化到0-1。如果你手里的数据是VOC格式的XML标注,需要先转成TXT格式,网上有现成脚本,你也可以自己写一个,核心逻辑就是解析XML里的xmin、ymin、xmax、ymax节点,除以图片宽度和高度得到归一化坐标。
训练之前把数据集目录结构整理好:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── dataset.yamldataset.yaml文件里写上类别数和类别名,然后用train.py启动训练。如果你的数据集只有几百张图,建议用yolov5s模型配合迁移学习,把预训练权重带上,冻结backbone的前几层,只训练后面的检测头。这样即使数据量不大,收敛效果也远比从零训练好。我实测过500张人脸数据集,yolov5s训练50个epoch,在测试集上的mAP能达到0.82,够演示用。
6.2 推理加速:TensorRT导出与批量检测
如果你的机器有NVIDIA显卡,Yolov5支持直接导出TensorRT引擎,推理速度能提升三到五倍。导出命令很简单:
python export.py --weights weights/face_detect.pt --include engine --device 0 --imgsz 640导出TensorRT引擎之后,detect.py里会自动识别.engine后缀的文件并加载。注意导出的引擎是绑定显卡型号的,换一台显卡需要重新导出。此外,如果你检测的视频流分辨率不是640x640,不需要改输入尺寸,Yolov5会自动缩放再送进模型,不需要你手动预处理——这个细节很多人不知道,以为必须把帧到640x640才能检测,实际上源码里的letterbox函数会自动处理。
6.3 多线程并行推理与最终验证
一个能明显提升系统流畅度的做法是把摄像头读取和模型推理放两个线程,读取线程只管把帧丢进队列,推理线程从队列取帧检测。这样能避免每帧等待摄像头I/O的时间,让GPU一直处于忙碌状态。源码里没有实现这个优化,你可以用Python的queue和threading模块加上去,逻辑不算复杂。
最后我每次部署这个项目都会强制走一遍完整流程:先确认CUDA可用,再确认权重文件存在,然后跑一帧静态图片验证检测链路,最后接视频流验证时序逻辑。任何一步失败都先解决再往下走,不要急着上摄像头。希望这份拆解能帮你少走弯路,顺利把项目跑起来。
本文还有配套的精品资源,点击获取