☰
YOLOV5口罩佩戴检测实战:从数据集到边缘部署全流程
2026/10/11 22:40:09 网站建设 项目流程

简介:这份资源是面向计算机相关专业学生与项目实战学习者的YOLOV5口罩佩戴检测完整方案,可直接用于毕业设计、课程设计或期末大作业。内容涵盖数据集、项目源码、训练好的模型权重以及标注好的数据,形成从数据准备到模型推理的闭环,帮助读者省去自行采集与标注的繁琐环节,快速复现并理解目标检测全流程。压缩包共149个文件,约139.76MB,以yaml配置文件、py源码、pyc编译文件、jpg与jpeg图像样本为主,另含pt模型权重、sh脚本、md说明文档、Dockerfile及ipynb笔记本等,兼顾训练、部署与文档阅读需求。目前已有238人学习下载,项目经导师指导并认可,代码经过严格调试,可运行性有保障。读者可据此掌握YOLOV5的配置、训练与推理方法,并在此基础上完成功能扩展或论文撰写。

1. 口罩佩戴检测这套数据集,拿到手第一件事该干什么

工地入口的闸机摄像头每天抓拍几千张人脸,安全员盯着屏幕找没戴口罩的人,十分钟就眼花。换成基于 YOLOV5 的口罩佩戴检测,单帧推理压到十几毫秒,漏检率还能压到个位数——这就是为什么口罩佩戴检测数据集、标注好的数据、训练好的模型这套组合,在工业现场和园区管理里一直有人反复折腾。你手上如果正好有一个「数据集+代码+训练好的模型+标注好的数据」的压缩包,别急着解压就train.py,先搞清楚三件事:标注格式是 YOLO txt 还是 VOC xml、类别到底是几类、预训练权重和你的类别数对不对得上。这三件事任何一件错了,后面训练 loss 不降、mAP 上不去,你连问题出在哪都找不到。这篇笔记按「先看懂数据、再跑通推理、最后自己训练和调参」的顺序拆,适合刚拿到数据集想复现的新手,也适合想把模型往 RK3568 或树莓派上搬的熟手。

2. 拆开压缩包:标注格式、类别定义与目录结构核对

2.1 先确认标注是 YOLO 格式还是 VOC 格式

YOLOV5 训练只认一种标注:每张图对应一个同名.txt,每行class_id x_center y_center width height,坐标全部归一化到 0~1。但很多口罩数据集是从 LabelImg 直接导出的 VOC xml,或者从 COCO json 转过来的。你解压后先看labels目录里是.txt还是.xml,这一步决定了你要不要写转换脚本。

# 看标注文件后缀分布,一眼判断格式 find ./dataset -type f \( -name "*.txt" -o -name "*.xml" -o -name "*.json" \) | \ sed 's/.*\.//' | sort | uniq -c # 抽查一个标注文件的前几行,确认是不是归一化坐标 head -n 5 ./dataset/labels/train/000001.txt

第一段命令统计后缀,如果全是txt基本就是 YOLO 格式;出现xml就要转。第二段head看内容,正常 YOLO 行长这样:0 0.453125 0.512000 0.093750 0.120000,五个数,第一个是类别索引,后四个都在 0~1 之间。如果你看到的是0.453125 0.512 0.093 0.120只有四个数,说明这个数据集把类别单独放在文件夹名里了,得补上类别列。

提示:归一化坐标出现大于 1 的值,说明标注时用的是绝对像素坐标,直接喂给 YOLOV5 会训练崩,必须先除以图像宽高。

2.2 类别定义必须和 data.yaml 完全对齐

口罩检测常见的类别划分有三种:两类(mask/no_mask)、三类(mask/no_mask/mask_incorrect)、四类(再加face_with_mask之类)。类别数直接决定模型检测头输出通道,写错了训练时不会报错,但推理结果全是乱的。

# data.yaml 示例,路径按你解压后的实际位置改 path: ./dataset train: images/train val: images/val test: images/test nc: 2 names: ['mask', 'no_mask']

nc是类别数,names的顺序必须和标注文件里class_id的数值一一对应。比如标注里0代表戴口罩、1代表没戴,那names[0]就必须是mask。我见过有人把names写成中文,训练能跑,但推理画框时标签乱码,排查半天。path用相对路径,train/val/test指向图片目录,YOLOV5 会自动去找同级的labels目录。

