基于Faster-RCNN的遮挡人脸检测实战:从源码解析到调参避坑

发布时间:2026/10/7 23:15:55
基于Faster-RCNN的遮挡人脸检测实战:从源码解析到调参避坑 简介这份资源是一套基于Faster-RCNN实现遮挡人脸检测的Python毕业设计完整项目面向计算机、人工智能、通信工程、自动化等专业的在校学生与教师也适合作为课程设计、作业或项目初期立项演示的参考。项目代码均经过实际运行测试答辩评审平均分达到94.5分具备较高的完成度与可复用性。压缩包共31个文件以20个py源码文件为核心辅以xml配置、txt类别与说明文档、ttf字体、md说明及dat模型数据等整体约73.92MB目录涵盖数据标注、模型训练、预测推理、mAP评估与视频检测等模块结构清晰。目前已有188人学习下载。读者可从中获得一套可直接运行的遮挡人脸检测方案理解Faster-RCNN在复杂遮挡场景下的网络搭建、锚框设计与ROI池化实现并借助README与脚本快速复现训练与测试流程也可在此基础上修改扩展用于毕设、课设或功能演示。1. 从一张遮挡人脸说起这套 Faster-RCNN 源码到底能跑出什么地铁闸机前口罩加帽子把脸挡得只剩一条缝普通检测器直接漏检——这是我第一次被遮挡人脸检测按在地上摩擦的场景。这套基于 Faster-RCNN 的 Python 毕业设计源码解决的正是这个问题在 VOC 格式数据集上训练一个能识别遮挡人脸的检测器附带完整的训练、推理、评估链路。资源包里是face_detect-master目录核心文件包括frcnn.py、train.py、predict.py、get_map.py还有shape_predictor_68_face_landmarks.dat这个关键的人脸关键点模型。它适合计算机、人工智能、通信工程等专业的在校学生做毕设或课设也适合刚入门目标检测、想找一个能跑通的完整项目练手的从业者。代码经过测试答辩平均分 94.5但别被分数迷惑——能不能复现取决于你对数据流和参数的理解。2. 拆开目录看骨架Faster-RCNN 的四个核心模块与数据流2.1 从frcnn.py到RoiPoolingConv.py主干网络怎么搭这套代码用的是 ResNet 作为 backbone配合 RPNRegion Proposal Network生成候选框再通过 ROI Pooling 把不同大小的候选框统一成固定尺寸最后送进分类和回归头。nets/resnet.py定义特征提取网络nets/RoiPoolingConv.py实现 ROI 池化nets/frcnn.py把整个流程串起来。和原版 Faster-RCNN 用 VGG 不同这里换成了 ResNet好处是梯度传播更稳训练时不容易炸。但代价是显存占用更高6GB 显存的卡跑 batch_size2 就有点吃力。我一般会先看config.py因为所有关键参数都在这里。比如self.num_classes要改成你的类别数加一背景self.input_shape决定输入图像尺寸默认是[600, 600]。如果你的人脸图片分辨率很高不改这个参数ROI 池化后的特征会丢失大量细节小脸直接检测不到。2.2 数据准备voc_annotation.py和get_gt_txt.py的分工VOC 格式的数据集需要两个文件train.txt和val.txt里面每行是图片路径加标注框坐标。voc_annotation.py负责把VOCdevkit/VOC2007/Annotations下的 XML 转成这种格式。运行前先确认VOCdevkit目录结构正确VOCdevkit/ └── VOC2007/ ├── Annotations/ # XML 标注文件 ├── JPEGImages/ # 原始图片 └── ImageSets/ └── Main/ # train.txt / val.txt然后执行python voc_annotation.py这个脚本会读取Annotations里的 XML解析出每个目标的bndbox写入2007_train.txt和2007_val.txt。注意如果你的数据集类别不是face要同步改model_data/voc_classes.txt里面每行一个类别名顺序必须和标注文件里的name一致。改完类别后config.py里的num_classes也要跟着变否则训练时分类头维度对不上直接报 shape 错误。get_gt_txt.py是评估阶段用的它把验证集的真实标注转成ground-truth目录下的 txt 文件供get_map.py计算 mAP。很多人跑完训练直接测 mAP发现结果全是 0就是因为没先跑这个脚本。2.3 训练入口train.py冻结与解冻的节奏train.py是训练主入口核心逻辑分两个阶段先冻结 backbone 训练几个 epoch再解冻全部网络微调。冻结阶段学习率设1e-3解冻后降到1e-4这是常见做法。代码里用Freeze_Epoch和Unfreeze_Epoch控制。# train.py 关键参数片段 Freeze_Epoch 50 # 冻结阶段训练轮数 Unfreeze_Epoch 100 # 解冻阶段训练轮数 Freeze_batch_size 8 # 冻结阶段 batch size Unfreeze_batch_size 4 # 解冻阶段 batch size显存不够就调小 lr 1e-3 # 初始学习率跑训练python train.py训练过程中会生成logs文件夹里面是 TensorBoard 记录。用tensorboard --logdirlogs可以看 loss 曲线。如果 loss 震荡厉害先检查学习率是不是太大或者 batch size 太小导致梯度噪声大。我一般会把Freeze_batch_size设成 4Unfreeze_batch_size设成 2虽然慢一点但稳。2.4 推理与评估predict.py和get_map.py怎么配合训练完权重保存在logs下predict.py加载权重做单张图片或视频推理。改predict.py里的model_path指向你的权重文件然后python predict.py它会读取img目录下的图片输出带检测框的结果。如果你想测视频用video.py把video_path改成你的视频文件路径。评估用get_map.py它会调用get_gt_txt.py生成的 ground-truth 和模型预测结果计算 mAP。跑之前确认classes_path和model_path都指向正确位置。mAP 低于 0.5 的话大概率是训练不够或者数据标注质量差先别急着调模型回去看标注框有没有画歪。3. 遮挡人脸检测的调参实战从 anchors 到 NMS 的五个关键点3.1 anchors 尺寸怎么定别照搬 COCO 的参数utils/anchors.py里定义了 RPN 的 anchor 尺寸和比例。默认是[128, 256, 512]三种尺度比例[0.5, 1, 2]。但人脸检测和通用目标检测不一样——人脸通常是竖长的宽高比集中在 0.6 到 0.9 之间。如果你直接用默认参数RPN 生成的候选框很多都不像人脸召回率上不去。我一般会改成[64, 128, 256]比例改成[0.5, 0.7, 1.0]。改完记得重新训练因为 anchor 变了RPN 的回归目标也变了。改太小会导致小脸检测不到改太大又浪费计算资源。如果你的数据集里人脸普遍偏小可以把最小尺度降到 32。3.2 学习率与 batch size 的搭配显存不够时的取舍config.py里lr默认是1e-3但这是针对 batch_size8 的情况。如果你显存只有 4GBbatch_size 只能设 2这时候学习率要相应降到2.5e-4左右否则梯度更新太猛loss 直接飞。常见做法是线性缩放lr base_lr * batch_size / 8。另外解冻阶段的学习率不要低于1e-5否则微调几乎没效果。我试过1e-6跑了 50 个 epochmAP 只涨了 0.3纯属浪费时间。3.3 数据增强遮挡场景下哪些增强真正有用代码里utils.py有rand函数做随机缩放和翻转。但遮挡人脸检测需要更有针对性的增强。我一般会加随机擦除Random Erasing模拟口罩、手、头发等遮挡物。具体做法是在utils.py的get_random_data里加一段# 随机擦除在图像上随机选一块区域置为均值 if np.random.rand() 0.5: h, w, _ image.shape erase_h np.random.randint(h // 10, h // 5) erase_w np.random.randint(w // 10, w // 5) y np.random.randint(0, h - erase_h) x np.random.randint(0, w - erase_w) image[y:yerase_h, x:xerase_w, :] np.mean(image)这段代码在每张图上以 50% 概率擦除一块区域强迫模型学习非遮挡部分的特征。注意擦除区域不要太大否则人脸信息全没了模型学不到东西。3.4 NMS 阈值遮挡人脸容易互相抑制非极大值抑制NMS的阈值在config.py里是self.nms_iou 0.3。这个值对普通人脸检测够用但遮挡场景下两个人脸靠得近IOU 容易超过 0.3导致其中一个被误删。我一般会调到 0.5让 NMS 更宽松。代价是可能多出一些重复框但漏检比误检更致命。如果调完还是漏检可以试试 Soft-NMS不过这套代码没内置需要自己改utils.py里的nms函数。改法是把 IOU 超过阈值的框的置信度按高斯函数衰减而不是直接置零。3.5 学习率衰减策略余弦退火比阶梯下降更稳代码默认用的是阶梯下降每 10 个 epoch 降一次。但我在遮挡人脸数据集上试过余弦退火mAP 能高 1.5 个点左右。改法是在train.py里把ReduceLROnPlateau换成CosineAnnealingLRfrom torch.optim.lr_scheduler import CosineAnnealingLR scheduler CosineAnnealingLR(optimizer, T_maxUnfreeze_Epoch, eta_min1e-5)T_max设成总 epoch 数eta_min是最小学习率。余弦退火的好处是前期学习率降得慢后期降得快适合需要精细微调的遮挡场景。4. 避坑与排查跑不通这套源码的五个血泪经验4.1 报错KeyError: face类别文件没对齐现象训练刚开始就报KeyError提示找不到face这个类别。原因model_data/voc_classes.txt里的类别名和 XML 标注里的name不一致。比如标注里写的是Face类别文件里写的是face大小写敏感。解决统一改成小写或者用脚本批量替换 XML 里的name字段。改完重新跑voc_annotation.py。4.2 显存溢出CUDA out of memorybatch size 和输入尺寸双杀现象训练到一半突然 OOM或者一开始就崩。原因input_shape设成[600, 600]batch_size 设成 86GB 显存根本扛不住。另外如果图片原始尺寸超过 1000px预处理时缩放不够也会撑爆显存。解决先把input_shape降到[416, 416]batch_size 降到 2。如果还不行检查utils.py里的get_random_data确保图片被缩放到input_shape以内。4.3 mAP 为 0get_gt_txt.py没跑或者路径不对现象训练 loss 正常下降但get_map.py算出来 mAP0。原因get_gt_txt.py生成的 ground-truth 文件路径和get_map.py读取的路径不一致。常见的是VOCdevkit/VOC2007/ground-truth目录不存在或者文件名对不上。解决先手动跑python get_gt_txt.py确认ground-truth目录下有 txt 文件且文件名和2007_val.txt里的图片名一一对应。4.4 预测框偏移严重anchor 和特征图没对齐现象推理时检测框位置明显偏了比如框在人脸左上角。原因改了input_shape但没改anchors.py里的 anchor 尺度导致 RPN 生成的候选框和实际目标尺寸不匹配。解决anchor 尺度要跟着input_shape等比缩放。比如input_shape从 600 降到 416anchor 也要乘以 416/600。4.5 训练 loss 不下降学习率太大或者数据标注有问题现象loss 一直在 10 以上震荡不收敛。原因学习率设成1e-2以上或者标注框坐标超出图片边界。解决先把学习率降到1e-4试跑 10 个 epoch。如果还不降用脚本检查 XML 里的bndbox坐标是否在[0, width]和[0, height]范围内。超出边界的框会导致回归目标异常loss 根本降不下来。5. 进阶技巧用关键点模型辅助遮挡判断与 mAP 验证5.1 用shape_predictor_68_face_landmarks.dat做遮挡区域标记资源包里带了 dlib 的 68 关键点模型很多人不知道它能干嘛。我的用法是先用 Faster-RCNN 检测出人脸框再用关键点模型预测 68 个点然后判断哪些点被遮挡了。具体逻辑是如果某个关键点周围的像素方差很小比如口罩区域颜色均匀就认为该点被遮挡。这样可以在检测框基础上输出一个遮挡区域掩码对答辩演示很加分。import dlib import cv2 import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) img cv2.imread(face1.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces detector(gray) for face in faces: landmarks predictor(gray, face) for i in range(68): x, y landmarks.part(i).x, landmarks.part(i).y # 取关键点周围 5x5 区域算方差 patch gray[max(0,y-2):y3, max(0,x-2):x3] if patch.var() 50: # 方差小说明区域平滑可能是遮挡物 cv2.circle(img, (x, y), 2, (0, 0, 255), -1) else: cv2.circle(img, (x, y), 2, (0, 255, 0), -1) cv2.imwrite(landmark_occlusion.jpg, img)这段代码把方差小于 50 的关键点标红其余标绿。阈值 50 是经验值你可以根据数据集调整。方差越小说明该区域越平滑越可能是口罩或手遮挡。5.2 验证 mAP 的完整流程从get_gt_txt.py到get_map.py很多人跑完训练直接看 loss觉得 loss 低就行。但 loss 低不代表 mAP 高因为分类和回归的 loss 权重不同。正确的验证流程是步骤命令作用1python get_gt_txt.py生成验证集 ground-truth2python get_dr_txt.py生成模型预测结果3python get_map.py计算 mAPget_dr_txt.py会遍历验证集图片输出每个检测框的坐标和置信度到detection-results目录。get_map.py对比ground-truth和detection-results按 IOU 阈值 0.5 计算 AP最后取平均。如果 mAP 低于预期先看detection-results里的框是不是太少——可能是置信度阈值设太高了config.py里self.confidence 0.5可以降到 0.3 试试。5.3 一个具体技巧用Vision_for_anchor.py可视化 anchor 匹配情况Vision_for_anchor.py这个脚本很多人忽略但它能画出每个 anchor 和真实框的匹配情况。跑一下python Vision_for_anchor.py它会输出一张图绿色框是正样本 anchor红色框是负样本。如果绿色框很少说明 anchor 尺度和目标尺寸不匹配回去改anchors.py。如果绿色框集中在某个区域说明数据分布不均匀需要检查数据集。我每次改完 anchor 参数都会跑一遍这个脚本比盲调高效得多。从那以后我每次改完 anchor 或输入尺寸都强制走一遍Vision_for_anchor.py可视化确认正样本比例在 30% 以上才开训练。希望帮到你。本文还有配套的精品资源点击获取