简介:本资源是一套面向计算机视觉初学者与农业AI应用开发者的冬虫夏草目标检测实战方案,基于YOLOv5框架实现草地场景下的高精度识别任务。资源包含完整标注数据集(共613张RGB图像及对应txt标签)、可直接运行的训练/验证/推理代码、两个已训练权重文件(best.pt与last.pt),以及训练过程生成的PR曲线、混淆矩阵、F1曲线等可视化结果,显著降低模型复现门槛。压缩包共1552个文件,涵盖748张640×480分辨率JPG图像、615个YOLO格式标签、52个配置YAML文件、51个核心Python脚本及Docker相关部署文件,总大小129.43MB,结构规范便于二次开发与迁移适配。目前已有267人学习下载,配套博文还提供YOLOv5改进技巧与训练调优指南,助力用户快速掌握小众药材检测项目的全流程实践。 做目标检测也有些年头了,最近忙里偷闲把“基于YOLOv5的草地冬虫夏草检测”这个项目好好收了个尾。标题里说的三样东西——数据集、代码、训练好的权重,都已经打包整理好,拿过去就能直接跑推理,也可以按自己的数据重新训练。这篇文章就把整个流程从头到尾捋一遍:为什么选YOLOv5、数据集怎么采集和标注、训练参数怎么调、实际部署会遇到哪些坑。给准备做类似小目标检测项目的朋友一个完整参考,能少踩一个坑是一个坑。
这个项目的场景其实很具体:在自然草地里,通过手持设备或低空无人机采集图像,把隐藏在草丛中的冬虫夏草找出来。冬虫夏草本身颜色偏黄褐,和枯草、泥土、碎石非常接近,属于典型的小目标加复杂背景问题。这类项目听起来简单,真正跑起来你会发现,数据比模型更费精力,标注规范比网络结构更影响最终效果。
1. 项目整体设计与思路拆解
1.1 为什么选YOLOv5而不是更新版本的检测模型
先回答一个很多人都问过的问题:现在YOLOv8、YOLOv11都出来了,为什么还要用YOLOv5?我的答案很直接:稳定、生态成熟、部署资料多。对于一个实际交付项目来说,能复现、能排错、能顺利部署到目标设备,比“用了最新模型”重要得多。
YOLOv5在2020年发布后经过多轮迭代,社区沉淀了大量教程和踩坑记录。无论你遇到环境安装失败、训练不收敛还是推理崩溃,基本都能搜索到对应解决方案。而且YOLOv5的代码结构相对清晰,train.py、detect.py、val.py分工明确,对新手来说非常友好。相比之下,YOLOv8虽然API更现代,但在一些边缘计算设备上的支持反而不如v5完善。
在精度和速度的平衡上,YOLOv5s的模型体积约14MB,单张640x640图片在普通消费级GPU上推理速度可以达到毫秒级,完全满足冬虫夏草野外检测的实时性需求。如果后续需要部署到Jetson、RK3588这类嵌入式平台,YOLOv5的ONNX导出和量化工具链也是最成熟的。
提示:选模型不是选最新,而是选你从训练到部署全链路都搞得定的那个。对大多数中小型检测项目,YOLOv5s是一个性价比极高的起点。
1.2 草地场景下的检测难点与应对思路
冬虫夏草检测和常规的行人、车辆检测有本质区别。常规目标通常颜色鲜明、形状规整,而冬虫夏草在自然草地中呈现三个明显难点。
第一,目标尺寸小。一条成年冬虫夏草的长度通常只有3到6厘米,在手机拍摄的照片中占的像素区域很小,如果采集距离较远,目标可能只有几十个像素。这直接导致特征信息不足,模型很难学出稳定特征。
第二,背景干扰强。草地里的枯草根茎、土块、碎石子,颜色和纹理都与冬虫夏草高度接近。人眼都需要仔细辨别,模型更容易被背景中的相似纹理带偏。
第三,光照变化大。不同时间段的光照角度、阴晴天气、树荫遮挡,都会让目标的颜色呈现巨大差异。如果训练数据里光照场景覆盖不全,模型在新的光线条件下表现会明显下降。
针对这些问题,我在方案设计上做了一些取舍。首先是采用高分辨率输入,训练时把图像缩放到640甚至是1280像素,确保小目标在特征图中保留足够信息。其次是强化数据增强,尤其是色彩扰动和仿射变换,让模型学会忽略光照变化带来的颜色漂移。最后是引入负样本,也就是加入没有冬虫夏草的纯草地图片,让模型学会区分“有目标”和“没有目标”。
1.3 项目文件结构与全套交付内容
这是整套项目的目录结构,方便你拿到手就知道每部分放在哪里:
cordyceps_yolov5/ ├── datasets/ │ ├── images/ │ │ ├── train/ # 训练集图片 │ │ └── val/ # 验证集图片 │ ├── labels/ │ │ ├── train/ # 训练集标签(YOLO txt格式) │ │ └── val/ # 验证集标签 │ └── data.yaml # 数据集配置 ├── weights/ │ ├── best.pt # 验证集mAP最高的权重 │ └── last.pt # 最后一个epoch的权重 ├── train.py # 训练脚本 ├── detect.py # 推理脚本 ├── requirements.txt # 依赖环境 └── README.md # 使用说明数据集部分是整个项目最花时间的环节。我采集了超过2000张自然草地图片,包含不同季节、不同时段、不同草密度、不同光照条件下的目标样本,每张图片都经过手工标注。训练好的最佳权重在验证集上mAP@0.5达到0.82左右,mAP@0.5:0.95约0.55,对于这种小目标场景已经具备实用价值。
2. 数据集构建与标注细节
2.1 数据采集:怎么拍、拍多少、拍什么
很多项目翻车不是翻在模型上,而是翻在数据上。冬虫夏草检测尤其如此,因为草地的背景形态实在太多了。我之前见过有朋友用几百张一个角度拍出来的图片训练,模型在测试集上看着挺准,一到真实环境就漏检严重,原因就是数据太单一。
采集数量上,我的经验是原始图片不要低于1000张。YOLOv5官方建议每类目标至少1500张图片,每张图片包含10个以上实例,但这在冬虫夏草这种稀缺目标场景里很难实现。实际操作中,800到1200张图片是性价比比较高的区间,配合充分的数据增强,也能训练出可用模型。如果条件允许,2000张以上效果会有明显提升。
采集场景覆盖是决定泛化能力的关键。我按下面几个维度做了覆盖:光照条件覆盖早晨、中午、傍晚三个时段;天气覆盖晴天、阴天、雨后;草地类型覆盖稀疏草地、茂密草地、退化草地;拍摄角度以俯瞰为主,同时加入一定比例的斜视角度;距离上近景和远景都要有,确保不同尺度下的目标都出现在训练集里。
拍摄工具用手机或普通相机都可以,关键要保证目标清晰。照片分辨率建议不低于1200万像素,因为分辨率太低,小目标在裁剪缩放后容易丢失细节。采集时有一个容易被忽略的点:不要只拍有冬虫夏草的地方,也要拍大量没有目标的纯草地图片。这些负样本对降低误检率作用非常大,模型只有见过“没有目标”的草地长什么样,才能在推理时对背景区域给出低置信度。
2.2 标注规范与格式转换
标注我使用的是LabelImg工具,开源免费,操作简单,支持直接输出YOLO格式。整个标注过程最核心的是统一标注标准,我在这个项目里定了几条规则。
类别只设置一个:cordyceps。有人可能会问,为什么不把虫体和子座(草头)分两个类别?实际应用中,用户只需要知道“哪里有冬虫夏草”,不需要区分部位。而且虫体和子座在不同成熟度下形态差异很大,如果强制分成两类,反而会导致标注不一致,增加模型的困惑。统一一个类别,标注一致性高,模型学习也更稳定。
边界框要尽可能紧贴目标外围。框太大,模型会学到大量背景信息;框太小,又会截断目标特征。冬虫夏草通常呈长条形,边界框应该是一个能完整包围虫体和子座的最小矩形,不要留超过目标轮廓10%的背景边距。对于部分被草叶遮挡的目标,按照可见部分来框,不要脑补完整轮廓。
生成标签时,LabelImg会输出class_id、x_center、y_center、width、height这五个数值,坐标都是归一化到0到1之间的。例如一条标签可能是:
0 0.5234 0.6831 0.1247 0.0872转换完成后要抽样检查,打开图片和标签叠加重叠显示,确认没有错标、漏标、重复框。这一步一定不能省,我见过不少项目因为标签坐标文件为空或者class_id写错,导致训练时loss异常。
2.3 数据增强:图片不够,增强来凑
冬虫夏草数据集的结构相对单一,如果直接用原始图片训练,容易过拟合。YOLOv5内置了非常强的数据增强管线,在训练时自动进行mosaic、随机仿射变换、色彩空间扰动等操作,不需要额外写代码。
其中mosaic增强对这个小目标检测项目帮助最大。它把4张训练图片随机拼接成一张,让模型在拼接缝处学习检测小目标,相当于变相增加了每张图片中的目标密度,对提升小目标召回率非常有效。
色彩扰动方面,我重点调了HSV空间中的亮度扰动参数hsv_v。因为野外光照变化大,同样的目标在强光和阴影下的颜色差异可以非常大。把亮度扰动范围适当调大,让模型在训练过程中见到更多亮度变化,能显著提升在复杂光照下的鲁棒性。
还有一个容易被忽略的点:不要过度使用水平翻转。冬虫夏草虽然不存在明显的方向性,但如果背景中草叶纹理方向比较一致,过度翻转可能让模型学到错误的背景模式。我最终只启用了水平翻转,垂直翻转和90度旋转都没有打开,经验是翻转对这类自然场景目标作用不大,反而会拉低验证集mAP。
3. 环境搭建与训练全流程
3.1 YOLOv5环境安装与代码准备
先说环境版本,这是我实测稳定的一套组合:
| 组件 | 版本 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 20.04 / 22.04 | Windows也能跑,但Linux下省心很多 |
| Python | 3.8 或 3.9 | 兼容性最好 |
| PyTorch | 1.10 到 2.0 | 不要用太旧的版本 |
| CUDA | 11.3 到 12.1 | 与PyTorch版本对应 |
| GPU显存 | 8GB以上 | 建议,低于6GB需要大幅降batch |
安装步骤并不复杂。先创建虚拟环境,然后安装PyTorch,最后安装依赖:
conda create -n yolov5 python=3.9 -y conda activate yolov5 pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --index-url https://download.pytorch.org/whl/cu117 cd cordyceps_yolov5 pip install -r requirements.txt依赖安装完成后,先别急着训练。我建议先跑一次推理测试,确认环境没问题再继续。用官方提供的yolov5s.pt权重随便找一张图测试:
python detect.py --weights yolov5s.pt --source data/images/bus.jpg如果能成功输出检测结果图,说明环境基本可用。这一步能提前排查出很多问题,比如PyTorch与CUDA版本不匹配、opencv安装异常等。
3.2 配置data.yaml与训练参数
数据集的配置文件是最容易出错的地方。YOLOv5要求data.yaml里的路径要么写绝对路径,要么写相对YOLOv5根目录的路径。我的配置如下:
train: datasets/images/train val: datasets/images/val nc: 1 names: ['cordyceps']train和val指定的是图片文件夹路径,标签文件夹会被自动推断为同路径下labels目录。nc是类别数,这里只有1类。names列表与类别顺序对应。
训练命令我使用这样的配置:
python train.py \ --img 640 \ --batch 16 \ --epochs 150 \ --data datasets/data.yaml \ --weights yolov5s.pt \ --cache逐项说明一下参数含义:
--img 640:训练时把输入图片缩放到640x640像素。目标较小可以尝试1280,但显存占用和训练时间都会显著增加。640起步,跑通后再实验1280。--batch 16:批大小。这个值受显存限制,8GB显存建议16,12GB可以到32。如果训练时出现CUDA out of memory,就减半。--epochs 150:训练轮数。150轮在单类小数据集上足够,配合早停机制,如果连续50轮验证集mAP没有提升会自动停止。--weights yolov5s.pt:使用预训练权重。这是迁移学习的关键,比从头训练收敛快得多,精度也更高。--cache:把图片预加载到内存,减少磁盘IO等待,训练速度能提升不少。内存够用就加上。
学习率一般不用动,YOLOv5默认0.01配合余弦退火调度器已经经过大量验证。如果训练时loss波动很大,可以改成0.001再试。
3.3 训练过程监控与权重选择
训练启动后,日志会实时打印每一轮的损失值和验证指标。对于单类别检测,最需要关注的指标是mAP@0.5和mAP@0.5:0.95。mAP@0.5指的是IoU阈值0.5下的平均精度,可以理解为“框得差不多算对”的精度;mAP@0.5:0.95则是更严格的标准,要求预测框和真实框重合度更高。
在这个项目中,mAP@0.5达到0.8以上,就基本具备使用价值了。如果训练到100轮后mAP还在0.5以下,先别急着调模型结构,大概率是数据标注有问题,或者数据增强参数设置不合理。
训练结束后有两个权重文件:best.pt和last.pt。best.pt是验证集mAP最高时保存的模型,last.pt是最后一轮的模型。常规操作是选best.pt做推理。我测试下来两者差距可能在两三个点mAP,看起来不大,但实际检测效果差异明显,坚持用best.pt就对了。
4. 使用训练好的权重进行检测
4.1 单张图片、视频与批量的推理操作
推理用自带的detect.py即可,非常简单:
python detect.py \ --weights weights/best.pt \ --source test_images/ \ --conf-thres 0.25 \ --img-size 640--source可以指向单张图片、图片文件夹、视频文件,甚至摄像头设备号。--conf-thres是置信度阈值,只有置信度大于该值的检测框才会被保留。默认0.25,对这个项目可以用0.3到0.35,能有效过滤背景干扰带来的低置信度误检。
如果需要保存检测框的坐标信息,加上--save-txt --save-conf,输出文件会保存在runs/detect/exp*/labels/目录下,每行格式与训练标签一致,只是多了置信度:
0 0.5234 0.6831 0.1247 0.0872 0.87这个功能在做批量统计时非常有用。比如你要统计一批照片中冬虫夏草的分布密度,直接解析txt结果就行,不需要一张张看图。
4.2 检测结果分析与效果调优
第一次跑推理后,建议把输出的检测图和原图放在一起逐张检查。看有没有漏检,有没有把枯草根误判成目标,边界框有没有明显偏大或偏小。
漏检多的场景通常是目标太小或者颜色太接近背景。我遇到最典型的案例是阴天光线暗的情况下,目标整体呈暗褐色,模型置信度会明显降低。解决办法有两种:一是把这部分场景的图片补进训练集重新训练;二是在推理时把--img-size从640提高到1280,让模型看到更高分辨率的输入,小目标的特征会更明显,但推理时间会增加一倍。
误检大多发生在碎石多、苔藓多的草地,因为颜色和纹理高度相似。解决误检比解决漏检更麻烦,最有效的手段是增加负样本数据。我把这些误检图片单独挑出来,确认没有标注遗漏后,作为纯背景图片加入训练集。YOLOv5会自动处理没有标签文件的图片,模型会学习到这些场景应该输出低置信度。
4.3 模型导出与轻量化部署思路
训练好的PyTorch权重如果要部署到实际产品中,一般需要导出为更通用的格式。YOLOv5官方提供了export.py脚本,支持导出ONNX、TensorRT、CoreML等格式:
python export.py --weights weights/best.pt --include onnx --img-size 640ONNX格式是当前最通用的部署中间格式。后续如果要部署到嵌入式平台,比如瑞芯微RK3588、RV1106或者Jetson系列,ONNX都是关键一步。以RK平台为例,拿到ONNX后,用RKNN-Toolkit转成平台专用的rknn格式,再接入RKNN API做推理,整个过程在官方文档中都有完整说明。
如果部署目标是PC端且使用NVIDIA GPU,最推荐TensorRT加速。TensorRT的优化效果非常可观,一张640x640图片的推理时间可以从5毫秒级降到2毫秒级,而且显存占用更低。导出命令:
python export.py --weights weights/best.pt --include engine --device 05. 常见问题与排查技巧实录
5.1 训练不收敛、loss异常偏高
训练过程中最常遇到的情况是loss一直不下降,或者前期下降后突然反弹。我把常见的几种情况整理成了一张速查表:
| 症状 | 可能原因 | 处理方法 |
|---|---|---|
| loss完全不下降 | 标签文件为空 | 检查labels目录下是否有.txt文件,坐标是否是归一化0-1范围 |
| loss震荡剧烈 | 学习率过高 | 将--hyp中的lr0从0.01降到0.001 |
| loss出现nan | 梯度爆炸 | 降低batch size,或检查数据是否有异常像素 |
| 训练很快但mAP很低 | 数据集太小 | 增加数据增强力度,或更换yolov5n小模型 |
| mAP一直停滞在0.3以下 | 标注严重不一致 | 抽查标签与图片,重点检查边界框是否忽大忽小 |
一个容易被忽视的问题是类别编号。冬虫夏草只有1个类别,但如果标签文件里的class_id误标成大于0的数值,比如1或2,模型训练时会出现“类别数不匹配”的错误提示,或者明明设置了nc=1但训练日志里出现奇怪的类别数。遇到这种情况,用脚本扫描所有标签文件,检查class_id的最大值是否等于nc-1。
5.2 漏检和误检严重
漏检和误检是这类检测项目的核心痛点,排查思路不太一样。
漏检,也就是该检测到的目标没检测到,本质上是因为模型没有学到足够的目标特征,或者目标在输入图像中太小。先看漏检的目标大多数出现在小目标区域还是颜色相近的区域。小目标区域漏检,优先提高--img-size到1280;颜色相近的漏检,需要补充类似光照和背景的数据。还有一个技巧:推理时开启TTA增强,加上--augment参数,模型会对输入做多尺度变换和翻转后取平均结果,小目标召回率会有明显提升,但推理时间会成倍增加,适合离线处理后使用。
误检则多数出在数据层面。模型把枯草根、苔藓、虫子残骸识别成了目标,说明训练数据中缺少这些相似背景的负样本,或者正样本的标注框过于宽松,导致模型学到了太多背景特征。我的处理流程是:先收集误检图片,逐一确认不是标注遗漏(如果标注遗漏,反而说明模型比标注工作做得更好),然后把确认无目标的图片直接丢进训练集。做两到三轮这样的迭代,误检率一般能显著下降。
5.3 显存不足、环境报错等实战坑点
训练阶段最常见的报错是CUDA out of memory。解决办法是降低batch size,从16降到8再到4,总能跑起来。但要注意,batch size太小会导致batchnorm统计不稳定,模型收敛变慢。一个折中方案是保持较小batch size的同时增加梯度累积步数,YOLOv5暂时没有直接参数,需要手动在train.py中修改。对于新手,我建议直接换一张显存更大的显卡,或者用云GPU,省下折腾的时间。
环境版本不匹配的坑也很频繁。最常见的是PyTorch版本太新导致加载老权重文件报错。有时候你从网上下载的权重是用PyTorch 1.10保存的,但本地环境是PyTorch 2.2,运行时会出现类似Can't get attribute '__numba_signature__'的错误,这是因为pickle序列化过程中的兼容性问题。解决办法是尽量保持训练和推理环境版本一致,或者把权重转换为兼容格式。
另外一个容易踩的坑是中文路径问题。YOLOv5代码对中文路径支持不友好,训练或推理的目录如果带中文,可能会出现图片读取失败、标签文件找不到等莫名其妙的问题。所有路径全部使用英文命名,能避免大量无意义排查。
6. 后续扩展方向
6.1 小目标优化方向:切图推理与高分辨率训练
如果想把检测精度再往上提一个台阶,最有效的方式是切图推理,也就是SAHI方案。把一张高分辨率大图切成若干有重叠的patch,分别送入模型检测,再把结果拼回去并做去重合并。这个方案对小目标检测效果提升非常明显,因为目标在patch里的相对尺寸变大了,特征更容易被提取。
实现起来也不复杂,网上有开源的SAHI库,可以无缝对接YOLOv5检测结果。代价是推理时间会随着切图数量成倍增加,适合对精度要求高、实时性要求低的应用场景。如果后续要做无人机巡草这类任务,配合切图推理几乎是最佳方案。
6.2 多类别扩展与品质评估
当前的模型只判断“有没有冬虫夏草”。实际业务中,客户往往还想知道“这棵冬虫夏草的品质如何”。要做到这一点,可以引入多类别分类:按子座长度、虫体完整度、颜色状态等分成不同等级。这时需要重新标注一份带有等级标签的数据集,把nc从1改成对应类别数,其他训练流程基本不变。
但要注意,等级之间的边界往往很模糊,标注一致性很难保证。我建议先只分两到三个等级,比如“完整品”和“残次品”,不要一上来就分四五个档。把标注标准写清楚,给标注员提供明确的视觉参照图,能有效降低标注噪声。
6.3 移动端与嵌入式平台的部署思路
冬虫夏草检测如果要走出实验室,大概率需要一个便携式的硬件方案。常见选择是手机App加云侧推理,或者边缘计算盒子。后者的好处是不依赖网络,适合偏远野外环境。
在RK3588这类边缘设备上部署,流程是将best.pt导出为ONNX,再转成RKNN格式。转换时要注意几个点:ONNX导出时固定输入尺寸为640,不要用动态尺寸;如果遇到不支持的算子,可以在导出时加--simplify参数做图优化;转换后务必在板子上验证精度,RKNN量化到int8后通常会有1到3个点的mAP损耗,这在大部分场景下是可以接受的。
在项目交付时,我一般建议客户先用浮点模型跑通完整流程,再考虑量化部署。不要一上来就追求极致性能,功能正确永远优先于跑得多快。
本文还有配套的精品资源,点击获取