1. 不平衡分类问题概述

在机器学习实践中,分类问题是最常见的预测建模任务之一。传统分类算法通常假设训练数据集中各个类别的样本数量大致相当,然而现实世界的数据往往呈现出严重的类别分布不平衡现象。这种类别样本数量差异悬殊的情况,我们称之为"不平衡分类问题"。

1.1 什么构成了不平衡分类

不平衡分类问题通常出现在以下场景中:

  • 医疗诊断中的罕见病识别(健康样本远多于患病样本)
  • 金融欺诈检测(正常交易数量远高于欺诈交易)
  • 工业设备故障预测(正常运行数据远多于故障数据)
  • 网络入侵检测(正常流量远多于攻击流量)

从技术角度看,当少数类与多数类的样本比例达到1:100、1:1000甚至更高时,就构成了严重的不平衡分类问题。这种情况下,传统机器学习算法往往表现不佳,因为它们优化的目标函数通常假设类别分布均衡。

1.2 不平衡带来的挑战

类别不平衡会导致模型训练面临几个核心问题:

  1. 评估指标失真 :准确率等传统指标在不平衡数据上会给出误导性结果。例如,在99:1的数据分布中,一个总是预测多数类的模型就能获得99%的准确率。
  2. 决策边界偏移 :算法会倾向于优化多数类的分类性能,导致决策边界向少数类方向偏移。
  3. 样本信息不足 :少数类样本数量过少,模型难以学习到有效的特征表示。

提示:在实际项目中,当少数类占比低于20%时,就需要考虑采用专门的不平衡数据处理技术。

2. 不平衡分类的核心解决策略

2.1 数据层面的处理方法

2.1.1 过采样技术

过采样通过增加少数类样本来平衡数据集,最常用的方法是SMOTE(Synthetic Minority Over-sampling Technique)。SMOTE的工作原理是:

  1. 对每个少数类样本,找到其k个最近邻
  2. 在这些邻居之间随机插值生成新样本
  3. 将合成样本加入训练集

SMOTE的Python实现示例:

from imblearn.over_sampling import SMOTE

smote = SMOTE(sampling_strategy='auto', k_neighbors=5)
X_resampled, y_resampled = smote.fit_resample(X, y)
2.1.2 欠采样技术

欠采样通过减少多数类样本来平衡数据集,常见方法包括:

  • 随机欠采样:随机删除多数类样本
  • Tomek Links:移除边界附近的多数类样本
  • Cluster Centroids:对多数类进行聚类后保留聚类中心

欠采样的主要风险是可能丢失重要信息,因此更适合数据量非常大的场景。

2.2 算法层面的改进方法

2.2.1 代价敏感学习

代价敏感学习通过为不同类别的误分类分配不同的惩罚权重。以逻辑回归为例:

from sklearn.linear_model import LogisticRegression

# 少数类的权重是多数类的100倍
model = LogisticRegression(class_weight={0:1, 1:100})
model.fit(X_train, y_train)
2.2.2 阈值移动

传统分类器使用0.5作为决策阈值,在不平衡数据中可以调整这个阈值:

from sklearn.metrics import precision_recall_curve

precision, recall, thresholds = precision_recall_curve(y_true, y_scores)
# 根据业务需求选择最佳阈值
optimal_threshold = thresholds[np.argmax(2*precision*recall/(precision+recall))]

2.3 集成学习方法

2.3.1 Balanced Random Forest

通过在每棵决策树的构建过程中进行欠采样来平衡类别分布:

from imblearn.ensemble import BalancedRandomForestClassifier

brf = BalancedRandomForestClassifier(n_estimators=100, sampling_strategy='auto')
brf.fit(X_train, y_train)
2.3.2 EasyEnsemble

通过多次对多数类进行子采样并分别训练分类器,最后集成结果:

from imblearn.ensemble import EasyEnsembleClassifier

ee = EasyEnsembleClassifier(n_estimators=10)
ee.fit(X_train, y_train)

3. 评估指标选择与实践

3.1 传统指标的局限性

在不平衡分类问题中,准确率(Accuracy)是一个具有误导性的指标。假设数据分布为99:1:

  • 一个总是预测多数类的模型准确率为99%
  • 但这对识别少数类毫无帮助

3.2 推荐使用的评估指标

3.2.1 混淆矩阵衍生指标
from sklearn.metrics import classification_report

print(classification_report(y_true, y_pred, target_names=['多数类', '少数类']))

重点关注:

  • 召回率(Recall):模型找出少数类样本的能力
  • 精确率(Precision):模型预测为少数类的样本中实际为少数类的比例
  • F1-score:召回率和精确率的调和平均
3.2.2 ROC AUC与PR AUC
  • ROC AUC:衡量模型在不同阈值下区分两类的能力
  • PR AUC:在不平衡数据中通常比ROC AUC更具参考价值
