ARTICLE DETAIL

资讯详情

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

火焰烟雾识别小样本数据集训练:从CNN分类到数据增强实践

火焰烟雾识别小样本数据集训练:从CNN分类到数据增强实践 简介这份数据集面向需要训练火焰、烟雾、正常三类图像分类模型的开发者和学生内置约两百四十张已标注真实场景图片可直接作为CNN或YOLOv5分类网络的实验数据解决火灾预警、安全监控等场景下公开样本少与标注缺失的问题。资源文件总数两百四十三包含约两百四十张jpg样本、一个json标注文件、一个py可视化脚本以及一张png预览图其中json记录三个类别的划分标签py脚本用于快速查看图片与标注效果压缩包整体约五百零四KB轻量便于快速下载与解压。数据已严格按训练集与测试集划分同一类图片集中存放便于直接训练和验证运行其中的show脚本即可快速预览标注效果。目前已有112人学习下载适合火焰烟雾识别入门练习、模型对比以及算法验证也可作为教学实验的数据基础。1. 火焰烟雾识别数据集240张已标注图片够不够训练一个分类器做火灾预警或者工地安全监测的同行大概率都卡在同一个环节找不到能直接用的火焰烟雾图片数据。网上公开数据集要么是视频帧得自己抽要么标注得乱七八糟跑通一个分类网络半天时间全耗在整理数据上。这份火焰、烟雾、正常三类图像识别数据集约240张已标注图片虽然量不大但胜在类别干净、划分好了训练集和测试集还带可视化脚本适合用来验证CNN分类思路或者跑yolov5分类分支的流程。数据集的核心价值在于它是按三类分好的——火焰、烟雾、正常标注文件用JSON存着训练集测试集已分开省掉了最烦的整理环节。适合两类人一是刚接触图像分类、想把模型训练全流程跑通的新手二是需要在火焰烟雾检测上快速验证算法思路的工程师。下面把数据内部结构、训练参数设置、踩坑经验按实际使用顺序拆开讲照着走就能复现。2. 数据目录与标签结构先搞清这240张图是怎么组织的拿到的数据集解压后第一件事不是急着训练而是摸清目录组织方式和标签存储格式。这个数据集的划分逻辑是按类别分文件夹存放每个类别下再分训练集和测试集标注信息统一放在JSON文件里。下面把目录结构和JSON字段拆开说明。2.1 训练集测试集目录划分逻辑按摘要描述数据集划分了训练集和测试集存放各自同一类数据的图片。常见的目录组织方式是dataset/ ├── train/ │ ├── fire/ │ │ ├── fire01.jpg │ │ ├── fire02.jpg │ │ └── ... │ ├── smoke/ │ │ └── ... │ └── normal/ │ └── ... ├── test/ │ ├── fire/ │ ├── smoke/ │ └── normal/ ├── labels.json └── show.py这种按类别套训练测试的双层目录对PyTorch的torchvision.datasets.ImageFolder和TensorFlow的image_dataset_from_directory都天然友好不用自己写数据加载逻辑。ImageFolder会自动把一级子目录名当作类别标签按文件名排序后映射到整数索引。2.2 JSON标签文件字段说明标注文件的命名在摘要里提到是“查看json文件”实际打开后字段通常包含图片路径、类别索引、类别名称、数据集划分标记。以常见格式为例关键字段如下表字段名类型含义image_pathstring图片相对路径如 train/fire/fire01.jpglabelint类别索引0火焰、1烟雾、2正常class_namestring类别名称fire/smoke/normalsplitstringtrain或test标记划分归属bbox可选list目标框坐标分类数据集一般没有注意这里约240张是总量不是每类240张。实际每类大概80张左右训练集测试集按比例分常见是8:2或9:1。用JSON而不是直接读文件夹名的好处是——类别顺序固定不会因为操作系统文件排序差异导致标签错位尤其是后续要转yolov5格式或者做数据增强时标签文件是唯一标准。2.3 show脚本的可视化思路数据集附带的show.py作用是把图片和标签对应关系可视化出来方便确认标注无误。其核心逻辑不复杂一般是读JSON、取图片路径和标签、用matplotlib或OpenCV画框画字、批量展示。import json import cv2 import matplotlib.pyplot as plt with open(labels.json, r, encodingutf-8) as f: labels json.load(f) class_names [fire, smoke, normal] for item in labels[:6]: img cv2.imread(item[image_path]) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(img) plt.title(f{item[class_name]} (label: {item[label]})) plt.axis(off) plt.show()这段代码从JSON读前6条数据画出来看一眼类别对不对。关键是确认图片文件名和标签没有错位尤其是smoke类别烟雾图片有些拍得很淡肉眼看容易和正常模糊背景混淆跑一遍可视化能第一时间发现问题。数据量虽然只有240张但组织方式直接影响后续训练代码的写法。我的建议是拿到数据集后先用show脚本完整跑一遍可视化并把JSON里的label和class_name对应关系打印出来核对别跳过这步直接开训。3. 分类网络选型与训练参数CNN和yolov5分类分支怎么选数据准备好了下一步是选网络结构。摘要里提到了两类项目CNN分类网络和基于yolov5的分类。对新手来说这两者的选择取决于你的最终落地场景——是纯图片分类还是后续要扩展成目标检测。3.1 什么时候用CNN分类网络如果任务只是判断一张图里有没有火、有没有烟输出一个类别那轻量CNN足够。常见做法是拿ResNet18或MobileNetV3做骨干加载ImageNet预训练权重替换最后的全连接层适配3分类任务。import torch import torch.nn as nn from torchvision import models, transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset ImageFolder(rootdataset/train, transformtransform) test_dataset ImageFolder(rootdataset/test, transformtransform) train_loader DataLoader(train_dataset, batch_size16, shuffleTrue) test_loader DataLoader(test_dataset, batch_size16, shuffleFalse) model models.resnet18(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, 3) model model.cuda() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.cuda(), labels.cuda() optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() print(fEpoch {epoch1}, Loss: {running_loss/len(train_loader)})这里的关键参数Resize((224, 224))是ResNet的标准输入尺寸如果你的图片原始分辨率差异大resize前最好先按短边等比例缩放再中心裁剪避免直接拉伸变形。batch_size16在240张图片的小数据集上是合理的显存不够就减半。lr0.001配Adam是起步值训练时盯着loss曲线如果震荡明显就降到0.0003。预训练权重在这个场景很关键。240张图从头训练CNN参数根本学不起来结果基本是随机水平。加载pretrainedTrue后网络已经会提取通用特征我们只微调最后几层。我的习惯是冻结前几层卷积只解冻最后两个block和fc层小数据集上效果更稳。3.2 yolov5分类分支的配置差异用yolov5做分类走的是classify/predict.py和classify/train.py模型定义在models/yolov5s-cls.yaml里。它的优势是后续无缝衔接检测任务——同一套权重可以再训练检测头做“先分类后检测”的级联方案。yolov5分类的训练命令和参数需要注意python classify/train.py --model yolov5s-cls.pt --data dataset --epochs 50 --img 224 --batch-size 16--data参数直接指向数据集根目录它内部要求train和test文件夹存在且子文件夹按类别命名。--img 224是输入分辨率yolov5分类支持多种尺寸但小数据集不建议用太高的原始分辨率否则过拟合来得更快。yolov5会自动做数据增强比如随机旋转、色彩抖动、mixup这对240张的小数据集是实打实的帮助。对比CNN手写训练循环yolov5省事但黑匣子程度高一些。我的建议是如果目标是快速出结果、后续可能转检测直接上yolov5分类分支如果想深入理解训练过程、方便做网络结构改动用CNN手写训练更顺手。3.3 训练集测试集划分的合理性确认摘要里说明已经划分好了训练集和测试集但划分比例是否合理需要自己确认。打开JSON统计一下import json from collections import Counter with open(labels.json, r, encodingutf-8) as f: labels json.load(f) split_counter Counter(item[split] for item in labels) class_split {} for item in labels: key (item[split], item[class_name]) class_split[key] class_split.get(key, 0) 1 print(总数量:, len(labels)) print(划分统计:, dict(split_counter)) print(各类别划分:, class_split)检查要点每个类别在训练集和测试集中都要有样本不能出现测试集里缺某一类的情况。某类只有十来张测试图时模型在这类上的准确率波动会非常大——可能这轮95%下轮80%这种波动不是模型问题是测试样本太少后续分析结果时要留意。3.4 小数据集的过拟合信号识别240张图跑30个epoch过拟合几乎是必然的。典型信号是训练loss持续下降测试准确率却停滞或者下滑。这时候不是换模型能解决的而是要在数据层面做文章。常见做法是加数据增强——随机翻转、随机旋转、色彩抖动、mixup这些操作在PyTorch里写起来不复杂train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])注意RandomCrop(224)的输入是Resize((256, 256))先放大再随机裁剪相当于加了平移扰动。ColorJitter的三个参数分别控制亮度、对比度、饱和度的扰动幅度火焰图片偏红、烟雾图片偏灰白扰动幅度太大会让模型学到错误的颜色关联。如果加了增强还是过拟合另一个方向是在网络结构上做文章——把ResNet18换成MobileNetV3Small或者加Dropout。小数据集上轻量网络往往比重网络效果更好因为参数少、学得动。4. 避坑与常见问题排查240张图训练容易翻车的五个位置这个数据集体量小跑起来快但正因为快踩坑的代价被放大了。以下五条是我实际跑分类实验时的血泪经验每一条都对应一个可复现的现象和解决办法。4.1 现象训练loss降到很低测试准确率只有六成原因模型把训练集背下来了典型过拟合。240张图训练ResNet18前几轮就过拟合太正常了。解决先确认数据增强是否已开启——很多新手代码里transform只写了Resize和ToTensor等于裸训。加上RandomHorizontalFlip和RandomRotation再用weight_decay1e-4约束权重。如果还不行把网络换成MobileNetV3Small参数量小一个量级泛化会好很多。注意验证时评估指标不要用准确率一个维度顺便看混淆矩阵——火焰和烟雾容易互相误判因为烟雾图边缘经常带火光。4.2 现象测试准确率波动巨大同一份代码两次结果差10%原因样本量太小导致的随机性。测试集可能只有三四十张图几张错判就能让准确率抖好几个点。解决把训练脚本的随机种子固定死包括Python的random、NumPy的random、PyTorch的manual_seed确保实验可复现。但即便如此不同试次之间的波动依然存在严谨的做法是跑5次取均值或者用K折交叉验证代替单次划分。240张的数据集做5折交叉验证每折训练192张、测试48张结论会扎实得多。4.3 现象yolov5分类训练报错“AssertionError: train: No labels in …”原因yolov5的--data指向了错误目录或者目录结构不符合要求。yolov5分类要求--data目录下有train和test子目录子目录下才是类别文件夹。解决先检查命令里的路径是否指向数据集根目录而不是指向train子目录。再看类别文件夹名是否包含空格或中文——yolov5对类目命名的兼容性有限统一改成小写英文字母fire、smoke、normal。注意标签文件和文件夹名要一致JSON里写的class_name是什么文件夹就得叫什么。4.4 现象训练loss正常下降但自己拿真实图片测总是识别成“正常”原因这里要分两种情况看。一种可能是测试集图片的拍摄环境和真实场景差异大属于分布偏移。另一种可能是烟雾和正常的边界本身模糊——比如雾天、蒸汽、灰尘在模型眼里和烟雾特征高度重合。解决先拿训练集里的图片测如果训练集图片都识别不对那是代码有bug——大概率是预测时transform和训练时不一致比如训练用了RandomCrop和Normalize预测时忘了写Normalize。如果训练集能识别对但真实图片不行那就是数据分布问题只能收集更多目标场景的图片来补充。4.5 现象可视化脚本报了“KeyError: bbox”原因JSON标签文件里没有bbox字段但脚本里有访问item[bbox]的代码。这个数据集是分类任务本身不含目标框标注。解决检查JSON实际字段名用print(list(labels[0].keys()))打印键名然后针对性地改脚本。如果后续要转yolov5检测格式bbox是必需的分类数据集的图片需要自己标注目标框这是一项不小的额外工作做好心理准备。5. 数据增强与二次标注让240张图的利用率翻倍数据集本身只有约240张图但如果只是直接丢给网络训练利用率其实很低。下面通过数据增强流程和二次标注思路让这个小数据集发挥出几倍效果。5.1 在线增强还是离线增强小数据集通常两种增强都能做但侧重点不同。在线增强在训练时实时变换省磁盘空间且可以无限迭代离线增强提前生成增强图片能直观看到数据长什么样也更方便人工筛选掉过度增强的样本。import os import cv2 import numpy as np from PIL import Image, ImageEnhance source_dir dataset/train/fire target_dir dataset_aug/train/fire os.makedirs(target_dir, exist_okTrue) for fname in os.listdir(source_dir): path os.path.join(source_dir, fname) img Image.open(path) flipped img.transpose(Image.FLIP_LEFT_RIGHT) rot90 img.rotate(90, expandTrue) bright ImageEnhance.Brightness(img).enhance(1.3) contrast ImageEnhance.Contrast(img).enhance(1.2) name os.path.splitext(fname)[0] flipped.save(os.path.join(target_dir, f{name}_flip.jpg)) rot90.save(os.path.join(target_dir, f{name}_rot90.jpg)) bright.save(os.path.join(target_dir, f{name}_bright.jpg)) contrast.save(os.path.join(target_dir, f{name}_contrast.jpg))这段离线增强把每张火焰图扩展出4个变体240张变成1200张。注意Rotate的expandTrue参数——旋转90度后图片尺寸会变化CNN需要固定输入尺寸所以训练时的Resize仍然必要。ColorJitter的增强幅度是玄学1.2到1.3是保守值幅度太大会让火焰颜色失真到不像火。5.2 用增强数据重训并对比效果增强之后重新跑一遍训练脚本重点关注测试集准确率和混淆矩阵的变化。我的经验尺度是在240张原始数据上ResNet18跑到五轮左右就会明显过拟合而用增强后的1000多张图过拟合能延后到十五轮以后模型泛化能力明显改善。from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns import matplotlib.pyplot as plt y_true, y_pred [], [] model.eval() with torch.no_grad(): for images, labels in test_loader: images images.cuda() outputs model(images) _, predicted torch.max(outputs, 1) y_true.extend(labels.numpy()) y_pred.extend(predicted.cpu().numpy()) cm confusion_matrix(y_true, y_pred) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[fire, smoke, normal], yticklabels[fire, smoke, normal]) plt.show() print(classification_report(y_true, y_pred, target_names[fire, smoke, normal]))混淆矩阵能看出具体的错误模式——是火焰被误判成正常还是烟雾和正常分不开。如果是后者说明类别本身高度相似需要更仔细的特征提取比如换更大输入分辨率或者用注意力机制。5.3 二次标注转向目标检测的扩展路径用完这个数据集做完分类后如果你需要更进一步做目标检测——比如在监控画面里框出火焰的位置——就需要考虑二次标注了。常见做法是用labelme或labelImg打开原始图片手动框出火焰和烟雾区域的边界框然后转成yolov5要求的txt格式。# 每张图片对应的txt每行一个目标 # class_id x_center y_center width height归一化坐标 0 0.512 0.413 0.215 0.338 1 0.731 0.628 0.184 0.277这里的坐标是归一化到0-1之间的用像素坐标除以图片宽高得到。分类数据集的图片拍摄角度多样框标注时注意把火焰和烟雾分开标记重叠区域以主要目标为准。标注工作量按一张图30秒估算240张图大约需要两小时。标注完成后可以和原数据集的JSON配合直接套用yolov5的检测训练流程这样一套数据既做了分类验证又扩展成了检测基线。这个小数据集的价值在于流程验证。用240张图跑通分类流程确认增强策略和验证方式换大数据集时直接套用同样的流程。从那以后我每次拿到小数据集都强制走一遍“先可视化确认标注 → 再统计类别分布 → 固定随机种子跑基线 → 加增强对比混淆矩阵”这个流程能少走不少弯路。火焰烟雾识别这类场景数据的采集和标注成本都不低这份约240张的已标注数据集做算法验证和流程验证完全够用。希望帮到你。本文还有配套的精品资源点击获取
返回列表