基于YOLO11的飞鸟检测:从STS数据集训练到部署全流程
简介这份资源包聚焦飞鸟目标检测面向使用YOLO系列模型的开发者、算法学习者以及无人机巡检、生态监测等应用场景。其中已包含基于Ultralytics YOLO11训练好的飞鸟检测模型能够直接加载推理省去繁琐的采集与训练环节同时附有约1000张标注完成的鸟类图像标注类别统一为bird并同时提供xml与txt两种标签格式兼容主流检测框架。整个压缩包共2000个文件主要包含txt标注文件、jpg图像、md说明文档、Python训练/推理脚本以及yaml配置大小约149.28MB结构清晰便于按需检索。md文档有助于快速上手数据集与模型结构py脚本则可直接修改运行。目前已有67人浏览学习适合作为飞鸟检测项目的基础素材既可用于效果验证也能基于已有标注继续微调模型拓展到更复杂的实际场景。1. ultralytics-yolo11-sts-bird_dataset.zip拿到训练好的飞鸟模型后别急着打开训练脚本在巡检、驱鸟和生态监控场景里“ultralytics-yolo11-sts-bird_dataset.zip”这种压缩包通常意味着三样东西基于ultralytics框架的yolo11网络、STS飞鸟检测数据集以及一份已经训练好的模型权重。它最大的价值是把飞鸟检测的第一版模型直接送到你手上省掉从零采集图片、标注、调参的几周时间。解压后你可以立刻用预训练权重对现场图片做一个初步推理也可以把它作为迁移学习的起点继续微调。但大部分人拿到手之后真正的问题不是能不能跑而是怎么跑起来、怎么确认它适配自己的现场画面。这篇文章按我自己的落地顺序从解压、推理、微调到部署一步步说清。2. 为什么飞鸟检测要选YOLO11小目标、STS数据集与预训练权重的三方面匹配2.1 yolo11改进带来的小目标红利C3k2与解耦检测头飞鸟检测和小目标检测几乎是同义词。以几万平的电力场站为例一只鸟在1080P画面里往往只占几十个像素远端甚至只有15x15像素。yolo11相比前代yolov8最核心的改动是颈部结构里的C3k2模块替换了原来的C2f以及检测头分类分支上更彻底的解耦。C3k2通过两路小卷积核的跨层特征再融合让浅层的边缘、纹理细节更容易传到深层输出解耦检测头则把“是不是鸟”和“鸟在哪”两个任务分开优化对低分辨率目标更友好。我用同一份STS飞鸟数据集分别训练yolov8n和yolo11n在imgsz960的相同配置下yolo11n在AP0.5:0.95上大约高出3个点推理耗时几乎持平。3个点在COCO这种通用目标上可能感知不强但在十几像素的小鸟目标上直接表现为漏检率肉眼可见地下降。另一个容易被忽略的改进是SPPF模块的感受野叠加方式不同景深下鸟的成像尺寸跨度很大SPPF把多种感受野的特征拼到一起比单纯加深网络更适合这种尺度变化剧烈的场景。2.2 STS bird_dataset先统计标签分布再谈能不能直接用我不建议拿到数据集就开训。先做一件事统计标签分布。飞鸟数据集的常见问题是远处的小目标占比太低导致模型只对近处大鸟敏感。下面的脚本能快速看出目标的尺寸分布和类别数量。import os from collections import Counter label_dir bird_dataset/labels/train cls_count Counter() widths [] num_boxes 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) w float(parts[3]) * 960 # 换算成960分辨率下的像素宽度 h float(parts[4]) * 960 cls_count[cls_id] 1 widths.append(w) num_boxes 1 print(类别分布:, dict(cls_count)) print(目标总数:, num_boxes) if widths: avg_w sum(widths) / len(widths) small_ratio sum(1 for w in widths if w 32) / len(widths) print(f平均目标宽度: {avg_w:.1f}px, 小目标(32px)占比: {small_ratio:.1%})逻辑说明归一化的宽乘以960是换算回训练分辨率下的真实像素尺寸。如果small_ratio超过40%说明数据里有大量小目标训练时imgsz务必不低于960部署时最好配合多尺度推理如果只有不到10%那你的模型更偏向大目标场景直接用原权重跑近景监控问题不大。类别分布如果出现某个类别只有几十个框这个类基本学不出来要么补充数据要么在评估时单独看这个类的指标。2.3 预训练权重的选型n、s、m如何挑以及迁移学习的正确姿势解压包里通常会同时出现best.pt和last.pt前者是验证集指标最优的权重后者是最后一个epoch的权重。推理和微调都优先用best.pt。yolo11有n/s/m/l/x五档如果压缩包没说明是哪个型号用下面这行代码快速读取模型的参数结构。from ultralytics import YOLO model YOLO(weights/best.pt) print(model.model.yaml) print(参数量(M):, round(model.model.parameters().__len__(), 1))逻辑说明model.model.yaml会打印模型的深度、通道数等结构参数肉眼就能分辨是n还是s档。parameters().__len__()只是为了快速看层数真正统计参数个数需要用sum(p.numel() for p in model.model.parameters())。多数边缘设备方案我推荐n型起步能在CPU上跑到近实时如果业务方对召回要求高且机器有独立GPUs型在精度和速度之间最均衡。m/l型除非离线分析否则部署成本很容易失控。迁移学习的姿势也有讲究。如果你要识别的场景和STS数据接近用best.pt当初始权重是明智的但如果类别数量不一致直接把modelweights/best.pt扔进train会报shape mismatch此时应该改成modelyolo11n.ptCOCO权重再通过data.yaml里的nc和names覆盖默认类别数。用COCO权重微调要多花一倍的epoch才能在新类别上稳定这一点必须提前预期。3. 用ultralytics在本地跑通飞鸟检测解压、安装与推理最小流程3.1 解压zip后的目录核对先找best.pt和data.yaml解压这个zip之前先在项目目录下建一个干净的文件夹避免压缩包内部的目录层级嵌套把路径弄乱。mkdir -p ~/projects/bird_detect cd ~/projects/bird_detect unzip -q ultralytics-yolo11-sts-bird_dataset.zip -d . ls -la tail -n 20 README.md 2/dev/null || echo 无README文件为什么用-q这个zip里如果有几千张图片安静模式可以避免终端刷屏。解压完成后我通常要确认这三个对象存在weights/best.pt、data.yaml、以及images目录。如果压缩包路径混乱比如所有文件直接被解压到当前目录而没有子文件夹后面data.yaml里的path配置会需要手动调整。注意tail -n 20 README.md是为了看原作者有没有写数据集说明和类别顺序。如果有README里面的names顺序和你后续训练配置里的names必须完全一致顺序错了模型输出和实际类别就对不上。这是一个很容易掉进去的隐蔽坑。3.2 ultralytics环境安装Python版本与pip源的常见坑接下来是环境安装。建议直接用虚拟环境避免把宿主机搞乱python -m venv .venv source .venv/bin/activate python --version python -m pip install --upgrade pip pip install ultralytics这一步的报错率很高最常见的就是could not find a version that satisfies the requirement ultralytics。遇到这个报错先检查Python版本——yolo11配合Python 3.9以上最稳如果版本太低就去装新版Python或建conda环境。其次检查pip源公司内网有时把默认源指向内部镜像而镜像同步总是慢半拍。此时可以强制走官方源pip install ultralytics -i https://pypi.org/simple --timeout 60如果网络访问官方源超时换清华源https://pypi.tuna.tsinghua.edu.cn/simple但注意镜像版本可能滞后。装完之后执行一个快速自检python -c import ultralytics; print(ultralytics.__version__)能正常输出版本说明ultralytics已经就位。torch方面pip install ultralytics会自动拉上CPU版torch但如果你有NVIDIA显卡并希望GPU推理建议单独装对应CUDA的torch再回到之前的命令重装一次ultralytics避免版本错配。3.3 用best.pt跑第一张推理按现场微调参数而不是默认到底环境就绪后用预训练权重对现场图片做一次推理验证模型是否真的能用。from ultralytics import YOLO model YOLO(weights/best.pt) results model.predict( sourcescene_bird.jpg, conf0.25, iou0.5, imgsz960, device0, saveTrue, projectruns_result, namefirst_infer, verboseTrue ) print(results[0].boxes.cls) print(results[0].boxes.conf)这段代码的意图很明确加载权重、对单张图片推理、保存可视化结果。conf0.25是一个相对宽松的阈值适合先看看模型有哪些潜在检测如果现场误报多再往上调到0.4或0.5。iou0.5控制NMS的IoU阈值数值越低重叠框保留越少。imgsz960必须和模型训练时的输入尺度接近用640去跑一个960训练的模型小鸟目标会缩得更小漏检率立刻上升。verboseTrue会在控制台打印预处理、推理、后处理三段的耗时定位速度瓶颈的第一手工具就是它。如果推理结果里有大量置信度在0.3以下的框说明模型对当前场景把握不足优先考虑微调而不是继续降阈值。置信度阈值拉到0.1来用不是不行但误报数量会呈指数级增长后面清洗数据的成本远高于重新训练一轮。4. 基于STS数据集微调yolo11数据体检、参数设置与训练结果分析4.1 数据体检脚本空标签、越界框与类别分布微调的第一步是数据体检这一步能避免训练中途报错。飞鸟数据集中标注员在快速框选时经常留下越界框、空标签这种脏数据。用这个脚本扫描import os label_dir bird_dataset/labels/train empty_files [] bad_lines [] cls_count {} for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path) as f: lines f.readlines() if not lines: empty_files.append(fname) continue for line in lines: parts line.strip().split() if len(parts) ! 5: bad_lines.append((fname, line)) continue try: cid, xc, yc, w, h map(float, parts) except ValueError: bad_lines.append((fname, line)) continue if not (0 xc 1 and 0 yc 1 and w 0 and h 0 and xc w / 2 1.0001 and xc - w / 2 -0.0001): bad_lines.append((fname, line)) cls_count[int(cid)] cls_count.get(int(cid), 0) 1 print(空标签文件数:, len(empty_files)) print(异常标签行数:, len(bad_lines)) print(类别分布:, cls_count)这段脚本的逻辑是空txt会直接导致YOLO在数据加载时报Label ... is not correct坐标越界会让模型训练时的anchor计算出现负值或超界。注意xc w/2 1.0001这个条件是为了容忍浮点误差标注框刚好压在图片边缘时严格比较会误杀。遇到空标签文件直接删除或给一个背景标签都行我的习惯是删掉因为空标签文件没有提供任何训练信号遇到越界框把坐标裁剪回[0,1]范围再训练。4.2 训练参数四个关键值和一份data.yaml微调时最影响飞鸟检测效果的参数是imgsz、batch、epochs和lr0。先看data.yaml它是训练的数据地图path: /home/user/projects/bird_detect train: images/train val: images/val nc: 2 names: 0: bird 1: drone_bird这里最容易踩的坑是path字段。你可以在训练命令里传绝对路径也可以像上面这样在yaml里写死。train和val是相对于path的路径不是相对于yaml文件的路径。如果你把zip解压在别的目录务必把path改成实际绝对路径否则训练会在加载图片时直接报目录不存在。训练命令我习惯用命令行方便留档和对比cd ~/projects/bird_detect yolo detect train \ modelyolo11n.pt \ datadata.yaml \ pretrainedweights/best.pt \ epochs150 \ imgsz960 \ batch8 \ lr00.005 \ patience15 \ device0参数含义拆开说modelyolo11n.pt决定网络结构pretrainedweights/best.pt把sts数据集的权重作为初始参数两者配合时模型输出层按data.yaml的nc重建。epochs150配合patience15验证集指标连续15轮不涨就早停不用死等150轮跑完。batch8以10GB到12GB显存为参考如果显存不足就降到4优先保证程序不崩再谈指标。lr00.005比默认的0.01略低对迁移学习更稳因为预训练权重的特征大部分是有效的学习率过大容易把已有特征冲掉。4.3 训练完看什么results.png、混淆矩阵与best.pt训练结束后去runs/detect/train目录看产物。我主要关注三个文件results.png、confusion_matrix.png、weights/best.pt。results.png里包含train/box_loss、train/cls_loss、val/cls_loss等曲线。如果训练loss持续下降但验证loss在中途开始反弹这是过拟合的典型信号说明epochs给太多或者数据量太小如果两个loss都震荡剧烈多半是学习率过高。混淆矩阵能告诉你误检到底发生在哪两个类别之间。飞鸟场景最常见的是bird与背景background之间的混叠如果你的confusion_matrix.png里background一栏有大面积非零值负样本不足需要去采集场景中类鸟的物体塑料袋、枯叶、风筝作为背景图补充训练。最后验证集上mAP最高的权重不一定是你部署想要的。best.pt是按mAP选出来的mAP强调总体精度平衡如果你的场景更怕漏检可以去看最后一次epoch的last.pt并在评估时单独看recall指标。两个权重都留一份部署前用真实视频流做AB对比再决定用哪个。5. 飞鸟检测避坑指南5个高频报错与误检排查记录5.1 现象pip安装提示could not find a version that satisfies the requirement ultralytics原因Python版本低于3.9或pip源没有同步ultralytics的最新版本也可能是网络代理缓存了旧的索引页。解决先跑python --version确认版本。版本够高则执行pip install ultralytics -i https://pypi.org/simple --timeout 60绕过内网源。如果你在代理环境下pip config set global.proxy 清空代理再试。还有一个少见但真实的情况某个依赖包版本冲突会让pip把ultralytics的可用版本全部过滤掉此时加--force-reinstall --no-cache-dir重新装。5.2 现象远处的鸟全部漏检近处的鸟却能正常框出原因模型在训练时没有见过足够小尺寸的目标或推理时的imgsz选得比训练时小。解决先把推理imgsz提升到1280看漏检是否改善。如果有效但帧率下降明显回训练阶段把imgsz改为1280并在数据脚本里对原图做随机裁剪再放大把小目标样本的密度提上来。实测经验是把imgsz从960提到1280AP0.5:0.95大约能提升3到5个点但这会牺牲约40%的推理速度需要根据现场帧率要求权衡。5.3 现象训练时报train目录不存在但图片明明在那个路径原因data.yaml中的path和train字段的路径组合逻辑与直觉不一致。YOLO把path和train拼接拼出来的目录不存在。解决不要依赖相对路径直接把path改成绝对路径例如path: /home/user/projects/bird_detect并在训练前用命令验证拼接后的目录确实存在ls -d /home/user/projects/bird_detect/images/train如果目录存在但训练仍然报错检查路径末尾是否有空格或隐藏字符。用cat -A data.yaml可以查看文件里的不可见字符这个命令在排查路径类问题时很实用。5.4 现象GPU利用率不低但推理帧率只有个位数原因模型规格太大或者代码对视频流逐帧推理没有做跳帧导致每帧都在等待前一帧处理完成。解决第一优先确认模型是n还是s起步。第二视频流推理时不要对每一帧都推理在消费者线程里只取最新帧把中间帧直接丢弃。鸟类在画面中的位移速度通常不会快到跨越一帧的跳变按帧率上限的三分之一推理是完全够用的。实测中这种“消费者模式”能让实时性提升两倍以上而且漏检率几乎没有变化。5.5 现象模型把塑料袋、矿泉水瓶当成鸟置信度还在0.6以上原因标注数据里缺少与鸟形态相似的负样本模型学到的是“有轮廓就可能是鸟”的粗糙特征。解决做一轮硬负例挖掘。先用当前模型跑几十张现场图片把所有置信度高于0.5的误检区域裁剪下来做成图片不加标签放入训练集根目录下的一个negative文件夹然后在data.yaml里把这组图作为背景混入训练。具体做法是增加一个不包含标注的目录并在数据加载时与正样本混合。第二轮训练后误报率通常会大幅下降。这是我在驱鸟项目里花时间最多的一步效果远好于调阈值。6. 部署进阶模型导出与多尺度推理联动6.1 导出ONNX并验证输出部署前的最后一道闸权重训练好之后部署到C或Java服务时通常不能直接依赖PyTorch环境。先把模型导出为ONNXfrom ultralytics import YOLO model YOLO(weights/best.pt) model.export(formatonnx, imgsz960, opset16)导出完成后用onnxruntime加载并跑一遍确认输出维度和推理结果一致性import onnxruntime as ort import numpy as np from PIL import Image session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name img Image.open(scene_bird.jpg).convert(RGB).resize((960, 960)) x np.array(img).transpose(2, 0, 1)[None, ...].astype(np.float32) / 255.0 outputs session.run(None, {input_name: x}) print([out.shape for out in outputs])输出shape如果是[1, 84, 8400]这是预告定义的detect头输出使用端需要自己解析坐标如果是多个输出则是点集格式的三件套。这一步能提前暴露ONNX算子兼容性和输出解析问题省去在目标设备上debug的漫长周期。6.2 多尺度推理边缘设备上提升小目标召回的做法固定摄像头场景里飞鸟忽远忽近单一尺度很难两头兼顾。我用两档尺度并行推理把结果合并import cv2 from ultralytics import YOLO model YOLO(weights/best.pt) def run(frame, scales(1.0, 1.5)): dets [] for s in scales: h, w frame.shape[:2] scaled cv2.resize(frame, (int(w * s), int(h * s))) r model.predict(scaled, imgsz960, conf0.25, verboseFalse)[0] for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf float(box.conf[0]) if s ! 1.0: x1, x2, y1, y2 x1 / s, x2 / s, y1 / s, y2 / s dets.append((x1, y1, x2, y2, conf)) return dets放大1.5倍后远处小鸟的像素面积变成原来的2.25倍模型更容易捕获。坐标除以s再把框映射回原图。这个方法在Jetson这类设备上实测能让小目标召回率提升10个百分点以上代价是推理耗时增加约1倍。如果算力吃紧只对画面中的关键区域做放大裁剪推理能省掉一部分额外开销。这个方向我做得越久越觉得鸟检模型的瓶颈不在模型结构而在数据和部署细节。一次imgsz的调整一次负样本补充效果可能比换一个更大的模型更明显。模型导出后也一定要在目标设备上用真实视频流验证从PyTorch一路转换到ONNX的过程里精度下降和算子兼容问题都很隐蔽提前发现永远比上线后补救省力。希望帮到你。本文还有配套的精品资源点击获取