ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YOLOv7口罩检测实战:从93%精度模型到落地部署的避坑指南

YOLOv7口罩检测实战:从93%精度模型到落地部署的避坑指南

简介:本资源提供一套基于Yolov7的口罩检测模型,面向计算机视觉学习者、目标检测开发者及公共场所防疫监控方案设计者。模型可识别戴口罩、未戴口罩和未戴好口罩三类情况,精度约93%,支持实时检测,适用于机场、车站、商场等人员密集场景的智能监控与安全提醒。压缩包共150个文件,约669.41MB,包含24个py脚本、26个yaml配置、4个pt权重文件、17张png与46张jpg图像、5个xml标注及Dockerfile等,覆盖训练、推理与部署所需的核心文件。资源内附详细使用教程,指导加载模型与推理,并提供多个训练好的模型供不同场景选择。目前已有3047人学习下载,适合希望快速集成口罩检测能力、了解Yolov7工程实践的开发者参考。

1. 从一张 93% 精度的口罩检测模型说起:它到底能识别什么

上周帮朋友看一个园区门禁的改造需求,对方丢过来一个压缩包,说“这是之前找人做的口罩检测,你看看还能不能用”。解压之后看到events.out.tfevents.1657682492.cj-Dell-G15-5510.13620.0这种 TensorBoard 日志、Dockerfile、yolov7.iml,还有bus.jpg、test_batch1_pred.jpg、test_batch2_labels.jpg一堆测试图,基本能判断这是一个基于 YOLOv7 训练好的口罩检测工程,不是空壳 demo。它要解决的核心问题很具体:把画面里的人脸分成“戴口罩”“未戴口罩”“没戴好口罩”三类,精度标称约 93%,能直接拿来做公共场所的防疫监控或企业园区的健康检查。适合谁?一类是想快速验证口罩检测落地效果的算法工程师,另一类是有监控视频流、想接一个现成模型做二次开发的人。但别急着上生产,这个包里有几个坑我后面会拆开讲。

2. 拆开这个 YOLOv7 口罩检测包:目录、权重与推理链路

2.1 从文件清单反推工程结构

拿到一个深度学习项目,我习惯先看文件清单,因为它比任何 README 都诚实。这个包里出现的文件大致能分成四类:

文件/目录类型作用
events.out.tfevents.*TensorBoard 日志训练过程的 loss、mAP、学习率曲线,用来判断训练是否正常收敛
Dockerfile容器构建文件固化 CUDA、PyTorch、依赖版本,避免“我这能跑你那不能跑”
yolov7.imlIDE 模块文件PyCharm/IntelliJ 的工程标记,说明作者是在 IDE 里开发的
bus.jpg测试图YOLO 系列经典的公交车测试图,用来快速验证推理链路
test_batch1_pred.jpg/test_batch2_pred.jpg推理结果图模型预测框可视化,直接看效果
test_batch2_labels.jpg标注可视化训练/验证集的真值框,用来对比预测和标注的差距

runs文件夹是 YOLOv7 官方仓库的默认输出目录,训练权重best.pt、last.pt和配置文件一般都在里面。如果你拿到的包里runs是空的,那大概率只有推理代码没有权重,得先确认权重文件在不在。

2.2 推理链路:从一张图到三个类别

YOLOv7 是单阶段检测器,一次前向就出框和类别,不像 Faster R-CNN 那样先出候选区域再分类。口罩检测这个任务里,类别只有三个:mask(戴口罩)、no_mask(未戴口罩)、bad_mask(没戴好口罩)。推理链路可以拆成五步:

  1. 读图或读视频帧,缩放到网络输入尺寸(YOLOv7 默认 640×640)。
  2. 归一化到 0~1,转成 tensor,送到 GPU 或 CPU。
  3. 网络输出多尺度特征图上的预测框、置信度、类别概率。
  4. 做非极大值抑制(NMS),去掉重叠框。
  5. 把框映射回原图坐标,画框、写类别和置信度。

