类别不平衡
少数类任务中准确率可能严重误导,需要合适指标、阈值、权重和严格的折内重采样。
学习前需要掌握
背景与问题
当目标类别很少时,整体准确率可能掩盖几乎所有重要错误。一个总是猜多数类的模型,既可能得到很高分数,也可能完全没有找到真正关心的样本。
不平衡问题还涉及阈值、代价与数据采集,不能只靠“把两类数量变相同”就认为任务已经解决。
概念定义
类别不平衡描述训练或评价中各类别支持数明显不同。是否构成问题,取决于目标类别、错误成本和模型评价,而不是存在一个放之四海皆准的比例界限。
样本加权、重采样、阈值调整与数据补充是不同工具:它们分别改变训练目标、采样分布、行动规则或信息覆盖,不能互相混称。
直观理解
在大多数东西都正常的场景,只说“我判断正常”就可能很准确。但如果任务是寻找少数异常,应该看它究竟找回了多少、误报了多少,以及复核负担多大。
99% 准确率可以对应零召回
构造 1,000 条记录,其中 990 条为负类、10 条为正类。全部预测负类的准确率是 0.99,但正类召回率为 0/10=0,十个目标一个也没有找到。
若按 n/(类别数×该类数量) 计算平衡权重,负类约为 0.5051、正类为 50。这个计算只说明训练权重,不是使用权重后的真实模型成绩,更不能代替独立评价。
核心原理
评价应报告每类支持数与混淆矩阵,再结合 Recall、Precision、PR 曲线和宏平均等指标。没有预测出任何正类时,Precision 的数学分母为零,应说明库采用的约定,而不是把它当成一个自然有定义的比率。
class_weight='balanced' 常根据训练类别频率提高少数类样本的损失权重,它并不生成新信息,也不保证概率已经校准。使用后仍需按验证协议选择阈值和模型。
重采样必须只作用于训练范围或每个训练折,不能先对全数据复制少数样本再划分,否则近乎相同的记录可能泄漏到验证中。
分层能帮助保持类别比例,但不能解决少数类太少、标签错误、实体重复和分布变化。增加代表性少数类数据,有时比重复已有几条记录更重要。
数学表达
Precision
预测为正的样本中有多少是真的。
变量含义
变量定义与本页概念定义一致,代码中的同名变量保持相同语义。
计算步骤
- 1统计每类数量。
- 2明确误报和漏报成本。
- 3选择主指标。
- 4建立 Dummy 基线。
代码对应位置
示例 1核对多数类基线与平衡权重:用固定类别计数说明为什么不能只看准确率。
Recall
真实正类中找到了多少。
变量含义
变量定义与本页概念定义一致,代码中的同名变量保持相同语义。
计算步骤
- 1统计每类数量。
- 2明确误报和漏报成本。
- 3选择主指标。
- 4建立 Dummy 基线。
代码对应位置
示例 1核对多数类基线与平衡权重:用固定类别计数说明为什么不能只看准确率。
F1
Precision 与 Recall 的调和平均。
变量含义
变量定义与本页概念定义一致,代码中的同名变量保持相同语义。
计算步骤
- 1统计每类数量。
- 2明确误报和漏报成本。
- 3选择主指标。
- 4建立 Dummy 基线。
代码对应位置
示例 1核对多数类基线与平衡权重:用固定类别计数说明为什么不能只看准确率。
计算与实现步骤
- 统计每类数量。
- 明确误报和漏报成本。
- 选择主指标。
- 建立 Dummy 基线。
- 在训练折内使用 class_weight 或重采样。
- 在验证数据上选阈值。
- 最终测试并分群分析。
代码实现
示例 1
核对多数类基线与平衡权重
example_01.py用途:用固定类别计数说明为什么不能只看准确率。
import numpy as np
from sklearn.utils.class_weight import compute_class_weight
y = np.array([0] * 990 + [1] * 10)
weights = compute_class_weight(class_weight="balanced", classes=np.array([0, 1]), y=y)
print(f"majority_accuracy={np.mean(y == 0):.4f}")
print("minority_recall=0.0000")
print(f"class_weights={weights[0]:.4f},{weights[1]:.4f}")代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
用固定类别计数说明为什么不能只看准确率。
y- 目标标签或连续目标值。
mean- 训练特征逐列均值。
Step 1 · 1–7 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import numpy as np
from sklearn.utils.class_weight import compute_class_weight
y = np.array([0] * 990 + [1] * 10)
weights = compute_class_weight(class_weight="balanced", classes=np.array([0, 1]), y=y)
print(f"majority_accuracy={np.mean(y == 0):.4f}")
print("minority_recall=0.0000")
print(f"class_weights={weights[0]:.4f},{weights[1]:.4f}")代码与数学原理
减去训练均值并除以训练标准差,对应代码中的 mean/std 或 StandardScaler。
预期输出或运行结果
majority_accuracy=0.9900 minority_recall=0.0000 class_weights=0.5051,50.0000
常见错误 · 4 条
- 不要在测试集上调用 fit 或 fit_transform;测试集只能复用训练参数。
- 只报告 Accuracy
- 先对完整数据重采样
- 用测试集选阈值
实际应用
- 欺诈检测
- 疾病筛查
- 故障告警
常见错误
- 只报告 Accuracy
- 先对完整数据重采样
- 用测试集选阈值
推荐学习资料
参考库不会生成虚假资源或无效外部链接。