☰
蜜蜂检测数据集与YOLO训练全流程:从标签到部署
2026/9/28 13:28:53 网站建设 项目流程

简介:面向目标检测与YOLO系列算法学习者,提供一套蜜蜂/大黄蜂识别数据集,含1404张带标注图像,可直接用于模型训练与验证。压缩包内共2000个文件,其中1230个XML标注文件对应VOC格式,770个TXT标注文件为YOLO格式,两类标签均按类别、中心坐标和宽高比例规范记录,同时包含已划分好的数据集配置data.yaml,适配yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流版本。压缩包整体约73.77MB,轻量易下载,标签格式双轨并存,便于在不同框架间切换使用。目前已有69人学习下载,适合需要快速获取可训练数据集、开展蜜蜂目标检测实验或对比标签格式的初学者与进阶开发者。

1. 蜜蜂检测数据集:1404 张带标签图像,YOLO 系列算法直接开训

做目标检测的人都知道,数据集的坑从来不在数量,而在标签格式和类别平衡。这份「大黄蜂-蜜蜂探测数据集-1404张图像带标签」是我最近在复现 YOLO 系列算法时淘到的,包含蜜蜂、大黄蜂、黄蜂三个类别,图像带完整标签,覆盖白天、逆光、密集花丛、个体重叠等真实拍摄场景。和很多从网上下载后还要自己清洗很久的数据集不同,这份数据已经切好训练集、验证集和测试集,并且自带 data.yaml 配置文件,解压后喂给 YOLOv5、YOLOv7、YOLOv8、YOLOv10 甚至 YOLO11 都能直接跑起来,不需要你改一行路径代码。

更难得的是标签同时提供了 YOLO 格式的 txt 文件和 VOC 格式的 xml 文件,分别放在两个文件夹里,这对想在不同框架之间切换、或者准备用 mmrotate 这类开源工具做检测的人来说非常省事。如果你是刚接触 yolo 检测的新手,或者正在做昆虫识别、农业植保、蜂蜜养殖相关的项目,这份数据集能让你跳过最耗时的标注阶段,直接把精力放在模型训练和调参上。

2. 数据集内部结构与标签格式:txt 和 xml 双格式到底怎么用

2.1 解压后的目录长什么样

拿到压缩包后,第一步不是急着训练,而是把目录结构摸清楚。常见做法是先把 zip 解压到一个不含中文和空格的路径下,比如D:\datasets\bee_yolo或/home/user/data/bee_yolo,这一步看似简单,但很多人翻车就翻在路径上有中文,导致 YOLO 在读取图像时直接报路径错误。

解压后你会看到几个关键部分:图像文件夹、两个标签文件夹(一个存 YOLO 格式的 txt,一个存 VOC 格式的 xml)、一个 data.yaml 文件,以及划分好的 train、val、test 子目录。项目正文里提到的img_0998_444.txt这类文件,就是 YOLO 格式的标签文件,文件名和图像文件名一一对应。比如img_0998.jpg这张图,对应的标签就是img_0998_444.txt。

这里要特别说一句,有些数据集下载下来是图像和标签混在一个目录里的,你得自己写脚本切分。这份数据集的作者已经把 train/val/test 比例分好了,train 大概占七成、val 占两成、test 占一成,具体比例以压包内实际文件为准。你拿到手直接看data.yaml里的配置就知道每个集合的路径。

2.2 YOLO 格式标签的坐标体系

YOLO 格式的 txt 文件每一行代表一个目标框,格式是五个值:类别索引、中心点 x 坐标、中心点 y 坐标、框宽度、框高度。关键点在于,x、y、w、h 全部是归一化后的相对值,取值范围在 0 到 1 之间,不是像素坐标。

举个例子,一张 640x640 的图像,某个蜜蜂框左上角在 (160, 128),右下角在 (320, 256),那么中心点是 (240, 192),宽度是 160,高度是 128。归一化后就是:

class_0 0.375 0.300 0.250 0.200

其中 x_center = 240 / 640 = 0.375,y_center = 192 / 640 = 0.300,width = 160 / 640 = 0.250,height = 128 / 640 = 0.200。

如果你需要验证标签是否正确,可以用下面的 Python 脚本把归一化坐标还原成像素坐标,并在原图上画出检测框:

