简介:这份资源面向计算机视觉入门与进阶学习者,以及需要落地农产品检测场景的开发者,提供一套基于YOLOv5的茶叶目标检测完整项目实战方案。包内共95个文件,以34个Python脚本和41个YAML配置为主,辅以Shell运行脚本、Markdown说明文档、Dockerfile容器化文件及Jupyter Notebook教程,压缩包约242KB,结构紧凑、便于快速部署与二次开发。项目覆盖数据配置、模型定义、训练与推理全链路:data目录下的coco、VOC、VisDrone等YAML可参考迁移至自建茶叶数据集,models中提供yolov5n至yolov5x多档网络结构及修改版配置,utils内含损失计算、数据增强、指标评估与日志工具,train.py、detect.py、val.py、export.py则串联起训练、检测、验证与模型导出流程。目前已有513人学习下载,适合希望掌握目标检测工程化流程、积累农产品识别项目经验的读者参考实践。
1. 茶叶目标检测为什么值得单独拆一遍:从嫩芽到成叶的识别难点
茶叶目标检测这个方向,乍看像是把通用目标检测换个数据集就能跑通的事,但真上手就知道没那么简单。茶叶嫩芽、一芽一叶、一芽二叶、成叶之间的视觉差异极小,颜色接近、纹理相似、遮挡严重,而且采摘期的光照变化剧烈,早上和中午拍出来的图像分布能差出一大截。这份基于 YOLOv5 的茶叶目标检测项目,把训练、验证、推理、导出整条链路都打包好了,源码目录里能看到train.py、val.py、detect.py、export.py这些核心入口,还有models/下的 yolov5n/s/m/l/x 五档配置和data/下的多份数据集 yaml 模板。它解决的不是"能不能检测到茶叶"这种粗粒度问题,而是让你能在一套可复现的工程结构里,把茶叶的等级分类或嫩芽定位真正训出来。适合谁?做农业视觉的算法工程师、想拿目标检测练手的学生、以及需要快速验证茶叶分拣方案的从业者。下面我按实际拆包顺序,把这份资源从环境到训练到踩坑完整走一遍。
2. 环境配置与目录结构:把 YOLOv5 跑起来之前先看清这些文件
2.1 目录里到底有什么,哪些是必须动的
拿到这个包,第一件事不是急着pip install,而是先把目录结构过一遍。根目录下train.py、val.py、detect.py、export.py是四个主入口,hubconf.py是给 torch hub 用的,requirements.txt锁依赖,setup.cfg管打包配置。models/里放的是网络结构定义和五档 yaml,common.py和yolo.py是 backbone 和 head 的核心实现,experimental.py里有一些实验性模块。utils/是工具库重灾区,loss.py管损失计算,metrics.py管 mAP 等指标,dataloaders.py管数据加载,augmentations.py管增强,general.py里是各种通用函数,torch_utils.py管设备选择,autoanchor.py管锚框自适应。data/下那一堆 yaml——coco.yaml、VOC.yaml、VisDrone.yaml、xView.yaml、SKU-110K.yaml、GlobalWheat2020.yaml、Objects365.yaml、hyps/myvoc.yaml——都是数据集配置模板,你要做的是照着VOC.yaml的格式写一份自己的茶叶数据集配置。scripts/里是下载数据集和权重用的脚本,utils/loggers/下接了 flask_rest_api、aws、docker、google_app_engine 这些日志后端,实际训练时用默认的就行,不用全配。
2.2 环境配置:conda 建环境到依赖安装的完整命令
YOLOv5 对环境不算挑剔,但版本对不上照样翻车。我一般用 conda 建一个干净环境,Python 选 3.8 或 3.9,PyTorch 选 1.8 以上带 CUDA 的版本。下面这套命令是我在 Ubuntu 20.04 + RTX 3060 上跑通的组合:
# 创建 conda 环境,Python 版本别太高,3.8/3.9 最稳 conda create -n tea_yolo python=3.8 -y conda activate tea_yolo # 安装 PyTorch,CUDA 11.3 对应 torch 1.10.1,按自己显卡驱动选 pip install torch==1.10.1+cu113 torchvision==0.11.2+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html # 安装项目依赖,requirements.txt 里锁了版本 pip install -r requirements.txt # 验证 GPU 是否可用,项目里有个 gpu_is_avilible.py 可以直接跑 python gpu_is_avilible.pygpu_is_avilible.py这个脚本名字拼写有点随意,但功能就是打印torch.cuda.is_available()和设备名,跑一下能省很多排查时间。requirements.txt里主要锁了matplotlib、numpy、opencv-python、pyyaml、tqdm、scipy、thop这些,版本冲突最常见的是numpy和opencv的搭配,如果装完 import cv2 报错,把 numpy 降到 1.23 以下通常能解决。
提示:如果你用的是 Windows,
torch_utils.py里选设备的逻辑会自动判断,但 DataLoader 的num_workers建议设成 0 或 2,设大了在 Windows 上容易卡死。
2.3 权重下载与预训练模型选择
YOLOv5 官方提供了 yolov5n/s/m/l/x 五档预训练权重,茶叶检测这种细粒度任务,我建议从 yolov5s 或 yolov5m 起步。yolov5n 太轻,嫩芽和成叶的特征容易混;yolov5l/x 参数量大,小数据集上过拟合风险高。权重文件放到项目根目录,train.py里用--weights yolov5s.pt指定就行。如果网络下载慢,scripts/目录下有下载脚本,或者手动从 release 页面拿。注意权重文件名要和models/下的 yaml 对应,用yolov5s.yaml就配yolov5s.pt,混用会报维度不匹配。
3. 茶叶数据集准备与配置:从标注到 yaml 的完整链路
3.1 数据标注:labelImg 到 YOLO 格式的转换
茶叶数据集一般自己采,手机或工业相机拍都行,关键是标注要统一。目标检测常用标注工具里,labelImg 最顺手,标完导出 YOLO 格式的 txt。每张图对应一个 txt,每行是class_id x_center y_center width height,坐标都归一化到 0~1。茶叶的类别看你的任务定,如果只做嫩芽检测就一类bud,如果分等级就bud、one_leaf、two_leaf、mature这样分。标注时有个血泪经验:嫩芽和成叶的边界框别画太紧,留一点余量,因为茶叶边缘模糊,框太紧训练时正样本太少,召回上不去。
# 一个简单的标注检查脚本,统计每类框数量,防止某类样本过少 import os from collections import Counter label_dir = "datasets/tea/labels/train" counter = Counter() for txt in os.listdir(label_dir): if not txt.endswith(".txt"): continue with open(os.path.join(label_dir, txt)) as f: for line in f: cls = line.strip().split()[0] counter[cls] += 1 print("各类别框数量:", dict(counter))这段脚本遍历 labels 目录,统计每个类别 id 出现的次数。如果发现某一类只有几十个框,训练时这一类基本学不出来,要么补标,要么在 yaml 里调低这类权重。class_id从 0 开始,和 yaml 里names列表的顺序一一对应,顺序错了标签就全乱。
3.2 数据集目录结构与 yaml 配置
YOLOv5 要求的数据集结构是images/train、images/val、labels/train、labels/val四层。茶叶数据集按 8:2 或 7:3 切分,验证集里要包含不同光照、不同遮挡程度的样本,否则 mAP 虚高。目录建好后,在data/下新建tea.yaml,照着VOC.yaml的格式写:
# data/tea.yaml path: ../datasets/tea # 数据集根目录 train: images/train # 训练集路径,相对 path val: images/val # 验证集路径 nc: 4 # 类别数,按实际改 names: ['bud', 'one_leaf', 'two_leaf', 'mature'] # 类别名,顺序对应 class_idpath是根目录,train和val是相对路径,这样换机器只要改path一处。nc必须和names长度一致,不一致训练直接报错。如果做单类嫩芽检测,nc: 1,names: ['bud']。常见做法是把 yaml 放在data/下,训练时用--data data/tea.yaml指定。
3.3 锚框自适应:autoanchor 什么时候该关
YOLOv5 默认在训练前跑一次 autoanchor,用 k-means 重新聚类锚框。茶叶目标普遍偏小,默认的 COCO 锚框偏大,autoanchor 通常能带来一两个点的提升。但如果你数据集特别小(比如不到 500 张),autoanchor 聚类出来的框反而不稳,这时候可以在train.py里加--noautoanchor关掉,用默认锚框。判断标准是看训练日志里Best Possible Recall这个值,低于 0.98 就让它自动聚,高于 0.98 关不关差别不大。
4. 训练、验证与推理:参数怎么设、指标怎么看
4.1 train.py 关键参数逐项说明
训练入口是train.py,参数几十个,但真正影响茶叶检测效果的就那几个。下面这条命令是我在茶叶数据集上跑通的配置:
python train.py \ --data data/tea.yaml \ # 数据集配置 --weights yolov5s.pt \ # 预训练权重 --cfg models/yolov5s.yaml \ # 网络结构,和权重对应 --epochs 200 \ # 训练轮数,小数据集 150~300 --batch-size 16 \ # 批大小,按显存调,8G 显存用 16 --imgsz 640 \ # 输入尺寸,茶叶小目标可试 800 --hyp data/hyps/hyp.scratch-low.yaml \ # 超参配置 --device 0 \ # GPU 编号 --workers 8 \ # 数据加载线程 --project runs/train \ # 输出目录 --name tea_exp \ # 实验名 --cache # 缓存图像加速,内存够就开--imgsz是茶叶检测的关键参数。嫩芽在 640 下可能只有十几个像素,调到 800 或 1024 能明显提升小目标召回,但显存和训练时间也上去。--hyp选hyp.scratch-low.yaml是因为茶叶数据集通常不大,低增强配置能减少过拟合。--cache把图像缓存到内存,小数据集上能提速不少,但内存小于 16G 别开。--batch-size和--imgsz要一起调,显存爆了就降 batch 或 imgsz。
4.2 训练日志与指标解读
训练跑起来后,控制台会打印每轮的 box_loss、obj_loss、cls_loss 和 mAP@0.5、mAP@0.5:0.95。茶叶检测重点看 mAP@0.5,一般能到 0.85 以上算可用。如果 box_loss 一直不降,检查标注框是不是归一化错了;如果 cls_loss 高但 box_loss 低,说明框定位准但类别分不清,多半是类别样本不均衡。runs/train/tea_exp/下会生成results.csv和一堆曲线图,results.csv可以直接用 pandas 读出来画图对比不同实验。
# 读取训练结果,对比两次实验的 mAP 曲线 import pandas as pd import matplotlib.pyplot as plt df1 = pd.read_csv("runs/train/tea_exp/results.csv") df2 = pd.read_csv("runs/train/tea_exp2/results.csv") df1.columns = df1.columns.str.strip() df2.columns = df2.columns.str.strip() plt.plot(df1["epoch"], df1["metrics/mAP_0.5"], label="exp1") plt.plot(df2["epoch"], df2["metrics/mAP_0.5"], label="exp2") plt.xlabel("epoch") plt.ylabel("mAP@0.5") plt.legend() plt.savefig("compare_map.png")这段代码读两个实验的results.csv,画 mAP 对比曲线。列名里有空格,用str.strip()清一下。通过对比能看出哪个超参组合收敛更快、上限更高。常见做法是固定其他参数,只调--imgsz或--hyp,一次只动一个变量。
4.3 val.py 验证与 detect.py 推理
训练完用val.py在验证集上跑一遍,确认没有过拟合:
python val.py \ --data data/tea.yaml \ --weights runs/train/tea_exp/weights/best.pt \ --img 640 \ --task valbest.pt是验证集上表现最好的权重,last.pt是最后一轮的。推理用detect.py:
python detect.py \ --weights runs/train/tea_exp/weights/best.pt \ --source datasets/tea/images/val \ # 可以是图片、目录或视频 --img 640 \ --conf-thres 0.25 \ # 置信度阈值 --iou-thres 0.45 \ # NMS IoU 阈值 --save-txt # 保存检测结果为 txt--conf-thres调低召回高但误检多,茶叶检测建议 0.25~0.4 之间试。--iou-thres控制 NMS 合并,茶叶密集时调低到 0.4 能减少漏检。--save-txt把结果存成 YOLO 格式,方便后续和标注对比算误差。
5. 避坑与排查:茶叶检测训练中最容易翻车的五个点
5.1 现象:训练 loss 正常但 mAP 一直是 0
原因:验证集路径写错,或者val的 images 和 labels 不对应,YOLOv5 找不到标签就当负样本,mAP 自然为 0。解决:检查data/tea.yaml里val路径,确认labels/val下每个 txt 和images/val下的图同名。跑val.py时加--verbose看有没有 "no labels found" 的警告。
5.2 现象:显存溢出,报 CUDA out of memory
原因:--batch-size或--imgsz太大,或者--cache把图像全塞进内存。解决:先把 batch 降到 8,imgsz 保持 640;还爆就关--cache,或者用--batch-size -1让 autobatch 自动找最大 batch。utils/autobatch.py就是干这个的,但自动找出来的值偏保守,可以手动往上加一档试。
5.3 现象:嫩芽检测召回低,成叶检测正常
原因:嫩芽目标小,640 输入下特征图分辨率不够,小目标在 P3 层就丢了。解决:把--imgsz提到 800 或 1024,或者在models/yolov5s.yaml里加一个 P2 检测层。加 P2 层要改 yaml 里的 head 部分,参数量会涨,但小目标召回提升明显。另一个办法是切片推理,把大图切成小块分别检测再合并。
5.4 现象:训练到一半 mAP 突然掉下去
原因:学习率太大,或者数据增强太猛导致分布偏移。解决:看results.csv里 lr 曲线,如果 lr 在后期还很高,把--hyp里的lrf调低(比如 0.01),让学习率衰减更彻底。数据增强方面,hyp.scratch-low.yaml里mosaic概率默认 1.0,小数据集上可以降到 0.5,减少拼接带来的噪声。
5.5 现象:推理时同一片茶叶被框多次
原因:NMS 的 IoU 阈值太高,重叠框没合并掉。解决:detect.py里--iou-thres从 0.45 降到 0.3~0.4,茶叶密集时效果明显。如果还不行,检查是不是--agnostic-nms没开,多类别时开这个能跨类合并。
6. 进阶技巧:用 export.py 导出与部署前的验证习惯
训练出best.pt只是第一步,真正落地要过导出和部署这关。export.py支持导出 ONNX、TorchScript、TensorRT 等格式,茶叶检测如果上边缘设备,ONNX 或 TensorRT 是首选。导出命令:
python export.py \ --weights runs/train/tea_exp/weights/best.pt \ --include onnx \ # 导出格式,可选 onnx/torchscript/engine --img 640 \ # 导出时的输入尺寸,要和推理一致 --batch-size 1 \ # 部署通常 batch=1 --simplify # 简化 ONNX 图,去掉冗余算子--simplify会调 onnx-simplifier,能把一些冗余节点去掉,推理速度有提升。导出后别急着部署,先用 onnxruntime 跑一遍验证输出和 PyTorch 一致:
import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") # 构造一个随机输入,模拟预处理后的图像 dummy = np.random.randn(1, 3, 640, 640).astype(np.float32) outputs = sess.run(None, {sess.get_inputs()[0].name: dummy}) print("输出形状:", [o.shape for o in outputs])这段代码加载 ONNX 模型,喂一个随机张量,打印输出形状。YOLOv5 导出后输出通常是[1, 25200, nc+5],25200 是三个尺度特征图的预测框总数。如果形状不对,多半是导出时--img和推理时不一致。我一般会拿同一张茶叶图,分别用 PyTorch 和 ONNX 跑,对比检测框坐标,偏差在 1e-3 以内才算过。
导出 TensorRT 的话,--include engine,但需要本机装 TensorRT,且导出时的 GPU 架构要和部署设备一致,否则 engine 加载会报错。树莓派上部署自己训练的 YOLOv5 模型,常见做法是先导出 ONNX,再用 onnxruntime 或 ncnn 推理,TensorRT 在树莓派上支持有限。
从那以后我每次导出模型,都强制走一遍"PyTorch 推理 → ONNX 推理 → 对比输出"这三步,哪怕多花十分钟,也比部署后发现框偏了再回头查强。希望帮到你。
本文还有配套的精品资源,点击获取