类别不平衡重采样

wen IT资讯 26

本文目录导读:

类别不平衡重采样

  1. 核心重采样策略
  2. 为什么不用简单随机过采样?
  3. 实现工具推荐
  4. 选择策略的实战建议
  5. 代码快速参考(sklearn风格)
  6. 需要警惕的陷阱
  7. 当重采样效果不好时?试试算法层方法

类别不平衡(Class Imbalance)是分类任务中的常见问题,即数据集中不同类别的样本数量差异巨大(例如欺诈检测中正样本仅占1%)。重采样是解决该问题的核心预处理手段。

以下是针对类别不平衡的重采样方法总结,按数据操作层面实现难度分类:

核心重采样策略

方法 核心思想 优点 缺点
随机欠采样 随机删除多数类样本,使与少数类数量持平。 简单、运行快。 可能丢失重要信息,导致模型欠拟合。
随机过采样 随机复制少数类样本,使与多数类数量持平。 简单、不丢失多数类信息。 容易导致过拟合(模型记住了重复样本)。
SMOTE 少数类样本之间插值生成新样本(而非简单复制)。 缓解过拟合,生成合理的新样本。 可能生成噪音(尤其在边界区域),对高维数据效果下降。
ADASYN 自适应合成采样,根据样本密度决定生成数量。 更关注难分类的少数类样本(密度低处)。 更容易受到噪声影响。
Tomek Links 清理“链接”样本(互为最近邻且类别不同),常用于欠采样。 去除边界噪音,使决策边界更清晰。 可能删掉有用信息。
Edited Nearest Neighbors 删除多数类中被邻居误判的样本。 比Tomek更彻底地清洗边界。 计算量大。
SMOTE + Tomek 先SMOTE过采样,再用Tomek清理模糊区域。 结合过采样与欠采样优点,减少噪声。 参数调优较复杂。
SMOTE + ENN 先SMOTE过采样,再用ENN清理。 比Tomek更激进,适用于噪音大时。 同上。

为什么不用简单随机过采样?

  • 简单复制导致模型在少数类样本上过拟合:模型会记住特征的精确重复,而不是学习泛化特征。
  • SMOTE的原理是在两个少数类样本的连线上随机插值: [ x_{new} = xi + \lambda \times (x{zi} - x_i), \quad \lambda \in [0,1] ] 这样生成的样本具有多样性,且保留了特征空间的连续性。

实现工具推荐

首选:imbalanced-learn(Python 库)

from imblearn.over_sampling import SMOTE, RandomOverSampler, ADASYN
from imblearn.under_sampling import RandomUnderSampler, TomekLinks, EditedNearestNeighbours
from imblearn.combine import SMOTETomek, SMOTEENN
# 应用示例
smote = SMOTE(sampling_strategy='auto', random_state=42)  # 'auto'自动平衡到多数类数量
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)

注意: 务必在训练集上重采样,测试集保持原始分布不变,否则会严重高估模型性能(数据泄露)。

在Scikit-learn Pipeline中的使用

imbalanced-learn提供了子类Pipeline,确保重采样仅作用于训练阶段,不污染验证/测试集。

from imblearn.pipeline import Pipeline
pipe = Pipeline([
    ('sampling', SMOTE()),
    ('clf', RandomForestClassifier())
])

选择策略的实战建议

场景 推荐方案 理由
总数据量很大 (>=10万) 随机欠采样Tomek 速度快,删除部分多数类对整体影响小。
数据量中等 (几千~几万) SMOTEADASYN 平衡效果与过拟合风险的最佳平衡点。
少数类样本非常稀少 (<50) SMOTE + ENN 先生成足够样本,再清除边界噪音。
特征维度非常高 (千维以上) Borderline-SMOTENoise-robust SMOTE 避免SMOTE在高维空间中产生扭曲的插值。
目标是检测异常/稀有事件 集成+重采样 例如使用 EasyEnsemble (训练多个欠采样子集集成) 或 BalanceCascade
极度不平衡 (1:1000以上) SMOTE + 集成学习Data Boost 单一重采样可能不够,结合算法层调整(如代价敏感学习)。

代码快速参考(sklearn风格)

from collections import Counter
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, recall_score
# 假设 X, y 存在严重不平衡
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"原始训练集分布: {Counter(y_train)}")
# 输出: Counter({0: 1000, 1: 50})
# 方法1:SMOTE
from imblearn.over_sampling import SMOTE
sm = SMOTE(random_state=42)
X_res, y_res = sm.fit_resample(X_train, y_train)
print(f"SMOTE后分布: {Counter(y_res)}")
# 方法2:SMOTE + Tomek
from imblearn.combine import SMOTETomek
smt = SMOTETomek(random_state=42)
X_res, y_res = smt.fit_resample(X_train, y_train)
# 训练模型
model = RandomForestClassifier(random_state=42)
model.fit(X_res, y_res)
# 评估(测试集不变!)
y_pred = model.predict(X_test)
print(f"召回率: {recall_score(y_test, y_pred):.2f}")

需要警惕的陷阱

  1. 绝对不要在重采样前划分训练/测试集。
  2. SMOTE不适用于图像、文本、序列数据(特征空间非欧几里得),对这些数据应使用数据增强(旋转、裁剪、同义词替换等)。
  3. 标签噪声敏感:如果少数类中有错误样本,SMOTE会放大噪音,建议先用ENN清理。
  4. 过采样可能引入偏倚:如果生成样本远离真实分布,模型泛化能力反而下降,此时应优先考虑代价敏感学习(调整class_weight参数)。

当重采样效果不好时?试试算法层方法

如果重采样带来的提升有限,考虑算法层面的修改:

  • 代价敏感学习:给少数类样本分配更高的误分类代价。
    # sklearn中直接设置
    model = RandomForestClassifier(class_weight='balanced')
  • 集成方法:如 EasyEnsembleBalanceCascadeimbalanced-learn提供)。
  • 单类分类:如果异常检测目标极稀有,使用 One-Class SVM / Isolation Forest

重采样是解决不平衡的“快修”路径,而代价敏感学习和算法改进是更根本的解决方案,实际项目中通常先用SMOTE快速提升召回率,若效果不够再结合其他方法。

上一篇Focal Loss

下一篇长尾分布处理

抱歉,评论功能暂时关闭!