2.3 目录结构核对与数据量统计

标准 YOLOV5 目录长这样,你对照检查:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml
import os from pathlib import Path root = Path("./dataset") for split in ["train", "val", "test"]: imgs = list((root / "images" / split).glob("*.*")) lbls = list((root / "labels" / split).glob("*.txt")) print(f"{split}: {len(imgs)} images, {len(lbls)} labels") # 检查图片和标注是否一一对应 img_stems = {p.stem for p in imgs} lbl_stems = {p.stem for p in lbls} missing = img_stems - lbl_stems if missing: print(f" 缺标注的图片: {list(missing)[:5]}")

这段脚本统计每个 split 的图片数和标注数,并找出有图无标注的样本。正常情况两者数量应该相等。如果train有 5000 张图但只有 4800 个标注,那 200 张图要么是负样本(可以保留,YOLOV5 支持空标注),要么是漏标了。负样本对降低误检有帮助,但比例别超过 10%,否则模型会偏向预测背景。

3. 用训练好的模型先跑通推理,再谈训练

3.1 环境安装与权重加载

拿到「训练好的模型」先别急着重新训练,用detect.py跑几张图,确认模型本身是好的。这一步能帮你排除掉「模型没问题但环境有问题」的情况。

# 建议 Python 3.8+,PyTorch 按你的 CUDA 版本装 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt # 用自带权重跑单张图,--weights 指向压缩包里的 .pt 文件 python detect.py \ --weights ./weights/best.pt \ --source ./dataset/images/test \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0

--weights是训练好的权重路径,--source可以是单张图、目录或视频。--img-size 640是推理分辨率,必须和训练时一致,否则精度会掉。--conf-thres是置信度阈值,默认 0.25,口罩检测场景如果误检多就往上调到 0.4~0.5。--device 0指定第一块 GPU,没有 GPU 就写cpu,但速度会慢十倍以上。跑完结果默认存在runs/detect/exp下,打开图片看框和标签对不对。

3.2 推理结果不对时的三个排查方向

如果框的位置明显偏移、标签全是同一个类、或者一张图几百个框,按这个顺序查:

第一,类别数不匹配。用python -c "import torch; ckpt=torch.load('best.pt', map_location='cpu'); print(ckpt['model'].nc, ckpt['model'].names)"打印权重里的类别数和名称,和你的data.yaml对比。第二,--img-size和训练不一致。第三,权重文件损坏,重新解压或换一个.pt试。

import torch ckpt = torch.load("./weights/best.pt", map_location="cpu") model = ckpt["model"] print("类别数:", model.nc) print("类别名:", model.names) print("输入通道:", model.yaml.get("ch", 3))

这段代码直接读权重里的元信息,model.nc是类别数,model.names是类别名列表。如果nc是 2 但你的data.yaml写了 3,推理时就会出错或结果异常。model.yaml里还能看到网络结构配置,比如是不是yolov5s。

3.3 批量推理与结果导出

现场部署前通常要跑一批测试图,统计漏检和误检。用目录作为--source,加--save-txt把检测框坐标存下来,方便后续用脚本算指标。

python detect.py \ --weights ./weights/best.pt \ --source ./dataset/images/test \ --img-size 640 \ --conf-thres 0.3 \ --save-txt \ --save-conf \ --project ./runs/test_infer \ --name exp1

--save-txt会在结果目录生成每张图的.txt,格式和标注一样,--save-conf额外保存置信度。--project和--name控制输出路径,避免每次覆盖。跑完后你可以写个脚本对比预测 txt 和真实标注 txt,算一下 mAP 或者简单的准确率召回率。

4. 自己训练:从超参数到训练曲线的完整流程

4.1 训练命令与关键参数

确认推理没问题后,开始训练。YOLOV5 的训练入口是train.py,核心参数就那几个,但每个都影响很大。

python train.py \ --data ./dataset/data.yaml \ --weights ./weights/yolov5s.pt \ --cfg ./models/yolov5s.yaml \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --workers 8 \ --device 0 \ --optimizer SGD \ --lr0 0.01 \ --lrf 0.01 \ --cos-lr \ --project ./runs/train \ --name mask_exp1

