020、数据集质量评估指标体系:标注一致性、框尺寸分布、类别均衡度量化方法
020、数据集质量评估指标体系标注一致性、框尺寸分布、类别均衡度量化方法上个月调YOLOv6的一个交通场景检测模型训练loss降得漂亮mAP也刷到0.78结果一上实车测试夜间场景直接崩了。排查了两天最后发现是训练集里夜间样本的标注框普遍偏大白天样本的框却紧贴目标边缘——标注一致性出了问题。这种坑光看mAP根本看不出来。数据集质量评估不是跑个脚本算几个数就完事。你得知道哪些指标真正影响模型行为哪些只是数字游戏。下面直接上干货都是我在YOLOv8/v11/v6项目里踩过的实坑。标注一致性最容易忽视的“隐形杀手”标注一致性说白了就是同一个目标不同标注员或者同一标注员不同批次画出来的框差异有多大。YOLO系列对框的回归精度极其敏感尤其是小目标。量化方法IoU一致性检验对同一张图让两个标注员分别标注计算每对标注框的IoU。理想情况是IoU 0.85。如果大量框的IoU落在0.6-0.8之间说明标注标准不统一。defcompute_label_consistency(bbox_a,bbox_b):# 这里踩过坑bbox格式必须是[x1,y1,x2,y2]别用xywh直接算x1max(bbox_a[0],bbox_b[0])y1max(bbox_a[1],bbox_b[1])x2min(bbox_a[2],bbox_b[2])y2min(bbox_a[3],bbox_b[3])inter_areamax(0,x2-x1)*max(0,y2-y1)area_a(bbox_a[2]-bbox_a[0])*(bbox_a[3]-bbox_a[1])area_b(bbox_b[2]-bbox_b[0])*(bbox_b[3]-bbox_b[1])union_areaarea_aarea_b-inter_area# 别这样写直接返回inter_area/union_area当union_area0时会除零returninter_area/union_areaifunion_area0else0.0实际项目中我一般抽5%-10%的样本做双标注计算平均IoU。低于0.8的类别模型训练时AP波动会很大。YOLOv11的loss设计对边界回归更敏感一致性差的话小目标AP直接腰斩。更狠的指标边界偏移标准差对每个标注框计算四条边相对于目标中心的偏移量然后统计标准差。这个指标能暴露“标注员习惯性把框画大或画小”的问题。defedge_offset_std(bboxes,img_w,img_h):# 归一化到[0,1]再算别用像素值不同分辨率下没法比offsets[]forbboxinbboxes:x1,y1,x2,y2bbox cx,cy(x1x2)/2/img_w,(y1y2)/2/img_h left_offset(cx-x1/img_w)/cx# 左边界偏移比例right_offset(x2/img_w-cx)/cx top_offset(cy-y1/img_h)/cy bottom_offset(y2/img_h-cy)/cy offsets.extend([left_offset,right_offset,top_offset,bottom_offset])returnnp.std(offsets)这个值超过0.15基本可以判定标注标准需要重新对齐。YOLOv6的anchor-free设计对边界偏移尤其敏感我见过一个项目因为这个指标0.2导致模型在边缘目标上漏检率飙升30%。框尺寸分布决定anchor设计的命门YOLOv8/v11虽然用了anchor-free但框尺寸分布仍然直接影响特征金字塔各层的分配策略。YOLOv6的anchor-based设计就更不用说了anchor尺寸和框尺寸分布不匹配训练直接崩。量化方法面积-长宽比二维分布别只看平均面积那玩意儿骗人。要画二维直方图横轴是归一化面积相对于图像面积纵轴是长宽比。defbbox_size_distribution(bboxes,img_w,img_h):# 这里踩过坑直接用像素面积不同分辨率下分布完全不一样areas[]ratios[]forbboxinbboxes:w(bbox[2]-bbox[0])/img_w h(bbox[3]-bbox[1])/img_h areas.append(w*h)ratios.append(w/hifh0else0)# 关键看面积分布是否有明显的多峰有的话说明目标尺度差异大# 别这样写只算mean和std会掩盖小目标和大目标共存的情况returnnp.array(areas),np.array(ratios)我一般把面积分成三个区间0.01小目标、0.01-0.1中目标、0.1大目标。如果某个区间占比超过60%说明目标尺度单一模型泛化到其他尺度会出问题。YOLOv11的P2/P3/P4/P5层分配策略需要根据这个分布来调整。更实用的指标尺度覆盖度计算每个尺度区间内目标数量的变异系数CV。CV 0.8说明某个尺度严重缺失模型在那个尺度上的特征提取能力会退化。defscale_coverage_score(areas,bins[0,0.01,0.1,1.0]):# 别这样写直接用np.histogram要处理边界情况counts,_np.histogram(areas,binsbins)# 去掉空区间否则CV会无穷大non_zerocounts[counts0]iflen(non_zero)2:return0.0# 只有一个尺度覆盖度极差cvnp.std(non_zero)/np.mean(non_zero)return1.0-min(cv,1.0)# 归一化到[0,1]越大越好这个分数低于0.3建议重新采样或做尺度增强。YOLOv6的anchor设计可以直接用这个分数来指导anchor尺寸的聚类初始化。类别均衡度长尾分布的量化武器类别不均衡是老生常谈但很多人只算个样本数就完事。真正影响模型训练的是“有效样本数”和“梯度贡献度”。量化方法有效样本数Effective Number传统方法用样本数占比但忽略了类别间的相似性。有效样本数考虑了特征空间的重叠度。defeffective_number(sample_counts,beta0.999):# 这里踩过坑beta取值很关键0.9-0.999之间取决于数据量# 别这样写直接用1/beta要保证有效样本数不超过实际样本数effective[]forninsample_counts:e_n(1-beta**n)/(1-beta)effective.append(e_n)returnnp.array(effective)用有效样本数算出的类别权重比直接用样本数倒数更鲁棒。YOLOv8的class_loss_weight设置我一般用这个值做归一化。更敏感的指标梯度贡献度Gradient Contribution Ratio每个类别在训练过程中产生的梯度占总梯度的比例。这个指标能暴露“某个类别虽然样本少但loss大反而主导了梯度更新”的问题。defgradient_contribution(losses_per_class,class_counts):# 别这样写直接用loss均值要加权样本数total_gradnp.sum(losses_per_class)ratioslosses_per_class/total_grad# 理想情况每个类别的梯度占比接近样本占比sample_ratioclass_counts/np.sum(class_counts)# 用KL散度衡量偏差kl_divnp.sum(sample_ratio*np.log(sample_ratio/(ratios1e-8)))returnkl_divKL散度超过0.5说明梯度分配严重失衡。YOLOv11的focal loss虽然能缓解但治标不治本。我遇到过的一个案例某个类别样本只占2%但梯度贡献度达到15%导致模型过拟合到这个类别上其他类别AP全掉。综合评估别只看单个指标这三个指标不是孤立的。标注一致性差会导致框尺寸分布失真框尺寸分布异常又会放大类别不均衡的影响。我一般建一个综合评分矩阵defdataset_quality_score(consistency,scale_coverage,class_balance):# 这里踩过坑直接加权平均会掩盖短板# 用几何平均任何一个指标差都会拉低总分scores[consistency,scale_coverage,class_balance]# 别这样写直接np.mean要处理0值scores[max(s,0.01)forsinscores]# 避免0值导致几何平均为0returnnp.exp(np.mean(np.log(scores)))这个分数低于0.5建议先修数据再训模型。我自己的经验是标注一致性权重可以给高一点因为它是其他两个指标的基础。个人经验性建议别信mAP的鬼话。mAP高不代表数据质量好它只能告诉你模型在测试集上表现如何但测试集本身可能就有问题。我见过mAP 0.85的项目标注一致性只有0.6换了个场景直接崩。标注一致性要定期抽检。别等模型训完了才发现。我一般每标注1000张图抽100张做双标注算IoU一致性。低于0.8就停训先修标注标准。框尺寸分布要跟anchor设计联动。YOLOv6用anchor-based框尺寸分布直接决定anchor聚类结果。YOLOv8/v11虽然anchor-free但特征金字塔的分配策略仍然依赖尺度分布。我习惯在数据预处理阶段就做尺度重采样让分布更均匀。类别均衡度要关注梯度贡献。样本数少但loss大的类别往往是难样本但也是过拟合的源头。我一般用有效样本数做class_weight同时配合数据增强而不是简单过采样。工具链要自动化。别每次手动跑脚本。我写了个pipeline每次数据更新自动跑这三个指标生成报告。低于阈值直接报警省得后面返工。最后说一句数据质量评估不是一次性的是持续迭代的过程。模型训得不好90%的情况是数据有问题剩下10%才是模型结构或超参数。先把数据搞干净再谈改进。