☰
风力涡轮机缺陷检测数据集与COCO JSON标注实践
2026/9/28 17:05:24 网站建设 项目流程

简介:一套面向风电运维与计算机视觉目标检测场景的风力涡轮机缺陷检测数据集,共包含一万八千九百一十二张标注图片,适用于有监督缺陷识别模型训练与算法验证。整体识别准确率可达百分之九十一点四,可为风力发电设备关键部件的表面缺陷自动检测提供高质量训练样本,有助于降低人工巡检漏检率。数据标注同时提供 YOLO、Pascal VOC XML、COCO JSON 三种主流格式,能够直接对接常见检测框架的数据接口,省去大量格式转换与标注整理环节。资源以 zip 压缩包形式提供,整体大小约五百八十四点四兆字节,解压后即可用于训练集与验证集划分,围绕缺陷分类、目标定位、模型迭代等方向开展实验。目前已有三百五十四人学习使用,适合需要真实工业场景数据支撑算法研究的算法工程师、风电运维人员以及高校相关专业学生。

1. 风力涡轮机缺陷检测数据集,为什么值得用 COCO JSON 格式跑一遍

风力涡轮机的叶片巡检,是我接触过的视觉落地场景里最“看天吃饭”的一个。无人机围着塔筒拍一圈,回来的素材常常是逆光、雾霾、运动模糊混在一起;叶片上的缺陷又是细裂纹、腐蚀斑、雷击黑点这类小目标,人眼盯屏半小时都会漏。多数团队其实不缺拍摄能力,缺的是能直接喂给检测模型的标注数据。这个风力涡轮机缺陷检测数据集给到 18912 张图片,标注统一成 COCO JSON 格式,声称 91.4% 的准确识别率,等于把“整理数据”和“格式适配”两步先替你做了。想做风机叶片自动巡检、工业缺陷检测基线验证的算法工程师,可以顺着这个标题把训练链路完整跑通。

2. COCO JSON 标注格式拆解:为什么缺陷检测选它而不是 VOC XML

COCO JSON 是目标检测领域最通用的标注交换格式,mmdetection、detectron2、ultralytics 都原生支持。缺陷检测选它而不是 VOC XML,有一个关键原因:叶片上的缺陷不全是矩形框能框住的,一条弯曲的裂纹用多边形勾勒,背景占比小,模型学到的特征更干净。这一章把 COCO JSON 的字段拆开讲清楚,后面改配置、写转换脚本才不容易翻车。

2.1 顶层结构:images、annotations、categories 三个数组怎么对起来

一个完整的 COCO JSON 标注文件,顶层是一个字典。对风力涡轮机缺陷检测来说,真正起作用的是三个数组:images 记录每张图片的 id、文件名和尺寸;annotations 逐条记录每个缺陷实例的位置和类别;categories 定义类别名到数字 id 的映射。其余 info、licenses 只是元信息,训练时框架基本不会读。

{ "info": {"description": "wind turbine defect dataset"}, "images": [ { "id": 1, "file_name": "WT_0001.jpg", "width": 1920, "height": 1080 } ], "annotations": [ { "id": 1, "image_id": 1, "category_id": 2, "bbox": [842.1, 356.4, 156.2, 28.7], "area": 4483.0, "segmentation": [[842.1, 356.4, 998.3, 351.2, 998.3, 385.1, 842.1, 385.1]], "iscrowd": 0 } ], "categories": [ {"id": 1, "name": "crack", "supercategory": "defect"}, {"id": 2, "name": "erosion", "supercategory": "defect"} ] }

这里有几个约定必须记死:images 里的 id 全局唯一,annotations.image_id 要能对上它;category_id 在 categories 里必须存在,类别 id 从 1 开始是 COCO 惯例,0 留给背景;bbox 是 [x, y, width, height],不是 x1, y1, x2, y2,这是新手最容易错的地方;area 对矩形标注来说可以直接取 w*h,但对多边形标注必须用多边形面积公式重算,否则评估时 pycocotools 按 area 做大小分桶,统计会偏。

