ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv5的鸟窝目标检测:从数据标注到边缘部署全流程实战

基于YOLOv5的鸟窝目标检测:从数据标注到边缘部署全流程实战 简介这份资源是面向计算机视觉初学者、课程设计学生及生态监测方向研究者的YOLOv5鸟窝目标检测完整项目包用于解决鸟类栖息地识别与定位的实际问题可作为课程设计高分方案或目标检测入门模板。压缩包共126个文件约108.06MB以42个Python脚本、35个YAML配置、16个编译文件、6个PyTorch权重及若干Shell、XML、图片和Dockerfile为主涵盖训练与推理代码、模型结构参数、标注数据、预训练权重及容器化部署配置目录结构清晰便于按模块查阅。目前已有184人学习下载。读者可借此掌握YOLOv5从数据准备、模型训练到推理部署的完整流程理解边界框标注与超参数调优方法并直接复用预训练模型在自有图像或视频上完成鸟窝检测为野生动物观察与生态保护研究提供可落地的技术参考。1. 鸟窝检测这件事为什么值得单独训一个 YOLOv5 模型电力线路、通信铁塔、风机叶片、铁路接触网这些设施上最常见的异物之一就是鸟窝。它不像塑料袋那样飘忽不定也不像风筝那样颜色扎眼鸟窝往往是枯枝、干草、泥土混在一起颜色和背景高度接近形状还特别不规则。更麻烦的是鸟窝一旦搭在绝缘子串附近或者横担上遇到潮湿天气就可能引发闪络轻则跳闸重则烧设备。所以巡检人员每年都要花大量精力去找鸟窝而人工看图效率低、漏检率高尤其是无人机拍回来的几千张图靠人眼一张张翻翻到最后眼睛都花了。这就是「基于 YOLOv5 的鸟窝目标检测源码模型」这个方向真正要解决的问题把巡检图像里的鸟窝自动框出来让后续的告警、复核、消缺流程能跑起来。YOLOv5 虽然已经不是最新的检测框架但它在小目标、密集场景下的稳定性以及部署到边缘设备上的成熟度仍然是很多一线项目的首选。这套源码和模型的价值不在于算法有多新而在于它把数据标注、训练配置、推理部署这条链路完整地串了起来你拿到手之后改一改类别、换一换数据集就能往自己的场景里套。适合谁看如果你手头有无人机巡检图或者铁塔监控图想快速验证鸟窝检测能不能落地如果你已经跑过 YOLOv5 的官方 demo但不知道自己的数据该怎么组织、参数该怎么调如果你需要把模型塞进 Jetson 或者树莓派里做边缘推理这套东西能帮你省掉大量试错时间。下面我按实际做项目的顺序把数据、训练、推理、踩坑这几块拆开讲。2. 鸟窝数据集怎么准备从原始巡检图到 YOLOv5 能吃的标签2.1 鸟窝标注的特殊性为什么通用标注规则在这里会翻车鸟窝和 COCO 里的那些类别不一样。猫狗行人都有明确的轮廓边界鸟窝的边界是模糊的——枯枝伸出去多远算鸟窝搭在铁塔角落的一小团草是鸟窝还是风吹上去的杂物这些问题在标注阶段不统一后面训练出来的模型就会学得乱七八糟。我一般会定三条硬规则。第一只标注有明确筑巢结构的区域零星几根树枝不算必须能看到盘绕、堆叠的形态。第二边界框以鸟窝主体最外沿为准伸出去的细枝如果超过主体宽度三分之一就不纳入框内。第三遮挡超过百分之五十的鸟窝如果还能判断出是鸟窝就标完全看不出形态的直接跳过不要硬标。这三条规则写进标注文档里所有标注人员必须对齐否则后面 loss 震荡的时候你连问题出在哪都找不到。标注工具用 LabelImg 或者 X-AnyLabeling 都行导出格式选 YOLO 格式也就是每张图对应一个 txt每行是class_id x_center y_center width height坐标全部归一化到 0 到 1 之间。这里有个血泪经验标注的时候类别名不要用中文也不要用空格直接用birdnest这种英文小写不然后面生成 yaml 文件的时候编码问题能折腾你半天。2.2 目录结构怎么摆一个能直接跑通的 data 目录模板YOLOv5 对目录结构有固定要求很多人第一次跑自己的数据就是卡在这里。下面这个结构是我用了很多次的模板直接照着建就行。birdnest_dataset/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── val/ │ ├── 101.jpg │ └── 102.jpg ├── labels/ │ ├── train/ │ │ ├── 001.txt │ │ └── 002.txt │ └── val/ │ ├── 101.txt │ └── 102.txt └── birdnest.yamlimages/train和labels/train里的文件名必须一一对应只是后缀不同。图片格式统一成 jpg 或者 png不要混着来YOLOv5 的 dataloader 虽然能处理但混格式容易在缓存阶段出玄学问题。训练集和验证集的比例鸟窝这种场景我一般按 8:2 切如果样本特别少比如只有几百张那就 9:1把验证集留够能覆盖不同背景就行。birdnest.yaml是数据集描述文件内容长这样path: /home/user/birdnest_dataset train: images/train val: images/val nc: 1 names: - birdnestpath写绝对路径train和val写相对路径。nc是类别数鸟窝检测一般就是 1 类。names列表里的顺序要和标注时class_id对应只有一类就写一个。这个文件放在数据集根目录下训练的时候用--data参数指过去就行。2.3 数据增强参数怎么设鸟窝场景下哪些增强是有效的YOLOv5 默认开了很多增强但鸟窝检测有它的特殊性。翻转、缩放、色彩抖动这些常规操作没问题但马赛克增强mosaic要小心。马赛克会把四张图拼成一张鸟窝本身尺寸就小拼完之后可能只剩几个像素模型反而学不到东西。我的做法是训练前期开马赛克到了最后几十个 epoch 关掉让模型在真实分布上收一收。具体参数在data/hyps/hyp.scratch-low.yaml里改关键几项mosaic: 1.0 # 前期保持 1.0后期可以降到 0.0 mixup: 0.0 # 鸟窝场景不建议开容易把两个鸟窝混在一起 copy_paste: 0.0 # 小目标场景可以试 0.1但鸟窝形态差异大慎用 degrees: 0.0 # 巡检图基本是正拍旋转增强意义不大 translate: 0.1 # 轻微平移可以模拟无人机位置偏差 scale: 0.5 # 缩放范围鸟窝大小差异大这个可以保留mosaic关掉的时机我一般是在总 epoch 的百分之八十处比如训 300 轮第 240 轮之后设成 0.0。mixup和copy_paste对鸟窝这种形态不固定的目标帮助有限反而可能引入噪声建议先关掉等基线跑通了再单独做消融实验。注意改完 hyp 文件之后训练命令里不要再重复指定这些参数否则命令行参数会覆盖 yaml 里的设置你会以为改了但实际没生效。3. 用 YOLOv5 训练鸟窝模型环境、命令和关键参数3.1 环境配置conda 建环境到依赖装齐的完整命令YOLOv5 对环境不算挑剔但版本对不上照样报错。我习惯用 conda 建一个干净环境Python 选 3.8 或者 3.9这两个版本和 PyTorch 的兼容性最稳。conda create -n yolov5_birdnest python3.9 -y conda activate yolov5_birdnest # 安装 PyTorch根据你的 CUDA 版本选对应命令 # CUDA 11.8 的情况 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 克隆 YOLOv5 源码如果你拿到的压缩包里已经有源码这步跳过 git clone https://github.com/ultralytics/yolov5.git cd yolov5 # 安装依赖 pip install -r requirements.txt装完之后跑一句python -c import torch; print(torch.cuda.is_available())返回 True 才算 GPU 可用。如果返回 False先检查 CUDA 版本和 PyTorch 版本是否匹配再看显卡驱动是不是太旧。这一步不通过后面训练会直接掉到 CPU 上速度慢到你想砸键盘。requirements.txt里有时候会锁一些比较老的包版本如果和你环境里已有的包冲突可以先把冲突的包单独升级或者降级不用死磕整个文件。我遇到过numpy版本冲突导致cv2导入失败的情况把numpy降到 1.23.5 就好了。3.2 训练命令拆解每个参数到底在控制什么环境好了之后训练命令其实不长但每个参数都值得说清楚。python train.py \ --weights yolov5s.pt \ --data /home/user/birdnest_dataset/birdnest.yaml \ --img 640 \ --batch-size 16 \ --epochs 300 \ --device 0 \ --workers 8 \ --project runs/train \ --name birdnest_exp1 \ --hyp data/hyps/hyp.scratch-low.yaml--weights指定预训练权重鸟窝检测我建议从yolov5s.pt开始模型小、速度快精度不够再换yolov5m.pt。--img 640是输入分辨率巡检图里鸟窝往往只占几十个像素如果你显存够可以提到 1280小目标召回会明显提升但速度会掉一半左右。--batch-size根据显存调16 是 8G 显存的保守值12G 可以上 32。--epochs300 是常规起点鸟窝数据如果只有几千张200 轮也够看验证集 mAP 什么时候平了就停。--workers是 dataloader 的进程数设成 CPU 核心数的百分之七十左右比较合适设太高反而会因为进程切换开销拖慢速度。--project和--name控制输出目录训练日志、权重、混淆矩阵都会存在runs/train/birdnest_exp1下面。训练过程中重点看几个指标box_loss和obj_loss是不是稳定下降mAP0.5有没有在涨precision和recall是不是同步在动。如果obj_loss一直不降大概率是标注有问题回去检查有没有空标签或者坐标越界。如果mAP卡在一个值不动了先别急着调参看看验证集图片是不是和训练集分布差太远。3.3 训练中断了怎么办断点续训和权重选择训练到一半断电或者显存爆了不用从头再来。YOLOv5 会把每轮的权重存在runs/train/birdnest_exp1/weights/下面last.pt是最后一轮的best.pt是验证集指标最好的。续训命令python train.py \ --weights runs/train/birdnest_exp1/weights/last.pt \ --data /home/user/birdnest_dataset/birdnest.yaml \ --epochs 300 \ --resume加了--resume之后优化器状态、学习率调度、epoch 计数都会接着上次的来不会重新开始。这里有个坑如果你改了--img或者--batch-size续训可能会报错因为模型输入尺寸变了最好保持和上次一致。最终用哪个权重不要只看best.pt。我一般会把best.pt和last.pt都拿去做一遍推理测试看实际图片上的框选效果。有时候best.pt在验证集上 mAP 高一点但实际看图会发现它漏掉了一些小目标而last.pt反而更稳。验证集指标是参考肉眼看的实际效果才是最终标准。4. 推理与部署让鸟窝模型真正跑在巡检流程里4.1 单张图和批量推理detect.py 的常用命令训练完之后先用detect.py快速看效果。# 单张图 python detect.py \ --weights runs/train/birdnest_exp1/weights/best.pt \ --source /home/user/test_images/001.jpg \ --img 640 \ --conf-thres 0.4 \ --iou-thres 0.5 \ --device 0 # 整个文件夹批量推理 python detect.py \ --weights runs/train/birdnest_exp1/weights/best.pt \ --source /home/user/test_images/ \ --img 640 \ --conf-thres 0.4 \ --iou-thres 0.5 \ --save-txt \ --project runs/detect \ --name birdnest_test--conf-thres是置信度阈值鸟窝检测我一般从 0.4 开始试漏检多就降到 0.3误检多就提到 0.5。--iou-thres控制 NMS 的合并阈值鸟窝密集的时候可以适当调低比如 0.4避免两个挨着的鸟窝被合并成一个。--save-txt会把检测框的坐标存成 txt方便后续做告警逻辑。推理结果默认存在runs/detect/exp下面图片上会画好框和置信度。如果你要集成到自己的系统里可以直接调 Python APIimport torch # 加载模型 model torch.hub.load(./yolov5, custom, pathruns/train/birdnest_exp1/weights/best.pt, sourcelocal) model.conf 0.4 model.iou 0.5 # 推理 results model(/home/user/test_images/001.jpg) results.print() # 打印检测结果 results.save() # 保存带框图片 df results.pandas().xyxy[0] # 转成 DataFrame方便取坐标 print(df[[xmin, ymin, xmax, ymax, confidence, name]])torch.hub.load的第一个参数是 YOLOv5 源码目录sourcelocal表示从本地加载不走网络。results.pandas().xyxy[0]返回的坐标是像素值可以直接拿去做裁剪或者告警判断。4.2 部署到边缘设备树莓派和 Jetson 上的注意事项巡检场景很多时候要把模型放到边缘设备上比如树莓派 5 或者 Jetson Orin Nano。树莓派上跑 PyTorch 原版会比较吃力建议先导出 ONNX 或者 NCNN 格式。# 导出 ONNX python export.py \ --weights runs/train/birdnest_exp1/weights/best.pt \ --include onnx \ --img 640 \ --batch 1 # 导出 NCNN树莓派上推荐 python export.py \ --weights runs/train/birdnest_exp1/weights/best.pt \ --include ncnn \ --img 640 \ --batch 1导出 ONNX 之后可以用 ONNX Runtime 推理树莓派 5 上单张 640 的图大概能跑到 5 到 8 FPS够做实时巡检了。Jetson 上可以直接用 TensorRT速度能到 30 FPS 以上。注意导出的时候--img要和训练时一致不一致的话精度会掉。边缘设备上还有一个坑输入图片的预处理要和训练时对齐。YOLOv5 默认是 letterbox 填充如果你自己写预处理忘了保持长宽比检测框会偏。最省事的办法是直接用 YOLOv5 源码里的LoadImages和letterbox函数别自己造轮子。4.3 推理结果怎么接进业务从检测框到告警的简单逻辑模型输出的是框业务要的是「哪个塔上有鸟窝」。中间需要一个简单的映射逻辑。假设你的巡检图文件名里带了塔号比如T023_20240501.jpg那就可以直接从文件名提取塔号把检测结果和塔号关联起来。import os import pandas as pd def parse_result(image_path, df): tower_id os.path.basename(image_path).split(_)[0] if len(df) 0: return None # 取置信度最高的一个框 best df.loc[df[confidence].idxmax()] return { tower_id: tower_id, confidence: float(best[confidence]), bbox: [float(best[xmin]), float(best[ymin]), float(best[xmax]), float(best[ymax])] }这个函数把每张图的检测结果转成一条记录置信度低于阈值的直接丢掉。实际项目里还会加一个面积过滤太小的框可能是误检比如小于图像面积千分之一的框直接忽略。这些规则不用写得太复杂先跑通闭环后面再根据误检情况慢慢加。5. 鸟窝检测的避坑与排查那些让我返工好几次的问题5.1 训练 loss 不降反升先查标注再查学习率现象训练开始几轮 loss 正常下降到十几轮突然飙升然后一直震荡不收敛。原因最常见的是标注文件里有坐标越界或者空文件。YOLOv5 在 dataloader 阶段不会报错但训练的时候这些异常样本会把梯度带偏。另一个可能是学习率设太大了hyp.scratch-low.yaml里的lr0默认是 0.01如果你 batch size 很小这个学习率就偏大。解决先跑一遍数据校验脚本检查每个 txt 文件是不是空的、坐标是不是都在 0 到 1 之间。YOLOv5 自带一个utils/general.py里的检查逻辑也可以自己写几行 Python 扫一遍。学习率方面batch size 小于 16 的时候把lr0降到 0.005 或者 0.001 试试。5.2 验证集 mAP 很高但实际图片漏检严重现象训练日志里mAP0.5到了 0.85 以上但拿新拍的巡检图去测一半的鸟窝没框出来。原因验证集和训练集是从同一批数据里随机切的分布太接近模型过拟合了。新图片的光照、角度、背景和训练集不一样模型就认不出来。另外如果验证集里鸟窝都是大目标而实际图片里鸟窝很小也会出现这种落差。解决切验证集的时候按场景切比如按不同的塔型、不同的拍摄时间切不要随机切。训练时把--img提到 1280增强小目标的特征。还可以加一些实际场景的负样本比如没有鸟窝的塔材图让模型学会区分。5.3 推理时框重叠严重一个鸟窝出好几个框现象一张图上同一个鸟窝被框了三四次框之间大量重叠。原因NMS 的iou-thres设太高了比如 0.7导致重叠框没被合并掉。或者是模型对鸟窝的边界学得不好预测框抖动大。解决把--iou-thres降到 0.4 到 0.5 之间大部分重叠框就能合并。如果还不行检查训练数据里有没有同一个鸟窝被标了多个框的情况这种重复标注会让模型学出多个预测。另外--conf-thres也可以适当提高低置信度的框直接滤掉。5.4 导出 ONNX 之后精度掉了一大截现象PyTorch 模型推理正常导出 ONNX 之后用 ONNX Runtime 跑框的位置偏了或者置信度变低。原因导出时的--img和推理时的输入尺寸不一致或者预处理没对齐。YOLOv5 导出 ONNX 默认是动态 batch但如果你推理时 batch 设成 1而导出时是 4也可能出问题。解决导出和推理的--img保持一致batch 都设成 1。预处理用 YOLOv5 源码里的letterbox不要自己写 resize。如果还偏检查 ONNX 的输入输出节点名字用 Netron 打开看一眼确保输入是images输出是output。5.5 显存不够用训练到一半 OOM现象训练开始正常跑了几十个 epoch 之后突然报 CUDA out of memory。原因YOLOv5 在训练过程中会保存一些中间变量如果--workers设太高每个 worker 都会占一份内存。另外验证阶段如果 batch 太大也会爆显存。解决把--batch-size降一档比如从 16 降到 8。--workers降到 4 或者 2。如果还不行在训练命令里加--noval先跳过验证阶段等训练完再单独跑验证。长期方案是换显存更大的卡或者用梯度累积模拟大 batch。6. 把鸟窝模型调得更稳几个我常用的进阶技巧6.1 用 TTA 和模型集成换几个点的召回TTATest Time Augmentation是推理阶段做增强把一张图翻转、缩放几次分别推理再把结果合并。YOLOv5 的detect.py里加--augment就能开。python detect.py \ --weights runs/train/birdnest_exp1/weights/best.pt \ --source /home/user/test_images/ \ --img 640 \ --augment \ --conf-thres 0.35开了 TTA 之后召回一般能涨两三个点但速度会慢三到四倍。如果是对实时性要求不高的巡检复核场景这个代价可以接受。模型集成则是把yolov5s和yolov5m两个模型的结果做加权框融合WBFYOLOv5 源码里没有直接支持需要自己写合并逻辑或者用ensemble-boxes这个库。我一般只在精度要求特别高的项目里做集成常规巡检单模型够了。6.2 小目标检测的针对性优化切片推理鸟窝在整张巡检图里可能只占几十个像素直接 resize 到 640 之后更小。切片推理SAHI的思路是把大图切成小块每块单独推理再把结果拼回去。SAHI 有现成的库可以配合 YOLOv5 用。from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov5, model_pathruns/train/birdnest_exp1/weights/best.pt, confidence_threshold0.35, devicecuda:0 ) result get_sliced_prediction( /home/user/test_images/large_tower.jpg, detection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2 ) result.export_visuals(export_dirruns/sahi_result/)slice_height和slice_width控制切片大小overlap控制切片之间的重叠比例设 0.2 是为了避免鸟窝正好被切在边界上。切片推理对小目标的召回提升非常明显代价是推理时间成倍增加。实际部署的时候可以先用整图推理做粗筛对可疑区域再做切片精检平衡速度和精度。6.3 持续迭代用误检样本做 hard negative mining模型上线之后一定会遇到误检。把误检的图收集起来人工确认哪些是真的没有鸟窝把这些图作为负样本加进训练集重新训一轮。这个过程叫 hard negative mining对降低误检率非常有效。我一般会维护一个hard_negatives文件夹每次巡检发现误检就丢进去攒到几百张就重新训一次。注意负样本的标注文件是空的 txt但文件名要和图片对应。重新训练的时候把负样本按比例混进训练集不要全塞进去否则正负样本失衡模型会变得过于保守。# 假设负样本放在 images/train_neg 和 labels/train_neg # 在 birdnest.yaml 里可以加一个 train_neg 路径或者直接把负样本复制进 images/train cp hard_negatives/images/*.jpg birdnest_dataset/images/train/ cp hard_negatives/labels/*.txt birdnest_dataset/labels/train/混完之后重新跑训练验证集上重点看误检率有没有降。如果误检降了但漏检涨了说明负样本加太多了减一部分再试。6.4 一个我踩过的坑别在验证集上反复调参最后说一个习惯问题。很多人看到验证集 mAP 不高就反复调conf-thres、iou-thres、学习率调到最后验证集指标好看了但测试集一塌糊涂。验证集是用来选模型的不是用来调参的。调参应该在训练集里切一小部分做 dev set验证集只在最后选权重的时候用一次。这个习惯我花了很久才养成但养成之后模型上线翻车的概率明显低了。鸟窝检测这个方向数据质量比模型结构重要场景覆盖比指标数字重要。把标注规则定死把验证集切好把误检样本持续加进去模型就会越来越稳。希望帮到你。本文还有配套的精品资源点击获取
返回列表