下面是一段我常用的推理骨架,基于 YOLOv7 官方detect.py改的,去掉了一堆参数,只留核心:

import torch import cv2 import numpy as np # 加载模型,weights 指向 runs/train/exp/weights/best.pt model = torch.load('runs/train/exp/weights/best.pt', map_location='cpu')['model'].float() model.eval() # 类别名要和训练时的 data.yaml 一致,顺序不能错 names = ['mask', 'no_mask', 'bad_mask'] img = cv2.imread('bus.jpg') h0, w0 = img.shape[:2] # letterbox 缩放,保持长宽比,短边补灰 img_resized = cv2.resize(img, (640, 640)) img_tensor = torch.from_numpy(img_resized[:, :, ::-1].transpose(2, 0, 1)).float() / 255.0 img_tensor = img_tensor.unsqueeze(0) with torch.no_grad(): pred = model(img_tensor)[0] # pred 形状 [1, 25200, 5+3],5 是 xywh+conf,3 是类别数 pred = pred[0] conf_thres = 0.25 mask = pred[:, 4] > conf_thres pred = pred[mask] # 这里省略 NMS 细节,实际用 torchvision.ops.nms print(f'检测到 {pred.shape[0]} 个候选框')

逻辑说明:torch.load读的是 YOLOv7 保存的 checkpoint,里面包含模型结构和权重;map_location='cpu'是为了在没有 GPU 的机器上也能加载。letterbox是 YOLO 系列的标准预处理,保持长宽比避免人脸被拉变形。conf_thres是置信度阈值,口罩检测场景我一般设 0.25 到 0.4,太低会误检,太高会漏检。类别顺序必须和训练时data.yaml里的names完全一致,否则会把“戴口罩”识别成“未戴口罩”,这种错误在监控场景里很致命。

2.3 训练侧的关键参数:为什么精度能到 93%

这个模型标称精度约 93%,从训练角度看,几个参数决定了它能不能达到这个数:

  • 输入尺寸:YOLOv7 默认 640×640,如果人脸在画面里很小,可以提到 1280,但显存和速度会翻倍。
  • batch size:单卡 16 或 32 比较常见,太小会导致 BN 层统计不稳。
  • 学习率:YOLOv7 官方用 SGD,初始 lr 0.01,配合 warmup 和 cosine 衰减。
  • 数据增强:Mosaic、MixUp、HSV 抖动,口罩检测里 Mosaic 能显著提升小目标召回。
  • 类别平衡:三类样本如果不均衡,比如“没戴好口罩”很少,模型会偏向多数类,需要过采样或调 loss 权重。

events.out.tfevents文件就是用来验证这些参数有没有起作用的。用tensorboard --logdir runs/train打开,重点看三条曲线:train/box_loss是否稳定下降、metrics/mAP_0.5是否爬到 0.9 以上、lr是否按预期衰减。如果 mAP 卡在 0.6 不动,大概率是标注质量或类别不平衡的问题,不是模型本身不行。

3. 把模型跑起来:环境、Docker 与批量推理的实操步骤

3.1 环境准备:Dockerfile 里藏着的版本约束

这个包里带了Dockerfile,这是好事,说明作者考虑过环境复现。YOLOv7 对 PyTorch 和 CUDA 版本比较敏感,我见过太多“pip install 完就报错”的情况。常见做法是直接看 Dockerfile 里的基础镜像和 pip 安装列表,把版本抄下来。

# 构建镜像,注意 Dockerfile 里的 CUDA 版本要和宿主机驱动匹配 docker build -t yolov7-mask:latest . # 启动容器,挂载当前目录和 GPU docker run --gpus all -it -v $(pwd):/workspace yolov7-mask:latest /bin/bash # 进容器后先验证 torch 能不能看到 GPU python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

