YOLO蜱虫检测数据集实战:从标签体检到小目标部署
发布时间:2026/10/2 2:54:27来源:尧图网络
简介这是一套面向YOLO系列算法目标检测任务的蜱虫数据集专为目标检测初学者和算法工程师准备可直接用于模型训练、验证和测试适合物体检测实验、科研对比或实际项目预研。资源包共2000个文件以xml标注文件为主压缩包大小约110.96MB标签同时提供YOLO格式的txt和VOC格式的xml两种标注内容其中txt每行记录类别索引及归一化后的中心点坐标、宽高xml则详细描述对象类别与边界框位置并附data.yaml配置文件可适配yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流版本数据集已完成划分免去手动切分与格式转换的繁琐步骤。目前已有82人学习下载。无论是科研实验还是实际项目这套数据都能让读者跳过繁琐的数据准备环节直接进入模型调参与检测效果优化大幅缩短前期准备时间尤其适合需要快速产出结果的目标检测场景。1. 先拆开看YOLO训练用的2400张蜱虫标签图到底长什么样做动物疫病监测或者野外寄生虫调查的人最痛苦的事情不是调参而是手里没有一张带标注的图。蜱虫这种目标又小、又喜欢趴在牲畜耳朵根和草丛叶尖上人眼找起来都费劲更别说让人一张张拉框。这份“YOLO算法-蜱虫数据集-2400张图像带标签.zip”说白了就是一份可以直接喂给YOLO系列模型的目标检测数据集2400张真实场景图像每张都有对应的边界框标签压缩包打开就能开始训练。它解决的问题很直接——让你跳过“从零标注蜱虫”这一周起步的脏活直接把精力花在训练参数、小目标漏检和实际部署上。适合谁用一类是做牛羊肉畜体体表蜱虫计数、兽药效果评价的农业工程师另一类是研究蜱传病原、需要从野外图片里自动识别媒介蜱类的疾控相关项目。对这两类人来说这份数据集的含金量不在2400这个数字而在“标签干净、格式统一、能直接进YOLO训练管线”。不过这年头下载到zip不等于能一键起飞解压之后要做的事还不少下面从数据体检开始讲。2. 解压校验与标签体检把YOLO数据集的底细摸清再动手2.1 解压后先查目录结构images和labels不是随便放的拿到任何“YOLO数据集.zip”我习惯的第二步不是急着开训练而是先解压看目录树。常见做法是压缩包里会有一个根目录下面有images/和labels/两个平级目录也可能自带data.yaml。如果你看到的是JPEGImages和Annotations那多半是VOC格式需要先转成YOLO格式。先跑一个命令看清全貌unzip YOLO算法-蜱虫数据集-2400张图像带标签.zip -d tick_data cd tick_data find . -maxdepth 2 -type d | sort ls images | head -n 5 ls labels | head -n 5这段命令把压缩包解压到tick_data目录然后只展开两层的目录结构再抽查前5张图片和标签文件名。为什么要卡maxdepth 2因为很多数据集会在子目录里再套train/val分层或者把标签按labels/train/xxx.txt放看两层目录能快速判断是平铺还是分层。判断标准很简单images和labels下的文件名要一一对应只是后缀不同.jpg/.png对.txt如果出现同名但后缀对不上就要警惕是不是真标签缺失。这里有个容易被忽略的点文件名可能带中文。比如压缩包标题本身就带“蜱虫数据集”内部文件如果也是中文名后续在Python里读取容易踩编码坑。我会顺手看一眼文件名ls images | grep -P [\x80-\xFF] | head如果输出有结果说明存在非ASCII文件名。建议直接批量改名成统一前缀加序号省得后面cv2.imread读到空路径。改名的命令是cd images for f in *.jpg; do new_name$(printf tick_%04d.jpg $(echo $f | md5sum | cut -c1-8)) mv $f $new_name done这段用文件哈希前8位做序号前缀能保证不重复、不丢文件。注意改完图片名labels下的同名标签也要跟着改否则训练时YOLO会报“找不到标签”。实际操作中我一般用Python脚本统一改两边的basename这里只是为了说明“文件名干净”是训练前的隐形门槛。2.2 标签格式核对YOLO txt的class x y w h到底指的是什么很多人解压后直接拿一份.txt就开训结果训练到一半发现loss飞到NaN回头一查标签坐标是像素值而不是归一化坐标。YOLO格式的标签文件是纯文本每行五个数类别序号、归一化后的中心点x、中心点y、归一化后的宽、归一化后的高。例如0 0.421875 0.603125 0.083203 0.075781这里的四个坐标全部除以了图片宽度和高度范围在0到1之间。class x y w h没有置信度条目——置信度是推理时模型输出的。如果看到一行有6个数字那可能是别的格式混进来了最常见的是class x1 y1 x2 y2的VOC转YOLO没转干净。我拿到数据会先用Python做一次坐标范围体检看是否有超过1或负数的异常值import os import numpy as np labels_dir tick_data/labels bad_files [] invalid_boxes 0 for f in os.listdir(labels_dir): if not f.endswith(.txt): continue path os.path.join(labels_dir, f) with open(path, r, encodingutf-8, errorsignore) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_files.append((f, field_count:%d % len(parts))) continue try: vals list(map(float, parts)) except ValueError: bad_files.append((f, not_number)) continue cx, cy, w, h vals[1:] if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): invalid_boxes 1 bad_files.append((f, coord_out_of_range)) print(异常标签文件数:, len(set(x[0] for x in bad_files))) print(异常框总数:, invalid_boxes)这段脚本逐行解析每个标签文件做两件事字段数必须等于5坐标必须在合法区间。对于异常文件我建议直接打开看原始内容判断是真标注错误还是混入了其他格式。注意errorsignore是为了防止标签里有隐藏的BOM头或者不可见字符导致解析崩溃。这里有个血泪经验如果这份数据集是从研究机构流传出来的标签可能是用LabelImg直接标注的格式通常没问题但如果是从某论文的补充材料里扒的很可能混着extra字段。所以体检这一关不能省。另外还要确认类别ID从0开始还是从1开始。很多VOC转YOLO脚本只把类别列表存成txt第一类的ID是0但总有转置1的版本。如果数据集的classes.txt里只有“tick”一个类别那ID死活都是0如果分了蜱种、发育期就要格外小心。2.3 用脚本统计类别与框分布2400张图里有多少“暗雷”校验格式只是第一步真正决定训练效果的还有类别的均衡度和目标尺寸分布。蜱虫数据集最常见的坑是“图像数看着多但大量图像是背景”或者“只有几百张有目标其余是空图”。YOLO训练时如果train和val划分不当把空图全划进训练集模型很容易学到“输出空检测”的捷径。我一般会写一个统计脚本把每个类别的框数、每张图的框数、以及归一化框面积打印出来import os from collections import Counter labels_dir tick_data/labels class_count Counter() img_box_count [] box_areas [] for f in os.listdir(labels_dir): if not f.endswith(.txt): continue n 0 with open(os.path.join(labels_dir, f), r) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: continue class_count[int(parts[0])] 1 w, h float(parts[3]), float(parts[4]) box_areas.append(w * h) n 1 img_box_count.append(n) print(类别框数:, class_count) print(单图框数分布: 最少%d, 最多%d, 平均%.2f % (min(img_box_count), max(img_box_count), sum(img_box_count)/len(img_box_count))) box_areas sorted(box_areas) print(框面积中位数: %.4f, 最小: %.4f, 最大: %.4f % (box_areas[len(box_areas)//2], box_areas[0], box_areas[-1]))这里最关键的是box_areas的中位数。蜱虫在整幅图里通常占比很小如果2400张图里框面积中位数低于0.05那这就是一个典型的小目标数据集。这时候直接按YOLO默认配置训练mAP可能看起来不错但实际部署时小目标漏检会非常扎眼。另外img_box_count能告诉你有没有空图如果平均每张框数只有0.3说明有大量空图混在里面。空图留着不删训练时会被当作负样本但对小目标检测帮助不大反而会让模型倾向预测“没有目标”。我的习惯是如果空图占比超过15%就先把空图单独移到一个empty/目录只在最终评估时放回去测误报。格式和分布都体检完这份数据集才算真正进入可训练状态。接下来就要写数据配置、选预训练权重把训练跑起来。3. 用YOLOv5/v8把训练跑通数据配置、预训练权重与最小训练命令3.1 写 data.yaml路径、类别映射与train/val划分YOLO系列对数据集的唯一要求就是一份data.yaml文件里面写清图片路径和类别名。2400张图的数据量不大我建议不要用默认的--data自带coco128而是直接写自己的配置# tick_data/data.yaml path: /home/user/tick_data # 替换成你的实际绝对路径 train: images/train val: images/val test: images/test nc: 1 names: 0: tick这里path是根目录train和val是相对于根目录的子目录路径。注意如果YOLOv5在训练时报train: No labels in images/train ...几乎都是路径问题。我见过的翻车场景是把train: /home/user/tick_data/images/train写成了绝对路径但path也写了结果路径重复叠加变成/home/user/tick_data/home/user/tick_data/...。解决办法是写相对路径并确保images/train和labels/train两个目录同时存在。另外nc: 1这个数字必须和标签文件里的类别ID对应。如果labels里出现ID 1而nc: 1训练初期会报class 1 is not in class list或者异常地不报错但loss很高。先看classes.txt再回去数标签文件里的最大ID这个顺序不要反。划分train/val时建议随机划分而不是按文件名前几个字符划分。因为很多数据集是连续场景拍摄的文件连续的部分可能来自同一只羊、同一块皮肤按时间顺序划分会造成数据泄漏。我一般用Scikit-learn的train_test_split固定random_state42保证复现import os import shutil from sklearn.model_selection import train_test_split src tick_data/images names [f for f in os.listdir(src) if f.endswith(.jpg) or f.endswith(.png)] train_names, val_names train_test_split( names, test_size0.2, random_state42, stratifyNone) for split, lst in [(train, train_names), (val, val_names)]: os.makedirs(ftick_data/images/{split}, exist_okTrue) os.makedirs(ftick_data/labels/{split}, exist_okTrue) for n in lst: base os.path.splitext(n)[0] shutil.move(os.path.join(src, n), os.path.join(ftick_data/images/{split}, n)) shutil.move(os.path.join(tick_data/labels, base .txt), os.path.join(ftick_data/labels/{split}, base .txt))这段代码把图片和标签同步移动到train和val子目录其中20%的图作验证。注意stratifyNone用的是默认随机划分如果蜱虫分多个类别且样本很不均衡应改成stratifylabels_array但要先把每个文件的类别做成一维标签再传进去。不过2400张图这种量级随机划分通常够用。3.2 预训练权重怎么选s/m/n的权衡和显存不够的解法这份数据集规模只有2400张从零训练不现实必须加载预训练权重。YOLOv5和YOLOv8都提供n/s/m/l/x五个尺寸对蜱虫这种小目标、单类别任务我的建议是优先用yolov5s.pt或yolov8n.pt。理由很直接模型容量越小越不容易在小数据上过拟合而且蜱虫目标小大模型感受野大反而不利。如果你手里只有一块8G显存的卡比如常见的RTX 3070/4060yolov8n配batch16和imgsz640能跑yolov5s也勉强能跑但别一上来就yolov5m显存不够时训练速度会像爬。还有另一个选择是用别人在类似昆虫/寄生虫数据集上微调过的权重但我不推荐原因有二第一来源不明预训练权重的背景分布可能跟你的蜱虫图差别很大第二COCO预训练权重虽然不含蜱虫但它学到了足够丰富的纹理和边缘特征微调两三百个epoch就够收敛。选yolov5s还是yolov8n可以看你的部署端想要更快推理就用n想要更高精度就用s。# YOLOv5 方式 git clone https://github.com/ultralytics/yolov5 2/dev/null || true cd yolov5 pip install -r requirements.txt python train.py --data ../tick_data/data.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 200 --device 0这段命令是YOLOv5仓库的标准训练入口。--img 640会把所有训练图缩放到640x640--batch 16受显存和CPU内存双重限制--epochs 200对2400张图已经偏保守。注意yolov5s.pt会由训练脚本在第一次运行的时候自动从网上下载如果下载慢可以提前把权重文件放到当前目录。如果换YOLOv8命令更简洁pip install ultralytics yolo train datatick_data/data.yaml modelyolov8s.pt \ imgsz640 batch16 epochs200 device0这里modelyolov8s.pt既作为初始化权重也决定了模型结构。YOLOv8用紫线命令符数据配置写法是datatick_data/data.yaml。如果你的环境里运行yolo命令报command not found多半是没把Python的Scripts目录加进PATH可以直接用python -m ultralytics代替。3.3 训练命令与五个必调参数epochs、batch、imgsz、patience、cache训练脚本本身不是玄学但参数选不对会白白消耗时间。我列一份适合2400张蜱虫图的基准配置再逐个讲为什么python train.py \ --data ../tick_data/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 300 \ --patience 30 \ --cache ram \ --augment参数说明epochs要设到300虽然通常150轮就收敛但小目标检测的mAP最后10个点往往靠后程涨起来。配合patience可以自动早停所以设长一点不亏。batch16是在8GB显存下的稳健值。如果显存只有6GB降到8同时考虑--img 640改成--img 512。注意batch不是越大越好对蜱虫这种小目标batch过大容易导致正负样本比例在批次内不稳定。imgsz640是速度精度的折中点。蜱虫目标太小用512会丢失细节用1280会大幅增加显存和训练时间且如果原始图像本身只有几百像素放大倍数后会引入插值伪影。建议先看原图分辨率如果原图大部分超过800x800才值得试--img 1280。patience验证集指标连续30轮不涨就早停非常关键。不设patience有可能训练到200轮过拟合又没人盯白白等了几小时。cache ram把图像一次性加载进内存避免训练时每次从硬盘读图。2400张图的高清JPG大约占用几个GB内存一般开发机顶得住。如果内存只有8GB去掉--cache或用cache disk否则训练会触发OOM直接崩。训练日志里重点看三个曲线val/box_loss、val/cls_loss和mAP0.5:0.95。如果val/box_loss降到0.02附近还在继续降而mAP不再涨说明模型已经开始拟合训练集了此时早停就起作用。训练结束后runs/train/exp/weights/best.pt就是你要保留下来的模型last.pt是断电恢复的后悔药留着别删。4. 蜱虫目标检测的常见翻车点从标注框到小目标漏检的排查清单4.1 翻车现场一mAP不低但实际数虫数漏一半现象用best.pt在验证集上跑mAP0.5有0.92看起来漂亮。拿到一张密密麻麻的蜱虫特写图做推理只检出三只肉眼数至少有七只。原因mAP是逐类平均的它对“漏检”惩罚不足尤其当验证集里每张图的蜱虫数量很少时模型学到的是“只要有蜱虫就算对”而不是“每只蜱虫都框出来”。此外YOLO的检测头默认针对通用目标对密集小尺寸目标一个网格只能预测有限的框重叠目标天然容易被merge。解决先在训练脚本里把--iou的NMS阈值调低推理阶段看召回是否提升如果提升有限回到训练层面把--hyp里的h参数anchor重新聚类。更直接的办法是改用YOLOv8的--close_mosaic 10策略后10轮关闭马赛克让模型适应真实目标尺度。如果还不行就把单张图中超过10只蜱虫的图片复制几份做随机裁剪增强强制模型学密集场景。4.2 翻车现场二背景干扰让模型把皮肤褶皱当蜱虫现象训练时loss正常但推理返回大量误检框框在皮肤纹理、草叶阴影上置信度还打到了0.6以上。原因蜱虫本身是背腹扁平的椭圆体颜色跟牛皮肤上自然褶皱、蜱虫叮咬后的结痂非常像。如果标签里没有“negative”类模型就只能靠纹理差异来区分而YOLO的骨干网络在浅层提取的纹理特征对这类近色目标并不鲁棒。解决第一个动作是收集一批不含蜱虫的“背景图”比如同批相机拍的干净皮肤、树叶背面放进训练集作为空标签图片负样本。YOLO训练时这些图片没有标签会参与背景分类。第二个动作是数据增强里把hsv_h、hsv_s的扰动范围加大让模型不依赖颜色而依赖几何轮廓。第三步如果误检集中在某个置信度区间例如0.5-0.65部署时把conf阈值设到0.8代价是召回略降但误报率可以压得很低。4.3 翻车现场三标注框没贴边训练时正样本被反复裁剪现象训练曲线很稳但用best.pt框出来的位置总是偏在蜱虫身体某一侧交并比IoU只有0.5左右导致mAP0.5尚可、mAP0.5:0.95很低。原因原始标注框带了一圈“留白”或者标注时把蜱虫腿算进去了。YOLO训练时的匹配机制是锚框与真实框算IoU如果真实框比实际目标大出一圈模型学到的中心点和宽高就都是偏的尤其蜱虫这种轮廓紧凑的目标框的误差会被放大。解决写脚本把所有标签按固定比例收缩——把中心点不变宽和高各缩小15%。注意不要全部无脑收缩先按框面积筛选只对面积小于0.02的框做收缩因为大框可能本来就包含了周围皮肤收缩反而丢掉上下文。收缩脚本如下import os labels_dir tick_data/labels shrink_ratio 0.85 for f in os.listdir(labels_dir): if not f.endswith(.txt): continue path os.path.join(labels_dir, f) with open(path, r) as fp: lines fp.readlines() out [] for line in lines: parts line.strip().split() if len(parts) ! 5: continue cls, cx, cy, w, h parts[0], float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if w * h 0.02: w w * shrink_ratio h h * shrink_ratio out.append(f{cls} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n) with open(path, w) as fp: fp.writelines(out)这个脚本把面积小于2%全图的框缩小15%。参数是经验值最好先抽几张图可视化对比收缩前后的效果再全量执行。注意保存前先备份原标签否则改坏了没有后悔药。4.4 翻车现场四zip解压出的乱码路径导致训练直接报错现象在Windows上用某个压缩工具解压后图片能打开但一到训练就报Not found: ../tick_data/images/train/螇.jpg路径里出现乱码。原因zip包在打包时用的文件名编码是简体中文GBK而Linux或新版macOS默认用UTF-8解码导致中文文件名变成乱码。更麻烦的是标签文件里的图片引用如果也带中文YOLO在读匹配时会对不上。解决不要用双击解压用Python的zipfile模块按GBK重新解码文件名再解压import zipfile import os zip_path YOLO算法-蜱虫数据集-2400张图像带标签.zip out_dir tick_data with zipfile.ZipFile(zip_path, r) as zf: for info in zf.infolist(): fname info.filename try: fname fname.encode(cp437).decode(gbk) except (UnicodeDecodeError, UnicodeEncodeError): pass target os.path.join(out_dir, fname) os.makedirs(os.path.dirname(target), exist_okTrue) with zf.open(info) as src, open(target, wb) as dst: dst.write(src.read())这段代码的核心在fname.encode(cp437).decode(gbk)zipfile读取原文件名时默认用cp437编码把字节转回来再按GBK解即可。如果你解压后已经乱码了把这套逻辑反过来跑一遍把乱码文件名还原。我每次处理国内来源的zip数据集都会先做这层解码后面的训练路径问题能少一大半。4.5 翻车现场五训练到一半loss变成NaN查了标签也没问题现象第40个epoch时train/box_loss突然变成NaN继续跑也不恢复且bug在每次训练的同一个epoch复现。原因最常见的是训练图像里出现全黑或全白的坏图这些图片在归一化后像素分布异常导致batch内出现无穷梯度另一种是标签文件里出现0 0 0 0 0这样的全零框YOLO的宽高为零损失计算直接除零。解决训练前用脚本扫描所有图片是否能被OpenCV正常解码再看有没有像素均值极端异常的图import cv2 import os bad [] img_dir tick_data/images/train for f in os.listdir(img_dir): p os.path.join(img_dir, f) img cv2.imread(p) if img is None: bad.append((f, imread_failed)) continue if img.mean() 8 or img.mean() 248: bad.append((f, extreme_mean)) print(损坏或异常图片:, bad)如果查出坏图直接删除对应的图片和标签。注意YOLO训练时.jpg损坏但能被OpenCV读出的情况不多更多是某些软件导出的PNG带12位深度OpenCV读出来是uint16YOLO的归一化会自动处理但保险起见统一用cv2.imwrite转成8位三通道JPG。5. 导出与部署让训练好的YOLO模型跑进实际检测流程5.1 导出ONNX或TorchScriptopset和动态轴怎么设训练完的best.pt是PyTorch权重不能直接放进OpenCV或者TensorRT。实际项目里常见的部署路径是把模型导出成ONNX再转成TensorRT引擎或者直接用ONNX Runtime跑。导出命令在YOLOv5里是这样python export.py --weights runs/train/exp/weights/best.pt \ --include onnx --opset 12 --dynamicYOLOv8则是yolo export modelruns/train/exp/weights/best.pt formatonnx dynamicTrue opset12其中--opset 12是兼容性和性能的平衡点。opset太老比如9不支持某些EfficientNMS算子导出会报错opset太新比如17在旧版ONNX Runtime上跑不了。--dynamic参数让输出的宽高不是固定640而是可以接受任意尺寸输入。但注意动态轴在TensorRT里会降低推理性能如果只跑固定640就不要开dynamic直接固定shape速度和显存占用都更优。导出后一定先用ONNX Runtime验证一次前向是否正常再部署。验证脚本很简短import onnxruntime as ort import numpy as np import cv2 sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) img cv2.imread(test_tick.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized cv2.resize(img, (640, 640)) input_tensor resized.astype(np.float32) / 255.0 input_tensor np.transpose(input_tensor, (2, 0, 1))[None] outputs sess.run(None, {sess.get_inputs()[0].name: input_tensor}) print(outputs[0].shape)这里模型输出维度一般是(1, 84, 8400)对应85 4个框坐标 1个objectness 80个类别分数。对单类蜱虫模型输出会变成(1, 6, 8400)因为你用YOLOv5时nc改成了1。如果输出维度和你预期不一致多半是导出时没指定类别数或者误用了COCO预训练模型的默认head。处理输出的后处理逻辑要跟着输出格式改别直接抄网上的COCO demo。5.2 推理参数conf、iou、max_det在蜱虫场景下的取值ONNX原始输出是一堆候选框要想拿到干净结果必须在后处理里做置信度过滤和NMS。YOLO推理相关参数有三个很容易被忽略conf_thres、iou_thres和max_det。conf_thres阈值越低召回越高误报也越多。蜱虫检测我一般先设0.25看效果再根据误报率调高到0.5。如果你发现模型输出一堆0.15置信度的碎片框那不是模型坏了而是阈值太低。iou_thresNMS合并框的IoU阈值。蜱虫密集重叠时这个值要调低到0.2-0.3否则两只紧挨着的蜱虫会被合并成一坨如果场景里目标稀疏、但背景干扰多iou_thres调高到0.5对误检影响不大。max_det限制单张图最多输出的框数。默认300对蜱虫场景通常足够。但如果某张图有上百只蜱虫max_det被你设成50那后面的框会被硬砍掉。做计数场景时设到500更稳妥。YOLOv5官方仓库的detect.py里默认值是--conf 0.25 --iou 0.45这两个值可以先用。注意ONNX Runtime推理时NMS要自己实现不要依赖PyTorch的torchvision.ops.nms除非你在Python环境里部署。C部署时用OpenCV的dnn::NMSBoxes注意它的iou_threshold定义和PyTorch略有差异需要先跑一次你本地前处理确认框坐标格式是 xyxy 还是 xywh否则NMS会出错。5.3 用验证集做P-R曲线和可视化别只看最终mAP到了这一步你可能觉得模型已经训练完了接下来就是写个循环对几百张验证图推理然后把mAP打印出来。但只看一个mAP数字很难发现误检集中在什么位置。正确做法是生成每张验证图的P-R曲线数据再叠加可视化检测结果。YOLOv5在val.py里已经会输出F1_curve.png、PR_curve.png和confusion_matrix.png可以直接用。但如果是自己写推理我建议用下面这段代码收集所有置信度排序结果画出单类P-R曲线import glob import numpy as np import cv2 import onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) all_scores [] all_labels [] img_files sorted(glob.glob(tick_data/images/val/*.jpg)) for f in img_files: img cv2.imread(f) ih, iw img.shape[:2] resized cv2.resize(img, (640, 640)) tensor cv2.cvtColor(resized, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 tensor np.transpose(tensor, (2, 0, 1))[None] out sess.run(None, {sess.get_inputs()[0].name: tensor})[0][0] # 解析out: 每列是 [x, y, w, h, obj_conf, cls_conf] scores out[4] * out[5] # 物体置信度乘类别置信度 gt_count 0 # 读取对应gt标签 gt_path f.replace(images, labels).replace(.jpg, .txt) with open(gt_path) as fp: for line in fp: if len(line.strip().split()) 5: gt_count 1 all_scores.append(scores.max()) all_labels.append(gt_count 0) all_scores np.array(all_scores) all_labels np.array(all_labels) for thr in [0.2, 0.3, 0.5, 0.7]: pred_pos all_scores thr tp (pred_pos all_labels).sum() fp (pred_pos ~all_labels).sum() fn (~pred_pos all_labels).sum() precision tp / (tp fp) if tp fp 0 else 0 recall tp / (tp fn) if tp fn 0 else 0 print(fconf{thr:.1f} precision{precision:.3f} recall{recall:.3f})这段代码没有逐目标算IoU只是按图级判断“这张图有没有蜱虫”用来快速判断模型是否把空图误检成有图。真正严格的P-R曲线需要逐框IoU匹配脚本会复杂一倍。但图级统计能让你看清一个非常关键的信息模型是不是在“空图上乱报”。蜱虫数据集里如果有大量空图而模型倾向乱报图级召回会很高但图级精确度很难看。当conf0.5时图级精确度和召回都超过0.9再谈部署不迟。6. 针对蜱虫小目标的增强组合拳mosaic、copy-paste与A/B验证6.1 mosaic和crop_fraction先拉大尺度多样性YOLOv5默认开启mosaic增强把4张图拼成一张大图这样做确实能显著提升小目标检测能力——因为4张图拼接后每个目标在拼接图里的相对尺寸被缩小了相当于被迫学习更小尺度的特征。但mosaic也有副作用训练后期如果一直用mosaic模型会适应“拼接感”到真实场景的整幅图时反而表现下降。所以YOLOv5在--hyp里有个mosaic参数YOLOv8则提供了close_mosaic参数最后N轮关闭。具体到蜱虫数据训练前期前80% epoch开启mosaic让模型看到不同背景组合下的蜱虫。最后10到20轮关闭mosaic换用随机仿射变换和hsv扰动恢复真实图像的分布。YOLOv8命令写法yolo train datatick_data/data.yaml modelyolov8s.pt \ imgsz640 batch16 epochs200 close_mosaic10close_mosaic10表示最后10轮关闭马赛克。如果发现验证集mAP在训练最后阶段有“断崖式下跌”通常就是mosaic关闭得太晚模型在验证时还没完全适应真实比例。2400张图的规模close_mosaic设15更保守。另一个被忽略的参数是crop_fraction只在YOLOv8里可用。它允许随机裁剪原图的一部分再缩放让模型看到目标的更大比例。蜱虫目标小提高crop_fraction相当于把目标的像素尺寸放大对缩小目标非常有效。常见配置yolo train ... scale0.9 translate0.2 fliplr0.5如果你的YOLOv5版本对应的超参在data/hyps/hyp.scratch-low.yaml里修改scale: 0.5增大到0.9translate: 0.1增大到0.2。改超参后训练会明显变慢但对原图幅面较小、蜱虫占比中等的场景这类增强比单纯堆图更有效。6.2 copy-paste增强给重叠目标创造更多正样本copy-paste增强把一张图中的目标实例裁剪下来贴到另一张图上对小目标物体检测的作用已经被很多研究证明。蜱虫经常群集出现目标之间互相遮挡普通翻转和缩放增强无法生成“更多蜱虫挤在一起”的样本而copy-paste可以。YOLOv5自带copypaste参数需在hyp里开启原理是选取同一batch里的两张图把其中一张的标注框内容裁剪过来粘贴到另一张的随机位置同时更新标签。开启方式# hyp.scratch-custom.yaml copypaste: 0.3python train.py --data ../tick_data/data.yaml --hyp hyp.scratch-custom.yaml ...注意copy-paste增强对GPU显存有额外开销且不是所有batch都适合。copypaste: 0.3的意思是30%的概率对每张训练图执行一次copypaste。如果训练速度肉眼可见变慢一半先降到0.15。这个增强最有效的前提是标注框是紧贴目标的。如果你的标注框带了大量空隙复制粘贴出来的目标会带着一圈错误背景反而会干扰模型。所以我在第4.3节强调先做标签收缩再做copy-paste顺序不能反。6.3 A/B对比验证同一份数据、同一个seed、两次实验数据增强是不是有效不能靠“感觉”。我建议做A/B实验同一份data.yaml同一份数据集划分同一个随机种子A组用增强默认值B组用你改过的增强参数各训200轮然后对比best.pt在验证集上的mAP和P-R曲线。A组命令YOLOv8示例yolo train datatick_data/data.yaml modelyolov8s.pt \ imgsz640 batch16 epochs200 seed42 \ scale0.5 translate0.1 fliplr0.5 close_mosaic0B组命令yolo train datatick_data/data.yaml modelyolov8s.pt \ imgsz640 batch16 epochs200 seed42 \ scale0.9 translate0.2 fliplr0.5 close_mosaic15 copypaste0.3这里seed42必须一致否则数据加载顺序不同对比就失去了意义。训练完成后yolo val datatick_data/data.yaml modelruns/detect/train/weights/best.pt # A组 yolo val datatick_data/data.yaml modelruns/detect/train2/weights/best.pt # B组比较两组输出里的mAP0.5和mAP0.5:0.95。如果B组mAP提升超过2个百分点就把增强保留如果只提升0.5个百分点甚至降低说明你的数据分布本身已经足够多样化增强组合不必要。再回看训练曲线中A组和B组的val/cls_loss——B组通常在更后段的epoch下降更稳定这个现象比mAP绝对值更有说服力。最后提一个我自己的习惯做对比实验时把A、B两组的每轮验证集输出都存到CSV里最后用一行Python画训练曲线对比图。因为只看mAP的话很难判断提升是来自增强还是来自随机波动。2400张图的数据集噪声很大同一个训练跑两次mAP差1.5个百分点是常事所以A/B实验要重复跑三次取中位数才敢下结论。蜱虫检测这个任务最耗时间的往往不是调模型结构而是把数据底细摸清楚、把增强策略验证明白。我从一开始拿到这份zip到最终部署到羊圈门口的自动计数探头上前后花了大约两周其中数据体检和A/B实验占了六成时间。训练本身反而很简单——YOLO全家桶已经帮我们把黑匣子捂得严严实实你要做的就是把该查的查完、该试的试完。希望这套流程能帮你也避掉那些坑。本文还有配套的精品资源点击获取
网站建设高端定制企业官网