简介:本资源面向计算机视觉学习者与目标检测开发者,提供一套细分类型飞机、鸟类与无人机的YOLOv5检测训练方案,重点在于可区分具体飞机型号,适合课程设计、科研实验与算法对比等场景。压缩包共约2000个文件,以1994个txt标签文件为主,配套3个Python脚本与3份PDF说明文档,整体约924.72MB,数据集已按YOLO格式整理并划分train、val、test,附有data.yaml,可直接用于YOLOv5、YOLOv7、YOLOv8等算法训练。资源内含1万多张标注图像,覆盖飞机、鸟类、无人机等类别,标签目录结构清晰,便于快速接入训练流程。另附环境配置教程与PyQt5使用说明,并提供界面脚本,方便搭建可视化检测演示。已有467人学习下载,适合希望快速复现细分目标检测实验、验证模型效果并积累工程经验的读者。
1. 从一张混淆矩阵说起:飞机、鸟类、无人机为什么总被 YOLOv5 认错
机场净空区监控、生态观测、低空安防这三个场景,最后都会撞上同一个问题:天上飞的东西太小、太远、太像。我最早做这类项目时,拿 COCO 预训练的 YOLOv5s 直接推理,结果一只远处飞行的白鹭被判成无人机,一架航模被判成鸟,飞机尾翼和机翼的局部又被切成两个目标。翻车的原因不复杂——COCO 里根本没有「无人机」这个类,而「bird」和「airplane」的样本大多是近距离、大目标、清晰背景,和监控画面里几十像素的小目标分布完全对不上。
所以「YOLOv5 细分类型飞机-鸟类-无人机检测训练模型+数据集+pyqt 界面」这件事,本质不是跑一个开源仓库,而是三件事串起来:一是把三类目标的细粒度差异用数据喂给模型,二是把 YOLOv5 的训练、验证、导出流程调通,三是用一个 PyQt 界面把推理能力封装成能交付给非技术用户的东西。它适合做课程设计、毕设、小型安防原型、生态监测 demo 的从业者和学生,也适合想把检测模型真正落地成桌面工具的人。
这篇不聊虚的,按「数据怎么整 → 模型怎么训 → 界面怎么接 → 坑在哪 → 怎么验证」的顺序走一遍。你照着做,能拿到一个可复现的三分类检测系统;你只想看边界,中间几章的参数表和避坑记录也够用。
2. 数据集构建:三类目标的标注策略与增强边界
2.1 为什么不能直接拿 COCO 的 bird/airplane 凑数
COCO 的 bird 类大概一万多个实例,airplane 三千左右,但无人机是零。更麻烦的是,COCO 的 bird 大多是近距离拍摄的静态鸟,姿态清晰、背景干净;而监控场景里的鸟是运动模糊的小黑点。直接混训的结果是模型学到「大而清晰=鸟」,一遇到远景小目标就崩。
常见做法是自建三分类数据集:plane、bird、drone。每类建议至少 1500 张有效标注图,其中远景小目标(目标框短边小于 40 像素)占比不低于 40%。如果拿不到这么多,用公开数据补充也可以,但要注意来源分布——航拍无人机数据集和地面仰拍监控的视角差异极大,混在一起会让模型对尺度更敏感。
标注用 LabelImg 或 X-AnyLabeling,输出 YOLO 格式的 txt:每行class_id cx cy w h,坐标全部归一化到 0~1。类别顺序必须固定,建议0=plane, 1=bird, 2=drone,并在data.yaml里写死,后面训练、推理、界面三处都要对齐,错一个就是全盘错位。
2.2 目录结构与 data.yaml 的写法
YOLOv5 对目录结构有约定,不按它来会在训练启动时报「No labels found」。标准结构如下:
dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml内容:
# 类别数必须和 names 长度一致,否则训练时分类头维度对不上 nc: 3 names: ['plane', 'bird', 'drone'] # 路径建议写绝对路径,避免从不同工作目录启动时找不到 train: /data/dataset/images/train val: /data/dataset/images/val test: /data/dataset/images/test这里有个容易忽略的点:nc和names必须严格对应,且顺序要和标注时的 class_id 一致。我见过有人标注时把 drone 放 0,yaml 里写['plane','bird','drone'],训练 loss 能降,但推理全乱——因为模型学的是「0 号类」,不是「plane」这个词。
2.3 针对小目标的增强参数怎么设
YOLOv5 默认的增强对普通目标够用,但对「飞机-鸟类-无人机」这种小目标密集场景,需要针对性调整。核心参数在hyp.scratch-low.yaml或自定义 hyp 文件里:
| 参数 | 默认值 | 建议值 | 作用与理由 |
|---|---|---|---|
mosaic | 1.0 | 0.8~1.0 | 四图拼接,提升小目标上下文,但过高会让小目标被裁切 |
scale | 0.5 | 0.3~0.5 | 随机缩放,小目标场景不宜过大,否则目标缩到不可辨 |
mixup | 0.0 | 0.0~0.1 | 混叠增强,小目标场景慎用,容易产生语义冲突 |
copy_paste | 0.0 | 0.1~0.3 | 小目标复制粘贴,对无人机这类稀疏类提升明显 |
hsv_v | 0.4 | 0.3~0.4 | 亮度扰动,模拟不同光照,别调太高否则夜间样本失真 |
flipud | 0.0 | 0.0 | 上下翻转对天空目标不适用,鸟和飞机不会倒着飞 |
copy_paste是 YOLOv5 里对小目标最有效的增强之一,它把标注目标抠出来贴到其他图上,直接增加小目标密度。但要注意:粘贴后的目标框要重新计算,YOLOv5 内部会处理,你只需要在 hyp 里开这个比例。
提示:增强参数改完先跑 10 个 epoch 看 loss 曲线,如果 cls_loss 震荡剧烈,多半是 mixup 或 mosaic 开太高,先把 mosaic 降到 0.5 试。
3. YOLOv5 训练:从环境配置到超参数调优的完整链路
3.1 环境配置与依赖版本锁定
YOLOv5 对环境不算挑剔,但版本错配会出各种玄学问题。我一般用 conda 建独立环境,锁死几个关键版本:
# 创建环境,python 版本建议 3.8~3.10,3.11 以上有些依赖轮子不全 conda create -n yolov5_abc python=3.9 -y conda activate yolov5_abc # 克隆官方仓库(这里只写通用做法,具体地址以你手头源码为准) git clone <yolov5-repo> cd yolov5 # 安装依赖,torch 版本要和 CUDA 匹配 pip install -r requirements.txt pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117关键点:torch和 CUDA 驱动必须匹配。如果你机器是 CUDA 11.7,就装 cu117 的 torch;装错了会在torch.cuda.is_available()返回 False,训练直接掉到 CPU,一个 epoch 跑几小时。验证命令:
import torch print(torch.__version__) # 应为 1.13.1+cu117 print(torch.cuda.is_available()) # 必须为 True print(torch.cuda.get_device_name(0))如果is_available()是 False,先查驱动版本nvidia-smi,再对照 PyTorch 官网的版本矩阵重装,别硬扛。
3.2 训练命令与关键参数逐项说明
假设你用 YOLOv5s 作为基线(速度快,适合桌面端部署),训练命令:
python train.py \ --data /data/dataset/data.yaml \ --weights yolov5s.pt \ --cfg models/yolov5s.yaml \ --epochs 150 \ --batch-size 16 \ --imgsz 640 \ --device 0 \ --workers 8 \ --optimizer SGD \ --lr0 0.01 \ --lrf 0.01 \ --patience 30 \ --project runs/train \ --name abc_v1逐项说明:
--weights yolov5s.pt:加载 COCO 预训练权重。三分类任务从预训练起步比从头训收敛快得多,尤其无人机样本少的时候。--epochs 150:三分类数据量不大时,100~200 足够。配合--patience 30,30 轮验证指标不升就早停,省时间。--batch-size 16:显存 8G 左右用 16,12G 可以上 32。batch 太小 BN 统计不稳,太大会掉点。--imgsz 640:输入分辨率。小目标多的话可以上 1280,但显存和推理耗时翻倍,桌面端要权衡。--lr0 0.01+--lrf 0.01:初始学习率和最终学习率因子,余弦退火从 0.01 降到 0.0001。SGD 对这个任务比 Adam 稳。--workers 8:数据加载线程,按 CPU 核数调,太多会抢内存。
训练启动后重点看三个输出:box_loss、obj_loss、cls_loss。三分类任务里cls_loss是核心,它降不下去说明类间特征没学好,多半是数据问题不是参数问题。
3.3 训练崩了怎么排查:loss 不降与过拟合的区分
训练翻车的典型表现有两种,处理方式完全不同。
第一种:loss 从第一轮就不降,或者降几轮后卡住。先看数据——用python utils/plots.py或自己写脚本可视化几张带框的图,确认标注框没跑偏。我遇到过标注时坐标忘了归一化,框全在图像右下角,模型当然学不到东西。再看学习率,lr0设 0.1 以上容易发散,0.001 以下收敛极慢。
第二种:训练 loss 一直降,验证 loss 先降后升,mAP 在某个 epoch 后掉头向下。这是过拟合,不是欠拟合。对策是加增强(提高 mosaic、开 copy_paste)、加 dropout(YOLOv5 在分类头有 dropout 参数)、减模型容量(从 YOLOv5m 换回 s)、或者直接加数据。别一看到验证掉点就加 epoch,那是反方向。
注意:YOLOv5 训练日志里的
mAP@0.5和mAP@0.5:0.95要分开看。小目标场景下 mAP@0.5 可能还行,但 mAP@0.5:0.95 很低,说明框的定位精度不够,这时候要考虑提高输入分辨率或调整 anchor。
4. PyQt 界面:把检测模型封装成可交付的桌面工具
4.1 界面功能拆解与线程模型
PyQt 界面最容易踩的坑是「推理卡死 UI」。YOLOv5 推理一帧几百毫秒到几秒,如果放在主线程,界面直接无响应。正确做法是把推理放到QThread子线程,通过信号槽把结果传回主线程刷新。
界面最小功能集:图片检测、视频检测、摄像头实时检测、结果保存。四个功能共用同一个推理核心,只是输入源不同。核心推理类封装如下:
import cv2 import torch from PyQt5.QtCore import QThread, pyqtSignal class DetectThread(QThread): # 信号:传回带框图像和统计信息 frame_ready = pyqtSignal(object, dict) def __init__(self, model, source, conf=0.25, iou=0.45): super().__init__() self.model = model self.source = source self.conf = conf self.iou = iou self.running = True def run(self): cap = cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame = cap.read() if not ret: break # 推理:YOLOv5 的 model 可直接接受 numpy 数组 results = self.model(frame, size=640, conf=self.conf, iou=self.iou) # results 是 pandas DataFrame,含 xmin/ymin/xmax/ymax/confidence/class/name df = results.pandas().xyxy[0] stats = {} for _, row in df.iterrows(): stats[row['name']] = stats.get(row['name'], 0) + 1 cv2.rectangle(frame, (int(row['xmin']), int(row['ymin'])), (int(row['xmax']), int(row['ymax'])), (0, 255, 0), 2) cv2.putText(frame, f"{row['name']} {row['confidence']:.2f}", (int(row['xmin']), int(row['ymin']) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) self.frame_ready.emit(frame, stats) cap.release() def stop(self): self.running = False self.wait()逻辑说明:DetectThread继承QThread,run()里循环读帧、推理、画框,通过frame_ready信号把结果发出去。主线程收到信号后只做QPixmap转换和setPixmap,不参与计算。conf和iou是推理阈值,后面界面上的滑块直接绑这两个参数。
参数说明:size=640要和训练时的imgsz一致,否则精度掉得厉害;conf=0.25是置信度阈值,界面可调范围建议 0.1~0.9;iou=0.45是 NMS 阈值,密集小目标场景可以调到 0.5~0.6 减少漏检。
4.2 模型加载与推理参数在界面上的映射
界面上的可调参数不要太多,三个就够:置信度阈值、IOU 阈值、输入尺寸。它们直接映射到推理调用:
# 主窗口里加载模型,只加载一次,全局复用 self.model = torch.hub.load('./yolov5', 'custom', path='runs/train/abc_v1/weights/best.pt', source='local') self.model.conf = 0.25 # 默认置信度 self.model.iou = 0.45 # 默认 NMS # 滑块回调:实时更新阈值 def on_conf_changed(self, value): self.model.conf = value / 100.0 def on_iou_changed(self, value): self.model.iou = value / 100.0这里有个细节:torch.hub.load的source='local'表示从本地仓库加载,不走网络。如果你把 yolov5 源码和权重打包进 exe,路径要用相对路径或sys._MEIPASS处理,否则打包后找不到模型。
4.3 打包成 exe 的依赖处理
PyQt + PyTorch 打包是个体力活。用 PyInstaller 时,torch 的动态库和 yolov5 的配置文件经常漏。推荐命令:
pyinstaller --noconfirm --windowed --name ABC_Detector \ --add-data "yolov5;yolov5" \ --add-data "runs/train/abc_v1/weights/best.pt;runs/train/abc_v1/weights" \ --hidden-import torch \ --hidden-import torchvision \ main.py--add-data把源码目录和权重打进去,--hidden-import防止 torch 被漏掉。打包后体积会到 1~2G,这是 PyTorch 的代价,接受不了就换 ONNX Runtime 推理,能压到几百兆。
提示:打包前先在干净环境里跑一遍
pip list,把没用的包卸掉,否则 PyInstaller 会把整个 site-packages 塞进去。
5. 避坑记录:数据、训练、界面三段的真实翻车现场
5.1 类别顺序错位导致推理全乱
现象:训练 mAP 正常,界面推理时飞机被标成鸟,无人机被标成飞机,整体偏移一位。
原因:标注时 class_id 从 1 开始(LabelImg 某些配置会这样),而data.yaml的names从 0 开始索引,训练时模型学的是 0/1/2,推理时映射错位。
解决:统一用 0 起始。标注完写个脚本扫一遍所有 txt,确认最小 class_id 是 0、最大是 2。发现从 1 开始的,批量减 1。
5.2 小目标漏检严重但 mAP 看着还行
现象:验证集 mAP@0.5 有 0.85,但实际监控画面里远处的小鸟和无人机大量漏检。
原因:验证集里小目标占比低,mAP 被大目标拉高了。模型在训练分布上表现好,但实际场景分布不同。
解决:单独统计小目标(框面积小于 32×32)的召回率,把它作为核心指标。如果小目标召回低于 0.6,提高输入分辨率到 1280,或开 copy_paste 增强,或调整 anchor 尺寸匹配小目标。
5.3 PyQt 界面推理时卡死无响应
现象:点「开始检测」后界面白屏,几秒后恢复,视频检测时一直卡。
原因:推理在主线程执行,阻塞了 Qt 事件循环。
解决:所有推理放QThread,主线程只做 UI 刷新。另外注意信号槽连接方式,跨线程用默认的AutoConnection即可,Qt 会自动排队。
5.4 打包后模型加载失败
现象:源码运行正常,打包成 exe 后报「找不到 best.pt」或「yolov5 模块不存在」。
原因:PyInstaller 打包后工作目录变了,相对路径失效;yolov5 的动态导入没被识别。
解决:用sys._MEIPASS拼接资源路径,--add-data把 yolov5 目录和权重都打进去,torch.hub.load的路径改成基于_MEIPASS的绝对路径。
5.5 摄像头推理延迟越跑越高
现象:实时检测跑几分钟后帧率从 20 掉到 5,内存持续上涨。
原因:cv2.VideoCapture的缓冲队列堆积,读帧速度跟不上推理速度,旧帧越积越多。
解决:在循环里加cap.grab()跳帧,或设置cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)把缓冲降到 1。另外每处理完一帧手动del frame并适时torch.cuda.empty_cache(),防止显存碎片累积。
6. 进阶验证:用混淆矩阵和单类 AP 判断模型到底能不能交付
训练完看总 mAP 只是第一步,真正决定能不能交付的是分类边界清不清楚。我一般会跑两件事:一是val.py生成混淆矩阵,二是单独算每类的 AP。
python val.py \ --data /data/dataset/data.yaml \ --weights runs/train/abc_v1/weights/best.pt \ --img 640 \ --conf 0.001 \ --iou 0.6 \ --task val \ --save-json--conf 0.001是为了让所有预测都参与 AP 计算,--iou 0.6是 COCO 风格的匹配阈值。跑完在runs/val/下会生成confusion_matrix.png,重点看非对角线元素:如果bird被大量判成drone,说明这两类的特征在模型眼里没分开,要么加数据,要么在损失里给这两类加权。
单类 AP 的解读有个经验阈值:plane和bird的 AP@0.5 通常能到 0.85 以上,drone因为样本少、形态多变,能到 0.75 就算可用。如果drone的 AP 明显低于另外两类,优先补无人机数据,尤其是不同机型、不同距离、不同背景的样本,而不是调参。
还有一个容易被忽略的验证:拿模型去跑一段完全没参与训练的实拍视频,人工数漏检和误检。我自己的习惯是,任何模型上线前必须过这一关,因为验证集再高也可能只是「背题」。有一次验证集 mAP 0.9 的模型,在实拍视频里把一只风筝连续判成无人机,原因就是训练集里没有风筝这类负样本。后来补了 200 张纯背景负样本(不含任何目标),误检才压下去。
所以如果你要投入这个方向,我的建议是:数据阶段就把负样本和难例当一等公民,训练阶段盯小目标召回而不是总 mAP,界面阶段把线程和打包这两关提前跑通。这套东西不复杂,但每一环都有它自己的脾气,急不得。希望帮到你。
本文还有配套的精品资源,点击获取