2.2 annotations 条目拆解:bbox、segmentation、area 在缺陷场景里的真实语义

叶片裂纹的特点是长宽比极大,一条裂纹可能宽只有几个像素,长度却横跨几百像素。如果只用 bbox 框,大面积背景被包进来,模型训练时会把这些背景纹理也当成缺陷特征,推理时误检率会明显上升。COCO JSON 的 segmentation 支持多边形点列,能贴着裂纹走向勾勒,这是它比 VOC XML 更适合缺陷检测的本质原因。

另一个容易出问题的是 area 字段。很多标注工具导出的 area 是按 bbox 面积算的,跟 segmentation 重算出来的面积对不上。评估阶段 COCOeval 会按 area 把目标分成 small、medium、large 三档统计 AP,面积错了,分桶就错,最后看到的 mAP 是失真的。我一般会在训练前用 pycocotools.mask 的 area 函数把所有标注的 area 重算覆盖一遍:

from pycocotools import mask as mask_utils import json def fix_area(ann_file, img_dir): with open(ann_file, 'r', encoding='utf-8') as f: coco = json.load(f) img_h = {img['id']: img['height'] for img in coco['images']} img_w = {img['id']: img['width'] for img in coco['images']} for ann in coco['annotations']: h, w = img_h[ann['image_id']], img_w[ann['image_id']] if ann['segmentation']: rle = mask_utils.frPoly(ann['segmentation'], h, w) ann['area'] = float(mask_utils.area(rle)) else: _, _, bw, bh = ann['bbox'] ann['area'] = bw * bh with open(ann_file, 'w', encoding='utf-8') as f: json.dump(coco, f, ensure_ascii=False) fix_area('annotations/instances_train.json', 'images/train/')

frPoly 会把多边形先转成 RLE 再算面积,这样算出来的值与 COCOeval 内部口径完全一致。参数上注意 frPoly 的第二个和第三个参数是 mask 的高和宽,顺序别反,反了轻则面积全错,重则直接报错。iscrowd 字段保持为 0,表示每个缺陷都是独立实例;如果标注工具导出成 1,cocoapi 会按 crowd 区域特殊处理,一般不参与常规训练,发现训练曲线异常先查这个字段。

2.3 18912 张图怎么划分 train/val:分层抽样而不是随机切

18912 张图不算小,但缺陷类别之间数量可能很不均衡,雷击损伤可能只有裂纹的十分之一。直接用 random split,小类可能在验证集里只出现几张,AP 曲线大幅抖动,甚至某次随机划分导致训练集里完全缺失某个类别。常见做法是按图片包含的缺陷类别做分层抽样。

import json import random from collections import defaultdict def stratified_split(ann_file, val_ratio=0.2, seed=42): with open(ann_file, 'r', encoding='utf-8') as f: coco = json.load(f) img_to_cats = defaultdict(set) for ann in coco['annotations']: img_to_cats[ann['image_id']].add(ann['category_id']) bucket = defaultdict(list) for img in coco['images']: cats = img_to_cats.get(img['id'], []) main_cat = min(cats) if cats else 0 bucket[main_cat].append(img['id']) random.seed(seed) val_ids, train_ids = set(), [] for cat_id, ids in bucket.items(): random.shuffle(ids) n_val = max(1, int(len(ids) * val_ratio)) val_ids.update(ids[:n_val]) train_ids.extend(ids[n_val:]) return train_ids, list(val_ids) train_ids, val_ids = stratified_split('annotations/instances.json')

