ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

茶叶目标检测实战:YOLOv5数据集标注与训练避坑指南

茶叶目标检测实战:YOLOv5数据集标注与训练避坑指南 简介这是一份基于YOLOv5的茶叶目标检测实战项目资源面向计算机视觉学习者和算法工程实践者解决茶叶识别、定位与分类场景中的模型训练与部署问题。资源涵盖完整的项目源码与流程教程包含YOLOv5各规格网络配置、数据处理与训练、验证、推理脚本以及Docker部署、云端平台接入等工程化支持适合希望从零上手目标检测并尽快落地项目的读者。压缩包共95个文件以YAML配置、Python代码及Shell脚本为主整体大小约242KB。已有513人学习下载。通过该资源可获得可直接运行的检测流程、多种数据集适配方案、模型结构修改示例及环境配置参考能够帮助快速搭建茶叶检测系统并理解YOLOv5的核心实现思路。1. 茶叶目标检测项目拆解YOLOv5 只是主干数据决定上限茶园里最贵的不是茶树是采茶的人工。茶叶目标检测要做的事情就是让算法在真实茶园环境中把嫩芽、嫩叶和成叶的位置自动框出来输出坐标与数量喂给采摘机械臂或者产量估测模块。这套基于 YOLOv5 的实战项目把训练源码、数据配置模板、流程教程打成一个完整包省去你到处拼代码的时间但也不要指望解压就能跑——环境、数据集、参数这三关每一关都卡得住人。它适合三类人做智慧农业设备的工程师、做茶园机器人课题的学生、以及手上没有完整目标检测落地经验想补齐短板的人。我的建议是拿到项目后先别着急敲训练命令先把检测目标定义清楚你框的是鲜叶还是干茶、输出要给谁用这决定了后面所有参数。2. 环境准备与基线跑通先把 YOLOv5 源码在本地转起来茶叶检测本质上是一个迁移学习任务你不需要从零训练卷积骨干YOLOv5 在 COCO 上的预训练权重已经学会了通用的边缘、纹理和颜色表示。因此我一般建议把这个项目里自带的模型文件放在一边重新从环境开始走一遍这样出问题你能知道是哪一环的锅。环境部分有三个坑是新手必踩的Python 版本太新导致依赖装不上、PyTorch 与 CUDA 版本不匹配导致 GPU 不可用、以及忽略requirements.txt里各项的版本约束导致训练时报错。2.1 选型逻辑为什么是 YOLOv5 而不是其他检测框架茶叶目标检测的场景有三个特点目标密集且尺寸小、背景光照多变、拍摄设备可能是低成本工控机。把这三个特点放在一起对比方案推理速度小目标表现部署难度在茶叶场景的结论Faster R-CNN慢帧率个位数好高依赖 detectron 生态不太适合采摘机械臂的实时控制YOLOv5快几十毫秒一帧中上靠分辨率与 anchor 调优低ONNX/OpenCV 都能接平衡点最好项目选它合理YOLOv8更快但超参数和接口变化大中上中部署生态略新也很好但配套资料多数围绕 v5看这张表就明白YOLOv5 不是指标最强的但它胜在生态成熟训练脚本稳定、数据格式简单、社区里关于用 YOLOv5 训练自己的数据集的资料最多。茶叶检测这类垂直项目最怕的不是模型不够强而是问题出现时找不到参考。YOLOv5 恰好能保证你搜到的问题大概率有人踩过而且能找到对应的代码片段。2.2 搭建 Python 与 PyTorch 环境的最小命令我习惯用 conda 给每个视觉项目独立建环境茶叶这套项目也不例外。创建一个干净的 Python 3.8 环境然后按顺序装 PyTorch 和项目依赖。conda create -n tea_yolov5 python3.8 -y conda activate tea_yolov5 # 这里按你的 CUDA 版本装对应的 torchCPU 机器把 cu118 换成 cpu pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt说明Python 用 3.8 不是怀旧是 YOLOv5 依赖的某些包在 Python 3.10 以上会出现编译兼容问题犯不着在这种地方多耗时间。torch 和 torchvision 必须来自同一个官方源否则训练时显卡识别不到是常事。requirements.txt 里包含 opencv、numpy、matplotlib 这些常用库安装过程如果看到某个包编译失败先换 pip 源再装不要直接跳过。装完以后确认 GPU 是否可用import torch print(torch.__version__) print(torch.cuda.is_available())返回 True 才继续输出 False 就去查 CUDA 版本匹配这个检查二十秒就能做能省掉后面一整天的排查时间。如果你只有 CPU 机器也不是不能做这个项目只是训练周期会拉长建议先用小数据集验证流程再找云 GPU 跑正式训练。2.3 下载官方权重并用一张茶图跑通 detect.py环境装好之后先不碰自己的数据。用官方 COCO 权重和一张茶园照片跑一次推理验证整条链路是通的。# 在 yolov5 目录下执行首次会自动下载 yolov5s.pt python detect.py --weights yolov5s.pt --source data/images --conf 0.4跑完检查runs/detect/exp目录下的标注图。这一步的意义是排除环境问题如果官方图片能正常画框说明 PyTorch、OpenCV、绘图依赖全部正常后续出问题一定是数据集或参数反之就要留在环境层排查。对于茶叶项目我会用手机在茶园顺手拍一张竖图缩到 640 宽再放进来看它把树丛误检成什么。COCO 权重当然不识茶芽但这一跑能看出你的场景里有没有大量容易被误检的干扰物。如果模型把远处的人、农具、椅子都框出来了说明你的采集环境里混进了容易干扰的物体得在数据阶段处理掉而不是等训练完再后悔。3. 茶叶数据集的采集与标注转换脚本与框选规则训练好不好一半在标注。茶叶检测和通用目标检测最大的区别在于数据本身茶园里叶片密集、互相遮挡嫩芽和背景的绿色相近而且光照对颜色影响极大。项目工程包里给的流程教程会告诉你目录怎么放我这边补上采集与标注里最影响结果的几个细节。3.1 采集铁律覆盖时间段、天气与拍摄高度茶叶检测的数据集建设不是拿手机随便拍几百张就完事。我一般要求采集时记三件事时间段、天气、拍摄高度。茶叶的颜色会随着日光角度变化清晨带露水的嫩芽反光强正午直射下嫩芽和成叶的色差最小阴天反而层次清楚。拍摄高度决定目标尺寸手持俯拍和无人机航拍得到的茶芽像素尺寸完全不同。建议一个完整的茶叶目标检测数据集至少包含晴天、多云、阴天三个天气组合时间段覆盖早上七点到下午五点拍摄高度固定在茶冠上方 0.5 米到 1 米之间和未来部署位置保持一致。本地可以用一级目录区分sunny、cloudy、overcast既能做训练集分层抽样也能在模型翻车时快速回溯是哪种光照导致的。3.2 LabelImg 标注茶叶的框选标准与类别约定茶叶目标检测项目的类别设计没有标准答案我见过三种做法只设一个bud类适合采摘机械臂只找嫩芽设bud、young_leaf、mature_leaf三类适合做嫩度分级再加stem做大叶茶修剪决策。类别越少标注成本越低模型越容易收敛。压缩包的流程教程里一般会给推荐类别我的原则是宁少勿多——如果业务不要求区分嫩叶和成叶就不要分类别间颜色重叠是茶叶检测最常遇到的标注难题。标注工具用 LabelImg 最常见导出格式直接选 YOLO 格式即可。框选规则要写死给标注员完整露出且无遮挡的嫩芽必须框被其他叶片遮挡超过一半的目标不框边界模糊的叶片不框只框边界清晰的同一片叶子只能有一个框不允许重复标这些规则不是洁癖。YOLO 训练阶段会把每个标注框作为真值不一致的框会直接拉高 loss表现为训练曲线抖动剧烈、mAP 上不去。标注完成后抽查 10% 的图如果发现同一个标注员在相同叶片上画框的宽高差超过 30%就是规则没执行到位得停下来重新对齐标准。3.3 目录组织与 YOLO 格式转换脚本YOLOv5 对数据集目录有固定要求这也是新手的第一个坑。规范的目录结构如下tea_dataset/ ├── images/ │ ├── train/ # 约 80% 图片 │ └── val/ # 约 20% 图片 └── labels/ ├── train/ # 与 images/train 一一对应的 txt └── val/ # 与 images/val 一一对应的 txt图片和标注文件的主文件名必须完全一致tea_001.jpg对应tea_001.txt。如果项目方给的是 VOC 格式 XML 或者 JSON需要转成 YOLO 的归一化 txt 格式。转换脚本我通常这样写import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text # 类名到数字 id 的映射和 tea.yaml 保持一致 cls_id class_map.get(name) if cls_id is None: continue bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 转成中心点 宽高的归一化坐标 x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 防 0 宽 0 高的脏数据 if w 0 or h 0: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))这段脚本里最重要的细节是除以img_w和img_h必须用原图尺寸而不是缩放后的尺寸否则归一化坐标整体错位。另一个容易踩的坑是光把 XML 里的name转成数字还不够class_map的映射顺序必须和后面tea.yaml里的类别顺序完全一致。我坚持用{cls_id} {x_center} {y_center} {w} {h}这种固定写法每行顺序固定六位小数足够。转换完成后立刻用下面的命令验证一遍数一数 txt 里的总目标数和图片上实际目标数是否大致匹配find tea_dataset/labels/train -name *.txt | xargs wc -l | tail -1wc -l统计的是总行数如果和你标注时记录的目标总数差得很远说明大概率有文件漏转或者转失败了。为稳妥我还会写一个五行的检查脚本把 txt 里的归一化坐标还原回图上并画框保存直观确认框和茶叶是否贴合这一步看一次后面训练能省很多排查时间。4. 训练流程与参数配置让 YOLOv5 学会找茶芽的关键命令数据准备好就进入训练。YOLOv5 训练自己的数据集有几条必经路径项目源码把整个链路都串好了你只需要改三个文件、敲一条命令。做完这三步训练就启动。4.1 编写 tea.yaml 数据配置YOLOv5 的配置文件是一个 YAML 文本放在data/tea.yaml作用是告诉训练器去哪找图片、有几类、类名是什么。最简版本如下# 茶叶目标检测数据集配置 path: ../tea_dataset # 数据集根目录相对于 yolov5 项目 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 2 # 类别数量 names: [bud, young_leaf] # 类别名顺序必须与标注 txt 的数字一致path字段是最容易错的地方。YOLOv5 会把train字段拼在path后面如果你把数据集放在和 yolov5 并列的目录就写相对路径../tea_dataset而不是绝对路径。换机器训练时这个相对路径关系不要变否则会出现AssertionError: train set not found。另外names列表的顺序和标注文件里的cls_id数字是一一对应的0 对应bud1 对应young_leaf顺序写反的后果是训练不报错、推理结果全错这类错误最难排查。4.2 启动训练关键参数与调参顺序配置文件写好后标准训练命令如下我加了--seed方便复现结果cd yolov5 python train.py \ --data tea.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 120 \ --device 0 \ --seed 42逐个说明参数--weights yolov5s.pt迁移学习的起点。茶叶目标检测数据集规模通常不大从 COCO 预训练权重起步收敛速度远快于从空网络开始训练这是 YOLOv5 官方推荐的迁移学习做法。--img 640输入分辨率。对茶叶这种小目标场景640 是下限我在第 5 章会展开讲为什么建议提到 960。--batch-size 16Batch size 受显存约束。8GB 显存配 16 很稳显存 6GB 就降到 8不要硬撑后面有替代方案。--epochs 120茶叶数据集有几百张图时120 轮足够收敛。mAP 曲线通常在 60100 轮之间进入平台期训练结束会自动保留best.pt和last.pt。--device 0指定第一张 GPU没有 GPU 的机器写--device cpu但训练时间会拉长十倍以上。训练超参数如学习率lr0、动量momentum、以及 mosaic 增强概率在data/hyps/hyp.scratch-low.yaml这类 hyp 文件里控制。YOLOv5 的默认超参数是为 COCO 这种通用数据集调的对茶叶场景我一般只动两个把mosaic从 1.0 降到 0.5——mosaic 会拼接四张图对茶叶这种小目标密集图来说目标被裁掉一半的概率很高反而干扰训练另外把hsv_s饱和度增强保持默认的 0.7茶叶嫩芽的颜色本身就是重要特征饱和度增强过大反而会造成嫩芽和成叶几乎无法分辨。4.3 训练日志与指标先看 loss 再看 mAP训练启动后每轮结束控制台会打印box_loss、obj_loss、cls_loss、mAP50等字段别被这一串数字绕晕。我通常按这个顺序看训练结果前 10 轮的 loss如果 loss 在下降说明数据没问题如果 loss 纹丝不动甚至是nan立刻停掉回到第 5 章对应的那一节查原因。从第 30 轮开始看 mAP50茶叶检测是单阶段任务mAP50达到 0.85 以上就属于能部署的水平mAP50-95是更严苛的指标研发阶段不用强求。训练结束看confusion_matrix.png混淆矩阵里如果bud被误判成young_leaf的比例超过两成说明这两个类别在颜色或形态上太接近实战里宁可把这两个类别合并。看控制台日志和看runs/train/exp/results.png是同一回事后者更直观。遇到 loss 曲线大幅震荡第一反应不是调学习率而是去看数据标注有没有明显错误——错误标注才是茶叶检测 loss 震荡的头号原因。5. 茶叶检测的 5 个常见坑现象、原因与排查路径做到这一步原理和流程都清楚了接下来全是实战里反复出现的坑。我挑五个在茶叶目标检测项目中最高频的问题按「现象、原因、解决」给出排查路径。5.1 小目标漏检输入分辨率太低现象嫩芽在茶冠层明明清晰可见模型却漏检或者只检到最近处的几个大目标稍远一点的全部丢掉。这是茶叶检测最典型的表现。原因约两千万像素的茶园原图被 YOLOv5 缩到 640 后单个嫩芽只剩下十几个像素。YOLOv5 骨干网络下采样 32 倍16 像素的目标在特征图里只占 1 个点特征几乎被卷积层洗掉。这是所有小目标检测的通病。解决把--img从 640 提到 960 或 1280训练和推理都用同一分辨率。显存不足就配合 5.5 节的方法降 batch。另外YOLOv5 训练前会自动用 k-means 重算 anchor 并打印Autoanchor提示如果看到 anchor 数量有调整说明默认 anchor 确实不适合你的目标尺寸分布建议用调整后的 anchor 多训一轮。还有一种做法是把 mosaic 增强关闭因为拼接裁剪会进一步缩小单个目标在训练图里的占比代价是数据多样性下降数据量大可以关数据少就开着。5.2 茶芽与背景融色光照和增强问题现象训练集 mAP 很高但在相机实时画面上嫩芽和全绿的叶片纠缠成一片模型不是漏检就是误检。原因真实茶园光照不可能是均匀的正午光。露水反光、阴影、逆光都让嫩芽的颜色特征不稳定。模型在标准化光照下学到的颜色边界遇到曝光不足时直接被背景淹没特征就失效了。解决采集阶段记录每张图的光照条件第 3.1 节的天气分组在这里派上用场。第二个手段是调 hyp 配置里的hsv_h和hsv_s我一般把hsv_s从默认 0.7 调到 1.0让模型对饱和度的变化更鲁棒对hsv_v亮度增强则保持 0.4 以下因为嫩芽的亮度和绿色对比度是重要线索增益太大会破坏对比信号。如果部署环境固定是户外自然光考虑在图像预处理里加一步自动白平衡能显著降低光照变化带来的误检这个改动写几行 OpenCV 就能实现。5.3 标注不一致多人协作的框选灾难现象训练 loss 始终不收敛到平滑验证集 mAP 在 0.7 上下徘徊同一片茶园的图片今天加框明天删框模型表现让人捉摸不透。原因标注员对「什么样的目标算一个目标」理解不一致。有人把遮挡一半的嫩芽也框了有人只框完整目标导致同一个位置的标注框在训练集里忽大忽小模型只能学到一个矛盾的平均结果。解决把框选规则写死在项目文档里第 3.2 节列过四条规则这里补充一条嫩芽只框芽尖以下三片叶的范围不把整根枝条框进去。标注完成后做交叉复核A 标完 B 抽查 10%发现偏差超过阈值就重新对齐。茶叶这种密集目标宁可少框不可错框错框会把相邻目标的特征一起污染后期想去掉脏数据就得重新训练。5.4 场景过拟合到单一园区或天气现象训练轮数越增加验证集 mAP 不再上升换个茶园、换个时间段实测就崩甚至把阴影误检成嫩芽。原因训练集全来自一个园区、一个季节、一台手机。模型学会的不是「茶叶」这个概念而是「这块茶园下午两点的茶叶」。深度网络对背景纹理很敏感树丛形态、土壤颜色都会成为隐式特征部署场景一变立马现出原形。解决采集时加一个维度——不同地块或不同种植密度。这样 mAP 曲线也会更稳。更省事的做法是关注几个公开的植物叶片数据集做预训练或者补充训练。如果你必须只用项目自带的图片我的建议是宁可减少训练集图片数量也要保证图片的地块和天气多样性这比数量更重要——900 张只来自一个园区的图通常打不过 300 张覆盖四个天气条件的图。5.5 训练崩溃nan loss 与显存不足现象训练到第 10 轮 loss 变成nan或者直接报CUDA out of memory。前者让整个训练过程白跑后者让 batch 一降再降降到 2 还是爆显存。原因nan loss 绝大多数由过大的学习率或标注中的w0、h0引起或者是归一化坐标越界。显存不足则是 batch-size 和--img同时拉得过高茶叶数据集如果用了 1280 分辨率显存压力会直线上升。解决nan loss 先跑完第 3.3 节给的检查脚本过滤掉所有宽或高为 0 的标注如果数据没问题把--lr0从默认 0.01 降到 0.005 再训一次只改一个变量。显存不足除了降 batch还可以开 AMP 混合精度在train.py命令里加--amp参数显存占用能降三分之一训练速度还能提一截——这是我在低配置机器上常用的后悔药。如果 8GB 显存跑 960 分辨率还是爆优先把 batch 降到 4而不是继续降分辨率因为分辨率降回去又会触发 5.1 的小目标漏检问题。6. 验证与导出把效果固化到工程里训练结束以后best.pt只是验证集上的最优解不能代表部署效果。我会先用val.py复核一遍指标再用detect.py跑一段部署环境的视频确认在真实抖动和光照下没有翻车。# 用验证集复测 metrics python val.py --data tea.yaml --weights runs/train/exp/best.pt --img 960 --conf 0.25 # 用视频验证稳定性fps 贴近部署感受 python detect.py --weights runs/train/exp/best.pt --source tea_test.mp4 --conf 0.25确认效果后导出环节我一般直奔 ONNX因为要接到树莓派 4B、RK3568 这类边缘设备export.py一条命令就够python export.py --weights runs/train/exp/best.pt --img 960 --include onnx --dynamic导出后先看 ONNX 推理和 PyTorch 推理的框是否一致再决定要不要继续做量化。我的习惯是如果模型在真实部署设备上单帧低于 10 FPS优先做剪枝YOLOv5 的社区脚本里带通道剪枝方案剪掉冗余通道后精度损失一般控制在 2% 以内远好于直接换更小的yolov5n从头训。量化到 INT8 是最后的手段茶叶嫩芽这种弱纹理目标对量化噪声很敏感INT8 精度掉 5% 都算正常范围务必用验证集重新测一次 mAP不要被导出工具显示的速度冲昏头。我最早做茶园项目时就曾跳过验证直接量化部署结果整套设备到了地里识别率掉了两成只能连夜把模型换回 FP16。后来养成一个习惯不管换硬件还是换精度都要用同一个val.py记录一份 mAP50 基线再动手改一次对比一次只有这样踩过的坑才算没白踩。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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