2. 数据集类别不平衡诊断:以病虫害识别为例

为什么先看分布,而不是先调模型

病虫害识别数据集的不平衡通常不是“某一类少一点”,而是少数病害类别样本极少、健康叶片与常见病害占据绝大多数。模型在交叉熵损失下会被头部类别主导,表现为总体准确率尚可,但长尾类别的召回率极低。诊断的第一步是把每个类别的样本数统计清楚,量化长尾程度,再决定重采样还是类别加权。

下面用一份教学示例数据说明完整流程。假设某番茄病虫害数据集共 8 个类别,统计结果如下(示例数据,非实测):

类别样本数占比与最大类比值
健康叶片420042.0%1.00
早疫病210021.0%0.50
晚疫病150015.0%0.36
叶霉病9009.0%0.21
白粉病6006.0%0.14
斑潜蝇危害4004.0%0.10
红蜘蛛危害2002.0%0.05
病毒病1001.0%0.02

最大类 4200、最小类 100,不平衡比约 42:1。按经验阈值划分:比值 ≥0.5 为头部,0.1~0.5 为中部,<0.1 为长尾,<0.05 为严重不平衡。据此红蜘蛛危害与病毒病属于需要重点处理的类别。

类别样本分布与不平衡分级(示例数据) 0 1000 2000 3000 4000 样本数 健康叶片 4200 早疫病 2100 晚疫病 1500 叶霉病 900 白粉病 600 斑潜蝇危害 400 红蜘蛛危害 200 病毒病 100 头部 ≥0.5 中部 0.1~0.5 长尾 <0.1 严重 <0.05 最大类 : 最小类 = 4200 : 100 ≈ 42 : 1

统计与分级的具体做法

统计本身不复杂,关键是统计口径要一致:按类别目录或标注文件中的类别字段计数,排除损坏图像与无标注样本,并记录每类的来源批次,避免同一植株的多张连拍图被当作独立样本而虚高数量。下面给出可直接运行的统计与分级脚本,输入为 labels.csv(两列:filepath,label),输出各类计数、占比、不平衡比与分级标签。

import csv
from collections import Counter

# 1. 读取并计数
counts = Counter()
with open("labels.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        counts[row["label"]] += 1

total = sum(counts.values())
max_c = max(counts.values())
min_c = min(counts.values())

# 2. 分级阈值(可按项目调整)
def grade(ratio):
    if ratio >= 0.5:  return "头部"
    if ratio >= 0.1:  return "中部"
    if ratio >= 0.05: return "长尾"
    return "严重不平衡"

# 3. 输出诊断表
print(f"总样本 {total},类别数 {len(counts)},不平衡比 {max_c/min_c:.1f}:1")
for label, c in counts.most_common():
    ratio = c / max_c
    print(f"{label:8s} {c:5d}  {c/total:6.1%}  比值 {ratio:.2f}  {grade(ratio)}")

# 4. 计算类别权重(逆频率,供加权损失使用)
weights = {label: total / (len(counts) * c) for label, c in counts.items()}
print("类别权重示例:", {k: round(v, 3) for k, v in weights.items()})

运行后可直接得到分级结果。以示例数据为例,病毒病权重约为 4200/(8×100)=5.25,健康叶片权重约为 0.125,两者相差 42 倍,这正是加权损失要补偿的量级。

如何判断影响程度并选择策略

不平衡比只是第一层信号,还要结合“绝对样本量”和“任务代价”判断。经验判断规则如下:

  • 长尾类绝对样本 ≥ 500 且比值 ≥0.05:优先用类别加权,改动小、不引入重复样本。
  • 长尾类绝对样本 100~500:加权 + 适度过采样(如复制 2~3 倍)组合使用,并配合强增强。
  • 长尾类绝对样本 <100:单纯重采样容易过拟合,应优先补充采集或使用迁移学习/小样本增强,加权只作为辅助。
  • 任务代价不对称(漏检病害代价高):即使比值不算极端,也应提高该类权重或降低其判定阈值,以召回率为优先指标。
诊断结论要落到可执行动作:对每个长尾类写明“当前样本数、分级、建议策略、验证指标”。验证时不要只看总体准确率,应分别报告宏平均召回率与长尾类单类召回率。
过采样只应作用于训练集,验证集与测试集必须保持原始分布,否则评估结果会被虚高。加权损失与过采样不建议同时大幅叠加,否则长尾类会被过度补偿而引发头部类性能下降。

完成上述统计与分级后,即可带着明确的类别清单进入下一步:对严重不平衡类别决定是补数据还是调权重,并用宏平均召回率与混淆矩阵验证策略是否真正改善了长尾类的识别效果。