这里 val_ratio 取 0.2,18912 张图大约留 3780 张做验证;seed 固定下来是为了复现,换 seed 会导致每次跑出来的基线不一样,项目追溯时说不清。按主类别分桶的代价是,一张图同时含 crack 和 lightning_damage 两种缺陷时,它只会进入一个桶,极少数多标签图的划分不够精确,但相对纯随机切已经稳得多。划分结束后要分别统计两个子集的类别数量,确认小类别没有被全部挤进某一侧。

3. 用 COCO JSON 跑通缺陷检测训练:从数据配置到 91.4% 的复现

数据格式理顺之后,接下来就是把标注文件真正喂进检测框架。这个章节按 mmdetection 的常规流程走一遍,从环境、配置到命令逐项说明。之所以选 mmdetection 而不是其他框架,是因为它对 COCO JSON 的支持是一等公民,改数据路径就能跑,不需要额外写 Dataset 类。

3.1 训练框架选择:为什么 mmdetection 适合缺陷检测基线

风电叶片缺陷检测有几个特性:缺陷尺寸差异大,一个叶片上同时存在大范围腐蚀和几像素宽的裂纹;图像分辨率高,无人机原图常在 1920×1080 以上;类别数量少,一般 3 到 5 类。mmdetection 的 FPN 多尺度特征正好覆盖这种尺寸差异,而且官方仓库提供了大量预训练权重,迁移到缺陷场景收敛快。detectron2 也能做同样的事,但 mmdetection 在自定义数据集上改动的文件更少,一个 config 文件就能覆盖数据、模型、训练策略三部分。

对比来看,用 ultralytics 的 YOLO 系也能吃 COCO JSON,但对 segmentation 多边形的利用不够,叶片裂纹这种细长目标在 YOLO 的矩形框范式下先天吃亏。第一版基线我一般直接用 Faster R-CNN R50-FPN,理由简单:稳,训练参数几乎不用调,官方 1x 配置跑 12 个 epoch 就能看到像样的结果。

3.2 修改数据配置:ann_file、data_prefix、metainfo 三个必改项

mmdetection 3.x 里配置数据集的入口是 train_dataloader 和 val_dataloader。拿到风力涡轮机缺陷检测数据集的 COCO JSON 后,先把目录整理成固定结构:

data/wind_turbine/ ├── annotations/ │ ├── instances_train.json │ └── instances_val.json └── images/ ├── train/ └── val/

对应 config 里的写法如下:

dataset_type = 'CocoDataset' data_root = 'data/wind_turbine/' metainfo = dict( classes=('crack', 'erosion', 'lightning_damage', 'leading_edge'), palette=[(220, 20, 60), (119, 11, 32), (0, 0, 230), (165, 255, 0)] ) train_dataloader = dict( batch_size=8, num_workers=4, dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_train.json', data_prefix=dict(img='images/train/'), metainfo=metainfo) ) val_dataloader = dict( batch_size=8, num_workers=4, dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_val.json', data_prefix=dict(img='images/val/'), metainfo=metainfo) )

metainfo 里 classes 的顺序必须和 JSON 中 categories 的 id 一一对应,classes[0] 对应 category_id=1,多写一个或少写一个都会在训练时报 num_classes 不匹配。data_prefix 是图片子目录,ann_file 是标注文件相对 data_root 的路径,这两项是新手最常改错的地方。palette 只影响可视化,随便给一组 RGB 就行。

3.3 训练命令与关键参数:batch size、学习率、epochs 怎么设

在 mmdetection 下跑的完整配置可以基于官方 Faster R-CNN 1x 配置做增量覆盖,新建 work_dirs/wt_faster_rcnn.py:

