
口罩佩戴检测这个毕设看起来只是“训练一个模型判断戴没戴口罩”但真正动手你会发现它牵扯的东西非常杂数据怎么来、标注怎么打、YOLO选哪个版本、训练半天精度上不去、最后又要做成一个能实时跑的视频检测界面。整个过程绕一圈下来几乎把深度学习项目从数据到部署的全流程都走了一遍。本篇就把我整理这套毕设开源项目包含源码论文的完整思路、实操步骤、踩坑记录和论文写作要点一次性梳理清楚内容适配有基础但没做过完整项目的学生也适合临时接手类似课题的开发者作为参考。1. 整体方案选型与核心思路拆解1.1 为什么是深度学习目标检测而不是传统图像处理接触过这类课题的人可能都想到过一个“捷径”先做人脸检测然后裁出人脸区域去做分类甚至直接用颜色统计、纹理分析判断口罩区域。这类传统方案看着简单实际一测就露馅光照一变、角度一偏、口罩颜色和肤色接近整个逻辑就崩了。核心原因在于传统特征工程是人手设计的很难覆盖真实场景里那种“宽松不确定”的条件。用深度学习目标检测本质上是让模型自己从大量标注样本里学习“什么是口罩”、“什么是人脸”、“什么人脸区域没有口罩遮挡”。这看起来只是换了个工具实际上是换了一套解决问题的思路不再以人脸为前置条件而是直接把目标定为三类——戴了口罩、没戴口罩、佩戴不规范。这个一张图就能同时输出多个目标的设定给后续做实时检测和统计分析省了很多事。1.2 模型选型YOLOv8为什么比老版本更适合毕设模型选择直接影响训练难度和答辩效果。老牌的选择是YOLOv5优点在于教程多、网上资料随处可见部署方案也成熟。但如果你把时间线放在如今YOLOv8在工程友好度和精度上都更有优势。YOLOv8默认采用anchor-free思路少了预设锚框的调参环节代码内部自动处理对新手更友好。主干网络用C2f结构替换了C3结构信息流动更充分小目标检测能力更强正好匹配口罩这种偏小尺寸的目标。框架内置了训练、验证、导出一条龙API从训练模型到导出ONNX只需几行代码对毕设这种工期紧张的项目非常关键。如果后续想在论文里加“创新点”YOLOv8更容易替换注意力模块比如SE、CBAM、CoordAtt改动位置清晰实验对比也好写。当然选择YOLOv8不完全等于无脑用最大版本。还是要回到硬件现实普通的笔记本显卡如GTX 1660、RTX 2060级别跑YOLOv8s绰绰有余算力更强的实验室机器或云GPU可以用YOLOv8m、YOLOv8l。我也见过用CPU训练YOLOv8n的选手100轮训了七八个小时不是不能跑但体验确实难受。所以选型号的时间点要放在搭建环境前先确认自己有什么硬件确定卡再去建环境能少走很多弯路。顺带提一点这个课题用SSD或Faster R-CNN也能做。Faster R-CNN精度尚可但实时性差摄像头场景很难达到流畅帧率SSD速度快、部署简单但小目标表现一般。综合精度、速度、训练难度、参考资料数量YOLO系列是最均衡的选择。1.3 整套系统的完整流程拆解这套系统表面上是“口罩检测”实际拆开是六个环节数据收集与清洗构造自己的训练数据集包含多种场景、光线、人脸角度。数据标注与格式转换三分类标注转成YOLO格式。模型训练与调参用YOLOv8在自建数据集上训练并评测指标。模型导出与推理封装导出ONNX模型封装成可供外部调用的检测器。桌面端/实时视频检测界面用PyQt5做界面接入摄像头实现实时识别。论文撰写与开源整理把以上内容组织成毕设论文并整理出可复现的代码仓库。其中最容易翻车的是第一步和第五步。很多同学把大量时间耗在训练某模型上等模型训好了才发现没有好用的界面最后答辩演示慌忙补一个简单的OpenCV窗口草草了事。我的建议是数据准备和界面开发一定要前置至少不要在最后一周才开始。2. 环境搭建、数据准备与标注实操2.1 环境配置版本对应关系远比想象中重要训练环境首选Windows Python 3.9或3.10显卡驱动NVIDIACUDA版本建议11.8及以上。PyTorch的安装不要直接pip install torch而是先到PyTorch官网查看对应的CUDA版本再安装否则可能出现“能装能用但GPU不可用”的尴尬。对于显卡驱动和CUDA的对应关系很多人不理解“驱动版本”和“CUDA运行库版本”的关系。简单说驱动是大管家CUDA是运行库PyTorch会调用运行库执行计算。只要驱动的版本够新就能运行多个CUDA版本的PyTorch但驱动版本太旧装了新版的CUDA也调用不了。所以环境出现问题先查驱动版本这个流程最省时间。基础依赖安装一般就是两行命令pip install torch torchvision pip install ultralytics # 或从源码安装 git clone https://github.com/ultralytics/ultralytics.git pip install -e ultralytics如果只有CPU环境就把torch安装为CPU版本训练慢归慢但至少能把配套的论文和接口全部跑通。环境搭建阶段我强烈建议用一个独立的虚拟环境避免不同项目之间包版本互相污染。这个习惯看着无所谓实际跑十几个实验的时候你会感谢它。2.2 数据集公开数据集打底自采数据保命网上能找到多个公开口罩检测数据集但直接把下载好的数据拿过来训练效果往往不够理想。因为公开数据集内容侧重点不在你现场演示的真实环境网络图片大多是新闻图、街拍角度、光线和教室、实验室差别很大。直接拿公开集训练放到摄像头下实测容易出现“打印出来的口罩”识别不准的情况。我的实践是用“公开数据集自建数据集”的组合策略。公开数据集负责提供基础的样本多样性特别是不同肤色、不同脸型、不同口罩样式的数据能极大增强模型的泛化性。自建数据集负责贴合你最终演示的真实场景。做法很简单拿手机或笔记本摄像头在教室、图书馆、实验室、宿舍走廊各拍一些素材。重点是覆盖不同时间段的光线条件用上课、吃饭、走路的姿势来变换角度必要时请几个同学配合一下一下午就能采集几百张高质量图片。这种混合策略的训练结果比单纯用公开数据有明显提升。最关键的是它能在答辩现场少出很多幺蛾子。自拍素材时要注意一点避免连续帧几乎相同。很多人拍视频后按帧提取图片结果连续几十张画面几乎一模一样等于一个样本反复训练了无数次对提升泛化能力没有帮助。正确做法是好比“拍照走一步再拍”或者按帧提取后每隔三五帧保留一张。2.3 标注实操三类好过两类框的质量决定模型上限标注环节用的是labelImg或X-AnyLabeling前者界面简洁、导出YOLO格式方便后者功能更强、支持半自动标注适合要标注上千张图片的大工作量场景。建议目标类别设置为三类with_mask正确佩戴口罩覆盖口鼻。without_mask未戴口罩面部清晰可见。mask_weared_incorrect口罩虽然戴了但露出口鼻或者下巴挡着。只做二分类也能满足基本检测但我建议做三分类。原因是“佩戴不规范”这个类别在你的论文实验分析里非常有用实属一个加分点而且标注时也不会增加太多工作量毕竟这类样本在自建数据集里很容易拍到。标注框的标准我在实践里总结为三条一是框要贴紧目标不包含大块背景下颌、额头这种模糊边界宁可框得紧凑一些也不要框得过松二是漏标比错标更可怕一张图片里漏掉一个“没口罩”的目标模型就会在这个位置学出一个错误信号三是对于遮挡严重、角度极端的困难样本直接放弃标注不要硬标因为标注者和标注者的判断标准不同训练时这些样本会引入额外的噪声。标注完之后默认会得到与图片同名的txt文件每行内容为class_id x_center y_center width height注意后面的四个值全部是归一化到0到1之间的小数。归一化是必须的因为模型输入图像可能被缩放成640×640如果标注信息不归一化换分辨率后所有框的位置就全乱了。2.4 数据划分与增强不是越多越好而是越合理越好数据划分我常用7:2:1即70%训练集、20%验证集、10%测试集。很多人只划分训练集和验证集这样最终论文里没法写“测试集上的最终准确率”还得临时补比较麻烦。划分的颗粒度也有讲究要按“文件夹”划分而不是按“文件”随机打乱。原因很简单如果同一个人的照片同时出现在训练集和验证集里验证指标会虚高答辩被追问时很难解释。数据增强方面YOLOv8内置了马赛克增强、随机翻转、色域调整。这些在线增强手段在执行训练时自动触发不需要额外生成图片文件。需要补充的场景是光线不足的夜拍或强逆光照片可以用albumentations做亮度、对比度微调但增强幅度不要过大否则模型会把“暗色调”误学成“口罩”的一部分。类别不平衡在这个课题里是必然的口罩戴得好的人总比摘口罩的样本多。处理方式有两种第一种是直接扩充“没戴口罩”的样本多采集一些路人摘口罩的照片第二种是给损失函数的不同类别分配不同权重。当样本差距小于五倍时用样本扩充就够了不必一上来就调损失权重否则容易把模型带偏。3. 模型训练、调参和性能分析3.1 数据配置文件与超参数解析训练前先准备一个数据集配置文件YOLOv8的配置语法# mask_detection.yaml path: dataset train: images/train val: images/val test: images/test nc: 3 names: 0: with_mask 1: without_mask 2: mask_weared_incorrectpath字段最好用相对路径不要在配置里写“C:/Users/xxx/...”这种绝对路径否则代码仓库发给别人之后别人要改一堆配置才能跑起来。关于超参数的初始值我的建议是batch_size先设16如果显卡显存不够比如报CUDA out of memory降到8或者4如果显存充裕到24G可以开到32甚至64。batch_size不是越大越好YOLO系列模型多数情况下16就能发挥出不错的收敛效果。epochs初期设成200配合早停机制。早停的意思是模型在验证集上连续多轮没变好就自动终止训练能节省大量时间。训练时每50轮保存一次模型最后再额外保存表现最好的和最后一次的模型。这个习惯很重要因为最后一次epoch往往不是表现最好的那个epoch。学习率这一项YOLO默认的lr0基本不用动除非你发现loss曲线剧烈震荡才需要把0.01调低到0.001。我用过几次手动调学习率之后发现对新手来说最容易出效果的做法是不动默认学习率先调整数据质量和epoch轮数数据问题解决了精度问题一半以上都跟着解决了。3.2 训练命令与日志解读在ultralytics框架下训练命令极简yolo detect train datamask_detection.yaml modelyolov8s.pt epochs200 imgsz640 batch16 patience30使用预训练权重yolov8s.pt本质是迁移学习。虽然预训练模型是在自然图像数据集上训练的类别里没有口罩但它学到的边缘、纹理等基础特征可以无缝迁移到口罩检测任务上不仅收敛快最终精度也比从零训练高出不少。如果你的数据量少更不要想着从零开始训练那个效果十有八九会让你头疼。训练过程中在终端里能看到四个关键指标曲线train/box_loss目标框回归损失数值下降代表框的位置预测得越来越准确。train/cls_loss分类损失用于判断类别是否正确。metrics/precision精确率代表“检出的目标中有多少是正确的”。metrics/recall召回率代表“所有应该是目标的样本中有多少被正确检出”。怎么看这组指标其实不是盯着某个数值单个看而是组合起来看。假如精确率很高但召回率很低说明模型比较保守只有非常像口罩的才敢框出来很多真样本被漏掉了反过来如果召回率很高但精确率低就是模型过于激进经常把相似的围巾、手部误判成口罩或者人脸。理想状态是两者同时趋向90%以上。如果这三个loss曲线中某个下降不动了不要盲目拉长训练轮数优先检查数据标注是否正确、正负样本是否过于悬殊。我见过有人为了提升数据集从几百张硬扩到几千张最后发现其中一半是同一段视频的相似帧白白浪费了算力。3.3 模型评估mAP不是唯一的答案模型训练结束后框架会自动在验证集上计算mAP。mAP0.5的含义是当预测框和真实标注框的IoU超过0.5时算检测正确取各个类别平均后的平均准确率。mAP0.5:0.95是把0.5到0.95的IoU阈值分成多档计算再取平均更严格。毕设论文里两个指标都要写但答辩时一般先看mAP0.5因为这是最直观的评价。如果追求论文里的数据更漂亮可以用“精确率precious / 召回率recall / F1-score”这种表格来辅助分析。它们和mAP的侧重不同F1-score是精确率和召回率的调和平均用来衡量全局表现比较直观。另外建议一定要跑一次混淆矩阵因为答辩时最有说服力的素材就是这个矩阵。它能直观展示每个类别的相互混淆情况有多少“佩戴不规范”被错误分类成了“未佩戴”有多少“未佩戴”被错误分类成了“正确佩戴”。针对混淆矩阵里的集中错误点去做数据补充是提升成绩最直接的方式。3.4 如何面对精度上不去的局面模型精度上不去第一反应不要调模型结构而是按这个顺序去排除数据质量检查。有没有标注框明显错位有没有漏标的难样本有没有同一画面反复出现导致的样本高度重合类别数量是否检查。如果某些类别只有几十张图直接把这类合并到二分类或者扩充样本。训练配置是否合理。batch_size是否明显过大导致训练震荡imgsz是否过低导致小目标信号丢失模型结构调整。最后一步才是加注意力机制、加小目标检测头等结构改进这部分可以服务于“论文创新点”但绝不是修复烂数据的办法。如果做了以上几步mAP稳定在85%左右对毕设来说已经是非常拿得出手的结果了。4. 实时检测系统与桌面端界面4.1 摄像头读取与画面预处理模型只是整个系统的一个零件真正的“系统”还得让它能把视频流跑起来。读取摄像头用OpenCV就行核心代码并不复杂cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) while True: ret, frame cap.read() if not ret: break cv2.imshow(detection, frame) if cv2.waitKey(1) 0xFF ord(q): break但这个基础代码存在两个问题。第一摄像头的默认输出分辨率经常是640×480画面在显示器上会明显模糊影响演示效果需要手动设置分辨率第二推理时如果直接使用大尺寸画面帧率下降会很厉害因此常见的预处理是先把帧缩放到640×640再做一次letterbox也就是保持宽高比填充到正方形避免图像变形。关于letterbox实际上YOLO框架在内部会自动处理这个操作但你要注意输入模型的数组必须是RGB顺序、通道顺序为height-width-channel并且除以255归一化到0到1范围。这些细节如果不处理好明明模型指标很好实际部署后结果却总是不稳定。4.2 推理模块封装成检测器类建议把模型推理封装成独立类这样做既可以提供给界面调用也能给命令行测试使用后续如果导出ONNX模型可以无缝替换实现。import cv2 import numpy as np from ultralytics import YOLO class MaskDetector: def __init__(self, model_path: str, conf_thres: float 0.4): self.model YOLO(model_path) self.conf_thres conf_thres def infer(self, frame_bgr): results self.model.predict(frame_bgr, confself.conf_thres, verboseFalse) boxes [] labels [] scores [] if results and results[0].boxes is not None: for box in results[0].boxes: boxes.append(box.xyxy.cpu().numpy().flatten().tolist()) scores.append(float(box.conf)) labels.append(int(box.cls)) return boxes, labels, scores置信度阈值conf_thres的选择有一定讲究。阈值设得太低比如0.2会导致很多误检框界面看过去满屏红框非常不专业阈值设得太高比如0.8又有可能放过真正没戴口罩的人。我用0.4到0.5之间居多具体还是要根据你训练出的模型表现来调整。4.3 用PyQt5搭建检测界面线程分离是关键毕设界面我用PyQt5最大原因是它和OpenCV的兼容性好在界面上显示视频流无非是把numpy数组转成QImage后更新到QLabel。但是界面卡顿的元凶几乎都出在“单线程”上摄像头读取和模型推理都是耗时任务如果都放在主线程里界面UI会等到当前帧处理完了才更新结果就是画面一卡一卡。正确做法是把摄像头读取和模型推理放进一个QThread工作线程把结果通过信号发回主线程更新界面。简化版示意from PyQt5.QtCore import QThread, pyqtSignal import cv2 class VideoThread(QThread): frame_signal pyqtSignal(object) stats_signal pyqtSignal(int, int, int) def __init__(self, detector): super().__init__() self.detector detector self.running True def run(self): cap cv2.VideoCapture(0) while self.running: ret, frame cap.read() if not ret: continue boxes, labels, scores self.detector.infer(frame) frame self.draw_boxes(frame, boxes, labels, scores) self.frame_signal.emit(frame) cap.release() def stop(self): self.running False self.wait()界面除了视频区域建议增加统计信息展示当前总人数、正确佩戴口罩人数、未佩戴口罩人数、佩戴不规范人数。这样答辩演示时对方能一眼看到系统的输出价值比单纯画框要有说服力得多。这些统计就是根据检测结果实时累加遇上同一个目标被重复检测的情况可以根据中心点位置做简单的去重处理。对于毕设来说同一目标重复计数的场景一般不会太严重但如果你接的是现场人流比较大的演示环境最好还是加一个简易的目标跟踪机制。另外如果使用情绪化的判罚逻辑要注意模型的误判间隔每隔几十毫秒刷新一次统计即可不要每帧都重新计数不然人数统计结果会跳来跳去显得很不稳定。4.4 模型导出与CPU部署方案论文里写“系统可以部署在低算力设备”是一回事实际能不能做到是另一回事。为了证明这一点最好的做法是把你训练好的模型导出为ONNX再用ONNX Runtime做一次CPU推理测试。导出命令在ultralytics框架里是from ultralytics import YOLO model YOLO(best.pt) model.export(formatonnx, imgsz640)这样会生成best.onnx然后推理就可以脱离PyTorch环境import onnxruntime as ort import numpy as np session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name output session.run(None, {input_name: preprocessed_image})[0]导出过程如果报错大概率是onnx版本和torch版本不匹配升级onnx到最新版基本能解决。CPU部署后的帧率取决于机器性能一般每秒10到20帧能达到接近实时作为论文实验完全够用。5. 论文写作框架与开源仓库整理5.1 论文骨架按系统流程组织而不是按学习顺序论文写作很多人的习惯是从“什么是深度学习”开始介绍洋洋洒洒写了三十页理论结果到自己的系统设计部分只有两页这是典型的比例错误。毕设论文最好按系统流程组织让读者跟着你的设计思路走而不是讲课。一套常规可用的章节结构是摘要交代背景、目标、采用方法、实验结论。第一章 绪论选题背景和研究意义、国内外现状、主要工作。第二章 相关技术介绍深度学习基础、目标检测理论、YOLO模型演化。第三章 系统需求分析与总体设计功能需求、性能需求、系统架构图。第四章 数据集构建与模型训练数据预处理、标注方案、YOLOv8训练、评价指标。第五章 检测系统设计与实现界面设计、检测流程、线程处理、部署方案。第六章 实验分析与总结不同模型对比、测试结果分析、不足与展望。这个骨架的要点在于每一章都能对应到代码仓库的某个文件夹或脚本写完论文后你能清楚说出“这段实验用的哪个脚本跑的”这对答辩质询帮助很大。5.2 中文论文用图规范与实验对比表系统架构图不用画得太花哨用Visio或draw.io画一个方框流程图就能满足要求关键是数据流向清晰视频输入 - 帧预处理 - YOLOv8模型推理 - 后处理NMS - 结果可视化与统计。实验对比表建议字段如下模型版本输入尺寸参数量帧率FPSmAP0.5mAP0.5:0.95YOLOv5s6407.2M3187.3%61.8%YOLOv8n6403.2M4288.1%63.2%YOLOv8s64011.2M3590.5%66.7%写这张表一定要注意公平性同一数据集、同一epoch数、同一输入分辨率。如果条件不统一结论就没有说服力答辩老师几乎一定会追问。“创新点”如果不知道怎么加可以把某注意力模块加入YOLOv8的骨干网络后和原模型做对比描述为“在骨干网络中加入通道注意力机制以增强对口罩小目标特征的表达”。你不用真的发明一个全新的模块把已有方法用在新场景里做分析在毕设层面完全够用。5.3 开源仓库整理质量比代码本身更重要“开源”不是把代码压缩包丢到网盘就完了。一个合格的毕设开源仓库应该满足README开头写明项目简介、训练集格式要求、测试效果图。提供requirements.txt或environment.yml保证换电脑后能一键安装。数据文件不要打进Git仓库图片动辄几百MB仓库会变得极其臃肿。代码中的绝对路径全部替换为相对路径或者用argparse接收外部参数。标注好的数据集目录结构和配置文件中path字段保持一致。另外一个细节容易被忽略训练好的权重文件如果是用GPU机器产出的大概率携带了模型结构信息和类名映射关系直接在CPU机器上运行推理是没有问题的但要注意torch版本不能差距太大否则加载权重时会报错报错信息经常会让你误以为模型损坏了实际只是版本不兼容。发布时可以打一个release标签把训练权重和演示视频附在release里这样对方不需要训练就可以直接体验效果。演示视频我建议专门录一段摄像头实时推理的短视频比静态图片更有冲击力。6. 常见问题速查与避坑实录6.1 高频问题对照表问题现象可能原因解决办法训练报CUDA out of memorybatch_size过大或模型过大降低batch_size到8/4或换更小的模型版本训练loss值显示NaN学习率太高/数据里有异常图片调低lr0到0.001检查图片是否有损坏文件精确率很高但召回率很低模型偏保守漏检很多降低conf_thres或增加困难样本数据视频检测画面卡顿推理在主线程或没有缩放帧使用QThread分离读帧与推理按640推理模型对特定角度检测很差数据中该角度样本太少定向补充对应角度的训练样本训练时突然中断想恢复训练中断后环境配置变化使用resume参数加载last.pt恢复训练ONNX导出报错torch与onnx版本兼容性差更新onnx到最新版本后重新导出摄像头画面镜像摄像头默认输出cv2.flip(frame, 1)翻转显示界面无法关闭进程占用摄像头PyQt没有释放摄像头资源在closeEvent里调用线程stop并release6.2 我在这类项目上踩过最深的一个坑初版我的数据集几乎完全依赖公开数据没有做现场场景拍摄结果现场演示时被人群中几个特殊角度的漏检搞得非常尴尬。后来补了现场照片之后效果才真正稳定下来。这件事让我明白一个道理深度学习项目里模型结构只决定分数的下限数据质量才决定分数能不能往上走。任何复现级别的模具换到你的现场环境都会出现不可控的差异唯一的解决办法就是补充现场上下文的数据。训练完成后最好在笔记本独立显卡和CPU两种环境下分别测试一遍。答辩现场的机器不一定是你的电脑提前确认兼容性问题能避免最尴尬的“现场给我装环境”场面。论文写作还有一个小技巧先写实验部分再回头写绪论和技术介绍。实验做完之后你对问题本质的理解已经比开题时深刻很多再写理论部分会更贴合你所用的方法。如果按章节顺序从头写到尾等到写实验时往往又发现前面的技术介绍有些地方写得不准确或过度使用了泛泛而谈的措辞。最后控制好时间数据采集和标注不要拖超过两周训练调参控制在两周内论文再留两周时间就会安全很多。