ARTICLE DETAIL

资讯详情

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

基于COCO格式的风力叶片缺陷检测:从数据体检到模型调参实战

基于COCO格式的风力叶片缺陷检测:从数据体检到模型调参实战 简介面向风力发电机组运维与视觉缺陷检测场景这份数据集包含5113张风机叶片实拍图像采用COCO JSON标准格式标注覆盖排水孔受损、雷击、污垢、漏油、PU胶带、表面裂纹、侵蚀等常见缺陷类别可直接用于目标检测、实例分割等深度学习任务。压缩包内含2000个文件其中1997张为JPG图片3个为JSON标注文件整体大小约174.49MB图片以风机型号如WTG-20、GA551分组命名便于按需拆分训练集与验证集。目前已有952人学习下载。数据集标注格式通用样本来自不同机组与拍摄角度既适合算法工程师快速开展风力叶片缺陷识别模型的训练与调优也适合高校研究生用于计算机视觉课题研究可省去大量人工标注时间针对性提升风电巡检智能化水平。1. 风力叶片缺陷检测数据集是什么5113张COCO格式图能解决什么问题500多台风机的一个巡检周期下来无人机拍回的叶片照片少说上万张。看片的人手就那么几个一张图要盯十几秒雷击、表面裂纹、污垢这些缺陷混在各种光影和油渍里稍一走神就漏标等叶片真出问题上塔一看已经是另一番景象。风力叶片缺陷检测数据集就是冲着这个痛点来的5113张实拍叶片图像用COCO JSON格式做了标注覆盖排水孔受损、雷击、污垢、漏油、PU胶带、表面裂纹、侵蚀这七类运维工单里最高频的缺陷。对正在做风机巡检视觉方案的团队来说这份数据集最值钱的不是那五千多张图而是它直接给到了标准COCO标注——省掉了从零标数据、统一格式、清洗标注这一两个月的脏活当天就能接进检测训练管线。这篇笔记就把拿到它之后的路走一遍先讲清COCO标注里哪些字段决定模型上限再按七类缺陷各自的形态去调检测器和训练参数最后把最容易翻车的几个坑摆出来。2. 拆解COCO JSON标注从images到annotations字段决定模型上限敢说COCO JSON格式的数据集标注文件里至少有三段images、annotations、categories。缺一段都不完整字段填得对不对又是另一回事。很多从LabelMe、labelImg导出的数据号称COCO格式实际上只写了bbox和category_idsegmentation要么空着要么是一串不闭合的多边形。做纯目标检测这还能糊弄过去一旦你想换实例分割模型、或者用mask做更精细的缺陷面积统计当场就露馅。2.1 bbox、segmentation、area三个核心字段怎么看images描述每张图的全局信息id、file_name、width、height。这里常出的问题是图像的宽高和真实文件不一致标注工具在缩放预览后导出框跟着漂。annotations里的bbox是四元组[x, y, width, height]左上角坐标加宽高绝对像素值不是归一化坐标。很多从YOLO格式转过来的数据bbox写成中心点加宽高模型训练时框全散掉——这是COCO格式最容易踩的类型错误。segmentation对多边形标注是一个二维数组每个多边形按[x1, y1, x2, y2, ...]的顺序排点。COCO官方用pycocotools做评估时会把多边形转成mask这要求多边形至少三个点且首尾点闭合。实操里见过不少标注软件导出的多边形坐标和图片尺寸对不上或者最后一个点和第一个点差了零点几个像素。area字段在COCO官方定义里是分割区域的面积不是bbox面积的乘积。做数据体检时如果发现大量area和bbox面积差超过一个数量级基本能断定标注工具偷懒把bbox面积塞进去了。这个字段不影响检测mAP的计算但它会误导你对真实目标大小的判断。2.2 用脚本做数据体检实例数量、尺寸分布与类别均衡拿到COCO标注文件后的第一件事不是急着配模型而是先把这个JSON读一遍看清七类缺陷各有多少实例、目标尺寸长什么样。以下这段脚本是我每次开工前的例行公事统计类别分布和bbox面积判断数据集里到底是小目标多还是大目标多这直接决定后面输入分辨率和anchor怎么调。import json from collections import Counter with open(annotations/instances_train.json, r, encodingutf-8) as f: coco json.load(f) # categories 定义了标注文件里的类别 id 到名称的映射 cat_id2name {cat[id]: cat[name] for cat in coco[categories]} print(类别映射:, cat_id2name) # 统计每个类别的实例数直接反映样本均衡程度 ann_counter Counter(ann[category_id] for ann in coco[annotations]) total sum(ann_counter.values()) for cat_id, cnt in ann_counter.most_common(): print(f{cat_id2name[cat_id]:18} {cnt:5} {cnt / total:.1%}) # 从 bbox 宽高重算目标像素面积不要依赖 area 字段 areas [] for ann in coco[annotations]: bw, bh ann[bbox][2], ann[bbox][3] areas.append(bw * bh) areas.sort() print(目标像素面积中位数:, areas[len(areas) // 2]) print(面积小于1024像素(约32x32)的目标占比:, sum(1 for a in areas if a 1024) / len(areas))读过之后重点看两组数。第一七类缺陷的样本是不是一头沉某个类只有几十个实例那这类缺陷单独靠数据喂不饱只能靠数据增强或加权重。第二目标面积中位数如果是两千像素以下说明相当一部分缺陷在512分辨率输入下只有二三十个像素默认的anchor尺度根本罩不住。这个脚本返回的不是一堆没用的统计数字它决定了你后面改配置的方向。有过一次真实经历拿到一批叶片数据表面裂纹的实例数是雷击的三倍但裂纹检测mAP反而更低一查面积分布裂纹目标的中位面积只有雷击的零头。小目标数量多不等于模型学得好。这里有一个值得单独说明的点area字段可能不可信。上面的统计我特意用bbox宽高乘积重算而不是直接读ann[area]。多数标注工具导出的COCO格式里area就是bbox面积而COCO官方的area是分割面积。如果后续要做基于面积的评估分析统一用自己重算的值别信标注文件里的。2.3 标注质量的两个隐藏信号目标密度与框的退化类别分布只是明面上的体检还有两个隐藏信号容易被忽略。第一个是单张图的目标密度。统计每个image_id对应的annotations数量如果平均值在每张图三四个以下而且大量图片只有一两个目标这个数据集的正样本多样性是偏弱的。模型会倾向于把图片中某个位置出现的叶片边缘暗条纹都当成缺陷误报就会上来。第二个信号是框的退化——bbox宽或高为0、坐标负数、超出图片宽高的标注。这类标注在训练时会造成loss震荡甚至变成NaN还会污染评估结果。img_id2wh {img[id]: (img[width], img[height]) for img in coco[images]} errors [] for ann in coco[annotations]: w, h img_id2wh[ann[image_id]] x, y, bw, bh ann[bbox] # 框越界标注框画到了图片外面 if x 0 or y 0 or x bw w or y bh h: errors.append((ann[id], bbox out of range)) # 框退化宽或高为 0训练时产生 NaN loss if bw 1 or bh 1: errors.append((ann[id], degenerate bbox)) print(问题标注数量:, len(errors)) for err in errors[:10]: print(err)越界标注多半是从别的格式转COCO时归一化坐标乘以错误宽高造成的退化框则是标注时手抖画出的线。发现这两种问题做法是直接滤掉而不是修正——把变形的框补全属于引入人工噪声对训练没有帮助。叶片缺陷检测有个特殊性叶片表面纹理、前缘磨损、阴影本身就接近某些缺陷的视觉特征标注稍微松一点模型就会把纹理当裂纹。所以这个环节宁可少样本也不要脏样本。3. 七类缺陷的检测难度排序雷击、裂纹、污垢不止是标出来这张数据集的七类缺陷视觉形态差别比想象中大得多。把它们看成七个不同的检测任务来对待比用一个通用模型硬套要有效得多。按我做过叶片检测的经验这七类可以分成三个难度梯队小目标为主的排水孔受损和PU胶带细长形态为主的雷击和表面裂纹大而边界模糊的污垢、漏油、侵蚀。每一类都有自己的脾气调参方向完全不同。缺陷类别典型视觉特征主要检测难点排水孔受损数十像素小孔边缘破损或堵塞目标极小高分辨率下才有稳定特征雷击树枝状黑色烧蚀、碳化条纹细长不规则通用anchor不匹配污垢大面积灰黑色渐变边界柔和与阴影、磨损色差难区分漏油油渍沿表面晕染颜色深灰边界模糊标注本身有噪声PU胶带灰白条状对比度低与叶片颜色接近翘边不明显表面裂纹细线状宽度十几个像素高长宽比且与叶片纹理相似侵蚀前缘麻点群大面积纹理变化边界不清晰与污垢易混淆3.1 细长目标与高长宽比雷击和表面裂纹为何让通用anchor翻车雷击和表面裂纹有一个共同点长宽比极端。雷击在叶片上表现为沿表面蔓延的树枝状烧蚀可能几十像素宽、几百像素长裂纹更是典型的细线结构宽度经常不到二十个像素。通用的Faster R-CNN默认anchor长宽比是[0.5, 1.0, 2.0]最大也就2倍。用这套默认配置去检长宽比5倍甚至8倍的目标正样本的IoU从一开始就偏低RPN阶段就给过滤掉了。这就是为什么很多人跑通第一个模型后发现雷击和裂纹的mAP比污垢低一大截不是模型笨是anchor压根没给人家匹配的位置。针对这类缺陷常见做法是把RPN的anchor长宽比扩展成[0.2, 0.5, 1.0, 2.0, 5.0]让网络有机会生成更扁的候选框。同时在数据增强阶段加随机旋转让模型对裂纹的方向不敏感。这里说一句经验雷击的烧蚀痕迹是有方向性的顺着叶片前缘蔓延但训练集里如果全是无人机沿叶片轴向拍的图模型会对方向过拟合换个拍摄角度就漏检。旋转增强不是可选项是必要项。3.2 小目标与大目标共存排水孔受损和污垢在分辨率上的矛盾排水孔受损属于典型小目标。排水孔本身直径在叶片照片里往往就几十像素受损特征更细微可能只是孔边缘的几个缺口。污垢则完全相反一片污垢能占到整个叶片宽度的三分之一边界还特别柔和。这两个类别对输入分辨率的要求是互相矛盾的分辨率打高了小目标特征保住了但显存和训练耗时成倍上涨分辨率打得保守污垢没问题排水孔受损直接学不到。常规做法是短边800像素起步用FPN的多尺度预测来照顾大小目标共存。如果条件允许短边提到1280甚至1600排水孔受损这类小目标的AP提升会非常明显代价是训练时间翻倍。这里建议先跑一版短边800的基线把各类AP记录在案再决定要不要为一个小目标类别付出计算代价。对叶片巡检这个场景排水孔受损漏检的后果没有雷击和裂纹严重工程上优先保大头。3.3 语义边界模糊漏油、污垢、侵蚀的标注噪声与处理策略漏油、污垢、侵蚀这三类问题难不在形状而在哪里算缺陷这件事本身就有争议。漏油的油渍是逐渐晕开的中间深、边缘浅标注员A画到深色区标注员B画到整个色差区同一个图两个框能差三倍面积。污垢更是和叶片本身的阴影、前缘磨损在灰度上纠缠在一起。这类目标的检测结果天然地看起来不准但缺陷本身就在那里只是边界概率化。对这种语义模糊的目标我的做法是把握两点第一训练时不要对这些类别用太难的正样本。漏油框边缘部分勉强算目标模型学出来的是模糊边界推理时会大量产出低置信度框后处理NMS一压全没了。第二评估时对这些类别的IoU阈值适当放宽mAP 0.5比mAP 0.75更有参考意义。标注噪声大的类别在IoU 0.75下AP会掉得很难看那不是模型退化是标注边界不一致导致的评估失真。4. 用COCO格式训练叶片缺陷检测MMDetection配置与可复现命令COCO标注配合MMDetection这类训练框架是最顺手的组合。框架原生支持COCO格式数据集放到位、改个配置就能开训不用像YOLO系列那样再写一遍格式转换脚本。下面以Faster R-CNN加ResNet-50 FPN骨架为例把完整的落地步骤拆开讲。选Faster R-CNN不是因为它最先进而是它训练稳定、调参路径清晰适合做叶片缺陷检测的基线模型。4.1 数据集目录怎么组织不用改标注文件的标准做法先按目标检测框架通用的目录结构把数据放好标注文件不动只做文件映射。data/wind_blade/ ├── images/ │ ├── train/ │ │ ├── blade_0001.jpg │ │ └── ... │ ├── val/ │ └── test/ └── annotations/ ├── instances_train.json ├── instances_val.json └── instances_test.jsonannotations目录里名为instances_*.json的文件只是约定俗成名字不重要里面必须是标准COCO结构。训练时框架按data_root加ann_file加data_prefix三处路径拼接去找到图片文件所以图片文件名要和JSON里images字段的file_name严格对上。另外值得警惕的一点标注文件的images里可能出现重复的file_name但不同的id框架默认每张图是一个样本重复文件名会导致同一张图被当成两张不同样本训练和验证之间形成数据泄漏。拿到数据后先做一遍查重比跑完一个模型再排查泄漏省时间得多。4.2 修改anchor与训练超参针对叶片缺陷的配置调整MMDetection 3.x的配置写法如下核心改动在rpn_head的anchor_generator和roi_head的num_classes。默认的num_classes是80COCO数据集叶片缺陷只有7类不改这个直接开训模型的输出头尺寸不匹配运行到一半就会报shape错误。还有metainfo里的类别顺序必须和标注文件里categories的id顺序一致否则模型学出来的类别和实际含义错位。# 基于 faster_rcnn_r50_fpn 修改的配置MMDetection 3.x 写法 model dict( typeFasterRCNN, backbonedict(typeResNet, depth50, frozen_stages1), neckdict(typeFPN, in_channels[256, 512, 1024, 2048], out_channels256), rpn_headdict( typeRPNHead, anchor_generatordict( typeAnchorGenerator, scales[4, 8, 16, 32], ratios[0.2, 0.5, 1.0, 2.0, 5.0], # 覆盖雷击/裂纹的细长形状 strides[4, 8, 16, 32, 64], ), ), roi_headdict( typeStandardRoIHead, bbox_headdict( typeShared2FCBBoxHead, num_classes7, # 七类缺陷不包含背景类 ), ), ) # 数据集的 metainfo 必须与标注文件 category_id 一一对应 train_dataloader dict( batch_size4, datasetdict( typeCocoDataset, data_rootdata/wind_blade/, ann_fileannotations/instances_train.json, data_prefixdict(imgimages/train/), metainfodict(classes( drain_hole_damage, lightning, dirt, oil_leak, pu_tape, surface_crack, erosion )), ), )ratios这一行是把通用配置从三个值扩到五个值代价是RPN的anchor数量变多显存占用略有上升但对雷击和裂纹的召回提升值得这个开销。scales控制anchor的底面积倍数如果第2章的数据体检显示小目标占比高把起步值从4降到2或者再往下探对排水孔受损更友好。frozen_stages1的意思是冻结backbone的前一个stage保留ImageNet预训练的底层视觉特征对中等规模数据集几千张图训练更稳不容易过拟合。4.3 三行命令跑通训练与评估数据集和配置就位后训练和评估各一条命令搞定。训练时加--work-dir让checkpoint和日志都落在单独目录方便中途恢复和对比实验。# 训练默认跑80个epoch验证集上表现最好的checkpoint会自动保存 python tools/train.py configs/blade/faster_rcnn_blade.py --work-dir work_dirs/blade # 评估指定checkpoint输出各类别AP python tools/test.py configs/blade/faster_rcnn_blade.py work_dirs/blade/best_coco_bbox_mAP_epoch_50.pth --cfg-options metricsbboxbest_coco_bbox_mAP_epoch_50.pth这种名字是MMDetection训练结束后自动生成的最佳模型命名规则具体文件名会随实际效果变化。评估时metricsbbox表示只评测检测框不评测分割——因为这里用的是纯检测模型segmentation字段只是标注数据的附属信息。叶片缺陷检测的第一版实验结果重点看三类数字训练loss曲线有没有下降趋势、val集上整体mAP、以及每一类的AP明细。整体mAP到了0.6以上只说明模型大体上能工作真正决定现场好不好用的是各类AP不拉胯。这里提醒一个训练环境相关的点叶片图片的长边经常到两三千像素直接进网络显存占用极高。常规做法是训练时做随机缩放把长边限制在1333以内配合batch_size4。如果显卡只有11G显存batch_size降到2同时把optim_wrapper的optimizer学习率从0.02等比缩到0.01不然收敛会不稳。这是框架从COCO预训练迁移过来的固定套路数据集规模变了、batch变了学习率就得跟着变。5. 避坑排查COCO标注与叶片缺陷训练的5个典型翻车现场这一章全是从实际训练叶片缺陷模型时踩出来的坑。每一条都按现象、原因、解决的顺序说不绕弯子。如果你训练时遇到奇怪的指标先来这章对照一下。5.1 训练loss为NaN找到bbox宽高为0的越界标注现象训练到某个epoch后loss突然变成NaN或者从第一个epoch开始loss就是inf。很多人第一反应是学习率太大调低学习率后发现还是NaN折腾半天想不通。其实叶片缺陷数据集里混入宽高为0的退化标注是NaN loss最常见的原因。bbox宽或高为0ROI Align在采样时计算出非法区域梯度直接爆掉。原因标注工具导出时出现退化框或从其他格式转COCO时坐标算错产生非法bbox。数据集在制作时这一层过滤未必做了。解决在训练前跑一遍第2章那个校验脚本把宽高小于等于1的标注直接过滤掉。另外一个隐蔽情况是segmentation为空但bbox正常纯检测训练不影响一旦加mask head就会报错所以校验脚本里也要检查segmentation是否为空。过滤掉问题标注后重新生成标注文件再训练就不会出NaN。5.2 雷击裂纹检测率极低anchor长宽比与输入分辨率没有对齐现象整体mAP看起来还行但拆开看每类AP雷击和表面裂纹的AP明显低于其他类别只有零点二几。从推理结果看大块的雷击烧蚀能检出来细长的裂纹几乎全漏。原因这两个类别的目标长宽比远超通用anchor覆盖范围某个裂纹目标宽15像素、长300像素默认的anchor框不可能和它产生高IoURPN在候选框生成阶段就错过了。另外输入分辨率若只有512裂纹宽度还不到8个像素特征在深层网络里已经退化。解决按第4章的方法把anchor长宽比扩展为[0.2, 0.5, 1.0, 2.0, 5.0]训练输入短边不低于800。改完跑一个epoch对比各类AP的变化细长类别的提升通常在5到10个点。5.3 训练集和验证集类别分布差太远随机划分与分层划分现象训练几轮后验证集mAP波动剧烈画出来的曲线像锯条一样上下跳。某一类缺陷在训练集里有二百个实例验证集里只有七八个一个误检就把这类AP从0.6砸到0.3。原因数据集划分用了简单随机划分没有考虑类别分布。叶片缺陷数据集里七类缺陷数量本就不均衡随机划分很容易造成个别类别在验证集里样本过少评估结果方差极大没法作为模型对比的依据。解决划分数据集时做分层采样——按category_id对图片分组保证每类缺陷在训练集、验证集、测试集中占的比例接近整体分布。同时保证验证集里每个类别至少有几张图让评估数字有意义。用COCO格式做分层划分的脚本网上有现成实现核心逻辑是遍历annotations里每个类别按比例把对应图片分到各子集。5.4 mAP正常但现场肉眼觉得漏检IoU阈值与任务预期的错位现象模型在验证集上mAP 0.5超过0.7正式部署到无人机巡检图片后现场人员说这模型漏了一堆缺陷看起来和评测结论完全对不上。排查发现现场人员认为的检出来是预测框和目标有重叠就行但评测用的IoU阈值是0.5部署推理时的置信度阈值却被调得过高。原因推理时的置信度阈值和评估时的阈值不是一回事。评估mAP时pycocotools会对每个类别扫描一系列置信度阈值取平均你看到的高mAP可能是在很低置信度下测出来的。部署时若把置信度阈值设到0.5以上大量低置信度的真阳性预测就被过滤了。解决从验证集里抽样两三张图用不同的置信度阈值跑推理可视化找到视觉上能接受的最低阈值再结合各类缺陷的AP调整出合理的阈值。叶片缺陷检测的现场场景里我一般把置信度阈值放在0.3到0.4之间宁可多出几个误报框交给人工复核不能漏掉真缺陷。5.5 每类单独mAP差距悬殊把整体指标拆开看现象训练完成模型整体mAP 0.62挺不错拿去汇报。但拆开看每类AP污垢类AP有0.78排水孔受损只有0.21。整体指标被样本量大的类别撑高了小类别的问题被掩盖。原因mAP是所有类别AP的平均类别间数量差异大会让整体指标失去参考价值。排水孔受损这类小目标样本数少、目标小AP低是预期内的事。解决每类单独统计AP重点看最低的两三类。如果排水孔受损AP不达标下一步优化方向明确得很——提高输入分辨率、给这类做更强的在线增强、或者补充该类别训练样本。不要因为整体mAP好看就收工叶片缺陷检测的用户最后要看的是哪个缺陷没检出来不是总分。6. 结果验证与进阶mAP之外叶片缺陷检测模型要过的三道关实验做到模型能收敛、整体mAP说得过去这只是第一道关。叶片缺陷检测要真正投入巡检流程还要再过三道关细粒度评估、混淆分析、部署形态取舍。第一道关是按类别计算AP并用混淆矩阵分析。pycocotools的评估输出有per-class AP直接看哪一类拖后腿。混淆矩阵则更直观——叶片缺陷检测里最常见的混淆是污垢和侵蚀互相误报以及裂纹被检成雷击。这两组类别在视觉上本来就接近混淆矩阵能告诉你是模型没学好还是标注本身就难区分。如果污垢与侵蚀两者互相误报贡献了大量错误可以考虑把这两类合并成一个大类或者利用叶片位置先验侵蚀靠前缘、污垢无固定位置做后处理过滤。第二道关是模型在低分辨率输入下的表现。边缘部署的推理设备算力有限通常需要把输入短边从800降到512甚至416AP会掉但掉多少决定这个模型能不能上机。做法是把训练好的模型分别在短边512、640、800下做推理评估看每类AP的下降幅度。如果雷击裂纹这类细目标在512下AP掉了一半说明该场景必须保留高分辨率输入或者改用带FPN的轻量骨架来平衡速度与精度。量化到FP16甚至INT8是部署前性价比最高的一步但对裂纹这种细线目标INT8量化容易出现某些通道的信息损失量化后必须回到验证集重新评估不能直接相信量化工具的报告。第三道关是模型输出的后续处理衔接。检测模型输出的是缺陷框但巡检工单需要的是叶片编号缺陷类型损伤程度。损伤程度不能光靠框的面积判断因为拍摄距离不同会导致同一缺陷在画面里大小差异很大。常见做法是把检测框裁出来单独走一个轻量分类网络输出严重程度分级。这里有个小技巧把严重程度分级和缺陷检测放到同一个多任务模型里会让训练样本的需求量翻倍起步阶段还是先拆成两个独立模型缺陷检测专注召回分级模型专注判断两个模型各自调优整体流程更可控。回过头看从拿到COCO JSON标注到能部署上线最重要的步骤不是训练命令写得有多花哨而是前期的数据体检和每类指标拆解。我现在拿到任何COCO格式的叶片数据集一定先把标注统计和退化框检查跑一遍再决定anchor和输入分辨率这个习惯帮我省掉了大量后期调试时间。叶片缺陷检测从来不是有个数据集就能跑出好结果的活儿把数据、anchor、评估三个环节扣死七类缺陷的模型才立得住。希望这篇笔记对你在做的风机巡检方案有实际帮助也祝你的模型第一次跑评估就被雷击裂纹的高AP惊喜到。本文还有配套的精品资源点击获取
返回列表