YOLOv5实战:香烟破损检测从数据集标注到树莓派部署全流程
发布时间:2026/10/1 2:40:25来源:尧图网络
简介面向计算机视觉入门与工业质检场景的YOLOV5实战项目围绕香烟破损缺陷检测任务展开适合希望快速上手目标检测训练与推理流程的开发者。资源包含完整代码、620张已标注图像数据集训练集320张、验证集80张另有对应标签及训练好的权重文件图像为3024×4032大尺度RGB图片覆盖头部破损、滤嘴破损等6类缺陷。项目已迭代100个epoch保存了训练曲线、混淆矩阵、PR曲线等评价结果最优mAP0.5约0.90mAP0.5:0.95约0.55训练与推理结果均保留在runs目录下经测试可直接运行。资源共1065个文件以480个jpeg图像、402个txt标签为主配套yaml配置、py脚本、pt权重及项目说明文档压缩包大小约608MB。已有220人学习下载适合作为工业缺陷检测实战参考也可用于YOLOV5的迁移学习与调参练习。1. 香烟破损检测问题不在模型而在标注与部署三周前有个做烟草质检的朋友找我说他们厂想用视觉检测筛掉破损烟支买来的设备识别率不稳定想看看用 YOLOv5 自己训一个行不行。我直接把这份「YOLOV5 实战项目香烟破损检测包含数据、代码、训练好的权重文件」拆开跑了跑结论是模型本身并不复杂真正卡人的是数据标注的边界、置信度阈值的选择以及训练完后怎么部署到产线设备上。这套资源里给了完整的数据集、源码和训练好的权重适合两类人——第一次用 YOLOv5 训练自己数据集的初学者以及想在树莓派这类边缘设备上跑检测的硬件玩家。接下来我把整个流程从环境配置到权重部署按实际踩过的坑一个一个给你捋清楚。2. 环境配置与源码结构先把地基打牢2.1 conda 环境与依赖版本用 Python 3.8 最省心YOLOv5 官方源码对环境的兼容性一直是个玄学问题不同分支要求的依赖版本差异不小。我试下来的经验是用 conda 创建独立环境锁死 Python 3.8 和 PyTorch 1.x 系列能避开八成以上的兼容性报错尤其是 Windows 上和树莓派 5 上的部署Python 3.9 以上的版本很容易在编译某些算子时翻车。# 创建独立环境避免和系统 Python 打架 conda create -n yolov5 python3.8 -y conda activate yolov5 # 安装 PyTorch 1.12.1 CUDA 11.3这是实际跑通过的组合 pip install torch1.12.1 torchvision0.13.1 --extra-index-url https://download.pytorch.org/whl/cu113 # 安装项目依赖requirements.txt 里已经锁好了所有关键包 cd yolov5-master pip install -r requirements.txt这段命令的逻辑很直接conda create负责把环境隔离出来避免你之前装的 TensorFlow 或者其他深度学习框架把依赖搅乱PyTorch 版本选择 1.12.1 是因为它和 YOLOv5 的 ops 兼容性最稳定再新的版本例如 2.x 在推理时可能遇到torch.jit相关的问题。requirements.txt是官方源码自带的里面包含numpy、opencv-python、matplotlib等基础库执行一次就能把训练和推理需要的组件装齐。提示树莓派 5 上不要装 CUDA 版本的 PyTorch官方没有提供 arm64 的 cu113 轮子直接用 CPU 版本pip install torch torchvision就行后面训练用云 GPU树莓派只负责推理。2.2 源码目录与关键文件别被十几个文件夹吓住解压项目后你会看到一堆目录但真正要关心的只有四个train.py训练入口、detect.py推理入口、data/数据集配置、weights/官方预训练权重和你训练好的结果。项目自带的runs/目录是训练过程的输出存档每次跑完会自动生成新文件夹不会覆盖之前的结果这一点比很多自写的训练脚本靠谱。我一般会额外做两件事。第一步把data/下的cigarette.yaml文件打开确认路径是否正确因为源码默认的路径是相对路径直接跑容易报FileNotFoundError第二步把weights/目录下的best.pt备份一份这是训练好的香烟破损检测权重避免后续调参时意外覆盖。2.3 依赖安装的坑不完全按照 requirements 顺序装requirements.txt里有个隐藏问题opencv-python在部分 Linux 发行版上装好后cv2.VideoCapture读不了 USB 摄像头报Unable to capture video。这不是 YOLOv5 的问题是 OpenCV 默认没有带 FFmpeg 支持。解决方法是装libopencv-dev系统库版本或者改用pip install opencv-python-headless但注意 headless 版本不能显示窗口纯训练没问题推理要看可视化效果就不行。我通常会在正式环境装带 GUI 的版本在服务器上装 headless 版本各取所需。3. 把香烟破损数据转成 YOLO 格式转换脚本与四个边界坑3.1 数据集目录结构训练前必须确认的五层体系YOLOv5 对数据集的目录结构没有硬性约束但官方推荐的 layout 是最不容易出错的。你看完项目里的datasets/文件夹会发现它把images和labels分开放各自有train和val子目录这直接对应训练时的数据加载逻辑。datasets/ ├── images/ │ ├── train/ # 训练图片几百到几千张不等 │ └── val/ # 验证图片通常取总量的 10%~20% ├── labels/ │ ├── train/ # 和训练图片同名的 .txt 标注 │ └── val/ # 验证集标注 └── data.yaml # 数据集说明文件data.yaml的内容很简单但每个字段都要仔细核对。我把实际用到的一段配置放在下面你可以直接对照项目里的改写# 数据集的根目录绝对路径写起来最省事我一般写死 path: /data/cigarette_datasets # 换成你自己的实际路径 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 # 类别信息是核心这份资源里只检测一种缺陷 nc: 1 names: [cigarette_broken]这段配置的逻辑很清晰path写绝对路径能避免从不同工作目录启动训练时报路径错误nc: 1意味着单类别检测如果你后续想同时检测cigarette_broken和cigarette_spot两种缺陷要改成nc: 2并把names加上对应名称。很多初学者在names列表里漏了类别顺序导致训练出来的模型预测结果和你标注的顺序对不上这是个特别隐蔽的错误。3.2 VOC 转 YOLO 格式核心脚本逻辑与坐标换算项目提供的原始标注如果是 XML 格式Pascal VOC可以直接用项目里自带的转换脚本也可以自己写一个逻辑并不复杂。核心是把 XML 里的xmin, ymin, xmax, ymax转成 YOLO 需要的cx, cy, w, h再除以图片宽高做归一化。我贴一段最常用的转换核心代码你在项目里找voc2yolo.py看到的核心逻辑也是这个import os import xml.etree.ElementTree as ET def convert_voc_annotation(xml_path, image_width, image_height, output_txt_path): 将 VOC 格式的单个 XML 标注转换为 YOLO 格式的 txt 标注。 参数说明 xml_path : VOC 标注文件路径 image_width : 图片实际宽度用于归一化 image_height : 图片实际高度用于归一化 output_txt_path : 输出的 .txt 文件路径 tree ET.parse(xml_path) root tree.getroot() with open(output_txt_path, w) as f: for obj in root.findall(object): # 只转换目标类别为 cigarette_broken 的目标 name obj.find(name).text if name ! cigarette_broken: continue # 读取边界框坐标并转换为归一化的 cx, cy, w, h bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 计算中心点坐标和宽高 cx (xmin xmax) / 2.0 / image_width cy (ymin ymax) / 2.0 / image_height w (xmax - xmin) / image_width h (ymax - ymin) / image_height # 类别 ID 写 0因为 data.yaml 里只有这一个类 f.write(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n)这段代码的逻辑是先从 XML 树里找到所有object节点筛选出类别为cigarette_broken的目标然后从bndbox节点提取边框坐标计算中心点坐标和宽高后归一化最后写入.txt文件。参数里最关键的是image_width和image_height很多人转出来框的位置偏了排查到最后发现是用了原图尺寸而非实际图片尺寸——如果你用 OpenCV 做过 resize一定要用 resize 后的尺寸否则坐标全部偏移。3.3 标注质量检查可视化确认比什么参数都重要转完格式后建议用val集做一次可视化检查把标签画回到图片上确认框的位置对不对。我习惯用一段 Python 脚本快速抽查import cv2 # 读取一张验证图片和对应的 .txt 标注文件 img cv2.imread(datasets/images/val/001.jpg) h, w img.shape[:2] with open(datasets/labels/val/001.txt) as f: for line in f.readlines(): cls, cx, cy, bw, bh line.split() cx, cy, bw, bh float(cx)*w, float(cy)*h, float(bw)*w, float(bh)*h # 把归一化坐标还原成像素坐标并画出矩形框 x1, y1 int(cx - bw/2), int(cy - bh/2) x2, y2 int(cx bw/2), int(cy bh/2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, broken, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(check_label.jpg, img)这段脚本用 OpenCV 读图、读标注然后把 YOLO 格式的归一化中心点坐标还原成像素坐标用红色矩形框画在图上。如果你发现框大面积偏移先检查转换脚本里除的是不是image_width和image_height而不是w和h搞反了如果发现“框是准的但香烟破损区域只框住了一半”那说明标注时本身就没把破损边缘框完整需要回去修标注而不是改代码。这个检查步骤值回你下载这套资源的时间。4. 训练与权重超参数、训练产物与迁移学习4.1 训练参数设置从预训练权重开始是最优解项目里的train.py支持从官方 COCO 预训练权重继续训练这是小数据集训练目标检测最值得用的技巧能大幅提升收敛速度和 mAP。我建议的训练命令如下参数含义会在后面逐一说明python train.py \ --data data/cigarette.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --img 640 \ --patience 30 \ --name cigarette_broken这几个关键参数比你想象的更能影响最终效果--weights yolov5s.pt使用 s 规模的预训练权重作为初始化。你有自己的 GPU 服务器建议先用 s 规模跑通整套流程再考虑换 m 或 l 规模提精度如果直接在树莓派上部署s 是性价比最高的选择。--batch-size 16这个值取决于显存大小。8GB 显存跑batch-size 16比较勉强我一般降到 8 或 4训练速度慢一点但能避免 CUDA out of memory。--epochs 100100 个 epoch 足够收敛配合--patience 30如果验证集指标 30 轮没有提升会自动早停不会白白耗费算力。--img 640推理分辨率如果你希望模型跑得更快可以改 416 或 320但 mAP 会相应下降。对香烟破损检测这种小目标任务我建议保持 640不要为了速度牺牲召回率。4.2 训练产物解读last.pt 不是白留的训练完成后runs/train/exp/weights/目录下会生成两个权重文件它们的用途有本质区别last.pt是训练过程中最后一个 epoch 的权重只在训练意外中断时用于续训一般不做推理用best.pt在验证集上 mAP 最高的权重这才是正式部署用的产物。我第一次用这份资源时直接拿last.pt推理效果不理想一度以为是数据问题后来才发现用best.pt一切正常——这是很多新手会踩的坑所以特别提醒你注意。你如果想验证训练效果可以用项目自带的val.py在验证集上跑一下得到 mAP、Precision、Recall 等关键指标。香烟破损检测这类缺陷检测项目我建议重点看 Recall 而不是 mAP因为漏检比误检代价更大破损烟支流到消费者手里投诉成本远高于多拦几支好烟的成本。如果 Recall 低于 95%先检查训练集里破损样本的数量和多样性再看置信度阈值是否需要调整。4.3 数据增强与超参数调整不要一上来就调结构YOLOv5 自带一套数据增强策略在data/hyps/hyp.scratch-low.yaml里定义包括随机旋转、色彩抖动、马赛克拼接等。对香烟破损检测我建议只调整两个最有效的增强参数hsv_h: 0.015 # 色相扰动范围调大容易导致颜色变化过大 hsv_s: 0.7 # 饱和度扰动范围香烟金色包装对饱和度敏感 fliplr: 0.5 # 水平翻转概率建议保留 0.5 mosaic: 1.0 # 马赛克增强训练初期有效后期可以调低这个配置文件的核心逻辑是破损检测本质是一个纹理细节感知任务过度颜色增强会让模型学歪掉把正常烟支的金色包装误判为破损黄斑。所以我建议把hsv_s从默认的 0.9 降到 0.7hsv_h保持小扰动。mosaic: 1.0在训练前 80 个 epoch 作用很大但在最后 20 个 epoch把它改成 0.5 或直接关掉模型能从完整目标上学到更精细的边界特征。5. 常见问题与避坑我踩过的三个典型翻车现场5.1 训练时报错 Found no class names in data/cigarette.yaml现象执行train.py后立刻报错提示找不到类别名称。原因YOLOv5 源码在读取data.yaml时需要names字段但部分历史版本读取的是names而不是nc项目里给的data.yaml写的是names: [cigarette_broken]但你如果在源码根目录启动训练相对路径指向的是data/cigarette.yaml这个文件内容没有问题。问题往往出在你从别处拷贝了这份配置YAML 的缩进解析错了导致names被忽略。解决先确认配置文件格式正确把names写成官方 YAML 格式并且确保names这一行没有多余空格。这里有个细节值得强调YAML 的缩进必须一致TAB 和空格混用会直接解析失败这是这类报错最高频的原因。5.2 识别率不错但漏检破损烟支置信度阈值不是越低越好现象model 在验证集 mAP 有 0.95但实际部署时连续漏检尤其在小破损区域上。原因detect.py默认的置信度阈值是 0.25低置信度的目标直接被过滤而小破损目标由于占像素面积小网络输出的置信度天然偏低。我开始也是调低阈值从 0.25 调到 0.05结果误检率暴增把正常烟支边缘阴影全部框了出来后来发现正确做法是调整 NMS 参数而不是盲目调置信度。解决推理时把--conf-thres保持在 0.15 左右关键是调--iou-thres将其从默认的 0.45 改为 0.3减少重叠框的误抑制。参数调整后破损小目标的召回率明显提升误检也没有暴增。如果你的场景对漏检零容忍还可以做推理时的多尺度测试增强但推理速度会慢 2~3 倍产线上要不要用取决于节拍。5.3 训练很快但 loss 曲线直线震荡不下降现象loss 曲线在训练初期没有明显下降趋势甚至在震荡。原因八成是学习率设置不当或者 batch size 太小导致梯度不稳定。我一开始把--batch-size设成 2因为显存小--lr没改结果训练 50 个 epoch 后 loss 还在 0.08 上下徘徊。后来把头 3 个 epoch 的 warmup 理解为默认值但问题还是没解决最终发现是数据没打乱——YOLOv5 默认会 shuffle但如果你改了--workers 0可能绕开了某些加载路径的默认行为。解决先把 batch size 提到 8 以上8GB 显存可以用 8如果还是震荡就把--lr从默认的 0.01 改成 0.001再做 10 个 epoch 的测试训练。我一般会同时开启--cos-lr让学习率以余弦方式衰减前 30 个 epoch 的震荡问题基本都能缓解。如果震荡仍然很厉害先查训练数据里有没有大量空标注的图片——全黑背景没目标的情况下模型会强行从噪声里学特征这是个容易忽视的玄学问题。6. 权重部署到树莓派 5验证工具与两个实战技巧训练好的best.pt要真正跑在产线或者边缘设备上通常要经过“导权重 → 转格式 → 内存优化”这条链路。树莓派 5 上部署 YOLOv5 模型是最近一段时间里很多人问的场景我拿这套资源里的权重在树莓派 5 上实测过能跑到可用的实时性——前提是你要把 PyTorch 模型转换成推理框架所需的格式。常用的方式是先导出 ONNX再转成 NCNN我记得在树莓派上更多选用 NCNN 的方式部署。# 在 x86 机器上先用 YOLOv5 官方脚本导出 ONNX python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 # 然后拷贝到树莓派用 ncnn 的优化工具转出 ncnn 参数与 bin ./onnx2ncnn best.onnx best.param best.bin这段命令的核心逻辑是export.py会把 PyTorch 动态图固化成 ONNX 静态图再去掉一堆对推理没用的算子onnx2ncnn再把 ONNX 转换成 NCNN 的二进制格式后者是专门为移动端和边缘设备优化的推理框架内存占用和速度都优于直接跑 PyTorch。如果你不想装 NCNN 工具链还有一条更省事的路径直接装onnxruntime用 Python 调 ONNX 模型推理树莓派 5 上用 ARM NEON 加速也能跑到每帧 100ms 左右。部署后我建议用下面这段 Python 代码快速验证模型是否正常避免下到现场才发现权重转换时尺寸对不上import cv2 import numpy as np import onnxruntime as ort # 加载 ONNX 模型并创建推理会话 session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name # 读取测试图片并预处理resize 到 640x640归一化到 0~1 img cv2.imread(test_broken.jpg) img_resized cv2.resize(img, (640, 640)) img_rgb cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_input img_rgb.astype(np.float32) / 255.0 img_input np.transpose(img_input, (2, 0, 1)) img_input np.expand_dims(img_input, axis0) # 执行推理并得到输出张量 outputs session.run(None, {input_name: img_input}) print(outputs[0].shape) # 确认输出维度通常是 [1, 25200, 6]这段脚本验证两件事模型能不能跑通、输出维度是否是[1, 25200, 6]25200 是 640x640 下三个尺度网格的预测总数6 是类别数加四个框坐标和一个置信度。如果你拿到的输出形状不是[1, 25200, 6]先确认导出时有没有加--img 640以及有没有修改过模型结构。在树莓派上部署还有一个容易忽略的细节内存带宽比算力更容易成为瓶颈。树莓派 5 的 CPU 算力足够跑 NCNN 优化的 YOLOv5s但如果你用 OpenCV 读 4K 摄像头再缩放帧率会被拖下来正确做法是用cv2.CAP_PROP_FRAME_WIDTH直接把摄像头采集分辨率设成 640 而不是读高清再缩。从那以后我每次拿到一份训练好的权重都强制走一遍“导出 ONNX → 在目标设备上跑一张真实样本 → 画出预测框比对原因”这三个步骤数据格式校验、阈值调整、设备兼容性都在这一轮里过掉。权重文件好不好用不能只看 mAP开枪之前先把子弹试了再说。这套资源里自带的权重和代码帮你省掉了从零开始攒环境的功夫希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网