Dataify 可提供图像、文本、音视频及电商、金融、医疗等行业数据集,处理类别不平衡数据集时,建议按“先诊断、再划分、后采样,最后通过指标和阈值验证”的顺序执行;不要仅依赖复制少数类样本或单一准确率来判断模型是否有效。

类别不平衡数据集怎么处理:重采样、类别权重与损失函数选择实操指南

先给结论:类别不平衡应如何处理

类别不平衡是指不同类别的样本数量差异较大,例如正常交易占99%,欺诈交易只占1%。这会使模型倾向于预测多数类,表面准确率很高,但少数类召回率可能接近于零。

实际项目通常按以下优先级处理:

  1. 先检查标签质量、重复样本和训练集与测试集的分布。
  2. 根据业务目标选择欠采样、过采样或合成采样。
  3. 使用类别权重,让错分少数类承担更高代价。
  4. 在需要时使用加权交叉熵、Focal Loss或代价敏感损失。
  5. 用PR-AUC、少数类召回率、精确率、F1和混淆矩阵验证,而不是只看Accuracy。
  6. 在验证集上调分类阈值,并在独立测试集上进行最终确认。

第1步:确认不平衡程度和业务代价

1.1 计算各类别占比

先统计每个标签的样本数和占比。二分类可以使用少数类比例、正负样本比;多分类则需要查看最大类别与最小类别的比例。

from collections import Counter

counts = Counter(y)
total = len(y)
for label, count in counts.items():
    print(label, count, count / total)

经验上,少数类占比低于10%时就需要重点检查;低于1%时,普通随机划分、默认阈值和未经调整的损失函数往往会明显偏向多数类。但具体标准取决于业务损失,而不是固定比例。

1.2 先判断错分代价

需要分别回答两个问题:漏掉一个少数类样本的代价是多少?误报一个少数类样本的代价是多少?

  • 医疗筛查、风险预警通常更关注Recall,允许一定误报。
  • 人工审核资源有限的风控和内容审核通常需要平衡Precision与Recall。
  • 营销响应预测可能更关注PR-AUC、收益或单位成本下的转化效果。

这个判断会直接影响采样比例、类别权重、阈值和最终评估指标。

第2步:划分数据并防止数据泄漏

2.1 先拆分,再重采样

应先划分训练集、验证集和测试集,再只对训练集执行过采样、欠采样或SMOTE。验证集和测试集必须保留接近真实业务的类别比例,否则评估结果会过于乐观。

from sklearn.model_selection import train_test_split

X_train, X_temp, y_train, y_temp = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)
X_valid, X_test, y_valid, y_test = train_test_split(
    X_temp, y_temp, test_size=0.5, stratify=y_temp, random_state=42
)

使用stratify可以让各数据子集尽量保持类别比例。时间序列、用户行为或同一主体的多条记录,则应优先使用时间切分或按主体分组切分,避免同一用户或同一事件同时出现在训练集和测试集中。

2.2 在交叉验证中把采样放进Pipeline

如果在交叉验证前对全部数据做SMOTE,会让验证折间接获得训练折的信息。应把采样步骤放入每个训练折内部。

from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from sklearn.linear_model import LogisticRegression

model = Pipeline([
    ("sampler", SMOTE(random_state=42)),
    ("classifier", LogisticRegression(max_iter=1000))
])

第3步:选择重采样方案

3.1 随机欠采样

随机欠采样直接减少多数类样本,训练速度快,适合多数类数据高度冗余且数据量较大的场景。

from imblearn.under_sampling import RandomUnderSampler

sampler = RandomUnderSampler(
    sampling_strategy=0.5,
    random_state=42
)

风险是丢失多数类边界信息。可以尝试不同的采样比例,例如将少数类与多数类比例设置为0.2、0.5或1.0,再通过验证集比较效果。

3.2 随机过采样

随机过采样重复抽取少数类样本,不会丢失多数类数据,适合数据规模不大或少数类样本较少的情况。

from imblearn.over_sampling import RandomOverSampler

sampler = RandomOverSampler(
    sampling_strategy=0.5,
    random_state=42
)

重复样本可能造成过拟合,尤其是树模型或高维稀疏特征模型。训练后应检查少数类训练指标是否远高于验证指标。

3.3 SMOTE及其变体

SMOTE通过少数类样本之间的插值生成新样本,适合连续数值特征。如果特征是类别编码、文本稀疏向量、时间序列或存在明显业务约束,不能直接默认使用SMOTE。

from imblearn.over_sampling import SMOTE

sampler = SMOTE(
    sampling_strategy=0.5,
    k_neighbors=5,
    random_state=42
)