import cv2 # 读取图像和对应标签 image_path = "img_0998.jpg" label_path = "img_0998_444.txt" img = cv2.imread(image_path) h, w = img.shape[:2] with open(label_path, "r") as f: lines = f.readlines() for line in lines: parts = line.strip().split() cls = int(parts[0]) x_center = float(parts[1]) * w y_center = float(parts[2]) * h box_w = float(parts[3]) * w box_h = float(parts[4]) * h x1 = int(x_center - box_w / 2) y1 = int(y_center - box_h / 2) x2 = int(x_center + box_w / 2) y2 = int(y_center + box_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f"class_{cls}", (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite("check_boxes.jpg", img)

这个脚本的核心逻辑是:把归一化坐标乘以图像宽高还原为像素值,然后从中心点坐标减去一半宽高得到左上角,加上一半宽高得到右下角。参数方面,cv2.rectangle的最后一个参数 2 是线条粗细,画 3 会更醒目;cv2.putText的 0.5 是字体缩放比例,图像分辨率高的话建议调到 0.8。跑完脚本打开check_boxes.jpg看看框是不是贴合蜜蜂身体,如果发现框偏移、过大或过小,说明原始标签本身可能有问题,需要做后续清洗。

2.3 VOC 格式标签的读取与转换

VOC 格式的 xml 文件和 YOLO 格式完全不同,它存储的是绝对像素坐标,不是归一化值。文件结构大致是这样:

<annotation> <filename>img_0998.jpg</filename> <size> <width>640</width> <height>640</height> <depth>3</depth> </size> <object> <name>bee</name> <bndbox> <xmin>160</xmin> <ymin>128</ymin> <xmax>320</xmax> <ymax>256</ymax> </bndbox> </object> </annotation>

注意这里的<name>是类别名称,可能是bee、bumble_bee、wasp,也可能是0、1、2这种数字索引。为什么提这一点?因为我在实际转换中遇到过类别名称和 data.yaml 里的 names 列表对不上的情况,比如 xml 里写的是bumblebee,data.yaml 里写的是bumble_bee,训练的时候模型会把这个框当成未知类别直接忽略,等于变相丢了一批标注。

如果你需要用 xml 做训练,但框架只认 txt,或者反过来,别手写转换,直接用脚本处理更省心:

import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, out_dir, class_map): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) out_lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_map: print(f"跳过未知类别: {name}") continue cls_id = class_map[name] box = obj.find("bndbox") xmin = int(box.find("xmin").text) ymin = int(box.find("ymin").text) xmax = int(box.find("xmax").text) ymax = int(box.find("ymax").text) x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h out_lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") base_name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base_name + ".txt"), "w") as f: f.write("\n".join(out_lines)) # 类别映射表:xml中的name -> 类别索引 class_map = {"bee": 0, "bumble_bee": 1, "wasp": 2} xml_to_yolo("img_0998.xml", "./labels_yolo", class_map)

这段代码的关键在class_map,它决定了 xml 里的类别字符串怎么映射到数字索引。如果 data.yaml 里 classes 顺序是bee, bumble_bee, wasp,那么索引 0、1、2 分别对应这三个名字,映射错了整个模型就学了错误的类别顺序。小数点后保留 6 位是常见做法,YOLO 训练时读取精度完全够用,保留太多位反而让文件变大,没有必要。

3. data.yaml 配置与 YOLOv8 训练流程:从命令到权重文件

3.1 data.yaml 到底配了什么

数据集的灵魂不只是图像和标签,还有 data.yaml 这个配置文件。它告诉训练框架三件事:训练集路径在哪、验证集路径在哪、类别有哪些。这个文件一般长这样:

path: ../datasets/bee_data train: images/train val: images/val test: images/test nc: 3 names: ['bee', 'bumble_bee', 'wasp']

path是数据集根目录,可以是绝对路径也可以是相对路径,取决于你从哪里执行训练命令。我一般用绝对路径,省得换目录后莫名其妙找不到数据。train、val、test三个字段是相对path的子目录路径。nc是类别总数,这里 3 表示有三类目标。names是类别名称列表,顺序必须和标签文件里的数字索引一一对应。

