多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

火焰数据集标注清洗与YOLO训练避坑指南

火焰数据集标注清洗与YOLO训练避坑指南 简介一份面向计算机视觉目标检测训练与评估的火焰识别数据集专门服务于YOLOv5等主流目标检测模型的开发流程内含1553张人工精选并准确标注的火焰图像。压缩包共5260个文件总大小约194MB其中JPG图片1853张TXT标注文件1854个XML标注文件1553个jpg与txt、xml一一对应TXT采用YOLO格式提供归一化边界框坐标XML采用VOC格式记录火焰位置与尺寸可直接划分训练集/验证集免去自行采集和标注的繁琐工作。该数据集在YOLOv5上已有实测指标mAP.5为0.953mAP.5:.95为0.679在常规与更严格IoU标准下均保持较高检测精度可作为火焰检测任务的效果基准同时图片覆盖多类场景有助于提升模型在不同光照和背景下的泛化能力。目前已有4898人学习下载适合火灾预警、智能监控、工业消防等场景的开发者与研究者借助现成标注数据快速完成模型训练、参数调优与工程化部署。1. 火焰数据集含标注好的标签到底稀缺在哪从一次夜间误报说起做智慧园区火焰识别项目这一年多我最大的感受是网上能找到的火焰数据集其实不少真正稀缺的是“标注好的标签”。这里的“好”不是指文件命名整齐而是标签内部有没有错位、漏标、类别混写、坐标越界这些暗病。上个月项目试运行算法在白天工地场景表现不错一到夜间就被路灯和红色车灯带偏连续翻车一星期最后查根因不是模型结构问题而是数据集里把大量“类似火焰颜色的物体”标成了正样本。作为一线工程师我把这次以及之前多次数据清洗的完整思路整理成这篇实战笔记从格式选型到切分脚本再到增强参数和避坑排查目标是让拿到火焰数据集含标注好的标签的从业者能直接避掉这些坑少走弯路。2. 先别急着训练解构火焰数据集的标注格式与标签体系2.1 COCO、VOC、YOLO 三种标注格式的选型对比拿到一份火焰数据集第一步不是打开图片看内容而是搞清楚它的标注格式。大多数公开的火焰数据集会提供 COCO、Pascal VOC 或 YOLO 格式中的一种或多种三者的存储方式和坐标体系差异很大选型直接影响后续脚本的复杂度和训练效率。格式存储方式坐标体系优点缺点COCO单文件 JSON嵌套字典结构像素级绝对坐标包含 segmentation 多边形点标准化程度高适合实例分割各类别统计方便JSON 文件大解析稍繁琐标注修改需要重新合并Pascal VOC每张图对应一个 XML 文件像素级绝对坐标即 xmin、ymin、xmax、ymax可读性好人眼排查方便单个文件独立适合分块处理XML 解析相对繁琐没有统一的类别汇总YOLO每张图对应一个 TXT 文件每行一个目标归一化相对坐标即 x_center、y_center、w、h训练效率高主流检测框架原生支持文件小坐标是浮点小数精度受损人眼直接读不方便我一般会把火焰数据集统一转成 YOLO 格式。原因很简单后续使用 YOLOv5、YOLOv8 或 RT-DETR 做训练时几乎不需要额外的数据加载代码。但有个前提如果数据集本身给出了高质量的分割掩码而且你想做精细的火焰轮廓分割那 COCO 格式的 segmentation 信息会更有价值。不过这不是大多数人训练检测模型的首选因为火焰边缘本身模糊分割标注的难度和噪声要远高于矩形框。从工程落地角度目标检测加矩形框已经能覆盖绝大多数火焰报警场景比如烟感摄像头、室内明火监控、园区早期火情预警。选 COCO 还是 YOLO背后其实是任务类型和后续迭代频次的取舍。2.2 标签体系设计单类还是多类标签类别是火焰数据集里最容易出问题的地方常见的标注方式大致有这三种只标一个 fire 类、区分 flame 和 smoke 两类、再细分成 flame、smoke、light_source 三类。第一种最省事但训练出来的模型在夜间很容易把路灯、红色尾灯、电焊火花误检成火。第二种是行业里更推荐的做法因为火焰预警的核心场景里阴燃阶段的烟雾往往比明火出现得更早单类标签会导致模型对早期烟雾完全免疫。第三种适合复核阶段把容易混淆的干扰光源单独列为负样本类别能显著降低误报率不过这也意味着你要付出更多的标注人力。需要特别提醒的是很多公开的火焰数据集标注里存在类别混写问题比如把 smoke 直接写成 fire或者把“火光反射在水面上的倒影”也标成了 fire。如果直接用这些原始标签训练模型会学习到“凡是亮橙色区域都是火”的糟糕映射。我在项目里通常会额外写一个标签映射表把原始标签名统一改写成既定类别。比如原始 JSON 里出现 fire、flame、fire_2全部映射到 flame出现 smoke_fire、smoke映射到 smoke。这个操作属于数据清洗的前置环节看起来简单但它直接决定了标签体系的一致性。没有这个映射步骤后面所有训练结果都可能被脏标签带偏。2.3 一个可执行操作的标签映射文件规划下面是我在项目里常用的一种标签映射文件写法用 YAML 格式维护既直观又能直接被训练脚本加载。# labelmap.yaml # 训练时类别的顺序就是 id 顺序尽量把核心目标类放前面 names: 0: flame 1: smoke # 可选定义容易混淆的负样本类别不参与损失计算 # 建议单独建立 hard_negative 目录存放负样本图片这段配置的核心价值在于显式地固定类别顺序。很多新手拿到数据集后直接打开模型默认的 coco.yaml 就开始训练导致模型把火焰识别成“人”或“椅子”因为预训练权重里根本没有 flame 这个语义。你必须自己新建 labelmap 并覆盖默认配置并在训练命令中通过--data参数指定。另外要注意类别一旦定了就不要在训练中途随意增删否则热启动或断点续训时会出现类别数量不匹配整个训练过程直接崩溃。如果你的项目需要区分阴燃和明火也可以把类别扩到 flame、smoke、ember 三类但每增加一个类别训练样本量至少要提升一倍这是需要提前评估的。3. 用脚本把原始图烧成训练集切分、清洗与标签纠错实战3.1 数据集切分必须按场景切分不要随机文件切分很多人拿到火焰数据集以后第一件事就是写个循环按比例随机分配训练集和验证集。这种做法在绝大多数普通图像分类任务里没问题但在视频帧抽帧形成的火焰数据集里几乎是灾难。公开的火焰数据集多数是从监控视频中按帧间隔抽出来的相邻帧之间的画面高度相似甚至同一段火焰燃烧过程会被连续抽几十帧。如果你随机切分同一段火焰的画面会同时出现在训练集和验证集中训练出来的模型 mAP 会格外高看起来效果很好但一部署到真实场景就原形毕露。我习惯先按视频来源或场景编号切分。import json, random # 假设你的 COCO 标注文件里每张图带有 video_id 字段 with open(flame_annotations.json, r) as f: coco_data json.load(f) # 按 video_id 收集所有图片而不是按单张图片随机划分 video_to_images {} for img in coco_data[images]: video_id img.get(video_id, unknown_video) video_to_images.setdefault(video_id, []).append(img) video_ids list(video_to_images.keys()) random.seed(42) random.shuffle(video_ids) split_idx int(len(video_ids) * 0.8) train_videos set(video_ids[:split_idx]) val_videos set(video_ids[split_idx:]) train_images [img for vid in train_videos for img in video_to_images[vid]] val_images [img for vid in val_videos for img in video_to_images[vid]] print(fTrain videos: {len(train_videos)}, Train images: {len(train_images)}) print(fVal videos: {len(val_videos)}, Val images: {len(val_images)})这段代码的核心逻辑是先按视频分组再对视频列表做切分确保同一个视频的帧不会同时落进训练集和验证集。随机种子固定为 42是为了实验结果可复现。切分完成后建议把 train_images 和 val_images 各自的图片 id 列表保存成 txt 文件后续转换格式或统计类别分布都会用到。这里还有一个进阶细节如果数据集里存在同一场景不同机位的拍摄最好再叠加一层场景 id 分组把机位相同或拍摄地点相同的画面归到同一组。否则同一地点白天和夜间的画面分别进入训练和验证虽然视频不同但背景特征高度相似仍然会带来轻微的数据泄漏。泄漏不严重时模型能用但验证集数字会虚高给项目验收带来误导。3.2 标签清洗检测越界框和空标签标签清洗是数据集落地过程中最枯燥也是回报最高的一步。公开数据集的标签质量参差不齐常见问题包括坐标值超出图片边界、目标宽高为 0 或为负数、标注框实际面积过小、以及部分图片没有对应的标签文件。这些问题如果不处理轻则训练时 loss 异常跳变重则直接导致程序崩溃。我处理过一份火焰数据集发现里面有几张图的 xmin 和 xmax 完全相等相当于标注了一条线模型根本没法从中学到有效特征。清洗脚本的思路很简单逐行遍历所有标签按规则过滤并给出明确报错。import os, glob label_dir labels/ image_width, image_height 640, 640 # 从对应图片信息中读取 for txt_path in glob.glob(os.path.join(label_dir, *.txt)): with open(txt_path, r) as f: lines f.readlines() valid_lines [] for line in lines: parts line.strip().split() if len(parts) ! 5: print(f[SKIP] Invalid format: {txt_path} - {line.strip()}) continue cls_id, x_center, y_center, w, h (float(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4])) # 归一化坐标必须在 0-1 范围内超出即为越界 if not (0.0 x_center 1.0 and 0.0 y_center 1.0): print(f[SKIP] Center out of bounds: {txt_path} - {line.strip()}) continue if w 0 or h 0: print(f[SKIP] Non-positive size: {txt_path} - {line.strip()}) continue # 过滤面积过小的噪声框比如小于图片面积千分之一的目标 if w * h 0.001: print(f[SKIP] Tiny object: {txt_path} - {line.strip()}) continue valid_lines.append(line) # 备份原始文件而不是直接覆盖 if len(valid_lines) ! len(lines): backup_path txt_path .backup with open(backup_path, w) as f: f.writelines(lines) with open(txt_path, w) as f: f.writelines(valid_lines) print(f[FIXED] {txt_path}: removed {len(lines) - len(valid_lines)} invalid lines)这份脚本里有一个容易被忽略的工程细节备份原始文件。直接原地覆盖是最危险的操作一旦清洗规则写错原始标注就永久丢失了。我把无效行移入备份文件而不是直接删除这样你随时可以对照复查确认过滤规则合理后再清理 backup 文件。面积阈值的设定需要根据你的实际检测目标调整。火焰检测中如果摄像头距离远小火苗可能只占图像很小一部分把阈值设得太大会误删有效样本设得太小又起不到清洗作用。建议先统计一遍数据集中所有标注框的面积分布画个直方图再决定阈值。实际操作里我遇到过不少人因为把面积阈值设成了 0.01结果把大量远距离小目标样本删掉了训练出来的模型对远处火点完全没有泛化能力这个问题会直接推高实际误报率。3.3 将 VOC XML 转为 YOLO TXT坐标换算与路径校验很多公开火焰数据集提供的是 Pascal VOC 格式的 XML 标注而训练主流检测模型需要 YOLO 格式。好在转换脚本本身不复杂核心是把像素绝对坐标换算成归一化的中心点坐标和宽高。但这份脚本里最容易出事的不是坐标换算而是图片宽高读取错误。有些数据集标注里的图片尺寸和实际图片文件尺寸不一致如果你直接采用 XML 里记录的宽高坐标换算结果就会整体偏移。import xml.etree.ElementTree as ET import os, cv2 def convert_voc_xml_to_yolo(xml_path, output_dir, target_classes): tree ET.parse(xml_path) root tree.getroot() # 务必从图片文件本身读取宽高而不是 XML 中的 size 节点 image_path root.find(path).text image cv2.imread(image_path) if image is None: print(f[ERROR] Cannot read image: {image_path}) return img_h, img_w image.shape[:2] txt_filename os.path.splitext(os.path.basename(xml_path))[0] .txt txt_path os.path.join(output_dir, txt_filename) with open(txt_path, w) as out_f: for obj in root.iter(object): cls_name obj.find(name).text.strip() if cls_name not in target_classes: continue cls_id target_classes[cls_name] # 提取 VOC 格式的原始坐标 bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 坐标越界修正宽高和中心点必须限制在图片范围内 xmin max(0, xmin) ymin max(0, ymin) xmax min(img_w, xmax) ymax min(img_h, ymax) if xmax xmin or ymax ymin: print(f[SKIP] Degenerate box: {cls_name}, {xmin}, {ymin}, {xmax}, {ymax}) continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 最终兜底保证归一化坐标不会超过 1.0 x_center min(1.0, max(0.0, x_center)) y_center min(1.0, max(0.0, y_center)) box_w min(1.0, max(0.0, box_w)) box_h min(1.0, max(0.0, box_h)) out_f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\n) print(f[DONE] {txt_path}) # 用法示例 target_classes {flame: 0, smoke: 1} convert_voc_xml_to_yolo(annotations/img_001.xml, yolo_labels/, target_classes)这段脚本里我特别做了三层防护从图片文件读取真实尺寸、坐标越界修正、归一化数值兜底。每一层都是为了应对不同的脏数据情况。真实尺寸校验解决的是 XML 元数据与图片内容不一致的问题这种情况在数据集中极其常见。越界修正解决了部分标注框顶点溢出图片边界的问题而最后一道兜底则保证浮点计算误差不会导致 yolo 训练解析失败。建议转换完成后抽取几十张图片把 txt 坐标还原成矩形框画在图上肉眼验证一遍而不是直接开始训练。这种可视化检查能发现 90% 以上的格式问题。4. 喂给检测模型前的最后一公里数据增强与采样策略4.1 增强参数设置的玄学别让马赛克扰乱了火焰的颜色数据增强是提升模型泛化能力的标准手段但火焰数据集在增强上有个明显区别于普通目标检测任务的特点火焰的颜色本身就是核心特征。如果你按默认参数做增强比如把色调偏移量拉满、饱和度大幅随机扰动、亮度剧烈变化模型会学到错误的不变特征。原本是靠橙黄色识别火焰增强后这一特征被破坏模型只能靠纹理和形状来判断鲁棒性反而下降。我调过一份增强配置在 hue 偏移 20 度的情况下模型把一棵红叶李树当成火焰训练日志里 false positive 一栏直线上升。这就是典型的增强参数没有约束地过度扰动属于自己给自己挖坑。import albumentations as A train_transform A.Compose([ # 火焰颜色是核心判别特征hue 只能做微调防止颜色偏移过大 A.HueSaturationValue(hue_shift_limit(-5, 5), sat_shift_limit(-10, 10), val_shift_limit(-15, 15), p0.5), # 亮度和对比度扰动同样要保守因为火焰图像的亮度本身偏高 A.RandomBrightnessContrast(brightness_limit(-0.1, 0.1), contrast_limit(-0.1, 0.1), p0.5), # 马赛克增强把多张图拼接成一张能有效提升小目标检测能力 A.Mosaic(p0.2), ])参数说明hue_shift_limit我限制在正负 5 度内这个范围基本不会改变火焰的橙黄色基调又能模拟不同环境光下略微变化的色温。sat_shift_limit正负 10 度用来模拟浓烟环境下饱和度降低的情况。brightness_limit设为正负 0.1是因为火焰本来就亮如果增强后把亮度调到极低火焰特征就完全丢失了。这里的Mosaic是一个例外它不做颜色扰动而是通过把四张训练图像拼成一张新图来增加单张图像中的目标数量和小目标密度这对比偏移参数重要得多。需要注意的是Mosaic的 p 值不要设置太高。火焰检测任务中很多样本本身就是火势较大的场景四张图拼接后每个目标的尺寸被压缩到原来的四分之一反而可能让模型过多关注小目标而忽略了中等尺寸目标的模式。4.2 采样策略如何应对“小目标火焰”与样本失衡除了参数层面的增强采样策略同样影响训练效果。火焰数据集中样本分布极不均衡大部分公开数据集以中近距离的大火图像为主真正用于早期预警的小目标火焰图像占比很小有些甚至不到 5%。如果不做任何采样干预模型会对小尺寸火焰严重欠拟合部署到高位摄像头或大范围巡检场景时漏报率会非常高。常见的做法有两种第一种是对小目标样本做简单的过采样在 DataLoader 里按图片中包含的小目标数量加权采样让模型每个 epoch 都能更多地看到小目标样本第二种是利用马赛克增强本身对小目标友好的特性把包含小目标的图像作为马赛克拼接的主体配合随机缩放进一步模拟不同距离下的火焰形态。from torch.utils.data import WeightedRandomSampler def build_sampler(dataset, small_obj_threshold0.01): weights [] for idx in range(len(dataset)): boxes dataset.get_boxes(idx) if boxes is None: weights.append(1.0) continue # 这里认为面积占比小于整体 1% 的框是小目标 small_count sum(1 for box in boxes if box.area small_obj_threshold) if small_count 0: weights.append(2.0) # 给含小目标的样本更高的采样权重 else: weights.append(1.0) sampler WeightedRandomSampler(weights, num_sampleslen(weights), replacementTrue) return sampler这段代码的核心在于给包含小目标的样本更高的采样权重相当于每个 epoch 里小目标样本的出现次数翻倍。权重设为 2.0 是一个可调参数实际项目中我试过 1.5、2.0、3.0。权重太高会导致大目标样本被严重稀释模型开始在小目标上过拟合权重太低又起不到均衡作用。建议在验证集上按尺寸分桶统计 AP 指标比如分别统计面积小于 0.01、0.01 到 0.1、大于 0.1 这三类目标的 AP然后根据小目标 AP 的改善情况来回调权重。这里顺带提一句负样本策略公开火焰数据集大部分不包含纯粹的负样本图像也就是完全没有火焰但容易误报的场景比如夜晚的路灯、红色招牌、电焊火花。建议你自己采集几百张这类图像加入训练集不标注任何目标让模型学习“这些背景不是火”。我项目里的误报率能降下来一半功劳来自这批负样本而不是模型结构改动。5. 火焰数据集常见坑与排查手册从标签错位到样本失衡5.1 验证 mAP 虚高部署后频繁误报现象训练过程中损失曲线正常收敛验证集 mAP 达到 90% 以上模型却又大又准的错觉。真正把模型部署到园区摄像头上之后误报频发几乎每个红色物体都触发报警。原因这是数据集切分导致的典型数据泄漏。公开火焰数据集大多由视频抽帧而来随机切分会把同一段火焰燃烧过程的连续帧同时分配到训练集和验证集模型在验证时实际看到了训练时见过的画面mAP 自然虚高。但真实场景里的火焰形态与训练数据存在分布差异所以部署后表现断崖式下跌。解决按照我在 3.1 里的方式先按视频序列或场景编号分组再按组来切分数据集。切分完后做一个简单校验统计训练集和验证集中是否存在来自同一视频或同一场景的图像。如果存在要么重新切分要么把重叠的样本移除。这一步做完验证集指标才会真实反映模型泛化能力。5.2 模型把整栋建筑框成火焰现象检测结果里出现了大量覆盖整栋建筑的大框而真实的火焰只在建筑一角模型完全没有定位能力。原因我在清洗多份公开数据集时发现有些标注员为了省事把整个着火建筑或大片烟雾区域用一个巨大的矩形框包裹起来而不是紧贴火焰轮廓标注。模型在训练时学习到“大框也是正样本”自然输出同样粗糙的预测框。这是边界框标注质量问题不是模型问题。解决在训练前做边界框尺寸分布统计把宽高比超过 3:1 或者宽高超过图片尺寸 80% 的标注框提取出来逐张检查。通常这类框都需要重新标注或直接剔除。我当时用一个简单脚本把面积占比前 5% 的框全部导出成图片列表人工复查一遍清洗掉了十几个明显粗糙的大框模型的定位准确率立刻提升。这类问题在数据清洗阶段不解决训练时无论调多少次 IOU 阈值都没用。5.3 模型对阴燃阶段的烟雾完全无感现象模型能检测明火但火灾初期的烟雾特征出现时模型毫无反应报警迟迟不触发。等到明火起来已经失去了最佳处置时机。原因公开数据集中标注的“火焰”基本以明火为主烟雾样本要么没有单独标注要么被归进“fire”类别。模型因此只学会了明火特征没见过烟雾形态。解决重新审视标签映射把 smoke 从 fire 里拆出来单独作为类别并补充烟雾样本来训练。如果手头的数据集里烟雾样本不够可以用目标检测模型的预训练权重在烟雾数据上做小步长微调或者收集公开的烟雾图像并二次标注。拆分类别后网络需要同时学习两种不同物理形态的目标训练时长和样本量都会增加但这是提升早期火情识别能力的必经之路。5.4 颜色增强过当夜间红灯和路灯成重灾区现象夜间场景误报率急剧上升红色刹车灯、交通信号灯、景观照明灯都被识别成火焰。原因这个坑出现在数据增强阶段而不是标注阶段。训练时把 HSV 颜色空间的扰动范围设得过大导致模型学到了“红色即可”这种错误的判别规则。火焰颜色的本质是燃烧产生的热辐射光谱与普通红灯相比火焰的颜色分布更宽核心特征在近红外端也有响应但这些信息通过普通 RGB 图像根本体现不出来。解决把 HueSaturationValue 参数的扰动范围收紧到正负 5 度以内同时加入包含红灯、路灯、车灯的负样本图像。如果条件允许可以尝试在模型输入端加入红外通道或热成像数据让模型真正学到火焰的温度特征。只依赖可见光 RGB 的情况下控制颜色扰动是防止夜间误报最关键的一步。5.5 标注框里混入倒影和遮挡物现象模型在积水路面、玻璃幕墙反光场景里频繁误报明明只有火焰倒影模型却输出置信度 0.8 以上的检测框。原因整理数据集时发现部分公开标注把地面上的火焰倒影也标成了正样本。倒影的纹理和颜色分布与真实火焰存在差异但对视觉模型来说它具备“橙色亮斑”的抽象特征模型难以区分。解决清洗数据阶段将倒影类标注统一删除或者在复检时把倒影样本移入难例负样本集。如果倒影场景在你的监控区域里不可避免更稳妥的做法是把带倒影的图像作为单独负样本加入训练集让模型见过这种干扰形态。之前我花了一个下午逐张检查含水面反光的图片删除了几十个倒影标注框最终把玻璃幕墙场景的误报率降了一半。6. 量化评估你的火焰标注质量一个轻量级的验证脚本模型训练完成后最容易被忽略的环节是评估标注质量对训练结果的影响。我习惯在训练结束后跑一个独立的标注质量验证脚本把预测框和标注框同时可视化导出并计算两者的差异分布。这一步不是为了调模型参数而是为了反向验证数据集本身有没有系统性标注偏差。import cv2, torch def visualize_predictions(model, image_path, label_path, output_path, conf_thres0.25): img cv2.imread(image_path) img_h, img_w img.shape[:2] # 读取标注标签并绘制绿色框 with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) 5: continue _, x_center, y_center, w, h map(float, parts) x1 int((x_center - w / 2) * img_w) y1 int((y_center - h / 2) * img_h) x2 int((x_center w / 2) * img_w) y2 int((y_center h / 2) * img_h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 模型预测并绘制红色框 results model(img) for pred in results.pred[0]: if pred[4] conf_thres: continue x1, y1, x2, y2 map(int, pred[:4]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, f{pred[5].int().item()}: {pred[4]:.2f}, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.imwrite(output_path, img) print(f[SAVE] {output_path})这个脚本做的事情很朴素把标注框绿色和预测框红色画在同一张图上当你连续导出几百张这样的可视化图并快速翻看时会直观地发现一些预测阶段难以察觉的问题。比如如果绿色框普遍大于红色框说明标注时把目标框得偏松模型学到的定位比标注更紧这时需要考虑清洗标注框。如果红色框反复出现在图的固定位置比如画面边缘或某个固定区域可能是训练时的增强或标注偏移导致了位置偏好。这类可视化层面的检查比任何统计指标都更直接因为人对视觉差异的感知远超对数字差异的感知。我自己特别深刻的教训是一份来自公开仓库的火焰数据集整体标注看起来非常规范每张图都有标签类别分布也合理但跑完验证脚本后发现大量标注框的中心点整体偏左上角偏移几百个像素。追溯原因是抽取视频帧时原始图像被缩放但标注坐标没有同步换算。这种系统性的偏移不靠可视化脚本很难发现而它会直接导致模型定位偏置部署时所有预测框都偏向图像的左上区域。那一刻我才真正理解了“标注好的标签”里的“好”字有多重要。自那以后不管用哪份数据集第一件事永远是跑一遍质量验证脚本输出可视化图肉眼巡查一遍再谈训练。希望帮到你。本文还有配套的精品资源点击获取
返回列表