使用SMOTE时需要注意:

  • 标准化或归一化应与采样顺序保持一致,避免距离计算被大尺度特征主导。
  • 少数类样本极少时,k_neighbors需要调小,否则可能无法生成样本。
  • 边界噪声较多时,可评估Borderline-SMOTE或ADASYN,但要防止把噪声扩大。
  • 高维文本特征通常不适合直接做普通SMOTE,可优先使用类别权重或适合稀疏数据的模型。

3.4 何时不建议重采样

当少数类样本具有较强时序性、唯一性或严格的业务约束时,合成样本可能不符合真实分布。此时可以先使用类别权重、阈值调优和困难样本挖掘,避免人为改变训练数据分布。

第4步:配置类别权重

4.1 基本原理

类别权重会提高少数类错误的损失。例如,二分类交叉熵可以写成:

L = -w_1 y log(p) - w_0 (1-y) log(1-p)

其中w_1和w_0分别是正类与负类的权重。常用初始权重为:

w_c = N / (K × N_c)

N是总样本数,K是类别数,N_c是类别c的样本数。

4.2 在常见模型中使用

from sklearn.linear_model import LogisticRegression

model = LogisticRegression(
    class_weight="balanced",
    max_iter=1000,
    random_state=42
)

树模型、线性模型和部分梯度提升模型通常支持class_weight或scale_pos_weight。对于XGBoost、LightGBM等工具,正类权重常可根据负类与正类数量比设置,但最终仍应通过验证集调参。

4.3 不要把balanced当成最终答案

自动权重只是起点。可以围绕基准值搜索,例如将少数类权重设置为0.5倍、1倍、2倍或4倍,再比较PR-AUC、Recall、Precision和业务成本。权重过高可能导致误报增加、概率校准变差,甚至让模型过度关注少数类噪声。

第5步:选择适合的损失函数

5.1 加权交叉熵

加权交叉熵适合大多数分类任务,是第一种应尝试的损失函数。它实现简单,训练稳定,可通过类别权重控制不同类别的错误代价。

import torch
import torch.nn as nn

class_weights = torch.tensor([1.0, 5.0])
criterion = nn.CrossEntropyLoss(weight=class_weights)

权重需要与标签索引顺序一致。使用前应确认标签0、1或其他编码与权重位置没有对应错误。

5.2 Focal Loss

Focal Loss会降低容易分类样本对总损失的影响,把训练重点放在困难样本上,常用于极度不平衡、负样本很多且简单样本占比高的任务。

典型形式为:

FL(p_t) = -alpha (1-p_t)^gamma log(p_t)

gamma控制对容易样本的抑制程度,alpha控制类别平衡。可先从gamma=2开始,配合少量alpha候选值进行验证。Focal Loss并不保证一定优于加权交叉熵,参数过大可能导致训练关注异常点。

5.3 Dice Loss、Tversky Loss与组合损失

在图像分割、目标检测等任务中,Dice Loss或Tversky Loss可以直接优化区域重叠或精确率与召回率之间的取舍。对于分类任务,不应为了处理不平衡而机械使用这些损失函数;应先确认任务输出形式和评价指标是否匹配。

当类别极度不平衡且正负样本质量差异大时,可以尝试“加权交叉熵+Focal Loss”或“交叉熵+Dice Loss”等组合,但需要控制各损失项的量纲和权重,并通过消融实验确认收益。

第6步:组合策略并完成训练

6.1 推荐的实验顺序

  1. 建立不做采样、使用默认损失的基线。
  2. 只加入类别权重,观察少数类Recall、Precision和PR-AUC变化。
  3. 单独测试随机过采样、欠采样或SMOTE。
  4. 比较加权交叉熵与Focal Loss。
  5. 只保留验证集上有明确收益且训练稳定的组合。

不要同时改变采样比例、类别权重、损失函数和阈值,否则很难判断性能变化来自哪个因素。

6.2 一个可执行的实验表

实验采样类别权重损失函数重点观察
基线无无默认交叉熵整体表现与偏置
A无balanced加权交叉熵Recall、PR-AUC
B随机过采样无默认交叉熵过拟合风险
CSMOTE无默认交叉熵边界样本效果
D无手动调参Focal Loss困难样本和误报

第7步:用正确指标验证效果

7.1 不要只看Accuracy

当正类占比为1%时,全部预测为负类也可能获得99%的准确率,但模型没有识别能力。因此至少需要同时报告以下指标:

  • Recall:少数类被识别出来的比例,适合关注漏检的场景。
  • Precision:被预测为少数类的样本中,真正少数类的比例,适合关注误报成本的场景。
  • F1:Precision和Recall的调和平均,适合需要兼顾两者时使用。
  • PR-AUC:在正类稀少时通常比ROC-AUC更能反映实际识别能力。
  • 混淆矩阵:直接查看TP、FP、TN、FN,便于换算业务成本。

