简介这份资源是面向计算机相关专业学生与项目实战学习者的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.yamlimport 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.8PyTorch 按你的 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; ckpttorch.load(best.pt, map_locationcpu); print(ckpt[model].nc, ckpt[model].names)打印权重里的类别数和名称和你的data.yaml对比。第二--img-size和训练不一致。第三权重文件损坏重新解压或换一个.pt试。import torch ckpt torch.load(./weights/best.pt, map_locationcpu) 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// /_} done5.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)})这段脚本对比原模型和量化模型在同一张图上的检测框用 IoU0.5 算匹配。匹配率低于 90% 就要警惕。我自己的习惯是每次转模型前先把验证集跑一遍存好结果转完再跑一遍对比这个「后悔药」能省掉很多返工。边缘部署没有银弹量化、剪枝、分辨率三者要一起权衡先保证召回率再压速度。希望帮到你。本文还有配套的精品资源点击获取