--weights这里用官方预训练权重(不是训练好的口罩模型),迁移学习能大幅加快收敛。--cfg指定网络结构,yolov5s最轻量,适合边缘部署;yolov5m/l精度更高但速度慢。--epochs 100是训练轮数,口罩检测这种二分类任务通常 50~100 轮就收敛。--batch-size 16看显存,8G 显存跑 640 分辨率大概能到 16,不够就降到 8。--lr0 0.01初始学习率,--lrf 0.01是最终学习率系数,配合--cos-lr余弦退火。--workers 8是数据加载线程数,设成 CPU 核心数左右。

4.2 超参数怎么调:从 yolov5 超参数说起

YOLOV5 自带data/hyp.scratch.yaml,里面有一堆数据增强和损失权重参数。口罩检测场景有几个值得改:

参数默认值口罩场景建议原因
mosaic1.00.5~1.0小目标多时保留,遮挡严重时降低
scale0.50.3~0.5口罩目标尺度变化不大,别放太大
fliplr0.50.5左右翻转合理,口罩不分左右
hsv_h0.0150.015颜色扰动保持默认
box0.050.05框回归损失权重,一般不动
cls0.50.5~1.0类别少时可适当提高

改法是在训练命令里加--hyp ./data/hyp_mask.yaml,把默认文件复制一份改。mosaic是四图拼接增强,对小目标检测帮助大,但口罩检测里人脸通常占画面比例不小,mosaic太强反而会让模型学到不自然的拼接边缘,我一般设 0.5。scale控制随机缩放范围,口罩尺寸相对固定,缩放太狠会让小口罩更难检。

4.3 训练曲线怎么看:loss、mAP 和过拟合信号

训练开始后,runs/train/mask_exp1下会生成results.csv和一堆曲线图。重点看三个:

train/box_loss和train/cls_loss应该持续下降,如果震荡剧烈,学习率可能太大,把--lr0降到 0.005 试试。metrics/mAP_0.5是验证集上的 mAP,正常会从 0 涨到 0.9 以上(口罩检测任务简单)。如果train_loss一直降但val_mAP不涨甚至下降,就是过拟合了,加数据增强、加 dropout 或者减少模型复杂度。

# 用 pandas 快速看 results.csv 最后几行 python -c " import pandas as pd df = pd.read_csv('./runs/train/mask_exp1/results.csv') df.columns = df.columns.str.strip() print(df[['epoch','train/box_loss','train/cls_loss','metrics/mAP_0.5','metrics/mAP_0.5:0.95']].tail(10)) "

这段脚本读训练日志,看最后 10 轮的 loss 和 mAP。如果mAP_0.5在最后 10 轮基本不动了,说明已经收敛,可以停。如果还在涨,加 epoch。

5. 避坑与排查:标注、训练、部署里最容易翻车的地方

5.1 标注坐标越界导致训练 loss 变 NaN

现象:训练几个 epoch 后 loss 突然变成nan,或者一开始就报AssertionError。原因:标注文件里有坐标大于 1 或小于 0 的值,YOLOV5 在计算损失时对坐标做了 clamp,但极端值会导致梯度爆炸。解决:写脚本扫描所有标注文件,把越界值截断到 [0,1],或者直接删掉这些异常标注。

from pathlib import Path for lbl in Path("./dataset/labels").rglob("*.txt"): lines = lbl.read_text().strip().splitlines() fixed = [] for line in lines: parts = line.split() if len(parts) != 5: continue cls, x, y, w, h = parts vals = [float(x), float(y), float(w), float(h)] vals = [min(max(v, 0.0), 1.0) for v in vals] fixed.append(f"{cls} {vals[0]:.6f} {vals[1]:.6f} {vals[2]:.6f} {vals[3]:.6f}") lbl.write_text("\n".join(fixed))

这段脚本遍历所有标注,把坐标截断到 0~1,同时跳过格式不对的行。跑之前先备份labels目录。

5.2 图片和标注文件名不一致导致漏读

现象:训练时提示No labels found,或者 mAP 一直是 0。原因:图片是001.jpg,标注是001.txt,但中间多了空格或大小写不一致。YOLOV5 用stem匹配,001.JPG和001.txt能匹配,但001 .jpg和001.txt不行。解决:统一重命名,去掉文件名里的空格和特殊字符。