参数说明:--gpus all让容器能用宿主机 GPU,前提是装了 nvidia-docker。-v $(pwd):/workspace把当前目录挂进去,这样训练输出和权重不会丢在容器里。如果torch.cuda.is_available()返回 False,先查宿主机驱动版本,再查 Dockerfile 里的 CUDA 版本,两者不匹配是血泪经验里最常见的翻车点。

3.2 单张图推理:先验证链路通不通

环境好了之后,别急着上视频流,先用bus.jpg跑一张,确认模型能出框。

python detect.py \ --weights runs/train/exp/weights/best.pt \ --source bus.jpg \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0

参数说明:--weights指向训练好的权重,--source可以是单图、文件夹或视频,--img-size要和训练时一致,--conf-thres是置信度阈值,--iou-thres是 NMS 的 IoU 阈值。跑完会在runs/detect/exp下生成带框的图,打开和test_batch1_pred.jpg对比一下,如果框的位置和类别对得上,说明推理链路没问题。

3.3 批量推理与视频流:从测试图到监控场景

单图通了之后,批量推理和视频流就是换--source的事。但监控场景有几个额外参数要注意:

# 批量处理一个文件夹 python detect.py --weights best.pt --source ./test_imgs --img-size 640 --conf-thres 0.3 # 处理视频文件,保存结果视频 python detect.py --weights best.pt --source ./monitor.mp4 --img-size 640 --conf-thres 0.3 --save-txt # 接摄像头,实时显示 python detect.py --weights best.pt --source 0 --img-size 640 --conf-thres 0.3 --view-img

--save-txt会把每个框的坐标和类别存成 txt,方便后续做告警逻辑,比如“连续 5 帧检测到 no_mask 就触发提醒”。--view-img是实时预览,调试时用,生产环境一般关掉。视频流场景里,--img-size不建议超过 640,否则帧率掉得厉害,除非你用 TensorRT 加速。

3.4 用 TensorBoard 回看训练:判断模型能不能信

events.out.tfevents文件不是摆设,它记录了训练全过程。我一般会重点看三个指标:

  • metrics/precision和metrics/recall:如果 precision 高但 recall 低,说明模型保守,漏检多;反过来则是误检多。
  • metrics/mAP_0.5:0.5 IoU 下的平均精度,93% 的标称精度一般指这个。
  • val/box_loss:验证集 loss,如果训练 loss 降但验证 loss 升,就是过拟合。
tensorboard --logdir runs/train --port 6006

打开浏览器看曲线,如果 mAP 在某个 epoch 后突然掉下去,可能是学习率没调好或者数据增强太激进。这些信息比任何口头描述都可靠。

4. 避坑与排查:口罩检测落地时最容易翻车的五件事

4.1 类别顺序错位:把“戴口罩”识别成“未戴口罩”

现象:推理结果里,明明戴了口罩的人被标成no_mask,或者反过来。原因:训练时data.yaml里的names顺序和推理代码里的names不一致。YOLO 输出的是类别索引,索引对应的名字错了,结果就全错。解决:打开训练时的data.yaml,把names列表原样复制到推理代码里,顺序一个都不能改。如果找不到data.yaml,用test_batch2_labels.jpg反推,看标注框的颜色和类别对应关系。

4.2 输入尺寸不匹配:小脸漏检、大脸框歪

现象:远处的人脸检测不到,或者框明显偏大偏小。原因:推理时--img-size和训练时不一致,或者预处理没做 letterbox,直接 resize 导致长宽比失真。解决:训练时用的 640,推理就用 640。如果场景里人脸普遍很小,可以提到 1280,但要重新评估速度和显存。letterbox 的补边颜色要和训练时一致,常见是灰色 114。

4.3 置信度阈值一刀切:误检和漏检两头不讨好

