尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

小绿叶蝉目标检测数据集:从数据体检到YOLOv8训练与切片推理

小绿叶蝉目标检测数据集:从数据体检到YOLOv8训练与切片推理 简介面向目标检测与农业害虫识别场景这份小绿叶蝉目标检测数据集提供了完整的VOC与YOLO双格式标注数据。资源共274个文件压缩包约12.81MB其中134张jpg图像、67份xml标注、71个txt标签文本另含1个yaml配置和1个cache缓存文件。VOC格式下JPEGImages存放图片、Annotations存放对应xml便于接入Faster R-CNN、SSD等经典框架YOLO格式下images与labels分别存放图片和标签并预置train.txt与val.txt划分文件可直接用于YOLO系列模型训练。图像覆盖小绿叶蝉的不同姿态与田间背景标注信息包含类别和边界框位置可服务于虫害计数、精准施药等农业监测任务也适合初学者用于迁移学习与模型调参。目前已有92人学习浏览轻量体积便于快速下载与迭代实验。1. 小绿叶蝉目标检测数据集先把“小目标”这三个字吃透茶园植保监测里小绿叶蝉是目标检测模型最难啃的对象之一成虫体长只有三四毫米趴在绿色叶片上几乎和背景融为一体常规模型一碰就是成片漏检。这份“小绿叶蝉目标检测数据集”就是围绕这个场景组织起来的一批带标注图像图像主要来自茶园或果园实拍覆盖不同光照、不同叶片密度下的虫体形态。它的核心价值是让你不必从零开始采集和标注直接用这份数据集把目标检测的训练、验证、推理整条闭环跑通。适合三类人做农业植保视觉系统的开发者、被小目标检测折磨的算法工程师、手头缺真实场景数据的毕业设计同学。但先讲清楚一件事它是场景专用数据集不是通用benchmark所以拿到手先做数据体检再谈训练顺序反了后面全是坑。2. 拆包与数据体检从标注格式到类别分布先确认数据没有“注水”2.1 先看标注格式是VOC还是YOLO后续脚本全看这一步解压“小绿叶蝉目标检测数据集.zip”之后第一件事不是急着翻图片而是打开目录结构看标注格式。农业目标检测数据集最常见的标注格式有两种VOC格式和YOLO格式。VOC格式下每张图片对应一个同名xml文件目标框写在objectbndbox标签里坐标是像素绝对值YOLO格式下每张图片对应一个同名txt文件每一行是class_id x_center y_center width height五个数值坐标全部归一化到01区间。两者对后续脚本的影响完全不同所以先分辨清楚。判断方法其实很直接随便点开一个标注文件如果是xml搜索一下有没有bndbox标签如果是txt用文本编辑器打开每行第一个数字就是类别id。我一般会把解压后的原始目录整理成统一结构让后续训练脚本不用到处找路径data/ ├── images/ # 所有图片 │ ├── train/ │ └── val/ ├── labels/ # 所有标注 │ ├── train/ │ └── val/ └── README.md # 数据说明与划分信息这个目录结构是按YOLO训练框架的默认约定来组织的训练时会自动去找images的兄弟目录labels文件名一一对应。整理好之后再写脚本统计就不需要关心原始目录长什么样了。接下来用一个小脚本把类别数量和标注框尺寸分布统计出来这一步很重要能判断出数据集里有没有类别不平衡问题。import os import xml.etree.ElementTree as ET from collections import Counter labels_dir labels cat_counter Counter() box_sizes [] for fname in os.listdir(labels_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(labels_dir, fname)) for obj in tree.iter(object): name obj.find(name).text cat_counter[name] 1 bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) w x2 - x1 h y2 - y1 box_sizes.append((w, h)) print(类别统计:, dict(cat_counter)) print(总标注框数:, len(box_sizes)) print(框平均尺寸: %.1f x %.1f % ( sum(b[0] for b in box_sizes) / len(box_sizes), sum(b[1] for b in box_sizes) / len(box_sizes), ))这段代码的核心价值是把标注情况量化类别统计告诉你数据集里只有小绿叶蝉一个类别还是混了其他虫种框的平均尺寸告诉你目标在原图上大概占多大。如果平均宽度只有二三十个像素那么后续训练时输入分辨率必须往上调否则目标在下采样过程中直接蒸发。这个脚本假设标注是VOC格式如果实际是YOLO格式换成读txt并乘上图片宽高还原像素坐标即可统计逻辑完全一样。2.2 给数据做一次全面体检重复图、空标注和极端小框数据体检不能只看标注还要检查图片本身。小绿叶蝉数据集如果来自持续采集很容易出现连拍帧、重复图以及未标注的空图片。这些样本混在训练集里轻则浪费时间重则干扰训练空图片会让模型把背景当成“不是虫”的证据而重复图会让验证集虚高看起来准一部署就打回原形。我一般会跑一遍图片查重和空标注检查这两个是标准动作# 用md5查重复图片 find images -type f -name *.jpg -exec md5sum {} | sort | awk seen[$1] {print $2} # 找出没有对应标注文件的图片 for img in images/*.jpg; do stem$(basename $img .jpg) [ ! -f labels/${stem}.txt ] echo missing label: $img done查重命令的原理是计算每张图片的md5哈希值哈希相同的文件大概率是同一张图第二段命令遍历所有图片检查同名标注文件是否存在。这里要特别提醒如果数据集里部分图片确实没有目标作者可能会保留图片但不给标注文件这种情况下不要把这类图片直接删掉而是单独放到一个background目录后续当负样本用。小绿叶蝉这种虫体极小的场景负样本的价值甚至比正样本还高。另外还要看一眼极端小的标注框有多少。我习惯把面积小于20×20像素的框单独列出来因为它们对训练贡献很小反而可能在数据增强时被裁剪掉。如果你发现这类小框占的比例超过三成那就意味着这个项目的难度核心是“极小目标”不是常规检测。2.3 这份数据集的适用边界能直接做什么不能强求什么把体检结果整理清楚之后就能判断这份数据集能支撑什么任务了。小绿叶蝉目标检测数据集最适合做的是YOLO系列模型的有监督微调、小目标检测的算法对比实验、以及植保场景的落地验证。如果你要做的是检测模型在类似虫害场景下的迁移这份数据集的图像分布能帮你快速起步。但也有明显的边界。首先如果标注格式是检测框而不是分割掩码那它不能直接用于分割任务其次数据里只有小绿叶蝉这个类别拿来检测茶尺蠖、蚜虫等其他害虫需要先做模型微调直接套用大概率效果一般最后如果图片全部来自同一片茶园、同一时间段模型的泛化能力会被局限在这类背景纹理上换场景前必须做背景增广。这些边界不是数据集的缺陷而是要提前知道的事。不知道边界就盲目训练后面每一步都是玄学。3. 用它训练YOLOv8数据划分、配置改写与第一轮训练3.1 按拍摄批次分组划分train/val 不要“串味儿”拿到干净的数据集之后接下来是训练。第一步是做数据划分。很多人上来就random.shuffle一下按8:2切分这在通用数据集上问题不大但在小绿叶蝉这种昆虫场景数据集上很容易翻车如果原数据是视频抽帧得来的同一个时间段抽出的相邻帧会被同时切进train和val模型在训练时已经“见过”验证集的背景纹理验证分数自然虚高。我一般会按文件名前缀分组假设文件名类似tea_20240601_001.jpg、tea_20240601_002.jpg前缀里的拍摄日期就是天然的分组依据。按组划分可以避免同一拍摄批次的数据横跨train和valimport os import random from collections import defaultdict img_dir images lab_dir labels groups defaultdict(list) for img in os.listdir(img_dir): stem os.path.splitext(img)[0] if os.path.exists(os.path.join(lab_dir, stem .txt)): group_key _.join(stem.split(_)[:2]) # 取拍摄批次前缀 groups[group_key].append(stem) group_keys list(groups.keys()) random.Random(42).shuffle(group_keys) val_count max(1, int(len(group_keys) * 0.2)) val_keys set(group_keys[:val_count]) train_stems, val_stems [], [] for gk, stems in groups.items(): if gk in val_keys: val_stems.extend(stems) else: train_stems.extend(stems) print(train:, len(train_stems), val:, len(val_stems))这段代码关键在于group_key的取法。如果数据集的命名规则不是前缀式的你也可以换成按文件夹分组或者按拍摄时间字段分组。核心思想是保证同一个场景片段只出现在一个集合里这样验证集的测试效果才真实。划分完成后把文件复制到之前建好的images/train、images/val、labels/train、labels/val四个目录即可。这里注意一个细节如果原始标注是VOC格式YOLO训练需要的是txt格式建议用ultralytics仓库自带的转换脚本批量处理不要手写转换逻辑容易在小数点精度上出问题。3.2 数据配置与训练参数小目标场景不改这几个参数等于白训划分好数据之后写数据配置文件。YOLOv8的数据文件是一个yaml内容非常简单但path字段一定要写绝对路径否则训练时会报找不到图片# data.yaml path: /home/user/leafhopper # 换成数据集所在绝对路径 train: images/train val: images/val names: 0: small_green_leafhoppernames里的类别顺序要和txt标注里的类别id一一对应。这份数据集如果只有一个类别就是0: small_green_leafhopper如果包含不同虫态就按实际类别数补齐。类别id错位是最常见的低级错误训练时loss不下降但报错也没有最后画混淆矩阵时才暴露。接下来是训练命令。我通常先在nano模型上跑通流程再决定要不要换大模型。这里给出一个针对小目标场景调过的训练命令yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz960 \ batch16 \ patience30 \ close_mosaic15 \ scale0.2 \ cos_lrTrue \ projectruns/leafhopper参数选择有讲究。imgsz960是这份数据集最值得关注的一个参数小绿叶蝉的标注框如果平均只有三四十像素输入分辨率用640会导致目标在下采样后只剩十几个像素特征基本丢失提到960能明显改善小目标的召回。代价是显存占用变高如果显卡只有8G显存batch要降到8或者先用640跑通再加rect参数。scale0.2是数据增强里缩放比例默认是0.5对小目标场景来说0.5的缩放会频繁生成比原目标更小的目标放大到几乎不可见反而干扰训练调小到0.2更合适。close_mosaic15的意思是最后15个epoch关闭mosaic增强让模型从“看拼贴图”切换到“看真实图”的收敛阶段这个小技巧能稳定最终的mAP。完整参数对比可以参照这个表按你的显卡显存和训练时长选择参数入门配置小目标推荐配置说明imgsz640960分辨率越高小目标保留的像素越多batch168~16受显存约束960下16batch约需20G显存scale0.50.2缩小增强幅度避免目标被缩没close_mosaic015最后15轮关拼接增强收敛更稳epochs100150配early stoppingpatience设303.3 训练日志怎么读loss、mAP50 和 mAP50-95 的读数习惯YOLOv8训练过程会在runs/leafhopper目录下生成results.csv这是判断训练状态的第一手资料。我习惯每隔一段时间停下来看一眼三组核心指标训练loss、验证集mAP50、mAP50-95。loss曲线整体下降说明模型在学验证集mAP50在早期快速上升中后期变缓是正常现象如果训练loss还在降但mAP50不再上升甚至掉头向下那就是过拟合了此时早停机制一般会触发。mAP50和mAP50-95的差距在这份数据集上会非常明显。小目标框小IoU计算对位置偏移极其敏感mAP50-95通常比mAP50低一大截。如果你的mAP50到了0.9但mAP50-95只有0.4不用慌这是小目标场景的常态也说明还有提升空间。观察验证集预测图比看曲线更直观yolo predict modelruns/leafhopper/weights/best.pt sourceimages/val saveTrue conf0.25保存的预测图里重点看两类错误漏检框少和误检框在叶片反光处。这两个问题在下一章详细说。训练到这里第一轮闭环就算跑通了。4. 避坑指南小绿叶蝉数据集上常见的五个技术翻车点4.1 漏标导致误检虫子太小标注员也会看花眼现象训练出的模型在验证集mAP不低但拿到真实茶园测试画面上误检框到处都是置信度还不低框住的都是叶片边缘或纹理杂乱的位置。原因这是数据集标注质量的问题而不是模型的问题。小绿叶蝉体长只有几毫米在1920分辨率下可能只占二三十个像素标注员疲劳之后容易漏标。Ground truth漏了虫模型学到的“虫”就不是完整的虫它会把没有对应标签的虫体区域当成背景导致推理时在类似区域疯狂误检。解决把第一轮模型的预测结果画出来挑置信度高于0.6的误检框集中复核。如果误检框对应的原图位置上确实有虫但没标注那就说明漏标严重需要补标。我一般会直接用半自动标注的方式把人头一轮标注过的标签拖进标注工具人工只做删减和微调比从零标快得多。4.2 光斑被标成虫标注规范不统一是数据质量的源头现象验证集PR曲线在低置信度区间出现异常抬升模型把叶片上的反光点、露珠高光当成虫。原因如果这份数据集由多人参与标注标注规范没有统一就会出现这类问题。小绿叶蝉背部有透明翅强光下会反光和叶片水珠、露珠反光在视觉上高度相似标注标准宽松的人会把所有亮点都框进去。模型学到的是“亮点”而不是“虫体”。解决写一个标签清洗脚本按HSV通道的形态学特征过滤疑似光斑的框。具体做法是把标注框内的图像块提出来计算饱和度均值光照反光块的饱和度通常明显低于真实虫体因为虫体本身有颜色。用这个特征做个粗筛再把可疑样本集中重新标注。这个步骤很值得做能明显提升推理端的精确率。4.3 背景过拟合训练集“太干净”换一个茶园就崩现象在训练集来源的同一片区测试效果很好一换到另一片茶园或果园精确率直接掉二十个点。原因数据集的图像如果全部来自同一机位、同一时间段、甚至同一片茶树品种背景纹理高度一致模型学到的可能是“这个位置上的绿色斑块”而不是虫体本身。这是场景专用数据集的通病不只是小绿叶蝉数据集的问题。解决训练时加入背景增强策略把HSV扰动调强一点再用随机裁剪从原图背景区生成负样本图如果条件允许去第二个拍摄点补拍50到100张不含虫的背景图放进训练集。数据集的边界在那个位置补数据就是在扩边界。4.4 混淆类问题不同发育龄期的外观差异比“虫 vs 背景”还要大现象预测结果里置信度分布出现双峰一部分框置信度很高一部分框始终在0.30.4徘徊。原因小绿叶蝉的若虫和成虫外观差异极大若虫体色偏黄绿没有翅膀几乎不动成虫有透明翅弹跳快体色更绿。如果数据集把不同龄期都标成同一个类别模型面对的是类内差异远大于类间差异的情况学起来很吃力。解决在标注时区分虫态类别至少区分若虫和成虫哪怕下游任务不需要区分模型也能因此学到更稳定的特征。如果数据集的标签已经固定可以在训练前把标注数据按虫态分桶训练两个单类别模型推理时合并结果。别指望一个模型扛下所有外观变化。4.5 mAP虚高但落地差小目标评测指标制造假象现象best.pt在验证集上的mAP50到了0.95部署到边缘设备后小目标一个都检不出来推理结果几乎不可用。原因mAP对小目标不敏感。一个只有三四十像素的框即使IoU偏差0.2对整体mAP的影响也就是一个小数点后几位而大目标位置稍微偏一点就会被判定漏检。更隐蔽的问题在于验证集本身漏标率高评测指标自然虚高。解决写一个按面积分桶的评测脚本把验证集目标按面积分为小于20×20、20×20到40×40、大于40×40三档分别统计召回率。我一般会这样看结果如果大目标召回率0.95、小目标召回率只有0.4那就别急着部署先把注意力放在小目标档位上。落地前用真实场景的抽帧视频做一次“伪在线测试”比任何评测指标都可靠。5. 一个冲刺技巧切片推理与置信度自适应把数据集的最后一点价值榨干5.1 为什么切片推理对小绿叶蝉这类目标特别有效训练结束后如果发现全图推理漏检严重最先值得尝试的优化是切片推理。小目标的困境在于一张1920分辨率的茶园实拍图输入YOLOv8后网络会先做5次下采样一个30像素的小绿叶蝉在深层特征图上只剩不到两个像素的信息检测头几乎不可能找回它。切片推理的做法是先把原图切成若干带重叠区域的小块每个小块单独送到模型里推理相当于让模型在“近距离”观察每个区域目标在patch里占的比例大得多特征更完整。这个思路对小目标场景极其有效代价是推理时间成倍增加。所以一般用在高精度场景比如植保站点的离线监测而不是实时视频流。我的经验是patch_size取640到960之间重叠率取0.2既避免目标被切到边界又不会产生太多冗余计算。5.2 自己写一个切片推理脚本几十行代码解决不一定要装专门切片推理库自己实现也不难。核心是滑动窗口采样把窗口位置信息记录下来推理后再把坐标映射回原图坐标系import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/leafhopper/weights/best.pt) def sliding_window(img, patch_size640, overlap0.2): h, w img.shape[:2] step int(patch_size * (1 - overlap)) patches [] for y in range(0, max(1, h - patch_size 1), step): for x in range(0, max(1, w - patch_size 1), step): # 越界时回退保证patch不超出图像边界 y1 min(y, h - patch_size) x1 min(x, w - patch_size) y1 max(0, y1) x1 max(0, x1) patches.append((x1, y1, img[y1:y1 patch_size, x1:x1 patch_size])) return patches img cv2.imread(tea_garden_sample.jpg) all_boxes [] for x1, y1, patch in sliding_window(img, patch_size960, overlap0.2): res model(patch, conf0.1, imgsz960, verboseFalse)[0] for bbox, conf in zip(res.boxes.xyxy.cpu().numpy(), res.boxes.conf.cpu().numpy()): bx1, by1, bx2, by2 [float(v) for v in bbox] # 坐标映射回原图 all_boxes.append([bx1 x1, by1 y1, bx2 x1, by2 y1, float(conf)]) # 用NMS合并重叠框 from ultralytics.utils.ops import non_max_suppression # 合并逻辑把all_boxes拼成ndarray后调用nms代码里的核心变量是sliding_window的patch_size和overlap。patch_size越大单次推理覆盖的面积越大但小目标保留效果越差overlap越大目标被完整包含在某个patch内的概率越高但冗余计算也越多。推理时的conf0.1故意设低是为了在切片阶段保留所有候选框最后用NMS合并否则被边界截断的候选框可能在低置信度阶段就被过滤掉。合并时有一个numpy细节all_boxes里的坐标、置信度一定要拼成(N, 5)形状的数组再传给NMS否则程序默默报错。坐标映射阶段最容易踩的坑是忘了把patch的起点坐标加回去导致所有框都集中在左上角。5.3 用验证集反推置信度阈值比固定0.25可靠得多切片推理把候选框找出来了最后一步是决定用多少的置信度阈值过滤。很多人固定用0.25但在小目标场景里正确目标的置信度往往在0.1到0.4之间广泛分布固定阈值会误杀大量真目标。我一般会在验证集上做一次置信度阈值搜索画F1曲线取峰值这一步能再挤出几个点的精确率import json import numpy as np # preds结构: [{conf: 0.32, tp: 1}, {conf: 0.11, tp: 0}, ...] # tp1表示与本真值框IoU0.5, tp0表示误检, tp-1表示被遗漏的真值 with open(val_predictions.json) as f: preds json.load(f) n_gt preds[num_gt] items preds[items] best_f1, best_conf 0, 0.25 for thr in np.arange(0.01, 0.50, 0.01): tp sum(1 for it in items if it[tp] 1 and it[conf] thr) fp sum(1 for it in items if it[tp] 0 and it[conf] thr) fn n_gt - sum(1 for it in items if it[tp] 1) precision tp / (tp fp 1e-9) recall tp / (tp fn 1e-9) f1 2 * precision * recall / (precision recall 1e-9) if f1 best_f1: best_f1, best_conf f1, thr print(最优置信度: %.2f, F1: %.4f % (best_conf, best_f1))这个脚本的逻辑很直白遍历0.01到0.5的阈值每次计算该阈值下的精确率和召回率找F1最高点。关键前提是val_predictions.json里每个预测框的tp字段要提前算好这个字段是在评测阶段通过预测框与真值框的IoU匹配得到的不是推理能直接输出的。验证集的真值框数量num_gt要统计正确否则召回率计算会失真。日志打印出的最优置信度就是部署时要设的参数别再用默认0.25。我记得有一次在一个山地茶园做验证第一轮结果差得没法看折腾了两天才发现问题是patch_size设成了和原图一样大切片推理完全失效白跑了一圈。从那以后我每次跑小目标检测都会强制把流程走一遍先统计标注分布再分组划分数据训练完必跑切片推理和阈值搜索一步都不省尤其是拿到现成数据集的时候数据体检这关不过就绝不上训练。希望帮到你。本文还有配套的精品资源点击获取
返回列表