# 批量把文件名里的空格换成下划线 find ./dataset/images -name "* *" -type f | while read f; do mv "$f" "${f// /_}" done

5.3 训练时显存溢出(CUDA out of memory)

现象:训练刚开始就报RuntimeError: CUDA out of memory。原因:batch-size太大,或者img-size太大。解决:先把--batch-size减半,还不行就把--img-size从 640 降到 416。另外--workers设太高也会占显存,降到 4 试试。如果用的是多卡,--device 0,1会做 DataParallel,显存占用翻倍,单卡够用就别多卡。

5.4 推理时框重叠严重(NMS 参数没调好)

现象:一张图里同一个人脸出现好几个框。原因:--iou-thres太高,NMS 没把重叠框抑制掉。解决:把--iou-thres从默认 0.45 降到 0.3~0.4。如果降了还有重叠,检查是不是模型本身没训练好,或者--conf-thres太低导致大量低置信度框。

5.5 部署到 RK3568 或树莓派时精度掉一大截

现象:PC 上 mAP 0.95,转到 RK3568 上只有 0.7。原因:量化(INT8)损失精度,或者输入分辨率被改小。解决:量化时用一批真实场景图做校准,别用随机数据;分辨率尽量保持 640,实在跑不动再降到 416 并重新训练。树莓派 4B 上跑 YOLOV5s 大概 2~3 FPS,建议用yolov5n或者做剪枝。

6. 把模型压到边缘设备:量化、剪枝与一个验证技巧

模型在 PC 上跑通只是第一步,真正落地往往要上边缘设备。以 RK3568 为例,常见做法是先把 PyTorch 权重导出 ONNX,再用 RKNN Toolkit 转成 RKNN 模型,中间做 INT8 量化。导出 ONNX 时注意--img-size和--batch-size 1,动态轴只保留 batch。

python export.py \ --weights ./runs/train/mask_exp1/weights/best.pt \ --include onnx \ --img-size 640 640 \ --batch-size 1 \ --dynamic

--dynamic让 batch 维度可变,方便后续量化工具处理。导出后用onnxsim简化一下,去掉冗余算子。量化校准集从验证集里抽 100~200 张,覆盖不同光照和角度,别只用正脸。量化后一定要在设备上跑一遍验证集,对比量化前后的 mAP,掉超过 5 个点就考虑混合量化(部分层保持 FP16)。

剪枝方面,YOLOV5 可以用torch-pruning或者自带的--prune参数(部分版本支持)。我一般先看model.model里各层 BN 的 gamma 值,接近 0 的通道可以剪掉。剪完要微调 10~20 个 epoch 恢复精度。一个验证技巧:把量化后的模型和原模型在同一批图上跑,用--save-txt存结果,写脚本算两个 txt 的 IoU 匹配率,低于 90% 就说明量化损失太大,得回退。

import numpy as np def load_boxes(txt_path): boxes = [] for line in open(txt_path): parts = line.split() if len(parts) >= 5: boxes.append([float(x) for x in parts[1:5]]) return np.array(boxes) def iou(a, b): # 简化版 IoU,输入是归一化 xywh ax1, ay1, aw, ah = a bx1, by1, bw, bh = b ax2, ay2 = ax1 + aw, ay1 + ah bx2, by2 = bx1 + bw, by1 + bh ix1, iy1 = max(ax1, bx1), max(ay1, by1) ix2, iy2 = min(ax2, bx2), min(ay2, by2) iw, ih = max(0, ix2 - ix1), max(0, iy2 - iy1) inter = iw * ih union = aw * ah + bw * bh - inter return inter / union if union > 0 else 0 orig = load_boxes("./runs/test_infer/exp1/000001.txt") quant = load_boxes("./runs/test_infer/exp_quant/000001.txt") matches = 0 for o in orig: if any(iou(o, q) > 0.5 for q in quant): matches += 1 print(f"匹配率: {matches}/{len(orig)}")

这段脚本对比原模型和量化模型在同一张图上的检测框,用 IoU>0.5 算匹配。匹配率低于 90% 就要警惕。我自己的习惯是每次转模型前先把验证集跑一遍存好结果,转完再跑一遍对比,这个「后悔药」能省掉很多返工。边缘部署没有银弹,量化、剪枝、分辨率三者要一起权衡,先保证召回率再压速度。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询