ARTICLE DETAIL

资讯详情

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

火灾烟雾数据集处理指南:从标注清洗到YOLOv8训练

火灾烟雾数据集处理指南:从标注清洗到YOLOv8训练 简介火灾烟雾图像标注数据集面向计算机视觉目标检测研究者与开发人员聚焦火灾和烟雾的智能识别与定位。数据集收录2257张火灾与烟雾相关图像每张图像均由专业人员细致标注提供PASCAL VOC与YOLO两种主流标注格式可分别用于精确边界框训练与实时检测模型优化适配Faster R-CNN、YOLOv3、SSD等常见检测框架。压缩包整体约266.14MB文件以高分辨率图像及对应标注文件为主便于按批次读取并直接接入训练流程。使用时可结合数据增强、类别比例调整与超参数调优提升模型在真实场景中的泛化能力。在智能安防、智慧家居、无人机巡检等应用中该数据支撑火情预警与应急响应系统的开发与验证。目前已有2282人学习下载对公共安全领域的算法研究与落地部署具有实用价值。1. 火灾烟雾图像标注数据集先搞清楚它是什么再决定要不要解压“火灾烟雾图像标注数据集”这个 rar 压缩包解决的其实是视觉检测里最不值钱但最要命的一环图有了标签对不对、能不能用心里没底。做火灾烟雾检测的人最头疼的往往不是模型选型而是公开数据集要么没标注要么有标注没场景要么标注格式乱成一锅粥。这类数据集通常打包了火灾、烟雾场景的原始图像和对应的标注文件格式以 VOC 或 COCO 为主目的是直接喂给目标检测模型做监督训练。适合做消防报警、工地安全、森林防火监控的算法工程师和毕设团队拿来当训练底料。这一篇按从业习惯讲清楚怎么拆包、怎么验标注、怎么转 YOLO 格式训练以及哪些坑必踩。2. 走进 rar 包内部标注格式、目录结构与三类常见组织方式拿到这种数据集压缩包很多人第一反应是解压后把图片拖进标注软件里肉眼过一遍。我建议反过来先花半小时把 rar 里的内容“读”一遍确认目录结构、标注格式、数量和类别分布。这一步能拦住后面大量的返工。因为不同来源的数据集内部组织方式差别很大直接照着一个 Deprecated 的脚本硬跑很容易出现“路径对不上、格式解析报错、类别名对不上”的三连翻车。2.1 标注格式先认三件套VOC、COCO、YOLO 怎么分辨火灾烟雾数据集的标注格式九成以上落在三种里先看目录名和文件后缀就能判断格式文件形态坐标基准适合任务VOCJPEGImages/ Annotations/ 下每个 xml 文件像素绝对值 xmin/ymin/xmax/ymaxPytorch/YOLO 系列转换的中间态COCOimages/ annotations 下单个 json 文件像素绝对值 [x, y, w, h]Detectron2、MMDetection、Mask R-CNNYOLOimages/ labels/ 下每个 txt 文件归一化中心点 x_center/y_center/w/hYOLOv5/v8、Ultralytics 全家桶如果 rar 解压后看到 JPEGImages、Annotations、ImageSets 三个目录基本是 VOC 系如果看到一个大的 instances_xxx.json是 COCO 系如果 images 和 labels 平级、旁边有个 data.yaml是 YOLO Ready 系。到这一步我会先把类别名拉出来看一眼确认数据集的标注口径是否统一。有些数据集里火焰叫 fire烟雾叫 smoke还有一类叫 fire_with_smoke火焰带烟这三类在训练时的边界策略完全不同。用一段小脚本提取全部类名import xml.etree.ElementTree as ET from pathlib import Path labels set() for xml_path in Path(Annotations).glob(*.xml): root ET.parse(xml_path).getroot() for obj in root.findall(object): labels.add(obj.findtext(name)) print(sorted(labels))逻辑说明这脚遍历 Annotations 下全部 VOC 格式 xml把每个name节点的值收集到 Set 里最终输出有多少个类别、每个类别叫什么。如果输出里同时出现 fire、smoke、fire_with_smoke后续转 YOLO 格式时就要决定这三个是各自独立类别还是合并成 fire_smoke 一个类别。参数说明Annotations 路径按实际目录改如果数据集是 COCO 格式把这个脚本换成对 json 里categories字段遍历即可逻辑等价。2.2 用最小命令清点数据集标签与图像是否一一配对格式认完下一步是清点数量。火灾烟雾数据集最常见的质量问题是“图像 5000 张标注只有 4000 份”剩下 1000 张是负样本还是漏标注直接决定你后面的训练策略。我用两个命令加一个脚本完成清点# 统计图片目录和标注目录下的文件数量 echo 图片数: $(ls JPEGImages | wc -l) echo 标注数: $(ls Annotations | wc -l)如果两个数字对不上不要急着补先用脚本查出缺的是哪些from pathlib import Path imgs {p.stem for p in Path(JPEGImages).glob(*)} xmls {p.stem for p in Path(Annotations).glob(*.xml)} print(有图无标注:, len(imgs - xmls)) print(有标注无图:, len(xmls - imgs))逻辑说明把 JPEGImages 和 Annotations 下的文件名取主名不含后缀放进两个 Set做差集。有图无标注的样本要么是故意留的负样本要么是漏标有标注无图的样本是脏数据后续训练前必须从标注目录里删掉否则数据加载阶段会直接报错。参数说明glob(*.jpg) 只匹配 jpg如果数据集里有 png、bmp 就把通配符补全写成glob(*.[jp][pn]g)或直接glob(*)再按后缀过滤。2.3 标注质量“裸检”坐标越界、类别缺漏、遮挡关系三个观察点数量对齐只是第一步标注内容的质量才是决定模型上限的关键。我会在训练前做一次“裸检”不追求精确只看三个观察点。第一个是坐标越界。VOC 标注里 xmax/ymax 超过图片宽高、xmin/ymin 出现负数说明标注工具或人工操作产生了脏框。YOLO 格式对这类值极其敏感归一化后一旦大于 1 或小于 0训练时 loss 会异常跳动。检查脚本import xml.etree.ElementTree as ET from pathlib import Path for xml_path in Path(Annotations).glob(*.xml): root ET.parse(xml_path).getroot() size root.find(size) W, H int(size.findtext(width)), int(size.findtext(height)) for obj in root.findall(object): b obj.find(bndbox) xmin, xmax int(b.findtext(xmin)), int(b.findtext(xmax)) ymin, ymax int(b.findtext(ymin)), int(b.findtext(ymax)) if xmin 0 or ymin 0 or xmax W or ymax H: print(f{xml_path.stem}: {obj.findtext(name)} 越界 {xmin},{ymin},{xmax},{ymax})逻辑说明按 xml 里的 size 节点拿到图像宽高再逐框比较边界是否越界。越界框的来源多是标注时拖框超出画布或者图像被 resize 后标签没跟着更新。参数说明这个脚本只检测不修越界少个位数可以转换后做 clamp越界多建议直接淘汰对应图片别让脏框污染训练。第二个是类别缺漏。肉眼扫图不现实我的做法是跑一个最简单的 YOLOv8 预训练模型去预测所有图片把置信度高于阈值但没有对应标注的检测视为“可疑漏标”。这一步是半自动筛查重点看两种图有明显火焰特征但标注文件为空的和标注类别与预测类别差异极大的。筛查结果人工复核即可。第三个是遮挡与密集场景。火焰区域往往伴随烟羽多个目标重叠时标注框会大面积交叠。如果同一张图里同类目标的 IoU 普遍超过 0.5说明标注框画得保守或者漏拆目标。这种情况在火灾烟雾数据集里很常见因为烟是半透明的边界本来就模糊标注员倾向于画一个大框包住整片烟。这类样本对训练的影响我放到第 5 章详细讲这里先记住一句话边界模糊的目标宁可多画几个小框也不要一个大框糊上去。3. 解压与预处理rar 包的安全打开与样本不均衡处理3.1 rar 解压的完整流程完整性测试、伪加密识别与乱码处理rar 格式在数据集领域流行是有原因的压缩率高适合打包大量 jpg能分卷适合传输。但正因为用得多从网上下载的 rar 包也最容易出问题。我的固定流程是先测完整性再解压再验证文件可读性。# 第一步测试压缩包完整性不释放文件 unrar t 火灾烟雾图像标注数据集.rar # 第二步保留目录结构解压到指定目录 unrar x 火灾烟雾图像标注数据集.rar ./fire_smoke/逻辑说明t 参数是 test只校验每个文件的 CRC 校验值不实际解压速度很快。x 参数是解压并保留压缩包内的目录结构这一点很重要——如果改用 e 参数所有文件会被摊平到同一个目录几百张图混在一起后面的配对检查直接没法做。参数说明unrar 在 Linux 下需要单独安装包名一般叫 unrar 或 rarWindows 下 WinRAR 自带命令行工具。若命令不存在优先确认工具装没装而不是换个解压软件硬试。解压时遇到“密码保护”的提示不要慌先确认是不是伪加密。伪加密是 rar 文件头里的加密标志位被置位但数据并没有真的加密。判断方法是输入任意密码看是否能继续解压如果任意密码能解出文件说明只是标志位问题用 7-Zip 直接提取通常就能绕过只有当你输入任意密码后持续报错才是真加密。真加密的数据集 rar 包基本可以直接放弃因为作者不提供密码的情况下暴力猜解的时间成本远高于重新找一份开源数据集。中文文件名乱码是第二个高频问题。压缩包在国外作者手里生成时用 GBK 编码Linux 下解压出来文件名变成乱码不影响训练但影响人眼排查。我的处理办法是在 Windows 下用 WinRAR 的“按文件名编码解压”功能或者在 Linux 下用unrar x -p结合convmv做编码转换。编码问题不致命但如果后续要把数据集传到 Linux 服务器上训练建议解压后立即统一重命名成纯英文路径否则某些增强库和 DALI 这类加载器会在中文路径上翻车。3.2 图像预处理尺寸统一、重复样本去重与 EXIF 方向修正解压完成后PR 阶段第一步是看图像的尺寸分布。火灾烟雾图像来源五花八门可能是监控截图、无人机航拍、网络爬取尺寸从 320x240 到 4000x3000 都有。我的建议是不强行统一到同一尺寸而是长边缩放到 1280、短边保持不变这样既控制训练时的显存占用又不过度损失小目标烟雾的像素。尺寸问题之外最容易被忽视的是重复样本。同一个 rar 包里经常出现同一事件的多帧连续截图这些截图在像素级几乎一致如果不做去重训练集和验证集会互相泄漏mAP 虚高。第一版去重用 md5 就够import hashlib from pathlib import Path seen {} for img in Path(images).glob(*.jpg): h hashlib.md5(img.read_bytes()).hexdigest() if h in seen: print(重复:, img.name, 与, seen[h].name) # img.unlink() # 确认后删除 else: seen[h] img逻辑说明读取整个文件计算 md5哈希相同的视为重复。对大图全集跑 md5 会比较慢2 万张图大约几分钟可以接受。参数说明如果担心 md5 全比对慢可以先对比文件大小只对大小相同的文件算哈希能减少一半以上计算量。删除前建议先打印列表人工扫一眼因为监控视频里连续帧之间可能有细微差异烟雾流动这类“近似重复”md5 查不出来要放到训练阶段靠数据增强去缓解。EXIF 方向问题在手机拍摄的火灾现场图里很常见。相机把 orientation 信息写进 EXIF像素数据本身没有旋转但标注软件读取时有的按 EXIF 转、有的不转最后标注框和实际物体朝向错位。用 PIL 批量修正from PIL import Image, ImageOps from pathlib import Path for p in Path(images).glob(*.jpg): img Image.open(p) exif img.getexif() if exif.get(274, 1) ! 1: img ImageOps.exif_transpose(img) img.save(p) print(修正方向:, p.name)逻辑说明EXIF 标签 274 是 Orientation 字段值为 6 或 8 表示需要旋转 90 度或 270 度为 1 表示正常。exif_transpose 按 EXIF 信息重排像素并清除方向标记。这个预处理必须在转换标注格式之前做否则标注框的坐标基准还是旋转前图像上的位置后续模型看到的图全被转正了框却停在原位置整个训练等于白做。3.3 样本不均衡处理负样本、小目标裁剪与难样本选择火灾烟雾数据集的不均衡通常不是“火焰类比烟雾类少”这种类别间不均衡而是“有目标样本多、负样本少”以及“大面积火焰多、小目标初期火灾少”。前者导致模型误检率压不下来后者导致真实场景里远处小火苗检不出来。负样本的思路很简单在 labels 对应目录下放一个 0 字节的 txt 文件图片本身保留在 images 目录里。YOLO 系列训练时会把这种图当背景图参与损失计算。难点在于选哪些图做负样本。地里的注意事项云雾缭绕的山区、工业白色蒸汽、清晨雾霾下的城市这些“形似烟雾但实际不是火情”的场景才是最有价值的负样本。如果数据包里这类图本来就有只是没标注直接留空 txt 即可如果没有只能从外部收集补充数据版权归属要留意。小目标问题是火灾烟雾检测里最影响实战效果的一环。一个 4000x3000 的航拍图初期火焰可能只占 100x80 像素直接 resize 到 640x640 后小火苗只剩 16x13 像素检测器基本不可能学到有效特征。常见做法是切图训练def slice_image(img, boxes, out_dir, win640, overlap0.2): step int(win * (1 - overlap)) W, H img.size idx 0 for y in range(0, H - win step, step): for x in range(0, W - win step, step): crop img.crop((x, y, x win, y win)) crop_boxes [] for bx in boxes: cx, cy, bw, bh bx x1, y1 cx - bw / 2, cy - bh / 2 x2, y2 cx bw / 2, cy bh / 2 if x2 x or x1 x win or y2 y or y1 y win: continue nx1, ny1 max(x1, x) - x, max(y1, y) - y nx2, ny2 min(x2, x win) - x, min(y2, y win) - y if nx2 - nx1 8 or ny2 - ny1 8: continue crop_boxes.append((nx1, ny1, nx2, ny2)) if len(crop_boxes) 0: crop.save(f{out_dir}/img_{idx:05d}.jpg) idx 1逻辑说明把大图按固定窗口大小、固定重叠率切成 patch同时把每个标注框平移到 patch 坐标系里只保留与 patch 有足够交叠的框。窗口 640 对应训练 imgsz640避免二次缩放。重叠率 0.2 是为了防止目标正好落在切缝上。参数说明win 和 overlap 是核心参数win 越大 patch 里上下文越丰富但小目标占比越小overlap 越大训练样本越多但耗时越长。我的经验是先按 0.2 跑一版看效果漏检严重再提到 0.5。切完 patch 后有个容易忽略的问题同一个目标在相邻 patch 里出现多次等于目标被重复训练。这个重复在 YOLO 的损失计算里会被放大。我的处理办法是用第 2.2 节的配对校验脚本跑一遍切图结果并统计每个目标的裁剪次数裁剪超过 4 次的 patch 随机丢弃一部分控制重复度。4. 用这个数据集训练 YOLOv8从 VOC 转 YOLO 格式到训练参数落地4.1 把标注转成 YOLO 格式转换脚本与坐标归一化细节前面确认了数据集是 VOC 格式后训练 YOLOv8 之前必须先转换标注格式。这一步看起来是体力活实际上最容易踩坐标系的坑。转换脚本的核心是把 VOC 的绝对像素坐标归一化成 YOLO 的中心点坐标加宽高import xml.etree.ElementTree as ET from pathlib import Path CLASSES [fire, smoke, fire_with_smoke] # 按数据集的实际情况改 def voc2yolo(xml_path: Path, out_dir: Path) - None: root ET.parse(xml_path).getroot() size root.find(size) W, H int(size.findtext(width)), int(size.findtext(height)) lines [] for obj in root.findall(object): name obj.findtext(name) if name not in CLASSES: continue b obj.find(bndbox) xmin, ymin int(b.findtext(xmin)), int(b.findtext(ymin)) xmax, ymax int(b.findtext(xmax)), int(b.findtext(ymax)) x_center (xmin xmax) / 2 / W y_center (ymin ymax) / 2 / H box_w (xmax - xmin) / W box_h (ymax - ymin) / H lines.append(f{CLASSES.index(name)} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) out_dir.mkdir(parentsTrue, exist_okTrue) (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines))逻辑说明VOC 的 bndbox 是绝对值x_center 取 xmin 和 xmax 的中点再除以图像宽度得到归一化的中心点坐标框的宽高也分别除以宽和高。之所以必须除以 W 再除以 H 而不是统一除以长边是因为 YOLO 的标签要求宽高比是基于原图的任何统一缩放都会导致框的位置偏移。参数说明CLASSES 列表的顺序决定了类别编号这个顺序必须和训练时的 data.yaml 完全一致否则模型训练完输出的类别是乱的。如果数据集里只有 fire 和 smoke 两类就把 fire_with_smoke 删掉。转换后抽查 5 个 txt用下面命令目测坐标是否合理# 查看某张图的标注内容第一列是类别后四列是归一化坐标 cat labels/火灾现场_001.txt4.2 train/val/test 划分用固定随机种子保证可复现数据集划分的坑十个人里至少五个人踩过。最常见的是全部文件随机打散后按比例取前 N 个做训练、后 M 个做验证看似公平但同一个场景的连续帧会同时出现在训练集和验证集里验证指标虚高。火灾烟雾数据集的正确划分方式是先按场景分组再在场景维度上划分保证同源图片只进一个集合。from pathlib import Path import random import shutil random.seed(42) # 固定随机种子复现结果 src Path(fire_smoke/images) out Path(fire_smoke_splited) img_paths sorted(src.glob(*.jpg)) # 排序保证顺序稳定 scene_ids {} for p in img_paths: scene p.name.split(_)[0] # 假设文件名前缀是场景编号 scene_ids.setdefault(scene, []).append(p) scenes sorted(scene_ids.keys()) random.shuffle(scenes) # 在场景维度打散而不是在图片维度打散 n len(scenes) for i, scene in enumerate(scenes): if i int(n * 0.7): split train elif i int(n * 0.85): split val else: split test for p in scene_ids[scene]: shutil.copy2(p, out / split / images / p.name) label_src Path(labels) / (p.stem .txt) if label_src.exists(): shutil.copy2(label_src, out / split / labels / p.name)逻辑说明先按文件名前缀的 scene id 把图片分组再对场景列表做 shuffle 和切分。这样做的效果是同一场景的 100 张连续帧全部进入训练集或全部进入验证集验证集的指标才反映真实泛化能力而不是记住特定背景。参数说明random.seed(42) 里的 42 可以换任何整数但一旦固定下来就必须保持一致否则每次运行划分结果都不同。test 集如果只用于最终评估也可以不参与数据加载单独留出来看模型上限。划分完后的目录结构直接对应训练时的 data.yamltrain: fire_smoke_splited/train/images val: fire_smoke_splited/val/images nc: 2 names: [fire, smoke]路径建议写绝对路径或用相对 data.yaml 所在目录的相对路径不要在训练命令里临时改。4.3 训练命令与关键参数imgsz、batch、mosaic 与预训练权重yolov8 训练自己的数据集主流程就是一条 yolo 命令。我一般从 yolov8s 起步先跑通再考虑模型放大yolo detect train \ datafire_smoke.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs150 \ patience20 \ close_mosaic10逻辑说明model 参数写 yolov8s.pt 表示加载 COCO 预训练权重这个权重在 ImageNet-1k 上预训练过主干对小数据集能显著加快收敛。patience20 表示连续 20 个 epoch 验证指标没有提升就早停避免空跑浪费时间。close_mosaic10 表示最后 10 个 epoch 关闭 mosaic 增强。参数说明imgsz640 是常规选择如果第 3 章里切过 patch这里要保持和 patch 尺寸一致batch16 是 16G 显存左右的推荐值显存小就减半不要硬顶epochs 在样本量 5000 张以内时 150 足够但数据增强强的数据集可能会在 100 轮后才完全收敛所以保留早停兜底。mosaic 这个增强对火灾烟雾场景是玄学。它把四张图拼在一起训练对小目标检测很有帮助但烟雾是半透明纹理拼接后边界混乱模型容易把拼接缝当成目标轮廓。我的做法是前 140 个 epoch 开着最后 10 个 epoch 关掉让模型在接近真实分布的纠正下收敛。这一步对最终 mAP 的影响通常有 1 到 2 个点。显存不够时别急着调低 batch先试这两招一是把 workers 降到 4 以下二是检查数据加载是否用了缓存数据溢出。如果 batch8、imgsz640 还在爆显存才是换 yolov8n 或调低 imgsz 的时机。yolov8m 及以上模型在火灾烟雾数据集上不一定带来正收益烟雾的弱纹理特性决定了大模型更容易在训练集上记住背景而不是学到烟雾本身。5. 训练中的避坑记录数据集引发的 5 个常见问题、排查与解决5.1 标签和图像错位导致 loss 不下降现象训练到第 30 个 epochbox loss 还在 1.2 附近震荡比正常收敛速度慢一倍。原因数据集里 jpg 和 xml 的文件名排序规则不一致比如 jpg 按字符串排序时 10.jpg 排在 2.jpg 前面而 xml 按数字排序。转换脚本按 glob 顺序遍历后txt 写给了错误的图片。用第 2.2 节的配对校验脚本查会发现“有标注无图”或“有图无标注”的数量为零但内容实际错位。解决不要信任肉眼检查训练前加一步校验。把配对脚本升级为“读取每张图的尺寸与对应 xml 的 size 节点比对”尺寸不一致的直接标记出来定位错位源头是重命名还是排序。确认错位后用图片文件名作为唯一主键重新生成对齐的标注文件名。5.2 mAP 不低但现场误检严重背景先验问题现象验证集 mAP50 有 0.85但拿到工地现场一测白色塑料布、白色货车、清晨的雾全被报成烟雾。原因数据集里的正样本几乎全是“晴天、绿色植被背景、火焰颜色明显”的图模型学到的不是烟雾的半透明纹理而是“亮白色区域 周围有火红色像素”的组合先验。负样本数量太少压制不住背景误检。解决从外部收集 500 到 1000 张无火灾的雾天、多云、白色建筑场景图标注文件留成 0 字节 txt混入训练集。不用单独建 background 类YOLO 对空标注图会直接当作纯负样本计算背景损失。加入后 mAP50 可能会掉两个点但误检数量会成倍下降这个指标变化是良性的。5.3 火焰和烟雾混在同一标注框内让分类头打架现象验证集里 fire 类的召回率很高smoke 类的召回率不到 0.3。打开预测可视化一看模型把火焰区域的烟雾都预测成 fire。原因原始标注里大量目标框同时框住了火焰和烟羽框中心落在火焰上类别却标成 smoke。转换脚本忠实保留了这些“混类框”训练时分类头的梯度互相矛盾。烟雾本身半透明即使同一个框里两类目标交错模型也更倾向于学颜色特征更强的 fire。解决对混类框做拆分。常见做法是计算烟羽区域和火焰中心点的相对位置把大框按水平或垂直方向拆成两个框分别标注更省事的方式是训练时把 fire 和 smoke 合并成单一类别 fire_smoke先把检测率提上去后续再通过细分模型区分。现实场景里报警器只需要知道“有没有火情”合并类别通常更实用。5.4 小目标烟雾完全检不出现象训练完模型对近距离火焰很好一旦目标在画面里占比小于 2%直接漏检。depth 视角的航拍图几乎全灭。原因imgsz640 的输入下小目标被下采样到十几个像素特征图上的响应太弱。标准 YOLO 的 anchor 分配策略对 16x16 像素以下的目标也极不友好即使数据增强里有小目标复制也难扭转。解决两条路并行。训练端用第 3.3 节的切片思路生成 patch 级训练数据推理端用滑窗切片推理再合并结果。切片推理时窗口重叠 0.5每个目标会跨多个窗口合并时用 NMS 去掉重复框。牺牲约一半推理帧率换来小目标召回率从 0.1 提到 0.5 以上在消防场景里这笔账划算。5.5 训练中途崩溃缓存损坏、编码错误与坏图现象训练跑到第 4 个 epoch突然报UnicodeDecodeError或者cv2.error再跑一次又在同一个 epoch 附近崩溃。原因rar 解压时个别文件 CRC 校验失败但被强制解出图片实际只有半截数据。数据加载器在 shuffle 到坏图时炸掉。另外 Windows 下解压的中文文件名在 Linux 上显示转义序列会导致 OpenCV 按错误的路径打开文件。解决训练前先跑一遍图像完整性扫描from PIL import Image from pathlib import Path bad [] for p in Path(images).glob(*): try: img Image.open(p) img.verify() except Exception: bad.append(p) print(坏图数量:, len(bad))逻辑说明PIL 的 verify 方法只检查文件结构不加载像素速度很快适合全库扫描。发现坏图后从 images 和 labels 下同步删除对应文件不要只删图片留下 txt否则数据加载时标签和图像数量重新对不上。参数说明如果扫描结果里坏图超过总量的 1%说明源头压缩包本身有问题建议回到最初的数据集发布页检查下载校验和而不是逐个修复坏图。6. 验证模型真实边界用混淆矩阵与合成样本查出模型的底气6.1 用验证指标定位误检来源模型训练完我会先跑一遍验证集并生成全部可视化图而不是只看一个 mAPyolo detect val \ datafire_smoke.yaml \ modelruns/detect/train/weights/best.pt \ splitval \ plotsTrue逻辑说明plotsTrue 会输出混淆矩阵、PR 曲线和每个类别的样本预测图。混淆矩阵能直接看出 smoke 被误检成 fire 的比例、背景被误检成 smoke 的比例。如果背景误检占大头回到第 5.2 节补负样本如果类间混淆严重回到第 5.3 节合并或拆分类别。参数说明splitval 指定验证集划分如果之前按场景划分过这里不要混用 test 集等全部参数调完最后再碰 test。6.2 合成烟雾图像扩样本半自动标注的实操路径真实烟雾图不好找合成烟雾图是有效的补充手段。把一张透明烟雾 PNG 叠加到一张不带烟雾的场景图上得到一张“近似真实”的训练样本from PIL import Image bg Image.open(scene.jpg).convert(RGBA) smoke Image.open(smoke_overlay.png).convert(RGBA) smoke smoke.resize((int(bg.width * 0.3), int(bg.height * 0.3))) bg.alpha_composite(smoke, (int(bg.width * 0.5), int(bg.height * 0.3))) bg.convert(RGB).save(synthetic_smoke.jpg)逻辑说明alpha_composite 按透明度叠图烟雾 PNG 的透明通道决定合成强度。合成图接着用人工或半自动标注工具打框就能扩充训练集。参数说明叠加位置一般选画面中上区域因为真实烟羽是从火源向上飘合成位置太随意会让模型学到错误的空间先验。过度合成会让模型过拟合到烟雾纹理细节合成样本占比控制在总量的 30% 以内比较稳。6.3 异源数据的最后一公里遥感与远场景遥感图像标注是火灾烟雾检测的前沿场景也是刚才这套流程里最吃数据质量的地方。卫星视角下火焰只是几个像素烟雾覆盖面积大但透明度极高VOC 这种水平框标注方式表达不了烟羽的任意方向分布。如果后续要接遥感或无人机航拍火情监测建议在基础检测模型跑通后单独收集异源数据做微调而不是把全部样本混在一起从头训练。我吃过的最大亏就是拿到数据集后直接开训跑了三天发现验证指标虚高又回头重做数据清洗前后浪费一周。后来形成习惯任何新拿到的标注数据集先花半天验数据再花半天定格式和切分策略最后才轮到训练。这个顺序倒过来省下来的时间总会在某个深夜以重训的形式还回去。希望这篇笔记能帮你少走这几步回头路把 rar 里的数据变成真正能上线的模型。本文还有配套的精品资源点击获取
返回列表