多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

口罩检测模型训练到部署全流程:YOLOv5参数调优与推理优化实战

口罩检测模型训练到部署全流程:YOLOv5参数调优与推理优化实战 简介基于深度学习的口罩检测系统是一份面向高校深度学习课程设计、毕业设计及期末大作业的完整工程资源利用YOLO目标检测算法实现口罩佩戴实时识别适合具备一定Python与深度学习基础的开发者学习参考。压缩包共45个文件以Python脚本、TXT标注/类别文件及模型配置cfg等为主整体仅2.64MB轻量易用。其中train.py、yolo.py、convert.py等脚本覆盖从数据标注、模型训练到推理检测的完整流程YOLOv3、Darknet53、YOLOv3-tiny三套配置便于对比不同网络结构的性能表现kmeans.py等工具还可辅助优化锚点设置。目前已有43人学习资料体量小但逻辑完整能帮助快速理解口罩检测项目的工程实现与调优思路对毕业设计答辩和课程作业提交具有较高参考价值。1. 口罩检测这个选题难点从来不在“训练”上口罩检测近几年几乎成了深度学习和计算机视觉的入门标配。一个“基于深度学习的口罩检测系统.zip”拿到手里最直接的疑问是解压之后能不能跑通我见过不少拿这类项目练手的朋友装完环境、跑完训练最后卡在“训练集看着还行拿到真实摄像头前一张脸都框不准”这一步。深挖下去问题通常不在模型结构而在数据、环境、参数和部署推理这几条暗沟。这篇帖子就顺着这条链路拆开讲让拿到这个方案的人在已有代码基础上看清每一步该干什么、参数怎么调、遇到怪现象从哪查起。目标很明确一两天内把它从“能跑通”推到“能用”而不是停留在训练完事。2. 数据是口罩检测的第一道坎选型、配比与标注转换2.1 模型选型为什么不是 Faster R-CNN也不是 EfficientDet做口罩检测本质是目标检测任务核心诉求是“快”和“准”的平衡。两阶段检测器比如 Faster R-CNN精度上限高但单帧推理慢在边缘设备上跑不动EfficientDet 精度不差但工程生态相对薄部署时踩的坑多。相比之下YOLO 系列在工程落地上是最顺的PyTorch 生态成熟、预训练权重好找、转 ONNX 再转 TensorRT 的路径已经被踩烂了。这就是为什么市面上多数口罩检测项目选 YOLOv5 做底座。具体到子版本选择YOLOv5s 是常见做法体积小、显存占用低、精度足够用。在 COCO 预训练权重上微调口罩这类单一类别任务通常几百张标注图就能收敛。YOLOv5m 或更大版本不是不能用而是收益递减明显——口罩目标的尺寸普遍偏小模型容量加大带来的增益会被小目标漏检吃掉一部分。对比维度Faster R-CNNEfficientDetYOLOv5s推理速度单卡慢几帧到十几帧中快实时工程生态一般一般好小目标能力强较强中但可通过数据增强补部署路径复杂中等ONNX/TensorRT 顺上手成本高中低如果你手里的“口罩检测系统.zip”里模型文件命名带yolov5s那基本就是这个选型思路。如果标题里只写了“基于深度学习”没写具体结构用 YOLOv5 微调是最稳妥的默认方案。2.2 公开数据集打底自制数据补场景这是数据配比的关键口罩检测的数据准备很多人上来就找“口罩数据集”四个字下载完就开工。但公开数据集的问题是场景单一大多是正面人脸侧脸、低头、遮挡、昏暗光线这些场景覆盖率低。常见的做法是用公开数据集打底然后针对性补充自己实际场景的数据。公开数据集里WIDER Face 带的人脸框可以作为负样本MAFA 数据集里有部分戴遮挡物的脸。另有一些专门标注了口罩的公开数据解压后通常是 VOC 格式的 XML 标注。我们一般会把这类数据作为训练集主体再自己拍或爬一些特定场景图——比如园区闸机、教室门口、商场入口——用 LabelImg 手动标注几百张。这里有个配比经验公开数据与自制数据的比例控制在 3:1 到 4:1 之间自制数据占比太低模型学不到你场景里的真实分布占比太高公开数据的泛化底子就白费了。标注时有个细节容易忽视口罩检测的目标类不是“人”而是“口罩”和“人脸”。更准确地说是“戴了口罩的脸”和“没戴口罩的脸”两个类别。有些人把所有“人”都框出来模型学到的就变成了“人体检测”口罩这个语义完全没学到。标注类别的定义决定了后面训练结果的上限。2.3 VOC 转 YOLO 格式转换脚本与四个边界坑YOLO 系列训练用的是 TXT 标注每行格式是class x_center y_center width height四个数值都是相对于图片宽高的比例范围 0 到 1。从 VOC 的 XML 转过来脚本本身不难难在几个边角情况。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, classes_dict): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes_dict: continue # 跳过未定义类别 cls_id classes_dict[cls_name] bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 关键XML里可能出现 xmax xmin 的脏数据 if x2 x1 or y2 y1: continue x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))这段脚本做了三件关键的事类别名映射、坐标归一化、脏数据过滤。坐标归一化为什么重要因为 YOLO 在训练时会按图片原始宽高做 letterbox 缩放如果你给的是绝对值坐标一旦训练时图片被 resize标注就全部错位。dirty data 过滤是血泪经验——公开数据集里经常混着xmax xmin的框不过滤的话训练 loss 会突然飙高又跌回来让人误判是学习率问题。四个边界坑里除了脏坐标还有三类一是图片本身就是 EXIF 旋转过的标注坐标是旋转前的训练出来的模型检测结果全部偏移二是类别名大小写不统一mask和Mask会被脚本跳过但你不自知三是归一化后宽或高为 0 的极细框训练时容易产生 nan loss四是中文路径Windows 上转换没问题但训练时读取会报编码错误。这几类问题用上面脚本的检查逻辑基本能拦住大半剩下的转入训练阶段再暴露。3. 训练环境与最小可跑链路Torch 版本、参数基准与显存策略3.1 深度学习环境配置版本不对跑起来全是玄学错误“基于深度学习的口罩检测系统”这类项目环境配置是拦截新手的第一堵墙。常见的错误是直接pip install torch拉最新版然后发现和项目里requirements.txt锁定的版本不匹配——CUDA 版本不兼容、torchvision 版本和 torch 对不上、opencv 版本冲突每一类都能制造出五花八门的报错。深度学习环境配置的关键方法论是先锁 Python 版本再锁 CUDA最后锁 PyTorch。Python 3.8 或 3.10 是主流选择CUDA 看显卡驱动版本旧驱动上硬装新 CUDA 会直接CUDA driver version is insufficient。深度学习所需要的编程语言层面训练阶段用 Python 没问题但如果你打算部署到摄像头边缘设备最终推理代码大概率要换成 C 或至少用 ONNX Runtime 的 Python API这个后面再展开。配置环境的推荐路径是 Conda 建虚拟环境再用项目自带的requirements.txt安装conda create -n mask python3.8 conda activate mask pip install -r requirements.txt装完后别急着训练先跑一条验证命令python -c import torch; print(torch.__version__, torch.cuda.is_available())torch.cuda.is_available()返回False的情况很常见但原因不止一种CUDA 没装、PyTorch 装成了 CPU 版、驱动太旧。先查nvidia-smi里的驱动版本再查pip list | grep torch里的包名——如果包名是torch而不是torchcu121这类带后缀的说明装的就是 CPU 版重新安装对应 CUDA 版本即可。这类问题排查顺序不对会浪费大量时间在无关的尝试上。3.2 YOLOv5 训练的最小命令与 7 个必调参数环境就绪后最小训练命令长这样python train.py --data mask.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --device 0mask.yaml是数据配置文件里面指定了训练集和验证集路径、类别数和类别名。yolov5s.pt是 COCO 预训练权重用它做初始化而不是从头训练收敛速度快一个量级。--img 640是输入分辨率--batch 16是批大小--epochs 100是训练轮数。7 个必调参数里前两个是--img和--batch。输入分辨率直接从 640 改到 320显存占用降约四倍但小目标检测能力会明显下降——口罩在画面上本来就小--img 320容易漏检。批大小决定梯度估计的稳定性数值太小小于 8训练曲线会剧烈抖动。第三个是--lr0初始学习率默认 0.01。小数据集上 0.01 偏高我一般降到 0.005 起步。第四个是--workers数据加载线程数默认 8在 Windows 上经常因为if __name__ __main__保护缺失报错改成 0 或 2 最稳。第五个是--patience早停耐心值默认 100小数据集上我习惯设 30 到 50省时间。第六个是--cache设为ram可以把图片一次性载入内存训练提速明显前提是内存够大。第七个是--weights这个容易被忽略——如果用--weights 强行从头训练单类口罩数据效果通常还不如用 COCO 权重微调。3.3 显存不够时的三种降载策略batch、图片尺寸与梯度累积口罩检测训练常见的现实约束是显卡只有 6G 或 8G 显存--batch 16 --img 640直接 OOM。此时有三种降载策略按推荐顺序排列。第一种是降 batch从 16 降到 8 或 4显存线性下降代价是训练噪声变大可以配合调低学习率抵消。第二种是降输入尺寸--img 512是一个中间值口罩这类中等目标受影响可控。第三种是梯度累积YOLOv5 的--nbs参数名义批大小就是这个用途设--nbs 64实际 batch 用 16优化器每 4 步才做一次权重更新等效于大 batch 的效果。python train.py --data mask.yaml --weights yolov5s.pt --img 512 --batch 8 --nbs 32 --device 0上面这组参数是我在 8G 显存卡上常用的起步配置实际 batch 8名义 batch 32每 4 步更新一次权重等效于 batch 32 的稳定性但显存占用只有 batch 8 的量级。这里有个细节用了--nbs之后学习率也应该相应调大因为优化器更新频率变低了。YOLOv5 会自动按 batch 比例缩放学习率但如果你手动指定了--lr0记得按比例修正否则模型收敛偏慢。这也是为什么很多人对比训练曲线时发现“别人 batch 64 比我 batch 16 收敛快”——不全是 batch 的功劳学习率也在起作用。4. 训练过程怎么看Loss 曲线、mAP 与多 Loss 权重调整4.1 三个 Loss 的下降曲线对应什么看到什么算正常YOLOv5 的训练日志会打印三行 Lossbox_loss、obj_loss、cls_loss分别对应边框回归、目标置信度、类别分类三个分支的损失。很多人盯着这三条曲线看“降没降”但不知道每条曲线异常时说明什么。box_loss下降曲线代表预测框和真实框的 GIOU 差异在缩小。这条曲线如果前 10 轮纹丝不动优先检查标注框格式是否转换正确尤其是x_center y_center width height是否被错误写成了x1 y1 x2 y2。obj_loss是最容易出问题的——它衡量的是“这个位置有没有目标”。如果这条曲线降得很慢常见原因是正负样本极度不均衡一张图里只有少数几个口罩框大部分区域是背景置信度分支学得慢。cls_loss在多类任务里区分能力至关重要但口罩检测通常只有两个类这条曲线一般不掉太多。正常的训练曲线特征三个 Loss 前 20 轮快速下降中段进入平台期缓慢下行后段偶尔小幅震荡。如果你看到box_loss已经很低但obj_loss还在高位大概率是背景误检——模型把衣服纹理、反光区域当成了目标。反过来box_loss高位但obj_loss很低说明模型能找到目标但框不准。4.2 如何调整多个 Loss 间的比例box、cls、obj 默认权重够不够多任务深度学习中Loss 权重的调整是个经典问题。YOLOv5 的默认权重不是写死的——训练时它会根据各 Loss 的数值量级自动平衡。具体机制是每个 Loss 除以它自身的历史均值让三个 Loss 处于同一量级再相加。所以理论上你不需要手动调它们的绝对比例。但实操中会遇到一个情况某个 Loss 数值突然爆高自动平衡机制被拉偏整体梯度方向被这个分支主导。这时候需要手动干预。YOLOv5 提供了--box、--cls、--obj三个超参数来控制各分支在总 Loss 中的增益系数。默认值是--box 0.05 --cls 0.5 --obj 1.0注意这里的数值不是直接权重而是乘以各自 Loss 之后的缩放因子。如果遇到obj_loss降不下去、背景误检多可以尝试把--obj从 1.0 提到 1.2 或 1.5相反如果置信度分支压得太狠导致召回率低就往回调。调整幅度建议一次不超过 20%每次调整后训练 20 轮再看曲线不要调一次跑 5 轮就下结论——Loss 曲线前几轮的噪声远大于信号。4.3 小数据集上的过拟合信号与早停取值口罩检测数据集通常不大几千张图是常态。过拟合的信号很典型训练 Loss 持续下降但验证集 Loss 在某个点掉头向上训练 mAP 逼近 1.0验证 mAP 停滞甚至回退。YOLOv5 自带的早停机制看的是验证集 mAP 是否连续patience轮没有刷新最佳值。这里有个参数细节--patience默认 100但对小数据集偏大。小数据集过拟合来得快100 轮耐心意味着你要多等很久才停。常见做法是设 30 到 50。还有一个容易被忽略的信号val阶段打印的P精确率和R召回率——如果P很高但R明显偏低模型偏向保守宁可漏报也不误报反之R高P低模型会乱报。口罩检测场景里漏报的代价远高于误报所以调参方向应优先保召回这属于典型的业务目标反推训练参数。如果验证 Loss 掉头向上再加数据增强是比调 Loss 权重更有效的修正手段。YOLOv5 的--hyp参数指向一个超参文件里面hsv_h、hsv_s、hsv_v控制颜色扰动flipud控制垂直翻转概率调高这些值可以增加数据多样性推迟过拟合的到来。但注意flipud对口罩检测有副作用垂直翻转后的“倒脸”在真实场景里几乎不存在加太多反而干扰模型学习正立人脸的特征分布。5. 口罩检测的四个高频翻车现场与排查方法5.1 推理时一张脸都框不出来预处理尺寸与 letterbox 的错位训练完模型跑推理脚本结果一张图一个框都没有这类“训练正常、推理全崩”的问题九成出在预处理环节。YOLOv5 训练时会把图片缩放到--img指定尺寸但不是简单 resize而是等比缩放后填充灰边letterbox。推理脚本如果直接把图片cv2.resize到 640×640没有做 letterbox模型看到的输入分布和训练时完全不同检测不出来是正常的。排查方法是把推理脚本读入的图片保存下来肉眼对比它和训练阶段预处理后的图是否一致。常见做法是直接用 YOLOv5 自带的letterbox函数处理推理输入。另一个高频原因是通道顺序训练时图片是 RGB但cv2.imread读出来是 BGR如果推理代码里没转模型看到的颜色通道全反了同样会全军覆没。这类问题用“把推理输入图打印出来看一眼”就能定位但很多人卡了好几天才发现是通道顺序。5.2 把下巴和口罩一起框进去锚框尺寸与 NMS 阈值检测框偏大、把口罩连同下巴甚至胸口一起框住是口罩检测里最常见的“框不准”问题。原因通常是两层一是默认锚框尺寸是为 COCO 数据集那 80 类目标设计的口罩这类小目标占比高的场景锚框尺寸偏大的话回归分支要费更多力气把框收紧结果就是收敛不彻底二是在训练脚本里没启用自动学习锚框。YOLOv5 在训练开始前会自动跑一遍 k-means 聚类学习适配当前数据集的锚框尺寸前提是--noautoanchor这个参数没被加上。如果你用的训练脚本里老老实实留了自动锚框那问题大概率出在 NMS 阈值上。推理时--conf-thres置信度阈值和--iou-thresNMS 阈值共同决定输出框。iou-thres默认 0.45如果设得太高比如 0.7相邻的高重叠框会被同时保留输出一堆冗余框设得太低也不行两个本来就该合并的框会被强制二选一框的边界就不稳定。调参思路conf-thres从 0.25 降到 0.15 看召回iou-thres从 0.45 调到 0.5 看框的稳定性。如果框还是偏大检查标注数据里是否存在大量“人脸加口罩”整块标注而不是只标口罩区域——标注的框偏大模型学到的回归目标自然偏大这属于数据问题不是参数问题。5.3 训练时 Loss 直接 nan学习率、AMP 与 BN 的锅训练跑到某个 epochbox_loss打印成nan训练进程还在继续但模型已经废了。这类问题三个主要原因。一是学习率太高。前文提过--lr0 0.01在默认配置下没问题但如果 batch 调小了却没按比例降学习率梯度更新步长相对变大Loss 容易冲爆。二是梯度累积溢出——PyTorch 默认用 FP32 训练但如果开启了 AMP自动混合精度FP16 下的梯度过小或过大都可能变成 inf 再变成 nan。三是 BatchNorm 的 batch 太小当--batch小于 8 时BN 层统计量抖动剧烈极端情况下方差为 0除零产生 nan。排查顺序先看第几个 epoch 开始 nan。如果是一开始就 nan检查数据和标注格式尤其是标注里有没有 width 或 height 为 0 的框这部分数据会被算成除零。如果训练中途突然 nan先关掉 AMP降低版本里是--amp默认关的再把学习率降一半最后把 batch 提到 8 以上。这三个动作大概率能救回来。这里有个容易误解的点——YOLOv5 的--nbs和实际 batch 处理会影响 BN 统计但 BN 用的是实际 batch 而不是名义 batch所以不要以为设了--nbs 32就能用 batch 4 去跑BN 照样会抖。5.4 CPU 推理 1 秒一帧模型裁剪、TensorRT 与推理框架的选择模型在 GPU 上跑得飞快部署到 CPU 或 Jetson 上就卡成幻灯片这是口罩检测项目里最现实的落坑现场。原因不复杂YOLOv5s 的 FLOPs 级别是 16G 左右CPU 上单帧推理耗时在几百毫秒到一秒以上全看 CPU 型号和是否做了优化。解决路径分三档。最低成本的是换更小的模型--weights yolov5n.pt或训练时直接用yolov5n.yaml结构模型体积和推理耗时直接砍一半代价是 mAP 掉 2 到 3 个点。第二档是导出 ONNX 用 ONNX Runtime 推理YOLOv5 官方提供了export.py导出后 CPU 推理速度比 PyTorch 原生快 30% 到 50%。第三档是量化PyTorch 的torch.quantization可以把权重从 FP32 压到 INT8速度进一步提升但精度损失明显口罩检测这种单类任务能承受得住。另一个常见全局问题推理脚本里在循环中重复加载模型和预处理函数。模型加载一次就够了但很多人把torch.load写进了循环里每帧都重新加载权重速度直接慢一个数量级。这类问题只要把耗时统计打出来对比load time和inference time立刻就能定位。6. 部署收尾与进阶从“能跑”到“好用”还差两步半6.1 模型验证的三种手段指标之外的确认方式训练日志里的 mAP 说明的是统计意义上的模型能力但上线前必须做三种手段的交叉验证。第一种是测试集指标把验证集换成完全没参与训练的数据重新评估一次 mAP 和召回率这是底线。第二种是单图可视化挑十几张包含正脸、侧脸、低头、遮挡、暗光等场景的图单张跑推理把框画出来保存成图片——这一步能发现指标看不出的问题比如“侧脸的框总是偏左半个身位”。第三种是视频连续帧用一段 60 秒的实景视频跑推理重点看连续帧之间框的稳定性框是平滑跟手还是抖动频繁这直接决定了用户观感。三种验证方式各有侧重点单张图看空间精度连续帧看时间稳定性测试集指标看整体水平。很多人只看第三项结果单张图一跑发现框全错位。6.2 YOLOv5 转 ONNX一条不太折腾的部署路径部署场景如果只有 CPU用 ONNX Runtime 是最常见的做法。导出命令一行python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 --batch 1导出后需要确认输入输出的形状。输入是[1, 3, 640, 640]输出是[1, 25200, 6]——25200 是三个尺度的预测框总数6 是x_center y_center width height confidence class。推理时读取输出的解析逻辑要和训练时的锚框设置保持一致否则解析出来的坐标全错。ONNX Runtime 推理的关键参数是providers设置。CPU 上用[CPUExecutionProvider]遇到onnxruntime版本和导出版本不兼容的报错优先固定onnxruntime1.15.0这类已知稳定版本——这是这条路径上最玄学也最常见的坑。6.3 多任务进阶口罩检测与人脸识别的方向做到“测口罩”之后很多人会往“戴口罩的人是谁”这个方向延伸——这就从单一检测任务变成了多任务深度学习一个人脸检测分支、一个口罩分类分支、一个人脸识别分支。多任务间的 Loss 比例调整策略参考第 4 章讲的原则按 Loss 量级归一化后再加权识别分支的 Loss 权重不宜一开始就调高否则检测分支会被带偏出现“认得出人但框不准脸”的尴尬局面。我自己做这个项目的习惯是训练阶段把断点续训打开--resume每 20 个 epoch 手动看一眼验证集的可视化结果而不是只看曲线部署阶段一定留一个“连续帧稳定性”的验收环节指标再漂亮框抖得像帕金森也没法用。这套流程虽然土但帮我拦下了不少“训练完以为万事大吉、一上视频就露馅”的翻车现场。希望这篇笔记能帮你在口罩检测这条路上少走几步弯路。本文还有配套的精品资源点击获取
返回列表