_base_ = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py' model = dict( roi_head=dict( bbox_head=dict(num_classes=4))) dataset_type = 'CocoDataset' data_root = 'data/wind_turbine/' metainfo = dict( classes=('crack', 'erosion', 'lightning_damage', 'leading_edge')) train_dataloader = dict(dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_train.json', data_prefix=dict(img='images/train/'), metainfo=metainfo)) val_dataloader = dict(dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_val.json', data_prefix=dict(img='images/val/'), metainfo=metainfo)) val_evaluator = dict( type='CocoMetric', ann_file=data_root + 'annotations/instances_val.json', metric='bbox')

然后启动训练:

python tools/train.py work_dirs/wt_faster_rcnn.py \ --work-dir work_dirs/wt_exp1 \ --cfg-options train_dataloader.batch_size=8

跑之前有几个参数要按显卡实际情况改。官方 1x 配置的 lr=0.02 是 8 卡环境下的默认值,单卡训练必须线性缩放,常见做法是 lr=0.02/8=0.0025,否则 loss 很容易一开始就 nan 或者震荡不收敛。batch_size 由显存决定,单张 3090 上 batch_size=8 比较稳,输入尺寸如果放大到 1600×1000,batch_size 要降到 4。epochs 方面,官方 1x 是 12 个 epoch,但缺陷检测这类小目标任务 12 个 epoch 往往不够,我会把 max_epochs 加到 24,val_interval 保持 1,每轮都看验证 mAP 曲线,方便判断是否过拟合。

参数COCO 官方 1x 默认风电缺陷推荐值理由
lr0.02(8 卡)0.0025(单卡)线性缩放,防发散
batch_size168单卡显存约束
max_epochs1224小目标收敛慢
val_interval11每轮盯验证曲线
输入尺寸1333×8001600×1000叶片原图缺陷占比小

3.4 91.4% 是怎么算出来的:评估口径必须对齐

标题里的 91.4% 准确识别率,拿到手先别急着当基线。这个数字常见两种口径:一是验证集 mAP@0.5=0.914,即 IoU 阈值 0.5 下的平均精度;二是图片级识别准确率,只要图里任意一个缺陷被框出来就算对。两种口径能差出十个百分点。复现这类数据集声明的精度时,第一件事是确认评估代码怎么统计。

用 pycocotools 独立评估的脚本如下:

import json from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval gt = COCO('data/wind_turbine/annotations/instances_val.json') with open('work_dirs/wt_exp1/val_preds.json', 'r') as f: dt = gt.loadRes(f.read()) evaluator = COCOeval(gt, dt, iouType='bbox') evaluator.evaluate() evaluator.accumulate() evaluator.summarize() mAP_all, mAP_50, mAP_75 = evaluator.stats[0], evaluator.stats[1], evaluator.stats[2] print(f"mAP@[.5:.95]={mAP_all:.4f} mAP@.5={mAP_50:.4f} mAP@.75={mAP_75:.4f}")

evaluator.stats 的前三个值依次是 mAP@[0.5:0.95]、mAP@0.5、mAP@0.75。如果宣称的 91.4% 对应 stats[1],那你要对齐的是 mAP@0.5 而不是默认的 mAP@[0.5:0.95];很多论文和数据集描述喜欢用 mAP@0.5,因为它数值好看。我在项目里会同时打印三个值,给甲方汇报用 mAP@0.5 方便对比,内部技术评审用 mAP@[0.5:0.95] 衡量真实定位精度。

提示:val_preds.json 必须是 COCO 预测格式,每条包含 image_id、category_id、bbox、score 四个字段,缺一不可。

4. COCO JSON 转换与标注工具配合:从 CVAT、LabelImg 到统一标注

如果你的缺陷数据不是直接用 COCO JSON 交付的,而是散落在各种标注工具里,这个章节讲怎么把它们转成统一格式。风电巡检数据经常是多团队协作产出,有人用 CVAT,有人用 LabelImg 存 VOC XML,最后合并时格式五花八门,转换脚本是省不掉的。

4.1 CVAT 导出 COCO 格式的操作路径与注意点

CVAT 是目前团队协作标注最顺手的工具,浏览器就能用,支持多边形标注,特别适合叶片裂纹的细长轮廓。导出路径是 Project 页面 → Export dataset → 选择 COCO 1.0 格式,导出一个 zip 压缩包。解压后标注文件在 annotations/instances_default.json,里面才是真正的 COCO JSON,外层还有一份 manifest.json 是 CVAT 自己的内容清单,别拿错文件喂给训练框架。

CVAT 导出有几个长期存在的坑要检查:categories 里的 id 可能从 0 开始,而 mmdetection 的 CocoDataset 默认类别编号从 1 开始,差一个偏移,训练时类别全乱;segmentation 的多边形点列顺序 CVAT 是保证闭合的,但坐标可能超出图像边界,需要裁剪。我每次导出后都会跑一遍下面的校验脚本再进训练。

4.2 VOC XML 转 COCO 的 Python 脚本:转换流程与边界条件

LabelImg 默认存成 VOC 格式的 XML,文件名、尺寸、目标框都写在 XML 里。转换成 COCO JSON 的核心是重新组织索引关系:XML 是“每张图一个文件”,COCO 是“全局数组 + id 关联”。下面这段脚本是常见做法,能处理 bndbox 整数坐标和漏标两种边界情况。

import json import xml.etree.ElementTree as ET from pathlib import Path def voc_to_coco(xml_dir, img_dir, category_map, output_json): images, annotations = [], [] ann_id = 1 for idx, xml_file in enumerate(sorted(Path(xml_dir).glob('*.xml'))): root = ET.parse(xml_file).getroot() filename = root.find('filename').text width = int(root.find('size/width').text) height = int(root.find('size/height').text) images.append({ 'id': idx + 1, 'file_name': filename, 'width': width, 'height': height }) for obj in root.findall('object'): name = obj.find('name').text if name not in category_map: continue bbox = obj.find('bndbox') x1 = float(bbox.find('xmin').text) y1 = float(bbox.find('ymin').text) x2 = float(bbox.find('xmax').text) y2 = float(bbox.find('ymax').text) x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(width, x2), min(height, y2) w, h = x2 - x1, y2 - y1 if w <= 0 or h <= 0: continue annotations.append({ 'id': ann_id, 'image_id': idx + 1, 'category_id': category_map[name], 'bbox': [x1, y1, w, h], 'area': w * h, 'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]], 'iscrowd': 0 }) ann_id += 1 coco_json = { 'info': {'description': 'wind turbine defect converted from VOC'}, 'images': images, 'annotations': annotations, 'categories': [ {'id': cid, 'name': name, 'supercategory': 'defect'} for name, cid in category_map.items() ] } with open(output_json, 'w', encoding='utf-8') as f: json.dump(coco_json, f, ensure_ascii=False) voc_to_coco( xml_dir='labels/xml/', img_dir='images/', category_map={'crack': 1, 'erosion': 2}, output_json='annotations/instances.json' )

category_map 的 key 必须是 XML 里 object/name 的原始字符串,漏掉的类别会在转换时被静默跳过,所以转换完要统计一下每个类别的数量,跟原始 XML 对比。segmentation 这里退化成矩形四角点,如果你的项目后续要用 Mask R-CNN 做实例分割,这个方法不够用,需要存原始多边形;如果只做目标检测,矩形 segmentation 完全满足训练要求。

4.3 转换后必须做的四步校验:id 映射、边界、尺寸、面积

转换脚本写得好不好,不在于一次跑通,在于跑完之后敢不敢直接拿去做训练。我一般转换完会执行四步检查,任何一步报错都先修数据再开训练。

检查一:image_id 和 category_id 的引用完整性,所有 annotation 引用的 id 必须真实存在。检查二:bbox 坐标非负且不超出图像宽高,这个在 VOC 转换脚本里做过一次,但多工具合并数据时必须再查。检查三:bbox 宽高必须大于 0,有些标注工具会导出 width=0 的异常框,训练时 loss 直接 nan。检查四:area 与 bbox 的一致性,这个用 2.2 节的重算脚本统一处理,顺手把面积口径全部对齐。

def validate_coco(ann_file): with open(ann_file, 'r', encoding='utf-8') as f: coco = json.load(f) cat_ids = {c['id'] for c in coco['categories']} img_ids = {i['id']: (i['width'], i['height']) for i in coco['images']} for ann in coco['annotations']: assert ann['image_id'] in img_ids, \ f"image_id {ann['image_id']} 不存在" assert ann['category_id'] in cat_ids, \ f"category_id {ann['category_id']} 不存在" x, y, w, h = ann['bbox'] assert w > 0 and h > 0, \ f"annotation {ann['id']} 的 bbox 宽高必须为正" img_w, img_h = img_ids[ann['image_id']] assert x >= 0 and y >= 0, \ f"annotation {ann['id']} 坐标越界" assert x + w <= img_w + 1e-6 and y + h <= img_h + 1e-6, \ f"annotation {ann['id']} 超出图像边界" print(f"校验通过:{len(coco['images'])} 张图," f"{len(coco['annotations'])} 个标注") validate_coco('annotations/instances.json')

校验脚本里的 assert 信息尽量带上具体 id,报错时能直接定位到是哪条标注坏了。放大 1e-6 是为了容忍四舍五入带来的 0.0001 像素级误差,实际标注里常有这种小数残留,不用过度敏感。

5. 避坑:风力涡轮机缺陷检测数据集使用中的 5 个典型问题

这个数据集标题公开的信息只有图片数量、精度和标注格式,真正的坑都在使用过程中暴露。下面 5 条是我在缺陷检测项目里反复踩过的,按“现象、原因、解决”三条线写清楚。

5.1 标注文件打不开:json.load 报 JSONDecodeError

现象:用 json.load 读取 COCO JSON 直接报 JSONDecodeError,错误信息指向文件末尾,看起来像文件被截断。

原因:多数情况是文件从压缩包直接拖拽出来,或者原始标注工具在写文件时编码不完整;也有 Windows 下换行符被意外修改的情况。

解决:先用命令行工具做一次格式体检:python -m json.tool annotations/instances.json,如果 json.tool 报错说明文件确实损坏。修复办法是把文件交给标注平台重新导出,不要在本地手工改 JSON,手工补括号只会造成新的不一致。文件本身完好但读取失败时,打开文件统一用 encoding='utf-8',避免 Windows 默认编码导致的中文路径或注释乱码。

5.2 91.4% 复现不出来:指标口径和类别 id 偏移是两大主因

现象:按数据集声称的 91.4% 去复现,自己训练完只有 70% 上下。

原因:第一优先级怀疑指标口径。91.4% 是 mAP@0.5 还是 mAP@[0.5:0.95],结果能差 10 个点以上。其次怀疑类别 id 偏移,CVAT 导出的 categories 从 0 开始而框架默认从 1 开始,所有类别整体错位,模型等于在学一套错乱的标签。

解决:先用第 3.4 节的评估脚本把 mAP@0.5 单独打印出来,同时把 categories 的 id 打印出来人工核对。再把训练集里每张图的可视化结果渲染一遍,看框的类别是否和图上缺陷一致。这一步不做,后面所有调参都是在黑匣子里碰运气。

5.3 裂纹缺陷几乎全漏报:细长目标被 anchor 和增强策略压死

现象:验证集上大块腐蚀区域检测效果不错,但裂纹类的 AP 只有个位数,几乎全漏。

原因:裂纹长宽比极端,Faster R-CNN 默认的 anchor ratio 是 [0.5, 1, 2],对长宽比 10:1 的目标覆盖不足。另一重打击来自 Mosaic 增强,四张图拼接会把跨边界的裂纹拦腰截断,标注和实际目标对不上,模型学到的是半截特征。

解决:把 anchor ratio 改成 [0.2, 0.5, 1, 2, 5],并关闭 Mosaic,改用 RandomFlip 加 RandomResize。如果输入分辨率允许,把测试时输入尺寸从 1333×800 放大到 1600×1000,裂纹的像素宽度从 5 个像素变成 8 个像素,特征提取难度直接降一个档次。

5.4 训练 loss 正常但验证 mAP 震荡剧烈:验证集划分不均衡

现象:训练 loss 平稳下降,验证 mAP 每个 epoch 上下跳动 10 个点以上,曲线像锯齿。

原因:验证集图片数量过少或者类别分布不均。小类别在验证集里只有三五张图,一张图漏检就拉低该类 AP 好几个点。用 2.3 节的分层抽样重新划分后,这个问题基本消失。

解决:把 val_ratio 从 0.2 提高到 0.25,并打印验证集每个类别的图片数量,确保最小类别至少有 30 张验证图。如果原始数据集中小类别本身只有几十张,优先考虑补充数据,而不是靠调评估参数掩盖问题。

5.5 训练报 num_classes 不匹配:config 和 JSON 的类别定义不一致

现象:启动训练报 error,提示 bbox_head num_classes 与数据集类别数不匹配,或者干脆报 IndexError: list index out of range。

原因:config 里 metainfo.classes 的元组长度、model 里 roi_head.bbox_head.num_classes、JSON 里 categories 数组长度,三者没对齐。比如 JSON 里实际是 4 类,但 config 只写了 3 个类别名。

解决:写一个启动前自检脚本,读取 JSON 的 categories 长度和 config 中 classes 长度做对比。这个脚本 30 行就能写完,放进训练目录,每次换数据都跑一遍。血的教训是,这类问题报错时机不固定,有时第一个 epoch 就炸,有时跑到第三个 epoch 才炸,排查成本远高于写脚本的成本。

6. 用 pycocotools 拆解评测结果:找出 91.4% 背后真正的短板

整体 mAP 只能说明模型及格了,项目要往上走,得知道是哪类缺陷在拖后腿、是小目标还是大目标更弱。这个章节讲三个评测拆解技巧,都是我实际调参时反复用的。

按类别拆 AP。把验证集里某个类别的标注和预测单独挑出来,重新跑一遍 COCOeval,能看到每一类的真实水平。代码上先过滤 gt,再过滤 dt,两边类别不一致时 COCOeval 会把多余预测当误报,结果失真:

import json from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval gt_full = COCO('data/wind_turbine/annotations/instances_val.json') preds = json.load(open('work_dirs/wt_exp1/val_preds.json')) for cat in gt_full.loadCats(gt_full.getCatIds()): cat_id = cat['id'] gt_sub = COCO() gt_sub.dataset = { 'images': gt_full.dataset['images'], 'annotations': [a for a in gt_full.dataset['annotations'] if a['category_id'] == cat_id], 'categories': [cat], } gt_sub.createIndex() preds_sub = [p for p in preds if p['category_id'] == cat_id] dt_sub = gt_sub.loadRes(json.dumps(preds_sub)) ev = COCOeval(gt_sub, dt_sub, iouType='bbox') ev.evaluate() ev.accumulate() ev.summarize() print(f"{cat['name']}: mAP@.5={ev.stats[1]:.3f}")

按目标尺寸拆 AP 同样简单,COCOeval 内部已经按 area 分了 small、medium、large 三档,直接读 stats 的第 3 到第 5 个值。叶片缺陷场景里,裂纹几乎全部落在 small 桶,如果 small AP 明显低于 medium,优先加输入分辨率而不是改模型结构。

最后一个技巧是我这一年养成的习惯:每次训练完,把验证集里 AP 最低的 20 张图单独渲染出来,画出预测框和真实框,用肉眼看是漏检还是误检。数据标注是能骗人的,背景纹理可能被标成腐蚀,category_id 可能标反,这些光靠数字发现不了。把这一步做扎实,基于这个数据集做二次开发才有意义,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询