不平衡数据处理:重采样与代价敏感学习
不平衡数据处理实战指南:重采样与代价敏感学习
在分类任务中,类别分布严重倾斜的数据集被称为不平衡数据。例如,在欺诈检测、罕见病诊断或客户流失预测中,少数类样本往往占比不足1%。直接在此类数据上训练模型会导致严重的偏向性:模型倾向于将所有样本预测为多数类,从而获得虚高的准确率,但完全丧失对少数类的识别能力。本教程将系统讲解两种核心应对策略:重采样技术与代价敏感学习。
为什么准确率会失效?
假设一个二分类问题,正样本(少数类)占1%,负样本占99%。一个永远预测负类的模型,准确率高达99%,但真正例率(召回率)为0。因此,评估不平衡数据模型必须使用精确率、召回率、F1值以及更稳健的AUC-ROC和PR曲线下面积。在动手处理数据前,请先设定正确的评估指标。
重采样技术:从数据分布入手
重采样通过调整训练集的样本数量来平衡类别,分为欠采样和过采样两大类。
1. 随机欠采样
从多数类中随机移除样本,使其数量与少数类匹配。
- 优点:简单快速,缩减数据集规模,缩短训练时间。
- 致命缺陷:可能丢弃大量含有重要信息的多数类样本,导致模型欠拟合。
- 适用场景:数据量极大且多数类内部冗余度高时。
2. 随机过采样
随机复制少数类样本,直至数量与多数类相当。
- 优点:保留所有原始信息。
- 致命缺陷:机械复制会导致模型对重复样本过拟合,尤其是决策边界过于具体,泛化能力极差。
3. SMOTE:合成少数类过采样技术
SMOTE(Synthetic Minority Over-sampling Technique)并非简单复制,而是在特征空间中创造新样本。工作原理如下:
- 对于每个少数类样本
x,找到它的k个最近邻(通常k=5)。 - 随机选择一个邻居
x_nn。 - 在
x和x_nn的连线上随机取一点,生成新样本:x_new = x + λ * (x_nn - x),其中λ是 [0,1] 之间的随机数。
from imblearn.over_sampling import SMOTE
sm = SMOTE(random_state=42)
X_resampled, y_resampled = sm.fit_resample(X_train, y_train)
变体优化:
- Borderline-SMOTE:仅对靠近决策边界的少数类样本进行合成,强化边界。
- ADASYN:根据学习难度自适应地生成样本,在更难分类的区域生成更多样本。
4. 结合欠采样与过采样
单纯使用SMOTE可能生成噪声,并导致多数类空间被“侵蚀”。此时可将SMOTE与欠采样结合:
- SMOTE + Tomek Links:SMOTE后,移除处于胶着状态的、互为最近邻的异类样本对(Tomek links),清理重叠区域。
- SMOTE + Edited Nearest Neighbors (ENN):生成样本后,若某样本的最近邻中有多数类,则删除该样本,进一步净化边界。
from imblearn.combine import SMOTETomek
smt = SMOTETomek(random_state=42)
X_res, y_res = smt.fit_resample(X_train, y_train)
重采样小结:所有重采样操作必须仅在训练集上执行,严禁在测试集或全局数据集上应用,否则会造成数据泄露,导致评估失真。
代价敏感学习:从模型决策层面入手
代价敏感学习不改变数据分布,而是为不同类型的分类错误赋予不同的惩罚代价。直觉上,将少数类误判为多数类的代价应远高于反向误判。
1. 类别权重法
大多数分类算法(如逻辑回归、SVM、树模型)都支持 class_weight 参数。设置为 'balanced',模型会自动按类别频率的倒数进行加权:weight_for_class_i = n_samples / (n_classes * n_samples_i)。
from sklearn.linear_model import LogisticRegression
# 自动平衡权重
clf = LogisticRegression(class_weight='balanced', random_state=42)
clf.fit(X_train, y_train)
手动指定权重可提供更精细的控制,例如设置 class_weight={0:1, 1:10} 意味着正样本误分的代价是负样本的10倍。
2. 在目标函数中嵌入代价矩阵
更一般化的代价敏感学习允许定义代价矩阵。设 C(i,j) 表示将真实类别 i 预测为 j 的代价。算法在训练时会最小化总代价期望而非简单的错误率。部分框架(如weka)原生支持代价矩阵,在Scikit-learn中可通过 sample_weight 为每个实例单独赋权,实现细粒度的代价控制。
3. 代价敏感的集成方法
集成学习可以与代价敏感思想深度结合:
- EasyEnsemble:对多数类进行多次欠采样,训练多个基分类器,集成输出结果,平衡了欠采样的信息损失。
- BalanceCascade:在每轮训练后,将已成功分类的多数类样本从后续训练集中移除,逐步聚焦于难以分类的样本。
- RUSBoost:在Boosting的每一轮迭代中,对多数类进行随机欠采样,再训练弱分类器,极其高效。
from imblearn.ensemble import EasyEnsembleClassifier
ee = EasyEnsembleClassifier(random_state=42)
ee.fit(X_train, y_train)
实战路线图:如何选择策略?
- 先定指标:确定业务关注的马修系数、F1或召回率阈值,切勿使用准确率。
- 数据量评估:
- 数据量足够大(数万级以上)→ 可尝试随机欠采样或EasyEnsemble。
- 数据量较小 → 采用SMOTE及其变体,配合交叉验证严防过拟合。
- 算法原生支持:优先检查所用模型是否支持
class_weight,这是零成本的好起点。 - 综合打法:常见最佳实践是SMOTE(生成样本)+ 代价敏感(模型加权)+ 集成学习的组合。例如,对XGBoost设置
scale_pos_weight同时输入SMOTE处理后的数据,常能取得稳健结果。 - 调参验证:采用分层交叉验证(StratifiedKFold),确保每折中类别比例一致,结合PR-AUC曲线选择最优阈值。
不平衡数据处理并非套用单一魔法公式。理解数据背后的业务代价,将重采样的数据增强能力与代价敏感的决策导向相结合,才能在真实场景中部署值得信赖的模型。