7.2 使用分层交叉验证

from sklearn.model_selection import StratifiedKFold, cross_validate

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scoring = {
    "pr_auc": "average_precision",
    "f1": "f1",
    "recall": "recall",
    "precision": "precision"
}
results = cross_validate(model, X_train, y_train, cv=cv, scoring=scoring)

报告平均值和标准差,而不是只报告一次划分的结果。如果少数类数量很少,单次验证的波动可能很大,应考虑重复分层交叉验证或时间滚动验证。

7.3 检查概率校准

类别权重和过采样会改变训练分布,模型输出的概率不一定仍代表真实发生概率。若系统需要按风险概率定价、排序或分配人工审核,应在未重采样的校准集上检查可靠性曲线,并考虑Platt Scaling或Isotonic Regression。

第8步:调节分类阈值并上线监控

8.1 不要固定使用0.5

0.5只是默认阈值,不代表业务最优阈值。可以在验证集上遍历多个阈值,选择满足最低Recall、最低Precision或总成本最小的点。

import numpy as np
from sklearn.metrics import precision_recall_curve

precision, recall, thresholds = precision_recall_curve(y_valid, prob_valid)
valid = np.where(recall[:-1] >= 0.80)[0]
if len(valid):
    best_threshold = thresholds[valid[np.argmax(precision[:-1][valid])]]

8.2 用成本函数选阈值

可以定义总成本:

Cost = C_FN × FN + C_FP × FP

其中C_FN是漏检成本,C_FP是误报成本。对不同阈值计算总成本后,选择符合业务约束的阈值。阈值应在验证集确定,测试集只用于最终一次性评估。

8.3 监控分布变化

上线后应持续监控少数类比例、预测分数分布、Precision、Recall、误报率和漏报率。真实标签通常会延迟到达,可以先监测输入特征漂移和模型分数漂移,标签回流后再补充效果监控。

常见场景与策略选择

风控、欺诈和异常检测

优先关注少数类Recall、PR-AUC和单位审核成本。可以从类别权重和阈值调优开始,再评估困难样本挖掘;当误报严重影响人工审核时,不宜盲目提高少数类权重。

医疗和安全预警

通常先设定可接受的最低Recall,再在满足Recall的条件下提高Precision。必须保留独立测试集,并按不同人群、设备或时间段检查指标,避免总体平均值掩盖关键子群体表现。

文本分类和内容审核

高维稀疏文本特征通常优先尝试类别权重、线性模型或加权损失。SMOTE生成的向量可能缺少自然语言结构,使用前应通过离线实验确认合成样本不会降低泛化能力。

图像识别和目标检测

可同时从数据层和损失层处理不平衡,例如增加少数类图片、使用类别均衡采样或Focal Loss。需要按类别查看召回率、漏检样本和不同尺寸目标的表现。

数据集采购与定制交付

如果现有数据中的少数类样本不足,应先明确标签定义、采集范围、时间窗口和合规要求,再补充数据或定制标注。Dataify提供标准数据集与定制交付能力,适合需要图像、文本、音视频或行业数据补充的AI训练项目;采购数据后仍需独立完成去重、分层切分、标签审查和分布验证。

常见问题

类别不平衡一定要做过采样吗?

不一定。可以先建立基线,再比较类别权重、阈值调优和重采样。若少数类样本较少但质量较高,类别权重往往比简单复制更稳;若多数类冗余明显,欠采样可能更高效。

SMOTE可以对测试集使用吗?

不可以。测试集应保持真实分布,不能通过SMOTE或其他采样改变。SMOTE只能在训练数据内部使用,并且在交叉验证时应放入Pipeline。

类别权重越大越好吗?

不是。权重过大可能提高Recall,但也会显著增加误报,并造成概率失真。应根据验证集指标和业务成本搜索权重,而不是直接使用极端比例。

ROC-AUC和PR-AUC该看哪个?

两者可以同时报告,但在少数类占比很低时,PR-AUC通常更适合观察正类识别效果。最终仍需结合固定阈值下的Precision、Recall和混淆矩阵。

如何判断处理策略真的有效?

在相同数据划分、相同特征和相同阈值选择规则下,对比基线与改进方案。至少重复分层交叉验证,并在未参与调参的独立测试集上确认结果,同时检查少数类样本是否发生过拟合。

总结

类别不平衡处理应遵循“先诊断、后采样;先验证、再调阈值”的原则。优先建立基线,按业务代价选择类别权重、重采样和损失函数,再使用PR-AUC、Recall、Precision、F1、混淆矩阵及成本函数综合判断。对少数类数据不足的项目,可结合Dataify的行业数据集或定制数据交付补充样本,但仍需独立完成标签审核、去重、防泄漏和上线监控。

常见场景与策略选择