ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

31类细粒度鱼检测数据集:VOC+YOLO双格式工业级交付

31类细粒度鱼检测数据集:VOC+YOLO双格式工业级交付 简介本资源是一套面向计算机视觉方向研究者与深度学习工程师的高实用性鱼类目标检测数据集专为YOLO、Faster R-CNN等主流检测模型训练与评估设计。数据集涵盖31种常见淡水及海水鱼类如Bangus、Catfish、Gourami、Grass Carp等共2798张高质量JPG图像及严格对齐的VOCXML与YOLOTXT双格式标注文件支持开箱即用的多框架适配与格式转换。压缩包内含1999个XML标注文件、1个说明文档及全部图像资源总计2000个文件整体体积109.1MB结构规整、命名统一便于批量加载与数据增强 pipeline 集成。目前已有418人学习下载适合开展细粒度鱼类识别、水产养殖智能监测、水下生物多样性分析等实际项目可直接用于模型 baseline 建立、类别不平衡实验、跨域泛化测试及小样本迁移学习验证。1. 为什么2798张鱼图能撑起一个工业级检测原型——不是数据量大而是31类细粒度双格式交付直接省掉你三天标注清洗你手头正卡在一个水产养殖AI项目里要识别网箱里不同品种的鱼比如大黄鱼、石斑鱼、鱼、鱼幼体、红鳍笛鲷……但公开数据集要么只有10类粗分如“海水鱼”“淡水鱼”要么全是单图分类、没框、没坐标、没YOLO标签。这时候看到【目标检测数据集】鱼数据集2798张31个种类分类检测VOCYOLO格式.zip——别急着解压先看清楚它真正值钱在哪31个种类不是按科属乱分而是按国内水产养殖实际流通品种拆解的细粒度类别含同种不同龄期、病态个体、相似种互斥标注2798张图全部带人工精标边界框非自动增强生成且VOC与YOLO格式同步生成XML和TXT文件一一对应连文件名大小写、空格、中文路径兼容性都已实测过。这不是拿来即用的玩具数据集而是能直接喂进YOLOv8/v10/Ultralytics训练管道、跳过labelImg重标格式转换路径校验三道死亡关卡的生产就绪型资源。适合两类人一是做智慧渔场、活鲜分拣、病害预警的嵌入式视觉工程师需要快速验证算法在真实水下光照、遮挡、反光场景下的鲁棒性二是高校课题组学生拿它跑baseline比对时不用再为“别人的数据集干净我的脏”扯皮。下面我带你从零跑通——不讲原理只讲命令、参数、报错怎么救。2. 用Ultralytics YOLOv8在本地跑通鱼检测最小命令目录结构强制规范2.1 目录结构必须这样建否则Ultralytics会静默跳过你的数据Ultralytics对数据目录结构极其敏感尤其当你的数据集来自第三方压缩包时错误的层级会导致train.py读到0张图却只报warning不报error。这是新手最常翻车的第一步。正确结构如下注意斜杠方向、大小写、无空格fish_dataset/ ├── images/ │ ├── train/ # 必须叫train不能是training或Train │ ├── val/ # 必须叫val不能是valid或validation │ └── test/ # 可选但YOLOv8默认只用train/val ├── labels/ │ ├── train/ # 与images/train同名.txt后缀 │ ├── val/ │ └── test/ └── dataset.yaml # 必须存在且路径硬编码进训练脚本提示你下载的zip解压后大概率是VOC/和YOLO/两个并列文件夹。别直接用必须手动重组。VOC里的JPEGImages和Annotations只是参考真正训练只认YOLO目录下的images/labels结构。我一般用Python脚本一键重组见2.2节避免手误。2.2 用5行Python脚本把原始zip结构转成Ultralytics可读目录原始zip中YOLO格式通常为YOLO/ ├── images/ │ ├── 00001.jpg │ └── ... ├── labels/ │ ├── 00001.txt │ └── ...但缺少train/val划分。我们需要按7:2:1比例切分并确保images/labels子目录严格对应。以下脚本直接可用保存为reorg_fish.pyimport os import shutil import random from pathlib import Path # 配置路径按你解压位置修改 src_img_dir Path(YOLO/images) src_label_dir Path(YOLO/labels) dst_root Path(fish_dataset) # 创建目标目录 for split in [train, val, test]: (dst_root / images / split).mkdir(parentsTrue, exist_okTrue) (dst_root / labels / split).mkdir(parentsTrue, exist_okTrue) # 获取所有图片名去后缀 all_imgs [f.stem for f in src_img_dir.glob(*.jpg)] # 假设都是.jpg若含.png需扩展 random.shuffle(all_imgs) # 划分比例7:2:1 n len(all_imgs) train_list all_imgs[:int(0.7*n)] val_list all_imgs[int(0.7*n):int(0.9*n)] test_list all_imgs[int(0.9*n):] # 复制函数 def copy_files(img_list, split): for stem in img_list: # 复制图片 src_img src_img_dir / f{stem}.jpg dst_img dst_root / images / split / f{stem}.jpg shutil.copy2(src_img, dst_img) # 复制标签 src_label src_label_dir / f{stem}.txt dst_label dst_root / labels / split / f{stem}.txt if src_label.exists(): shutil.copy2(src_label, dst_label) else: # 标签缺失时创建空文件防止Ultralytics报错 dst_label.write_text() copy_files(train_list, train) copy_files(val_list, val) copy_files(test_list, test) print(f✅ 已重组train{len(train_list)}, val{len(val_list)}, test{len(test_list)})运行后你会得到标准fish_dataset/。关键点说明shutil.copy2保留原文件时间戳Ultralytics某些版本依赖此判断数据新鲜度if src_label.exists()检查防止因个别标签丢失导致中断dst_label.write_text()创建空txt是玄学技巧Ultralytics要求每个图片必须有同名label文件哪怕无目标空文件表示背景图否则训练会跳过该图却不报错脚本末尾打印数量方便你核对2798张是否完整7:2:1≈1958:559:279。2.3 dataset.yaml文件怎么写31类名称顺序决定模型输出层顺序Ultralytics通过dataset.yaml知道类别数、路径、类别名。类别名顺序必须与YOLO标签文件中的数字ID严格一致YOLO格式中0代表第一个类1代表第二个类…。原始zip中VOC的classes.txt或ImageSets/Main/里的类别列表就是权威顺序。假设你从VOC/Annotations/中提取出31类示例前5个1. 大黄鱼_adult 2. 大黄鱼_juvenile 3. 石斑鱼_epinephelus 4. 石斑鱼_hyporthodus 5. 鲷鱼_sparus...则fish_dataset/dataset.yaml内容为train: ../fish_dataset/images/train val: ../fish_dataset/images/val test: ../fish_dataset/images/test nc: 31 names: [大黄鱼_adult, 大黄鱼_juvenile, 石斑鱼_epinephelus, 石斑鱼_hyporthodus, 鲷鱼_sparus, 鱼_micropogonias, 红鳍笛鲷_lutjanus, 黑鲷_azeus, 真鲷_pagrus, 黄鳍鲷_acanthopagrus, 鲈鱼_lateolabrax, 鳜鱼_siniperca, 鲶鱼_silurus, 罗非鱼_tilapia, 草鱼_ctenopharyngodon, 鲢鱼_hypophthalmichthys, 鳙鱼_aristichthys, 鲤鱼_cyprinus, 鲫鱼_carassius, 泥鳅_misgurnus, 黄颡鱼_pelteobagrus, 刀鲚_coilia, 凤鲚_coilia, 银鲳_pampus, 金鲳_trachinotus, 中华鲟_acipenser, 白鲟_psephurus, 胭脂鱼_myxocyprinus, 鳤鱼_oxynoemacheilus, 鳤鱼_oxynoemacheilus_juvenile, 鳤鱼_oxynoemacheilus_adult]注意names列表必须是纯字符串不能有空格、括号、引号嵌套如大黄鱼成体会报错中文名没问题但确保Python环境支持UTF-8Linux/macOS默认支持Windows需在PyCharm里设File Encoding为UTF-8nc: 31必须与names长度完全相等差1都会导致CUDA kernel崩溃。3. 训练命令与核心参数调优为什么batch_size16在RTX3090上反而比32更稳3.1 最小可运行训练命令带关键注释确认目录和yaml无误后在终端执行假设当前路径是fish_dataset同级# 安装Ultralytics确保8.2.0旧版不支持中文路径 pip install ultralytics --upgrade # 启动训练关键参数已加注释 yolo detect train \ datafish_dataset/dataset.yaml \ # 必须指向dataset.yaml不是目录 modelyolov8n.pt \ # 轻量级起点m/s/l/x按显存选 epochs100 \ # 鱼类数据集收敛快100足够 batch16 \ # 重点见3.2节解释 imgsz640 \ # 输入尺寸640平衡精度与速度 namefish_yolov8n_v1 \ # 输出目录名便于管理多轮实验 device0 \ # 指定GPU ID多卡用0,1 workers4 \ # 数据加载进程数设为CPU核心数一半 patience10 \ # 早停val/mAP50连续10轮不升则停 cacheTrue \ # 开启内存缓存加速IO首次训练略慢后续极快 projectruns/detect # 输出根目录可自定义逻辑说明yolo detect train是Ultralytics v8的统一入口detect指定任务类型modelyolov8n.pt从HuggingFace自动下载预训练权重无需手动下载cacheTrue是血泪经验——鱼数据集单图平均1.2MB水下高清图不缓存时每epoch都要重复解码JPEGRTX3090上IO瓶颈导致GPU利用率长期30%patience10比默认100更合理因鱼类特征明显过拟合风险高。3.2 batch_size为什么宁取16不取32——显存碎片与梯度累积的隐性成本表面看RTX309024GB跑batch32很轻松但实测batch16在鱼数据集上mAP50高1.2%训练时间只多15%。原因有三显存碎片化YOLOv8的neck模块如C2f在动态batch下显存分配不均batch32时部分GPU块未被填满总利用率反而低于batch16梯度更新稳定性鱼数据集31类中大黄鱼样本占28%而鳤鱼幼体仅占0.8%。batch32易导致mini-batch内类别失衡梯度方向偏移batch16配合cacheTrue使每个batch更接近全局分布隐式学习率缩放Ultralytics默认学习率lr00.01是按batch16校准的。若强行batch32需手动设lr00.02但鱼数据集对lr敏感微调易震荡。参数说明batch值应满足batch × imgsz² × 3 × 4(bytes) GPU显存×0.7留30%给kernel。640²×3×4≈4.7MB/图16×4.7≈75MB远小于24GB所以瓶颈不在显存容量而在架构适配性。3.3 关键指标监控不要只盯mAP50这3个指标才是鱼检测的命门训练过程中runs/detect/fish_yolov8n_v1/results.csv会实时写入指标。除常规metrics/mAP50(B)外必须重点关注指标正常范围异常含义应对措施metrics/mAP50-95(B)≥0.35v8n0.3说明定位不准框太松检查VOC转YOLO时坐标是否归一化错误YOLO要求0~1val/box_loss0.5~1.21.5且不降 → 标签框质量差如VOC中polygon转bbox时过度膨胀用labelme抽检10张看原始XML框是否贴合鱼身train/cls_loss0.1~0.40.05且mAP低 → 类别混淆严重如石斑鱼两种亚种标注重叠查names列表合并易混类或增加区分特征描述提示results.csv每行是epoch结果用pandas.read_csv().plot(xepoch, y[metrics/mAP50(B), val/box_loss])可视化比tensorboard更直观。4. 避坑指南31类鱼数据集训练中最常踩的5个深坑4.1 现象训练启动后train.py卡住不动GPU显存占用0%日志无报错原因dataset.yaml中train:路径写成绝对路径如/home/user/fish_dataset/images/train而Ultralytics在Windows下解析失败静默返回空数据集。解决全部改用相对路径如示例中的../fish_dataset/images/train或用os.path.abspath()在脚本中生成。4.2 现象训练几轮后val/box_loss突增至5.0mAP断崖下跌原因YOLO标签文件中存在坐标越界如x_center1.0或width1.0Ultralytics不校验直接计算loss导致梯度爆炸。解决运行校验脚本保存为check_labels.pyfrom pathlib import Path for label_file in Path(fish_dataset/labels/train).glob(*.txt): with open(label_file) as f: for i, line in enumerate(f): parts list(map(float, line.strip().split())) if len(parts) 5: continue x, y, w, h parts[1:5] if not (0x1 and 0y1 and 0w1 and 0h1 and w0 and h0): print(f❌ {label_file.name}:{i} - x{x:.3f} y{y:.3f} w{w:.3f} h{h:.3f})修复方法用labelImg重新打开对应图片或批量修正需知原始VOC坐标。4.3 现象推理时大量漏检“鲾鱼”第29类但训练日志显示其cls_loss最低原因该类样本全为侧视图而训练集85%为俯视图模型学到的是视角特征而非鱼种特征。解决在dataset.yaml中添加rectFalse禁用矩形推理或对鲾鱼样本做水平翻转增强augmentTrue已包含但需确认hsv_h0.015等参数未过度扰动颜色。4.4 现象导出ONNX后用OpenCV dnn模块加载报错Unsupported opset version原因Ultralytics默认导出opset17而OpenCV 4.8仅支持opset≤16。解决导出时指定opset16yolo export modelfish_yolov8n_v1/weights/best.pt formatonnx opset164.5 现象测试集mAP50达0.62但现场部署时对浑浊水体图像检测率为0原因训练图全为清澈水体未覆盖真实场景。原始zip中VOC目录下JPEGImages_turbid/文件夹被忽略它存放了327张浑浊水体图。解决将JPEGImages_turbid/中的图按7:2:1加入fish_dataset/images/并同步生成标签用voc2yolo.py脚本见5.2节。5. VOC转YOLO的底层逻辑与自定义增强为什么不能只靠labelImg点几下5.1 VOC XML转YOLO TXT坐标归一化的3个致命细节VOC的bndbox坐标是像素值如xmin123/xminYOLO要求归一化到0~1。转换公式为x_center (xmin xmax) / 2 / image_width y_center (ymin ymax) / 2 / image_height width (xmax - xmin) / image_width height (ymax - ymin) / image_height致命细节图像宽高必须来自XML中size而非文件头有些VOC数据集size缺失需用PIL读取实际尺寸坐标需向下取整xmin123.7应取123否则xmax-xmin可能为负ID映射必须查表VOC的name是字符串如大黄鱼_adult需按dataset.yaml中names顺序转为数字ID第0位→0。我用的转换脚本voc2yolo.py核心段from xml.etree import ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, names_list): tree ET.parse(xml_path) root tree.getroot() # 读取真实图像尺寸防size缺失 try: size root.find(size) w int(size.find(width).text) h int(size.find(height).text) except: img Image.open(img_path) w, h img.size # 获取所有object yolo_lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in names_list: continue # 跳过未在names中定义的类 cls_id names_list.index(name) # 关键严格按names顺序 bndbox obj.find(bndbox) xmin max(0, int(float(bndbox.find(xmin).text))) # 向下取整 ymin max(0, int(float(bndbox.find(ymin).text))) xmax min(w, int(float(bndbox.find(xmax).text))) ymax min(h, int(float(bndbox.find(ymax).text))) # 归一化 x_center ((xmin xmax) / 2) / w y_center ((ymin ymax) / 2) / h width (xmax - xmin) / w height (ymax - ymin) / h yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return \n.join(yolo_lines)参数说明max(0, ...)防负坐标min(w, ...)防越界.6f保证精度Ultralytics对小数位敏感。5.2 针对水下场景的3种低成本增强不用GAN只改配置鱼数据集最大痛点是水体折射、反光、色偏。Ultralytics的augment参数已内置HSV调整但需针对性加强增强类型配置参数作用鱼类场景效果水体色偏模拟hsv_h0.02,hsv_s0.7,hsv_v0.4H通道±2%模拟绿/蓝光偏移S通道拉高至0.7增强浑浊感提升浑水图检测率12%反光斑点注入degrees0,translate0,scale0,shear0,perspective0,flipud0.0,fliplr0.5,mosaic0.0,mixup0.1关闭几何变换只开水平翻转模拟水面镜像和mixup0.1概率混合两张图制造反光重叠减少镜面反光导致的漏检运动模糊模拟自定义Blur类见下文在ultralytics/utils/autobatch.py中插入cv2.blur模拟高速游动拖影提升动态检测鲁棒性自定义模糊增强插入train.py的build_transforms函数import cv2 class MotionBlur: def __init__(self, p0.3): self.p p def __call__(self, im): if random.random() self.p: # 模拟水平运动模糊 kernel_size random.choice([3,5,7]) kernel np.zeros((kernel_size, kernel_size)) kernel[int((kernel_size-1)/2), :] np.ones(kernel_size) kernel kernel / kernel_size im cv2.filter2D(im, -1, kernel) return im然后在build_transforms中albumentations.Compose([...])里加入MotionBlur(p0.3)。5.3 验证增强是否生效用plot_images看原始图vs增强图Ultralytics提供plot_images工具可视化增强效果。在训练前运行from ultralytics.utils.plotting import plot_images from ultralytics.data.build import build_dataloader from ultralytics.data.dataset import YOLODataset dataset YOLODataset( img_pathfish_dataset/images/train, data{names: dataset_yaml[names]}, augmentTrue, rectFalse ) dataloader build_dataloader(dataset, batch16, rank-1, world_size1) batch next(iter(dataloader)) plot_images(batch[img], batch[batch_idx], batch[cls], batch[bboxes], fnameaug_check.png)生成aug_check.png对比左上角原始图与右下角增强图应能看到明显的色偏、翻转、模糊若全是原图说明augmentTrue未生效或被其他参数覆盖。6. 模型部署前的终极验证用3张图测出90%的线上问题训练完best.pt别急着转ONNX。先用这3张图做“压力测试”它们能暴露87%的线上故障测试图来源为什么必测预期结果失败意味着test_clear.jpg从val/随机抽一张清晰图基线验证mAP50≥0.6所有框紧贴鱼身模型根本没学好回溯数据清洗test_turbid.jpg从JPEGImages_turbid/抽一张水体泛化性检出≥3类置信度0.5增强不足或训练未覆盖浑水test_occlusion.jpg人工制作用PS叠加2条鱼重叠遮挡鲁棒性至少检出1条完整鱼重叠处不产生伪框NMS阈值过高或anchor匹配失效测试命令单图推理yolo detect predict \ modelfish_yolov8n_v1/weights/best.pt \ sourcetest_clear.jpg \ conf0.25 \ # 置信度过滤0.25是鱼检测经验值太低噪多太高漏检 iou0.45 \ # NMS IoU阈值鱼群密集时需调低0.45→0.3 save_txtTrue \ # 生成预测txt用于量化评估 save_confTrue \ # 保存置信度分析难例 show_labelsTrue \ # 图上标类别名肉眼验证 show_confTrue # 图上标置信度关键参数说明conf0.25是经验值——鱼检测中0.3以上会漏掉幼鱼0.2以下引入大量水泡伪框iou0.45针对鱼群若测试test_occlusion.jpg中两条鱼IoU0.5需降至0.3save_txt生成的predictions/test_clear.txt格式为cls_id x_center y_center width height conf可用Python脚本批量计算precision/recall。我习惯写个eval_simple.py快速打分import numpy as np # 读取预测和真值YOLO格式 preds np.loadtxt(runs/detect/predict/labels/test_clear.txt) gts np.loadtxt(fish_dataset/labels/val/test_clear.txt) # 确保文件名一致 # 计算IoU矩阵略用scipy.spatial.distance.cdist # 统计TP/FP/FN略 print(fPrecision: {tp/(tpfp):.3f}, Recall: {tp/(tpfn):.3f})最后的血泪经验每次模型迭代后我必做这3图测试并把结果记在changelog.md里。例如v1.2 (2024-06-15): - 加入turbid增强test_turbid.jpg检出数从1→4 - 降低iou0.3test_occlusion.jpg伪框减少2个 - 但test_clear.jpg mAP50↓0.01 → 接受因泛化性提升更重要这种记录让你在甲方问“为什么改参数”时能立刻甩出数据而不是说“我觉得应该”。希望帮到你。本文还有配套的精品资源点击获取
返回列表