这里有个非常容易踩的坑:数据集压包里可能自带 data.yaml,但其中的path是作者机器上的绝对路径,你解压到自己电脑后路径就对不上了。所以拿到数据集第一件事,是打开 data.yaml 把path改成你自己机器上的实际路径。

3.2 用 YOLOv8 开训:命令与参数含义

配置好 data.yaml 后,训练就很快了。假设你已经装好了ultralytics包(YOLOv8、YOLOv9、YOLO10 以及 YOLO11 都在这个包里),用命令行直接开训:

yolo detect train \ model=yolov8n.pt \ data=/home/user/data/bee_yolo/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ patience=20 \ project=/home/user/runs \ name=bee_detect_v8n

参数的含义逐个说明一下:model=yolov8n.pt用的是 nano 版本,参数量最小、训练最快,适合先跑通流程;如果你追求精度,可以换yolov8s.pt或yolov8m.pt。epochs=100是训练的轮数,蜜蜂这类目标不算特别难,100 轮基本能看到收敛趋势。batch=16是每批图像数量,如果你的显卡显存只有 8GB,建议降到 8 或 4,否则会爆显存。device=0表示用第一张 GPU,没有 GPU 就写device=cpu,但训练时间会大幅拉长。patience=20是早停参数,连续 20 轮 mAP 没提升就自动停止,防止过拟合。

训练过程中控制台会输出每轮的 loss 值、mAP50、mAP50-95 等指标。如果 loss 一路下降、mAP50 稳定在 0.9 以上,说明数据集质量不错,模型学到位了。如果 mAP50 迟迟到不了 0.5,先别急着加 epochs,回头检查标签文件是不是有问题、图像和标签是否对得上、类别是否均衡。

3.3 训练完成后怎么验证

训练结束后,project目录下会生成best.pt和last.pt两个权重文件。best.pt是验证集上表现最好的权重,last.pt是最后一轮的权重。验证测试集效果用这个命令:

yolo detect val \ model=/home/user/runs/bee_detect_v8n/weights/best.pt \ data=/home/user/data/bee_yolo/data.yaml \ split=test \ imgsz=640

加split=test是为了让 YOLO 在测试集上做验证,默认情况下它只跑 val 集。输出的指标里你要重点看两个:mAP50和mAP50-95。mAP50 是 IoU 阈值取 0.5 时的平均精度,mAP50-95 是阈值从 0.5 到 0.95 每步 0.05 的平均值,后者更严格,正常来说前者比后者高 10 到 20 个百分点。如果你的 mAP50 高但 mAP50-95 很低,说明模型对小目标或密集遮挡场景的定位还不够准。

4. 实操避坑指南:从标签错位到训练翻车的 5 个真实问题

4.1 类别索引和 data.yaml 对不上,模型学了错误分类

现象是训练时 loss 能降下来,但验证时 mAP 很低,打开预测结果发现蜜蜂被识别成黄蜂,黄蜂被识别成蜜蜂。

原因是数据集的 txt 标签文件里用的类别索引顺序,和 data.yaml 里的 names 列表顺序不一致。常见做法是解压后先随便挑 5 个 txt 文件打开,人工确认第一列的索引值范围,再和 data.yaml 的 names 一一比对。我一般会写个快速脚本统计所有标签的类别索引分布:

import os label_dir = "labels/train" class_count = {} for filename in os.listdir(label_dir): if not filename.endswith(".txt"): continue with open(os.path.join(label_dir, filename), "r") as f: for line in f: cls = line.strip().split()[0] class_count[cls] = class_count.get(cls, 0) + 1 print(class_count)

如果输出的类别索引是 {0: 800, 1: 400, 2: 204},说明三类都有样本,索引是 0、1、2 没问题。如果出现 3 或更大的索引,那就是标签文件不干净,需要清洗。

4.2 图片和标签文件名对不上,训练时大量告警

现象是训练日志里出现大量WARNING ⚠️ ... label ... not found之类提示,实际用来训练的图像明显少于数据集总数。

原因是文件名前缀不一致,有的图叫img_0998.jpg,标签却叫img_0998_444.txt,中间多了后缀。严格来说 YOLO 要求同名匹配,文件名多一个下划线就匹配失败。解决方法是写个脚本批量改名,把我前面提到的那段xml_to_yolo里的文件名提取逻辑反过来用一次,把标签文件名改成和图像完全一致再训练。

