资讯动态

YOLO石油泄露数据集实战:三种标签格式转换与训练全流程

发布时间:2026/9/29 23:55:02 来源:尧图企业网站定制
简介面向目标检测学习者与石油泄漏监测应用开发者这份YOLO石油泄露目标检测数据集提供了真实场景下的高质量图像与人工标注可支撑从YOLO环境搭建、数据格式转换到模型训练与验证的完整流程。压缩包共2000个文件容量约117MB其中1000张图片对应的标签已按voc xml、coco json、yolo txt三种格式分别存放另有6个环境搭建与训练教程页面、3个Python划分脚本和1个模型训练yaml配置目录结构清晰便于直接选择所需格式开展训练。当前已有658人学习。配套内容不止于数据Linux与Windows双平台的YOLO安装配置说明、按案例修改训练自己数据集的教程以及训练集/验证集/测试集划分脚本能帮助初学者减少配环境与造数据的弯路快速完成石油泄露场景的目标检测模型训练与评估。1. YOLO石油泄露目标检测数据集为什么1000张图加三种格式标签能省一半时间石油泄露检测的难处不是“看到油膜”而是从颜色接近海水、红外图里深浅渐变的小目标中把它找出来误报率和漏检率都很难压。标题里的这个“YOLO石油泄露目标检测数据集”打包了1000张已标注图片同一份标注导出成了VOC、COCO、YOLO三种格式还配了划分脚本和训练教程组合起来就是一条“从标注到训练一次跑通”的流水线。对刚接触目标检测、想用现成数据把YOLO流程走通的学生以及准备用YOLO评估海上巡检可行性的一线工程师这套资源的用法都是同一个解压、检查、转格式、划分、训练跳过最耗时的人工标注和格式转换。这篇文章就按这个顺序把每一步的坐标计算、参数设置和最容易翻车的地方拆开讲。2. 三种标签格式互转VOC、COCO、YOLO 的结构差异与转换脚本拿到数据集后第一眼看到的往往是三个目录Annotations、labels、annotations.json分别对应VOC、YOLO、COCO三种格式。很多初学者会问明明是同一批框为什么要存三份因为不同工具链只认其中一种标签。标注软件常用 VOC 的 XML 或 COCO 的 JSON 导出而主流训练框架读取的是 YOLO 的 txt反过来你要是想用 CVAT 复查标注它导入 COCO 最顺手。同一份标注保留三种格式本质是让你不用在工具之间来回转换但这不意味着零风险——三种格式的坐标含义完全不同混用一处就会让模型训练结果变成黑匣子。2.1 三种格式同一批框三种坐标写法先建一个最基础的对照表后面所有脚本都围绕这几行差异写格式存储形态坐标内容类别编号VOC每张图一个.xmlxmin, ymin, xmax, ymax绝对像素标签名字符串COCO整个数据集一个.jsonx, y, width, height绝对像素category_id从 1 开始YOLO每张图一个.txtx_center, y_center, width, height归一化class_id从 0 开始VOC 的 XML 里object标签下有一个bndbox四个值分别代表左上角和右下角的绝对像素坐标类别名写在name里。COCO 的 JSON 主体是三个数组images记录每张图的宽度、高度和文件名annotations记录每个框的bbox和category_idcategories负责把 id 映射回类名。YOLO 的 txt 最直接每行五个数类别id、中心点x、中心点y、宽、高全部除以原图宽高做归一化。转换的核心就是坐标换算。VOC 到 YOLO 的公式是cx (xmin xmax) / 2 / img_ww (xmax - xmin) / img_w高度同理用img_hCOCO 到 YOLO 则先要把左上角加宽高换算成中心点。这里最容易出错的分寸是x 方向的偏移只能用图片宽去归一化y 方向的只能用图片高两个分母不能混用。另一个隐蔽的坑是类别编号COCO 的category_id从 1 开始YOLO 的class_id从 0 开始转换时忘了减 1训练出来的模型预测的类别会整体错一位。2.2 VOC 转 YOLO从 XML 到 txt 的最小脚本常见做法是用xml.etree.ElementTree解析 XML不引入额外依赖。下面这段脚本可以直接丢进数据集目录跑import xml.etree.ElementTree as ET import os CLASSES [oil_leak] # 必须与训练配置 data.yaml 中的 names 顺序一致 def voc2yolo(xml_file, out_txt, img_w, img_h): tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: continue # 跳过不关心的类别 cid CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 中心点坐标和宽高x 方向除以图宽y 方向除以图高 cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cid} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt, w, encodingutf-8) as f: f.write(\n.join(lines)) # 批量转换示例遍历 Annotations 目录读取同名图片尺寸 xml_dir Annotations img_dir images out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue stem os.path.splitext(xml_name)[0] # 读原图尺寸时用 cv2 或 PIL下面以 PIL 为例 from PIL import Image img Image.open(os.path.join(img_dir, stem .jpg)) w, h img.size voc2yolo(os.path.join(xml_dir, xml_name), os.path.join(out_dir, stem .txt), w, h)这段代码里CLASSES列表的顺序就是 YOLO 的类别编号顺序这个顺序一旦定下来就不能改。训练时data.yaml的names如果和它不一致模型训练不会报错但推理结果的类别标签全是错的属于最典型的“看起来训练成功、实际完全没法用”。另外注意读尺寸时用 PIL 的img.size返回(宽, 高)不是(高, 宽)手滑写反会让所有框的归一化坐标全体变形。提示如果 XML 里没有size节点一定从原图读尺寸不要用 XML 里的宽高因为部分标注工具的 XML 尺寸字段不可靠尤其是图片被压缩过的情况下。2.3 COCO 转 YOLO注意 category_id 从 1 开始COCO 转 YOLO 的代码常见做法是按image_id聚合annotations再逐条写 txtimport json import os def coco2yolo(json_path, label_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) images {img[id]: img for img in data[images]} anns {} for ann in data[annotations]: anns.setdefault(ann[image_id], []).append(ann) os.makedirs(label_dir, exist_okTrue) for img_id, ann_list in anns.items(): img images[img_id] stem os.path.splitext(img[file_name])[0] img_w, img_h img[width], img[height] lines [] for ann in ann_list: # COCO 的 category_id 从 1 开始YOLO 的 class_id 从 0 开始 cid ann[category_id] - 1 x, y, w, h ann[bbox] # 左上角 x, y 框宽高 cx (x w / 2) / img_w cy (y h / 2) / img_h lines.append(f{cid} {cx:.6f} {cy:.6f} {w / img_w:.6f} {h / img_h:.6f}) with open(os.path.join(label_dir, stem .txt), w) as f: f.write(\n.join(lines)) coco2yolo(annotations.json, labels_coco2yolo)这里有个容易忽略的细节COCO 的bbox可能是浮点数转换时千万不要为了整齐做int()取整。1000 张图的石油泄露场景里很多框是贴着油膜边缘画的取整会丢掉小数部分的框偏移训练时 IoU 计算跟着受影响。另外攻击点在于ann[bbox]的长度一定是 4如果用的是带分割标注的 COCO 文件segmentations是多边形坐标和bbox无关别混着读。2.4 转换完先验证再进训练所有标签转完后不要直接开训。先随机抽三张图把 txt 里的归一化坐标乘回原图宽高画框保存肉眼核对框的位置和形状是否和油膜区域吻合。这一步花五分钟能挡掉大部分格式转换的翻车框整体偏移、宽高被压扁、类别编号错位这些问题看输出图一眼就能发现。没有 OpenCV 的话用matplotlib的Rectanglepatch 也能画重点不是画得好看而是确认坐标换算没有系统性错误。3. 数据集划分脚本为什么按场景分组以及一套可复现的划分方式数据集里有划分脚本很多人直接双击运行完就往训练里送等到验证集指标虚高到 0.95 才反应过来不对劲。石油泄露数据的采集方式和自然场景不同无人机或者固定机位拍摄时同一个泄露事件的连续帧高度相似——背景几乎不动只有油膜边缘轻微变化。如果划分脚本只是把所有图片随机打散同一个事件的多张连续帧会同时出现在训练集和验证集里验证集的数据分布和训练集几乎重叠指标自然好看但模型一遇到从未见过的新画面就露馅。这种“数据泄露”在目标检测数据集里造成的假象比模型本身的效果问题更难排查。3.1 三种划分策略与数据泄露问题常见划分方式有三种。第一种是纯随机划分代码最简单但只适合独立拍摄、彼此无关的图片第二种是按文件名前缀或采集批次分组把同一事件的连拍帧全部归到同一个集合里这是石油泄露这类巡检数据最稳妥的做法第三种是按目标数量分层划分适合正负样本不均衡的数据集——比如 1000 张图里有大量正常海面负样本纯随机划分可能把负样本都分进训练集验证集里几乎没有负样本误报率完全没被检验出来。对标题里这套资源我的习惯是先用第二种方式划分再做一次负样本比例检查。石油泄露检测里负样本不是噪音它们负责压制模型对海水纹理、船舶尾迹的误报如果训练集和验证集的负样本比例差太多训练曲线会很奇怪loss 在降但验证集的 precision 上不去。处理办法是划分前先统计每个分组里含目标框的图片数量确保每个集合里正负样本比例接近原始数据集。3.2 按场景分组 固定随机种子的划分脚本下面这段划分脚本可以直接抄思路是先按文件名前缀分组再在组级别做随机分配保证同一事件的连拍帧不会跨集合import os import random random.seed(20240601) # 固定种子保证每次划分结果一致 img_dir images label_dir labels train_txt train.txt val_txt val.txt test_txt test.txt train_ratio, val_ratio 0.7, 0.2 # 剩余 10% 做测试集 # 1. 按前缀分组。假设文件命名是 platform_001.jpg / ship_002.jpg groups {} for name in os.listdir(img_dir): if not name.lower().endswith((.jpg, .jpeg, .png)): continue stem os.path.splitext(name)[0] # 跳过没有对应标签的图片 if not os.path.exists(os.path.join(label_dir, stem .txt)): continue prefix stem.split(_)[0] # 按场景前缀分组 groups.setdefault(prefix, []).append(name) # 2. 组级别打乱并切分 group_names list(groups.keys()) random.shuffle(group_names) n_train int(len(group_names) * train_ratio) n_val int(len(group_names) * val_ratio) def write_list(path, selected_groups): with open(path, w) as f: for g in selected_groups: for img_name in groups[g]: img_path os.path.abspath(os.path.join(img_dir, img_name)) f.write(img_path \n) write_list(train_txt, group_names[:n_train]) write_list(val_txt, group_names[n_train:n_train n_val]) write_list(test_txt, group_names[n_train n_val:]) print(len(group_names), 个场景组已写入三个 txt)这个脚本有两个参数需要按实际数据调整一是stem.split(_)[0]的分隔规则如果文件名是纯数字编号那就改成取前 4 位字符二是比例1000 张图用 7:2:1 是常见比例但如果某个场景组特别大比如一个视频序列占了 400 张按组划分会出现某组占比过高这时候可以先缩小组内采样数或者把该组的帧做抽稀再划分。提示划分后一定要验证三个 txt 里的图片路径都真实存在且对应的 label 文件都存在。这个检查脚本里已经写了一半实际使用时可以把 label 存在性检查也加到写入循环里避免某个 txt 混进缺失标签的样本。3.3 三份清单在训练中怎么用生成的三个 txt 配合 YOLO 的data.yaml使用train和val字段直接指向这两个 txt不需要把图片复制到子目录。用 txt 而不是目录的好处是灵活——想换比例、想去掉某些样本改文本文件就行不用动图片存储。测试集的 txt 日常训练用不到但建议保留等最终模型训完用它做一次独立评估这部分我放在最后一章讲。另外给一个后悔药划分完先别删原始图片和原始标签。后续如果发现某个场景组在验证集里表现特别差或者训练集里混进了损坏图片你可以随时重新划分而不需要重新解压整个 rar。4. YOLO 训练教程落地data.yaml、训练命令与关键参数怎么设格式转完、划分完毕接下来就是把数据送进 YOLO 训练。现在主流做法是直接用 Ultralytics 的 YOLO 训练脚本一条命令就能跑起来但很多人死在前置配置上data.yaml写错路径、names顺序和标签编号对不上、imgsz设得不符合小目标检测需求。这一章把训练教程里最关键的三个文件和一个命令讲透剩下的交给训练日志。4.1 data.yaml 的写法路径、清单与类别名data.yaml是训练流程里最容易被忽视的配置文件百度一搜“YOLO 训练自己的数据集”第一课都是写它。石油泄露检测是单类任务但names依然要用列表形式并且顺序必须和转换脚本里的CLASSES完全一致# 石油泄露单类检测的 data.yaml path: /data/oil_leak_dataset # 解压后的数据集根目录绝对路径最省事 train: train.txt # 训练集图片清单 val: val.txt # 验证集图片清单 test: test.txt # 测试集清单可选 # 类别编号从 0 开始 names: 0: oil_leakpath写绝对路径是最稳的做法因为训练命令可能在任意目录执行相对路径容易出现“File not found”但报错信息不明确的情况。train和val这两个字段的值会与path拼接如果直接写绝对路径的 txt记得path也要能对上。names是字典还是列表都行但顺序永远按编号来一个字节都不能错。4.2 完整训练命令与关键参数基础训练命令如下以小模型起步、显存友好为优先yolo detect train \ modelyolov8n.pt \ datadata.yaml \ epochs150 \ batch16 \ imgsz960 \ patience30 \ optimizerauto \ close_mosaic10 \ device0逐个说参数方便你按自己机器条件改。modelyolov8n.pt是官方预训练权重n 是 nano 版本只有几 MB对石油泄露这种目标小但背景杂的场景n 起步能快速验证 pipeline 是否通。epochs150看起来多但配合patience30实际会在验证集指标连续 30 轮不提升时自动早停不会傻跑完。batch16在 960 分辨率下大约需要 16GB 左右显存如果你的卡是 8GB就降到 8或者把imgsz降到 640。imgsz960是关键设置石油泄露油膜往往只占图片很小一块640 分辨率下目标像素可能只有十几个960 能让小目标的特征保留明显更多显存不够时优先降batch别降imgsz。close_mosaic10表示最后 10 轮关闭马赛克增强因为马赛克会让小目标被切割得更碎关掉后模型能在真实分布上做最后收敛。optimizerauto让框架自己选省心。第一次跑建议用默认超参只改imgsz、batch和epochs。石油泄露数据集只有 1000 张属于中小规模大批量和高学习率容易直接过拟合lr0可以保守点设成 0.005。如果用的是 2 卡或 4 卡机器加参数device0,1数据并行训练 YOLO 内置支持不需要改代码。4.3 训练过程的监控看到什么算正常训练开始后重点看两个曲线train/box_loss和val/box_loss。石油泄露场景里目标边界比较模糊油膜的边缘本身就是渐变过渡框的精确度天然受限所以 box loss 不会像人像检测那样降到很低。正常现象是训练集 loss 稳步下探验证集 loss 在某个点后开始震荡甚至回升那就是过拟合信号patience会自动停住。如果验证集 loss 从头到尾纹丝不动别急着加数据先回去检查标签转换环节——大概率是归一化坐标算错了模型学了个寂寞。训练结束时输出目录下会有一堆结果文件包括confusion_matrix.png、results.png、val_batch_pred.jpg。先看val_batch_pred.jpg这是验证集预测结果的可视化如果框全部落在油膜上说明格式转换和训练都通了如果框满天飞再去看混淆矩阵和 loss 曲线。5. 石油泄露检测训练常见的五个坑现象、原因、解决这一章是我最想让你认真看的部分。石油泄露检测数据集的整理难度不高但训练过程中的翻车现场高度一致。下面五条都是常见且隐蔽的坑每条按照“现象 → 原因 → 解决”写方便你对照排查。5.1 类别编号错位训练不报错推理结果全错现象训练过程一切正常loss 正常下降验证集 mAP 也很高但拿训练好的权重跑到新图片上预测结果里出现了一些明显不对的类别标签或者所有框的类别都和实际不符。原因VOC 转 YOLO 时CLASSES列表的索引顺序和data.yaml里names的顺序不一致。比如转换脚本里CLASSES [oil_leak]对应编号 0但data.yaml里写成了names: {0: leak_oil}如果类别名拼写不一致框架不会报错模型只在编号层面工作最终输出的类别映射就乱了。解决做一次全流程校验训练前用脚本打印一份类别编号对照表转换脚本、data.yaml、txt 文件三方的编号和类名必须逐字对上。多类别时这个问题尤其致命单类数据稍微好排查但也别心存侥幸。5.2 归一化坐标分母用错框全部压扁或拉长现象训练时 box loss 偏高且降不下去验证集预测框的宽高比例明显不对劲——框是扁的或者高度只有正常的一半。原因转换公式里把 x 方向坐标除以了图片的高或者写成了统一的max(w, h)。xml 里width和height是分开的字段归一化必须 x 方向用图宽、y 方向用图高。这类错误在单张看时不容易发现因为框的位置大致还在目标附近只有画出来对比原图边界才能看出宽高被系统性地扭曲。解决转换脚本里单独打印几组原始坐标和归一化坐标手工验算一遍公式。再抽三张图把 txt 坐标乘回原图尺寸画框和原图标注叠在一起看。这一步省不得。5.3 图片与标签文件名对不上大量样本被静默丢弃现象训练时发现每个 epoch 的样本数量比预期少或者验证集里有些图片永远没有预测框。翻日志才注意到训练框架把找不到标签的图片直接跳过了。原因下载的数据集里jpg 和 txt 的命名不完全一致。常见的有三种情况图片叫leak_001.jpg而标签叫leak_001.txt这种前缀一致但后缀不同导致匹配失败或者解压时系统给文件加了(1)后缀或者部分图片本身就是负样本没有对应标签文件但划分脚本没做过滤就写进了清单。解决跑一个文件名一致性检查脚本统计 images 和 labels 两个目录下同名文件的数量差异。负样本图可以存在但要在清单里明确标注或者直接单独分一个负样本目录不要混在正样本清单里让框架猜。5.4 BatchNorm 崩溃loss 变成 NaN训练直接中断现象训练跑到中间某个 epochloss 曲线突然掉到 NaN之后全是 NaNresults.png上留下一段断崖。重启训练后问题复现只是崩溃的 epoch 位置不一样。原因batch 太小加学习率偏大是常见的组合。石油泄露图像有不少是夜间红外、大范围暗色像素值方差大BatchNorm 在小 batch 下统计量不稳定一个极端样本就能让梯度爆炸。这个现象在 YOLO 训练里有个专门的讨论叫“bn 崩溃”小数据集上特别容易出现。解决先把batch提升到 16 或 32如果显存不够就降低imgsz再把lr0降到 0.003 以下。另外把warmup_epochs保持默认的 3 轮不要关掉 warmup它是防 NaN 的第一道防线。改完之后如果还崩加载预训练权重时换成官方原版yolov8n.pt不要用别人二次转换过的权重。5.5 数据泄露导致验证集指标虚高新的监控画面上效果崩盘现象训练时验证集 mAP50 高达 0.95模型看起来已经收敛得很好但部署到新的监控画面后漏检率明显比训练时高一个档次。原因划分脚本不够严谨同一个场景的连续帧被随机分到了训练集和验证集。验证集里藏着和训练集几乎一样的画面模型其实是在“背题”而不是在“解题”。石油泄露数据中无人机巡航路径上的连拍帧相似度极高这种问题几乎必现。解决用 3.2 节的分组划分脚本重新划分保证同一个场景的所有帧只进入一个集合。判断是否发生数据泄露的一个土办法把验证集里检测效果最好的几张图找出来和训练集里相似度最高的图做对比如果背景几乎一样那就中招了。另外验证集指标高不要开心太早测试集的独立评估才是最终通行证。6. 训练完怎么验收混淆矩阵挑着看抽帧比着看训练结束后的验收阶段很多人只盯一个数字mAP。石油泄露检测这种小目标、低对比度场景mAP 要分开看。mAP50 反映的是框大致到位就行对油膜这种边缘模糊的目标更友好mAP50-95 对框的精确度要求高分数低一点是正常的不用焦虑。关键在于如果 mAP50 高但 mAP50-95 很低说明模型能找到目标但框偏了对后续的泄漏面积估算这类应用会有影响如果两者都低先怀疑标签级数据问题别急着调模型。混淆矩阵的总和不是 100%这是正常现象。矩阵里每个格子的比例是相对“真实目标总数”来的漏检的框不会出现在任何格里背景区域被误检也会单列一类所以列求和可能小于 100%。看到矩阵里background列有值不是坏事反而说明模型对海面纹理有过学习真正需要警惕的是oil_leak那一行的对角线占比太低意味着大量油膜区域被模型漏掉这才是巡检场景最不可接受的失败模式。我最推荐的验收手段是抽帧对比。训练完输出目录下的val_batch_pred.jpg只展示了模型挑的一部分样本不够全面。建议单独跑一次批量推理yolo predict modelruns/detect/train/weights/best.pt \ sourceval_samples/ \ imgsz960 \ conf0.25 \ saveTrue把验证集里随机抽出的 20 张图注意从不同场景组抽别从同一段连拍里抽放进一个目录推理保存后逐一和原图对比重点看两张图一张是大面积油膜、高置信度的容易样本一张是小油膜、背景干扰强的困难样本。如果容易样本都漏检检查置信度阈值是不是设太高如果困难样本全检不出来说明数据里小目标占比不够后续该做的是补充近距离采集或者把 imgsz 再提一档。我自己的习惯是训练完隔一天再回来看这批抽帧图刚训完时的兴奋感容易让人忽略漏检。这套“数据检查 → 格式转换 → 分组划分 → 训练 → 抽帧验收”的流程适用于任何一个小样本工业检测场景。石油泄露数据集帮我们把最费时的标注工作省掉了剩下的稳定性靠的是每一步验证而不是盲目相信脚本。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