ARTICLE DETAIL

资讯详情

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

YOLOv8训练火灾烟雾检测:从数据集解压到标注转换全流程

YOLOv8训练火灾烟雾检测:从数据集解压到标注转换全流程 简介目标检测算法研究人员和开发者可直接使用这份数据集进行火灾烟雾识别模型的训练与验证。数据包含2257张高质量实拍图像专业人员逐张完成边界框级标注清晰标出火灾与烟雾的具体位置及类别为训练稳定模型打下数据基础。压缩包约266.14MB同时提供PASCAL VOC与YOLO两种主流标注格式前者适合精确评估目标位置与类别后者能直接用于YOLO系列模型训练也可适配Faster R-CNN、SSD等常见检测框架。目前已有2282人学习下载在安防监控、智能家居、应急响应等场景中具有较强实用价值。利用这批数据开发者可进行类别均衡调整、数据增强与模型调参从而训练出能实时识别火源和烟雾的目标检测系统为早期预警和快速处置提供有力技术支撑。对于研究火情早期识别、烟雾扩散分析等方向的团队这也是一份可直接上手的数据基础。1. 火灾烟雾图像标注数据集压缩包里装的是消防视觉的第一份燃料做消防预警或安防监控的工程师多半有过这种经历模型要识别的是早期火灾烟雾可公开渠道能找的图像标注数据集要么是火焰特写要么烟雾和现场混在一起、标签根本没法直接用。火灾烟雾图像标注数据集就是冲这个缺口来的——一个 .rar 压缩包装着整理好的火灾与烟雾现场图片以及逐张对应的标注文件。它的价值不在图片多而在已标注拿到手就能直接进入数据检查、格式转换和训练环节。适合正在做目标检测、尤其打算用 YOLOv8 训练自己数据集的人也适合刚入门、需要一个干净数据集练手的学生。2. 拿到手先过压缩包这关.rar 解压与数据集结构盘点这类数据集发布时几乎都压成 .rar因为图片多、标注文件多压成一包便于分发。但 .rar 在 Linux 服务端不如 zip 友好所以第一步不是急着写训练代码而是先把解压工具备好再把目录结构摸清楚。这一步做不对后面所有路径都是白写。2.1 用 7-Zip 还是 unrar命令行解压 .rar 的最小操作常见做法是Windows 上装 7-Zip 就够右键直接解压Linux 服务器上建议装 p7zip 或 unrar。训练基本要在 GPU 服务器上进行所以我一般直接在命令行解压省得来回传文件。# Ubuntu / Debian 安装 p7zip-full自带 7z 命令 sudo apt install p7zip-full # 解压到指定目录-o 后面不要加空格 7z x 火灾烟雾图像标注数据集.rar -o/home/user/fire_smoke # 解压前先看压缩包内容确认有没有嵌套一层同名目录 7z l 火灾烟雾图像标注数据集.rar逻辑说明7z x保留压缩包内的目录结构完整解出-o指定输出目录。这个参数有个容易翻车的地方等号形式的-o和路径之间不能有空格写成-o /home/user会被当成一个无效选项很多人第一次都栽在这。7z l只列清单不落盘先看一眼包里是直接一堆图片还是外面又套了一个文件夹免得解完找不到数据在哪个层级。参数说明压缩包很大时可以加-y跳过覆盖确认。如果 7z 报不支持的 rar 版本换unrar x 包名 目标目录/更稳。解压完成后用du -sh对比一下实际大小和解压包大小差距明显就说明解压不完整需要复查。这里多说一句为什么数据发布方偏爱 .rar 而不是 zipWinRAR 对图片这类重复性高的文件压缩率更好还支持分卷和恢复记录传输大目录时比 zip 可靠。代价就是你在 Linux 上必须多装一套工具。官网或分享页一般会说明解压密码或者无密码但如果压缩包标记的是伪加密——文件头加密位置了一、数据本身并没有加密——新版 7-Zip 和 unrar 通常能无视标记直接解出来不用绕任何弯子这点在遇到解压工具反复要密码时值得先确认。2.2 目录结构怎么读图片与标注按什么规则对应解压后别急着点开图片先把目录树打出来。tree -L 2 /home/user/fire_smoke常见做法是这类数据集通常包含 images 和 annotations 两个一级目录图片按 JPEG 或 JPG 命名同名 XML 或 JSON 是标注文件。也有的把图片和标注放在同一个目录靠后缀区分。文件名对应规则多数是IMG_0001.jpg配IMG_0001.xml这是 VOC 系数据集的经典命名方式——但千万别默认有的数据集用0001.jpg配0001.txt有的用 uuid 随机串对不上号。所以拿到手第一件事永远是随机抽几个文件对一遍把命名的实际情况记录下来。怎么快速判断标注格式打开一个标注文件看到annotation根标签就是 PASCAL VOC看到images和annotations两个数组就是 COCO JSON看到每行五个数字class x y w h就是 YOLO 原生格式。这一步判断错了后面整个转换脚本都得推倒重写。确认结构之后做三个基础统计# 统计图片数量判断数据集规模值不值得训练 find images -type f \( -name *.jpg -o -name *.jpeg -o -name *.png \) | wc -l # 抽几个标注文件的头部看类别字段有哪些取值 find annotations -name *.xml | head -3 | xargs -I{} head -30 {}逻辑说明数量级决定策略。几百张只能做迁移学习微调和流程演示几千张才有充分训练的价值这个判断要先于一切参数调整。head 命令看的是标注文件里name标签的实际取值火灾烟雾数据集常见的有 smoke、fire也可能混入 smoke_fire、smog 这类冗余标签后面统一映射时都要处理。再检查图片本身有没有损坏的。很多数据集在压缩前就有坏图解压不报错训练时却报Corrupt JPEG data甚至直接卡死。python -c from PIL import Image import glob bad [] for p in glob.glob(images/*.jpg): try: Image.open(p).load() except Exception as e: bad.append((p, str(e))) print(损坏图片数量:, len(bad)) for b in bad[:5]: print(b) 逻辑说明Image.open只读文件头真正把像素载入内存的是.load()所以判断坏图必须调用 load。这一步扫描出来的坏图列表要保留后面做数据集划分时把对应图片和标注一起剔除不然会在训练中途触发奇怪的显存报错几乎没法排查。3. 读懂标注格式把 XML/JSON 转成 YOLO 能吃的 txt无论这份数据集给的是哪种格式只要你想跑 YOLOv8最终都要落到每张图一个.txt、每行一个目标的形式。转换这一步是整条链路里最容易出错但也最值得花时间做扎实的地方。3.1 三种常见标注格式的差别与选型格式载体坐标表示典型工具适合场景VOC XML每张图一个 .xml左上角 xmin,ymin 与右下角 xmax,ymax 绝对像素LabelImg中小数据集、纯检测COCO JSON整个数据集一个 .jsonbbox 绝对像素segmentation 多边形LabelMe / CVAT大数据集、将来要扩展分割YOLO txt每张图一个 .txt归一化中心点坐标与宽高YOLOv8 直接吃训练输入标准选型理由很直接如果你只做目标检测不做分割VOC 和 COCO 最终都要转成 YOLO txt。YOLO 格式每行是类别id 中心点x 中心点y 宽 高四个数值全部除以图片宽高归一化到 0~1。这个归一化是整个转换脚本里最有技术含量的部分整数除法、忘记除宽、宽高取反任何一个低级错误都会让所有标注框变成乱飞的黑匣子而训练指标还看不出明显异常。3.2 写一个 xml2yolo 转换脚本坐标归一化是核心import os import xml.etree.ElementTree as ET # 类别映射必须和后面 data.yaml 里的 names 顺序完全一致 CLASS_MAP {smoke: 0, fire: 1} def convert_voc_xml(xml_path, img_w, img_h, out_root): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text # 数据集里混入的冗余标签直接忽略 if name not in CLASS_MAP: continue 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) # 越界保护标注偶尔超出图片边界先裁剪再归一化 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) img_name os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_root, img_name .txt), w) as f: f.write(\n.join(lines)) # 已通过 head 确认 VOC 格式后批量转换 os.makedirs(labels, exist_okTrue) for xml_file in os.listdir(annotations): if not xml_file.endswith(.xml): continue img_name xml_file[:-4] .jpg img_path os.path.join(images, img_name) if not os.path.exists(img_path): print(缺少图片:, img_path) continue # 读原图尺寸YOLO 归一化的分母必须是真实宽高 from PIL import Image w, h Image.open(img_path).size convert_voc_xml(os.path.join(annotations, xml_file), w, h, labels)逻辑说明脚本做了三件关键的事——把 XML 里的字符串坐标转成浮点数在归一化前先把越界框裁剪回图片范围内避免出现负值或大于 1 的坐标以图片实际像素尺寸做分母计算中心点和宽高。最后一句校验很关键如果 XML 存在但对应的 JPG 缺失这条标注必然被训练流程忽略提前打印出来省得后面对着 mAP 发懵。参数说明CLASS_MAP的字符串取值以第一步 head 看到的name为准顺序直接决定类别 id必须和 data.yaml 保持一致。: .6f保留六位小数足够 YOLOv8 使用数值过大反而让文件膨胀。如果数据集里带difficult标记遍历 obj 时补一句if int(obj.find(difficult).text) 1: continue就能滤掉难例对稳定训练有帮助。3.3 划分 train/val/test不要用 random 直接切import os, random images sorted(os.listdir(images)) random.seed(42) random.shuffle(images) # 火灾烟雾数据常有连拍先按文件名前缀分组再切分 from collections import defaultdict groups defaultdict(list) for img in images: prefix img.split(_)[0] # 同场景共用前缀 groups[prefix].append(img) group_keys list(groups.keys()) random.shuffle(group_keys) n_train int(len(group_keys) * 0.8) n_val int(len(group_keys) * 0.15) train_keys group_keys[:n_train] val_keys group_keys[n_train:n_train n_val] # 按组写入对应 split 的 images 和 labels 目录 for split, keys in [(train, train_keys), (val, val_keys)]: os.makedirs(fdatasets/{split}/images, exist_okTrue) os.makedirs(fdatasets/{split}/labels, exist_okTrue) for key in keys: for img in groups[key]: # 拷贝图片并复制同名 txt 到 labels 目录 pass逻辑说明随机切分有一个隐蔽陷阱——火灾烟雾数据很多来自同一段监控视频连拍相邻帧几乎一样。如果这些相似图片同时进了 train 和 val验证集等于漏题训练的 mAP 虚高一上真实场景立刻打回原形。按文件名前缀分组再切就是先把同场景图片绑在一起保证验证集是模型没见过的场景。随机数种子固定为 42是为了实验可复现别人重跑你的流程能得到一样的划分。参数说明这里给的 0.8 / 0.15 是通用比例test 剩 5% 可留可不留。数据总量只有几百张时建议不留 test把更多样本留给训练用 val 的混淆矩阵和 PR 曲线就足够判断模型状态了。4. 用 YOLOv8 跑通火灾烟雾检测训练命令与 4 个关键参数数据转换完接下来就是把这份数据集喂进 YOLOv8训练出一个能用的火灾烟雾检测模型。YOLOv8 是目前做这类检测最顺手的框架配置好数据文件、敲一条训练命令就能跑。4.1 先写 data.yaml路径、类别数、类别名一个都不能错# data.yaml路径建议写绝对路径 train: /home/user/fire_smoke/datasets/train val: /home/user/fire_smoke/datasets/val test: /home/user/fire_smoke/datasets/test nc: 2 names: [smoke, fire]逻辑说明YOLOv8 约定数据目录下必须存在images和labels两个子目录训练脚本会按同名配对自动找到图片与标注所以目录名不能改。nc是类别数names是类别名列表两者必须匹配且 names 的顺序必须和转换脚本里的 CLASS_MAP 完全一致否则会出现训练时类别张冠李戴但指标还正常的怪象。参数说明路径写成绝对路径是踩出来的经验。写相对路径时只要你 cd 到别的目录再跑训练报错信息就变成找不到数据看起来像数据集坏了实际只是路径解析问题白白浪费半小时。4.2 训练命令与 4 个关键参数pip install ultralytics yolo detect train \ data/home/user/fire_smoke/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectfire_smoke_exp \ namebaseline逻辑说明modelyolov8s.pt会在第一次运行时自动下载 COCO 预训练权重。对几百到几千张的小数据集迁移学习是刚需从头训练很难收敛。patience20的意思是验证集指标连续 20 个 epoch 不提升就提前停止这是小数据集上防止过拟合的后悔药省电也省时间。参数说明epochs火灾烟雾这种目标边缘模糊的场景50 个 epoch 往往还没稳住150 个又开始过拟合先用 100 看曲线走势再决定加减。imgsz640 是速度和精度的平衡点。如果你发现标注的 smoke 目标普遍很小可以试 1280但显存占用约翻四倍batch 要对应减半。batch按显存说话。8G 显存跑 yolov8s、imgsz640 时 batch 16 是上限再大就 OOM。报显存不足时先降 batch而不是换模型。4.3 训练完看什么metrics 与 PR 曲线的读法训练结束后输出目录里会有weights/、results.png、confusion_matrix.png等文件。这里有个玄学点大家习惯盯 mAP50但消防场景更该看精确率 P 和召回率 R 的平衡因为漏检的代价远大于误检——宁可把蒸汽框成烟雾多报几个警也不能真着火时没反应。results.png里重点看 val 的 Recall 曲线如果训练结束时它还在明显上扬说明 epochs 不够如果 Precision 掉得厉害说明开始过拟合。confusion_matrix.png里能直接读到烟雾被分到背景的比例这个比例超过 30% 时先别急着调网络回头查标注框是不是漏标了大量小目标数据问题不解决模型怎么调都是白费。训练完跑一次推理验证一下整体效果yolo detect predict \ modelfire_smoke_exp/baseline/weights/best.pt \ source./samples \ conf0.25 \ saveTrue逻辑说明source可以是单张图或一个目录saveTrue会把带框的结果图存到 runs/detect 下。conf 阈值这个参数在烟雾检测上值得单独调一轮烟雾是半透明目标模型输出的置信度天然偏低0.25 会把相当一部分真目标过滤掉我一般会降到 0.1 再跑一遍对比用眼睛判断是误检多还是漏检多。5. 火灾烟雾数据集的 5 个坑从解压报错到模型漏检训这类数据集的过程中翻车集中发生在几个固定位置列出来供你对照排查每条都是能直接抄的解决路径。5.1 解压报 CRC 校验错误或提示需要密码现象7z x解压到一半弹出 CRC 失败或者 unrar 直接要求输入密码而分享页明明写着无密码。原因传输过程丢包导致压缩包不完整是比较常见的解释另一种是伪加密——压缩包头的加密位被标记了数据本身没加密普通工具一看加密标记就去要密码。解决CRC 错误先重新下载仍然报错就把报错的那个文件单独用7z e 包名 文件名 -o/tmp提取看能不能抢救。伪加密则好办换成 7-Zip 19.00 以上版本或 unrar 直接解。判断是不是伪加密可以用文件查看器打开压缩包头看加密位但更快的办法是先换新工具大部分这类包直接就能解开。5.2 训练时 loss 正常但 mAP 一直是 0现象loss 在降训练曲线也正常验证集指标却始终接近 0像是模型完全没学到东西。原因标注 txt 里有非法数据——坐标越界、类别 id 超出 nc 范围、行内字段数不是 5。YOLOv8 训练时会静默过滤掉非法框过滤比例太高等于在拿空气训练。解决跑一段脚本扫一遍 labels 的合法性。import glob bad [] for f in glob.glob(labels/*.txt): for line in open(f): parts line.strip().split() if len(parts) ! 5: bad.append((f, 字段数不对, line.strip())) continue cls int(parts[0]) vals [float(v) for v in parts[1:]] if cls not in (0, 1) or any(v 0 or v 1 for v in vals): bad.append((f, 数值越界, line.strip())) print(非法行数:, len(bad)) for item in bad[:10]: print(item)逻辑说明这段脚本在训练前做硬校验字段数必须是 5类别 id 必须在类别集合里四个坐标必须在 0 到 1 之间。跑完把非法行对应的图片和标注找出来修掉或删除再训练就不会出现 mAP 为 0 的诡异局面。5.3 烟雾区域小、目标密集一个都检不出来现象稀疏的大烟雾能检出来密集的早期小火苗和烟柱一个不剩。原因烟雾是半透明目标没有清晰边缘标注框实际只框住了烟柱中心框的很大一部分区域贴着无纹理的透明背景。YOLOv8 默认开启的 mosaic 增强会把小目标裁得更碎等于主动加大难度。解决小数据集上先把mosaic0.5甚至关掉试试imgsz 提到 1280让每个小目标在特征图上的像素数多起来。补一批纯背景负样本让模型学会没烟就不框这也比单纯加深网络有效。5.4 精确率不低但召回率很低云和蒸汽被大量放过或框上现象验证集里十张带烟雾的图只检出六张同时把两朵云、一团蒸汽框成了 fire。原因原始数据里混入了大量视觉上接近烟雾的负样本——云、雾、排汽、白色反光。标注人员通常只标了明显烟雾没标看起来像但不是烟雾的难分背景模型没见过这些负例只能凭烟雾的白色纹理乱猜。解决手工收集一批难分背景图放进训练集并保持全图无标注必要时把云/蒸汽也标成一个负类参与训练。这是消防烟雾检测的老办法比调 IoU 阈值和置信度阈值都稳。5.5 Linux 训练时报 No such file or directory现象Windows 上解压、预览、转换全正常拷到 Linux 上跑训练YOLOv8 说找不到图片。原因目录或文件名里带中文、空格、括号。压缩包内层目录名如果是中文data.yaml里路径虽然写对了shell 脚本拼接路径时也容易被空格拆词。解决解压后第一件事就是把整个目录重命名为纯英文小写比如fire_smoke_v1文件名的中文和特殊字符也一并替换。这是数据集落地最常见的血泪经验很多人的转换脚本没问题卡就卡在路径命名上。6. 进阶先做一次标注分布体检再决定要不要补数据训练之前花十分钟统计一下 bbox 的尺寸和长宽比分布往往比调任何训练参数都值钱。这个习惯帮我避开了无数次无效实验。import glob import numpy as np ws, hs, cls_ [], [], [] for f in glob.glob(labels/*.txt): for line in open(f): parts line.strip().split() if len(parts) ! 5: continue cls_.append(int(parts[0])) ws.append(float(parts[3])) hs.append(float(parts[4])) ws np.array(ws) hs np.array(hs) print(标注框总数:, len(ws)) print(小目标占比(面积图面积2%):, round((ws * hs 0.02).mean(), 3)) print(窄长条占比(宽高比3):, round((ws / hs 3).mean(), 3)) print(类别0占比:, round(np.mean(np.array(cls_) 0), 3))逻辑说明小目标占比高优先提 imgsz 而不是换大模型窄长条目标多说明数据来自俯拍或广角监控普通 anchor 配置可能不匹配类别占比悬殊训练时就要考虑在 loss 里加权重或对少数类做欠采样。这些结论在训练前知道能直接决定参数方向。拿我自己的教训收个尾接手过一批火灾烟雾数据没做分布检查直接训mAP50 卡在 0.5 附近上不去后来一统计发现超过一半的标注框是视频连拍里截出的窄长条烟柱在 imgsz640 的缩放后只剩几个像素宽模型根本没有足够信息去学。从那以后每个新数据集到我手上先跑十分钟分布统计再谈训练。这个习惯值回所有调试时间希望帮到你。本文还有配套的精品资源点击获取
返回列表