4.3 数据增强导致蜜蜂形态失真,误检率上升

现象是训练时 mAP 很好看,但拿真实场景照片一测,大量误检,把背景里的褐色树叶、黑色石头都框成了蜜蜂。

原因是 YOLOv8 默认开了随机翻转、马赛克增强,蜜蜂的纹理和颜色在翻转后虽然对模型来说是合理的数据扩充,但黄蜂和蜜蜂的斑纹本身就容易混淆,过度增强会让模型学到颜色特征而不是形态特征。我一般会在训练时关掉mosaic和fliplr,改成mosaic=0.0、fliplr=0.0,或者把增强强度调低,看着模型在真实场景下的泛化能力再决定要不要加回来。

4.4 图像中有大量小目标,默认 anchor 不匹配

现象是 mAP50 尚可,但 mAP50-95 非常低,预测结果里远处的小蜜蜂完全没框出来。

原因是蜜蜂经常在花丛深处,目标像素只有几十乘几十,YOLOv8 默认的 anchor 尺寸对这种小目标不够敏感。常见做法是训练时增大输入分辨率,比如imgsz=1280,但显存消耗翻几倍;另一个做法是用yolov8m或yolov8l这类更大模型。从血泪经验来看,先试着把imgsz=960跑一轮,很多小目标就出来了。

4.5 训练时爆显存,batch 一调就训练崩溃

现象是显存在训练 2 个 epoch 后溢出,程序直接退出;调小 batch 后又出现数据加载瓶颈,GPU 利用率低到个位数。

原因是不同类别的图像尺寸差异大,YOLO 默认做 letterbox 缩放,如果数据集里存在超大分辨率图像,内存占用会瞬间飙升。解决方法是先统一图像尺寸,写脚本把图片缩放到统一大小再训练,或者在训练参数里配rect=True让 YOLO 按图像比例自动分组 batch,减少显存浪费。

5. 把 best.pt 部署到真实场景:推理脚本与置信度阈值调优

训练完不能只停在验证集指标上,真正要看的是拿手机随手拍一张花丛照片,模型能不能把三只蜜蜂分开。这里我给你一套部署推理的完整脚本,以及两个关键参数的调优方向。

from ultralytics import YOLO import cv2 model = YOLO("/home/user/runs/bee_detect_v8n/weights/best.pt") img = cv2.imread("field_test.jpg") results = model.predict( source=img, conf=0.35, iou=0.45, imgsz=640, verbose=False ) boxes = results[0].boxes for box in boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() conf = float(box.conf[0]) cls = int(box.cls[0]) if cls == 0: label = "bee" color = (0, 255, 0) elif cls == 1: label = "bumble_bee" color = (255, 0, 0) else: label = "wasp" color = (0, 0, 255) cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) cv2.putText(img, f"{label} {conf:.2f}", (int(x1), int(y1)-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite("result.jpg", img)

conf=0.35是置信度阈值,低于这个值的框会被过滤掉。蜜蜂检测场景里,如果你宁可多框也不能漏,就调到 0.25;如果只想保留确定的结果,就调到 0.5。iou=0.45是非极大值抑制的 IoU 阈值,控制重叠框的合并力度,两个靠得很近的蜜蜂如果被各自框出来,这个值调低到 0.3 能减少误合并。

我在实际部署中遇到过一种情况:模型对黄蜂的置信度普遍比蜜蜂低 0.1 左右,因为黄蜂体型更细长,特征不如蜜蜂明显。这种情况下不要动 conf,而是单独给黄蜂类别的置信度做一个后处理偏移,比如固定阈值 0.3,蜜蜂按 0.35 过滤。这种方法不优雅但有效,算是工程上的土办法。

如果你需要把推理结果输出成树莓派或者 Jetson 上的实时流,记得把推理图像尺寸降到 416 或 320,速度能提升一倍以上,精度损失在蜜蜂这种大目标上几乎可以忽略。从那以后我每训练完一个数据集,都会强制在真实拍摄的陌生照片上跑一遍推理日志,而不是只看验证集指标,这个习惯帮我避开了好几轮模型过拟合的翻车。希望这套从数据检查到部署推理的流程帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询