from sklearn.metrics import roc_auc_score, average_precision_score

roc_auc = roc_auc_score(y_true, y_scores)
pr_auc = average_precision_score(y_true, y_scores)

3.3 业务导向的评估

最终评估应结合具体业务场景:

  • 欺诈检测:可能更关注高召回率(尽可能捕捉所有欺诈)
  • 医疗诊断:可能更关注高精确率(避免误诊带来的恐慌)

4. 实战案例:信用卡欺诈检测

4.1 数据集准备

使用Kaggle信用卡欺诈数据集:

  • 总样本数:284,807
  • 欺诈样本占比:0.172%
  • 极度不平衡场景
import pandas as pd
from sklearn.model_selection import train_test_split

data = pd.read_csv('creditcard.csv')
X = data.drop('Class', axis=1)
y = data['Class']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y)

4.2 模型构建与比较

4.2.1 基准模型(无处理)
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier()
rf.fit(X_train, y_train)
# 测试集Recall: 0.71
4.2.2 SMOTE过采样
from imblearn.pipeline import make_pipeline

pipeline = make_pipeline(
    SMOTE(sampling_strategy=0.1),
    RandomForestClassifier()
)
pipeline.fit(X_train, y_train)
# 测试集Recall: 0.83
4.2.3 代价敏感学习
rf_cs = RandomForestClassifier(class_weight='balanced_subsample')
rf_cs.fit(X_train, y_train)
# 测试集Recall: 0.85

4.3 模型优化与阈值调整

通过PR曲线寻找最佳操作点:

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

y_scores = rf_cs.predict_proba(X_test)[:,1]
precision, recall, thresholds = precision_recall_curve(y_test, y_scores)

plt.plot(recall, precision)
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.show()

# 选择使F1最大的阈值
f1_scores = 2*precision*recall/(precision+recall)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx]

5. 常见问题与解决方案

5.1 过采样导致的过拟合

问题现象 :训练集表现很好,但测试集表现大幅下降
解决方案

  • 使用SMOTE变种如Borderline-SMOTE或ADASYN
  • 结合交叉验证评估模型泛化能力
  • 添加正则化项控制模型复杂度

5.2 欠采样丢失重要信息

问题现象 :模型对多数类的识别能力下降
解决方案

  • 使用集成欠采样方法如EasyEnsemble
  • 采用基于聚类的欠采样保留多数类分布特征
  • 结合过采样和欠采样(如SMOTEENN)

5.3 类别权重设置

问题现象 :不知道如何设置class_weight参数
经验法则

  • 初始设置:多数类权重=1,少数类权重=多数类数量/少数类数量
  • 通过网格搜索微调权重
  • 考虑误分类代价(如欺诈检测中漏判的代价可能远高于误判)

5.4 极度不平衡场景(<0.1%)

特殊挑战

  • 少数类样本可能不足以学习有效特征
  • 噪声样本影响更大

应对策略

  • 异常检测方法(如Isolation Forest)
  • 半监督学习利用未标注数据
  • 主动学习迭代标注最有价值的样本

6. 工具与资源推荐

6.1 Python库

  1. imbalanced-learn :专门处理不平衡数据的工具包

    • 提供多种过采样、欠采样方法
    • 集成学习算法
    • 与scikit-learn兼容的API
  2. scikit-learn

    • class_weight 参数支持
    • 丰富的评估指标
    • 多种分类算法实现

6.2 实用代码片段

自定义评估指标:

from sklearn.metrics import make_scorer

def f2_score(y_true, y_pred):
    return fbeta_score(y_true, y_pred, beta=2)

f2_scorer = make_scorer(f2_score)

集成采样与模型:

from imblearn.ensemble import BalancedBaggingClassifier
from sklearn.tree import DecisionTreeClassifier

bbc = BalancedBaggingClassifier(
    base_estimator=DecisionTreeClassifier(),
    sampling_strategy='auto',
    replacement=False,
    random_state=42
)

6.3 学习资源

  1. 书籍

    • 《Imbalanced Learning: Foundations, Algorithms, and Applications》
    • 《Learning from Imbalanced Data Sets》
  2. 论文

    • "Learning from Imbalanced Data" (He & Garcia, 2009)
    • "A Survey of Predictive Modelling under Imbalanced Distributions" (Branco et al., 2015)
  3. 在线课程

    • Coursera "Machine Learning with Imbalanced Data"
    • Kaggle相关竞赛和notebooks

在实际项目中处理不平衡分类问题时,我通常会遵循这样的流程:首先分析数据不平衡程度,然后选择合适的评估指标,接着尝试不同的采样策略和算法改进方法,最后通过交叉验证和业务指标确定最佳方案。记住,没有放之四海而皆准的解决方案,最佳方法往往取决于具体的数据特点和业务需求。

更多推荐