简介面向草地环境下冬虫夏草检测需求的YOLOv5完整方案包含已标注数据集、可运行源码与预训练权重适合计算机视觉学习者、农业智能化研究人员及目标检测开发者。包体共1552个文件总量129.43MB以748张jpg图像和615个txt标签为核心辅以yaml配置、py脚本、训练日志及pt权重等结构完整可直接开展训练与推理。数据集按训练/验证划分分别提供491张和122张640×480高分辨率RGB图片每图均含多个完整标注目标。项目已训练20个epoch保存了混淆矩阵、PR曲线、F1曲线及最优权重map0.5达到0.96map0.5:0.95为0.51网络尚未收敛增加epoch有望进一步提升精度。已有271人学习适合用于科研实验、课程设计或冬虫夏草识别系统的快速搭建。1. YOLOv5 做草地中冬虫夏草检测这份资源能帮你省掉多少事YOLOv5 做草地中的冬虫夏草检测最难的不是把模型跑起来而是让模型在密密麻麻的草丛里认出一根露头的子座。冬虫夏草露在地表的部分又细又小颜色接近枯草一个 640×640 的训练图里标注框常常只有十几像素宽。直接用默认的 COCO 参数训练很容易出现 mAP 看着还行、一到实拍图上漏检一片的情况。这份资源带标注好的数据集、YOLOv5 训练代码和训练好的权重拿到手能直接跑 detect也能拿数据做迁移学习。适合做野外小目标检测实验、中药材资源调查自动计数、以及想拿真实场景数据练手的同学省掉的是标注、清洗数据、试错参数这几段最容易翻车的时间。2. 任务拆解冬虫夏草为什么难检YOLOv5 要围绕哪些参数做调整2.1 冬虫夏草检测的难点小目标、背景干扰、正负样本不平衡冬虫夏草在草地场景里属于典型的小目标检测。它的子座从土里伸出来长度大约在 2 到 8 厘米在照片里占的像素比例很小。视觉特征上子座和枯草的茎秆颜色相近和土壤背景的灰度对比弱。另外一个容易被忽视的问题是正负样本不平衡一张图上往往只有一到两根虫草草地背景占据绝大部分区域。YOLOv5 在训练时通过 anchor 和目标框的 IoU 匹配来分配正样本小目标锚框匹配难度高模型很难从稀疏的正样本里学到稳定的特征。在这种任务上常见的做法是先分析数据集边界框的像素分布再决定要不要放大输入分辨率、要不要调整 anchor。如果直接沿用 COCO 预训练权重去 fine-tune模型的浅层特征对「草丛纹理」已有一定适应性但深层语义特征需要重新学。所以资源里自带训练好的权重本质上就是帮你跳过了「最开始连虫草都框不准」的那个阶段。2.2 YOLOv5 默认配置在草地场景下的短板YOLOv5 默认的输入尺寸是 640默认 anchor 是为 COCO 的 80 类目标设计的最小 anchor 对应大约 4×4 像素的特征图网格。对冬虫夏草这样的小目标来说640 分辨率下特征图最大是 80×80每个网格对应原图 8×8 像素。虫草子座如果只有 12×30 像素在特征图上也就一两个网格模型很难给出稳定的置信度。短板还体现在数据增强策略上。默认训练开启 mosaic这对小数据集能显著提升泛化能力但 mosaic 拼图时会把子座裁到图片边缘甚至跨图小目标一旦被裁剪GT 框被去掉的概率也变高容易造成漏检。FLIP 操作同理左右翻转对虫草这类竖直生长的目标影响不大但上下翻转一般不开会把形态学特征搞乱。参数取舍的逻辑大概是小目标场景优先调img和anchor再考虑hyp.scratch.yaml里的增强系数。盲目增加epochs对这类小数据集的收益有限反而更容易从第 100 个 epoch 开始过拟合val 的 mAP 开始掉。2.3 数据集目录结构与 data.yaml 编写模型先要知道数据长什么样拿到资源后先检查数据集目录结构。YOLOv5 训练要求 images 和 labels 一一对应每张图片的标注是同名同目录层级下的 txt 文件。常见结构是datasets/cordyceps/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml每行标注格式为class x_center y_center width height坐标是归一化后的值范围在 0 到 1。类别默认用 0 表示代表冬虫夏草这类目标具体类别名由 data.yaml 里的names列表定义。data.yaml 的写法如下path: datasets/cordyceps train: images/train val: images/val nc: 1 names: 0: cordycepspath是相对或绝对路径train和val是相对于path的路径。nc是类别数资源只标了冬虫夏草一个类别所以是 1。训练前先手动检查一个标注文件cat datasets/cordyceps/labels/val/00001.txt正常输出类似0 0.4821 0.5537 0.0182 0.0431一行就是一个目标框。如果发现行数极多但框都很接近比如一堆 0.001 级别的宽高说明原标注有问题后面训练出来预测框也会乱飘。2.4 训练主要参数怎么定img、batch、epochs 与预训练权重训练命令的核心参数就四个--weights、--img、--batch、--epochs。先用资源里给的权重继续训练或者用官方预训练yolov5s.pt做迁移学习建议用前者因为你已经知道这个权重在当前数据上能收敛。python train.py --data data.yaml --weights runs/train/cordyceps/weights/best.pt --img 640 --batch 16 --epochs 150 --cache--img决定输入分辨率草地场景里我一般先试 640如果小目标漏检严重再升到 1280代价是显存占用接近翻倍。--batch受显存限制16G 显存跑img640时 batch 16 通常没问题跑img1280就得降到 4 或 8。--cache会把数据预加载到内存能显著提高小数据集训练速度代价是占用内存。关于 epochs小数据集上 150 是一个偏保守的数。--patience默认是 100表示连续 100 个 epoch 验证指标没有改善就早停。如果数据集只有几百张图我一般会关掉早停直接用固定 epochs 训练因为小数据集 mAP 波动大早停容易在低谷误判。3. 训练全流程操作从环境准备到产出自己的权重3.1 环境和依赖torch、CUDA 与 YOLOv5 代码的搭配YOLOv5 对环境要求不高但版本匹配是个玄学。官方仓库要求在 Python 3.8 以上PyTorch 版本建议 1.7 到 2.xCUDA 按 PyTorch 对应版本安装。如果机器没有 GPUCPU 也能跑只是训练时间会翻十几倍。git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txtrequirements.txt里包含 opencv、matplotlib、pandas 等依赖。安装完先验证 torch 是否正常调用 GPUpython -c import torch; print(torch.cuda.is_available())输出True说明 CUDA 可用。这里要留意 PyTorch 的 CUDA 版本和驱动版本不匹配的问题常见报错是CUDA error: no kernel image is available for execution on the device这通常是 PyTorch 版本太老或太新重装对应 CUDA 版本的 torch 即可解决。3.2 数据划分与标注格式检查训练前先做一遍质量校验把数据集放进datasets/cordyceps后先写段脚本检查标注里有没有越界和空文件。YOLO 归一化坐标如果超过 1 或小于 0会导致训练时出现 NaN loss。import os label_dir datasets/cordyceps/labels/train empty 0 bad 0 for f in os.listdir(label_dir): p os.path.join(label_dir, f) lines open(p).read().strip().splitlines() if not lines: empty 1 continue for line in lines: parts line.split() if len(parts) ! 5: bad 1 else: vals list(map(float, parts[1:])) if not all(0 v 1 for v in vals): bad 1 print(fempty files: {empty}, bad lines: {bad})这段代码的逻辑是遍历训练标签目录把空标注文件单独计数再逐行检查每行是否有 5 个字段、坐标是否落在 0 到 1 之间。empty过多说明很多图没有正样本模型会学到「永远不输出目标」bad大于 0 必须清洗否则训练集里有脏数据训练曲线会莫名跳动。我一般会把空文件和坏数据直接剔除而不是修改坐标因为手工改坐标容易改出不一致的数据分布。3.3 跑通 train.py关键参数逐个说清训练入口是train.py完整命令如下python train.py \ --data data.yaml \ --weights weights/best.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --workers 4 \ --name cordyceps \ --exist-ok--data指向前面写的 data.yaml。--weights可以传官方yolov5s.pt也可以传资源里自带的best.pt。--workers是数据加载线程数Windows 上限流设为 4Linux 可以到 8设太高会在数据瓶颈时造成 CPU 过载。--name决定输出目录runs/train/cordyceps。--exist-ok允许同名覆盖否则第二次跑同一个 name 会自动加序号。训练过程中重点看两个指标train/box_loss和val/mAP_0.5。box_loss 只降不升说明定位在收敛val mAP 在某个区间振荡是正常现象关键看走势是不是整体向上。每次 epoch 结束会在runs/train/cordyceps/下存last.pt和best.ptbest 是按验证集 mAP 最高的 checkpoint 保存的。3.4 用训练好的权重做检测detect.py 的调用姿势训练完直接做推理验证看模型在实拍图上到底是什么表现。detect.py 的常见用法python detect.py \ --weights runs/train/cordyceps/weights/best.pt \ --source datasets/cordyceps/images/test \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf--conf-thres一般设在 0.2 到 0.3小目标置信度普遍低设太高漏检严重--iou-thres是 NMS 去重阈值多个框叠一起压掉低分的那个--save-txt把检测结果存为 YOLO 格式 txt--save-conf会在 txt 里额外附置信度分数这两项用于批量评估后期处理最有用。输出在runs/detect/exp下每张图一张标注文件同时会画框并保存图片直接看带框图就能判断模型当前的倾向性错误。3.5 训练结果里有哪些文件best.pt 和 last.pt 怎么选训练输出目录runs/train/cordyceps/里result.png是训练全过程曲线从上到下依次是损失、PR 曲线、mAP 曲线。weights/下两个权重文件last.pt是最后一个 epoch 的模型best.pt是验证集 mAP 最优的模型。实际部署建议用best.pt因为它的验证集表现最优。但如果你的验证集和部署场景差异很大比如验证集是正午顺光拍的部署图是傍晚逆光拍的last.pt反而可能更抗过拟合。我一般会两个都保留切换着跑一轮 detect眼睛比 mAP 更诚实。4. 草地场景实测我在这个项目里踩过的坑和排查思路4.1 标注框太小导致正样本贡献不足现象训练正常跑完mAP 到了 0.8 以上推理测试图时小虫草几乎全漏测出来的却是一些大尺寸的疑似目标。原因标注框宽高小于 8 像素时在 640 分辨率下特征金字塔最大的 80×80 输出层也只覆盖 1 个网格YOLOv5 的 label assign 策略里小框的匹配难度本来就高正样本数量严重不足。解决先统计数据集中最小框的像素分布把低于 12 像素的样本剔除或重新标注框全一点把子座和连接地表的少量杆都框进去。训练时把--img从 640 升到 1280小目标在特征图上的尺寸翻倍正样本匹配成功率会明显上升。这两个操作叠加漏检率基本能降一半。4.2 枯草被误检成虫草背景干扰型错检现象conf-thres 降到 0.25 后遍地的枯草茎、细树枝都框出来置信度在 0.3 到 0.5 之间和真阳性混在一起。原因草地背景和子座特征重叠度高模型学到的特征不是「虫草结构」而是「细长暗色物体」。这是正负样本不平衡直接导致的负样本在特征空间里数量碾压正样本把决策边界推向了高召回方向。解决给验证集和训练集补一段「纯草地背景」样本不加标注。YOLOv5 对无标注图片的处理方式是不贡献定位 loss只贡献背景分类 loss能有效拉高误检的置信度阈值。资源数据里的训练图如果都是带虫草的我在补数据时会把一张大图切成 4 块切成全景四分之一缩放避免标注坐标全部归一化后越界。4.3 best.pt 不等于可靠模型mAP 的假收敛现象训练到第 40 个 epoch 时 mAP 到了 0.85到第 100 个 epoch 时掉到 0.8 左右但 best.pt 选的是 0.85 的版本推理却发现泛化差。原因小数据集的 mAP 波动大best.pt 记录的可能只是一个偶然踩中验证集分布的高点本质上是过拟合了验证集。解决我一般不看单点 mAP而是看result.png里val/mAP_0.5曲线的平滑区间取曲线稳定段的权重。实操上把--patience关掉跑完 150 个 epoch然后用last.pt和best.pt各跑一遍测试集对比检出的目标数量分布选出更符合直觉的那个。4.4 显存溢出 OOMimg 和 batch 的关系没算清现象训练到第 1 个 epoch 直接报CUDA out of memory连权重都没保存。原因batch 和 img 两个参数对显存的消耗是相乘关系img1280时单张图的张量大小是640的 4 倍batch 再乘 16显存直接爆掉。解决按「显存缩半、batch 减半」的笨办法快速定位。12G 显存跑 640 分辨率batch 极限一般在 32跑 1280 分辨率直接降到 8。如果必须用大 img还可以把--cache关掉减少内存压力或换成显存占用更小的yolov5s结构而不是yolov5m。4.5 换了一个 YOLOv5 版本后权重文件加载报错现象用另一台机器上的新版 YOLOv5 代码加载旧版训练的 best.pt报KeyError或直接崩溃while 循环无法正常跑 detect。原因YOLOv5 各版本的模型 yaml 结构有调整老权重里的 key 和新代码期望的 key 对不上部分加载会拿到一堆默认值输出全乱。解决训练和推理要用同一版本代码。资源里的权重我会先确认是在哪个版本下训练的再配对运行。如果需要在不同环境部署先跑export.py把 best.pt 转成 TorchScript 或 ONNX 格式这两种格式跨版本兼容性强很多。转换命令很简单python export.py --weights best.pt --include onnx5. 指标评估与参数调优mAP 之外还要看漏检分布5.1 用 val.py 得到完整的评估指标训练完先用val.py把验证集结果导出来不要只看 train 过程里自动输出的那几个数验证集上的 PR 曲线和混淆矩阵能告诉你模型在哪些条件下犯错最多。python val.py \ --data data.yaml \ --weights runs/train/cordyceps/weights/best.pt \ --img 640 \ --conf-thres 0.001 \ --iou-thres 0.6这里刻意把--conf-thres降到 0.001是为了画出完整的 PR 曲线而不是只看某一个置信度阈值下的单点指标。YOLOv5 在验证完成后会在runs/val/exp下生成混淆矩阵真正值得看的是对角线之外的光标落在哪里。我的习惯是拿混淆矩阵和测试集的错检图对齐如果「背景预测为虫草」那一格占比大就回头补背景负样本如果「虫草预测为背景」占比大就优先提升输入分辨率和数据增强强度。5.2 从 fail 图反推调参方向验证完目录里会有不少预测框画错的图我的判断顺序是框错位置看 anchor 和目标尺寸匹配漏检看小目标特征层输出重复框看 NMS 阈值置信度虚高点看背景过拟合。漏检频发时第一步不是调 hpy 参数而是把验证集里漏检图的小目标框尺寸统计一遍。如果最小框普遍小于 10×10先升输入分辨率如果框是细长的检查模型锚框长宽比——YOLOv5 默认 anchor 长宽比有 1:1、1:2、2:1 等组合虫草子座比例接近 1:5完全不在默认 anchor 分布内模型很难精确定位。import json # 假设你导出过模型的锚框配置 anchors [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]] gt_boxes [(0.01, 0.04), (0.02, 0.06), (0.015, 0.03)] for w, h in gt_boxes: for i in range(0, len(anchors), 2): a_w, a_h anchors[i] / 640, anchors[i 1] / 640 iou min(w / a_w, a_w / w) * min(h / a_h, a_h / h) if iou 0.3: print(fmatched anchor: {a_w:.4f} x {a_h:.4f})这段代码的思路是用 IoU 近似公式快速判断 GT 框和哪个 anchor 层匹配iou大于 0.3 才认为是有效匹配。如果全部 anchor 匹配不到就要靠--multi-scale或多增强 anchor 来解决。实际工程里 YOLOv5 训练时会根据数据全自动重新聚类 anchor--noautoanchor可以关掉但虫草场景我并不建议关。5.3 输入分辨率、anchors 与 NMS 阈值的小目标专项调优对小目标场景我把参数分为三档第一档是--img能升到 960 或 1280 就优先升它是所有改进里收益最直接的第二档是自动 anchor 聚类train.py 启动时会自动跑一段kmeans_anchor脚本把 anchor 聚成符合当前数据分布的尺寸这个默认行为不要关第三档是 NMS 参数--iou-thres默认 0.45在密集虫草场景可以调到 0.5 以上避免同一根虫草被多个框重复覆盖但调太高也会让相邻小目标被合并成一个框。还有一个容易忽略的参数是--conf-thres和--iou-thres的搭配。我在做统计时通常先跑一遍低 conf-thres 拿到全量候选再在后期过滤而不是直接要求检测环节一步到位。5.4 数据增强参数在虫草数据上的取舍YOLOv5 的超参文件data/hyp.scratch.yaml里hsv_h、hsv_s、hsv_v控制 HSV 抖动草地上光照变化本来就复杂默认值不用动太多。最需要调整的是mosaic和mixup。mosaic 对小目标训练是把双刃剑它能提升模型对多目标在视野中分布不均的适应但 4 张图拼在一起时每个目标的相对尺寸缩水一半反而更加剧小目标难题。我这个任务上会把 mosaic 概率从默认的 1.0 降到 0.5 左右。fliplr左右翻转概率 0.5 一般是安全的虫草没有左右对称问题。不建议开启flipud上下翻转物体上下颠倒后特征语义完全变了子座形态的锐利度会被打乱。6. 自定义推理脚本批量检测并输出虫草数量 CSV训练好了权重真正要落地到统计需求时建议脱离 detect.py 写一个精简推理脚本直接输出每张图的虫草数量。我经常用这样的 Python 脚本完成批量检测import torch import cv2 import glob import csv from pathlib import Path model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) model.conf 0.25 model.iou 0.45 results [] for img_path in glob.glob(test_imgs/*.jpg): results.append((img_path, len(model(img_path).xyxy[0]))) with open(counts.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, count]) writer.writerows(results)torch.hub.load加custom参数可以直接加载本地权重conf和iou是成员变量设置后对每次推理生效。model(img_path)返回的是一个 Results 对象.xyxy[0]是 N×6 的检测框张量第 5 列是置信度第 6 列是类别。这个脚本的可扩展点在results.append里再加一列总面积或平均置信度就能做趋势分析。资源里带的数据和权重是这套流程的起点但任何数据集都不可能覆盖所有拍摄条件。我在实际做冬虫夏草数量统计时每个批次的现场照片都会人工抽查十几张反向校准置信度阈值。从那以后我拿到任何小目标检测任务都会强制先跑一遍框尺寸分布统计再决定要不要转 ONNX 部署、要不要升分辨率——这个习惯就是从这次冬虫夏草项目里养成的希望帮到你。本文还有配套的精品资源点击获取