现象:阈值设 0.5 时漏检多,设 0.2 时误检多,怎么调都不满意。原因:三类样本的置信度分布不一样,“没戴好口罩”这类边界模糊的样本,置信度普遍偏低。解决:不要全局用一个阈值,可以按类别设不同阈值。比如mask用 0.4,no_mask用 0.3,bad_mask用 0.25。YOLOv7 的推理代码里改一下conf_thres的判断逻辑就行。

4.4 Docker 里 GPU 不可用:驱动和 CUDA 版本打架

现象:容器里torch.cuda.is_available()返回 False,或者报CUDA driver version is insufficient。原因:宿主机 NVIDIA 驱动版本低于 Dockerfile 里 CUDA 要求的版本,或者没装 nvidia-docker。解决:先在宿主机跑nvidia-smi看驱动版本,再对照 CUDA 版本要求表。如果驱动太旧,要么升级驱动,要么换一个低版本 CUDA 的基础镜像。别在容器里折腾驱动,驱动是宿主机的。

4.5 视频流延迟高:逐帧推理没有做跳帧

现象:接摄像头后画面卡顿,延迟好几秒。原因:每一帧都跑一次推理,GPU 再快也扛不住 30fps 的 640 输入。解决:跳帧处理,比如每 3 帧推理一次,中间帧复用上一次的结果。或者用 TensorRT 加速,YOLOv7 转 TensorRT 后速度能提升 2 到 3 倍。监控场景里,人走动没那么快,跳帧完全可接受。

5. 进阶技巧:用 test_batch 图做回归验证与阈值调优

5.1 把测试图当成回归用例

包里那几张test_batch1_pred.jpg、test_batch2_pred.jpg、test_batch2_labels.jpg不是随便放的,它们是最好的回归验证集。我一般会这样做:把test_batch2_labels.jpg当成真值,把模型在同样图片上的预测结果和它对比,数一数漏检了几个、误检了几个。如果预测图和标注图差距很大,说明模型在这个场景下不可信,得重新训练或微调。

具体操作上,可以写一个小脚本,把预测框和标注框做 IoU 匹配,统计 TP、FP、FN:

def iou(box1, box2): # box 格式 xyxy x1 = max(box1[0], box2[0]) y1 = max(box1[1], box2[1]) x2 = min(box1[2], box2[2]) y2 = min(box1[3], box2[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter / (area1 + area2 - inter + 1e-6) # 对每个预测框,找 IoU 最大的标注框,超过 0.5 算 TP # 没匹配上的预测框算 FP,没匹配上的标注框算 FN

这个脚本跑一遍,你就能拿到这个模型在你关心的场景下的真实 precision 和 recall,而不是只看标称的 93%。标称精度是在特定验证集上的,换一个场景可能掉到 80% 甚至更低。

5.2 阈值调优:用 PR 曲线找最佳工作点

置信度阈值不是拍脑袋定的。把模型在所有测试图上的输出按置信度排序,逐步降低阈值,画 precision-recall 曲线,找 F1 最大的点。口罩检测场景里,如果业务更怕漏检(比如防疫检查),就把 recall 调高,阈值降低;如果更怕误检(比如自动告警),就把 precision 调高,阈值升高。

我一般会准备一个小脚本,遍历 0.1 到 0.9 的阈值,输出每个阈值下的 TP、FP、FN,然后选一个业务上能接受的平衡点。这个过程比反复改代码试要快得多。

5.3 模型选择:多个权重怎么挑

项目里提到提供了多个训练好的模型,这很实用。不同权重的区别一般在:训练轮数、数据增强策略、输入尺寸。挑的时候看三个指标:在你自己的测试图上的 mAP、推理速度(FPS)、模型大小。如果部署在边缘设备上,模型大小和速度比精度更重要;如果是服务器端,优先选精度高的。

从那以后我每次拿到一个训练好的检测模型,都强制走一遍“测试图回归 + 阈值扫描 + 类别顺序核对”这三步,不再只看标称精度就上线。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进