在真实世界的机器学习应用中,我们经常会遇到类别分布严重失衡的数据集。比如信用卡欺诈检测中,正常交易占比可能高达99.9%,而欺诈交易仅占0.1%;在医疗诊断场景中,健康样本数量可能远超患病样本。这种类别不平衡会带来两个核心问题:
首先,评估指标会失真。如果直接用准确率(Accuracy)评估,一个将所有样本预测为多数类的傻瓜模型就能获得99.9%的"高准确率",这显然没有实际意义。其次,模型训练过程会天然倾向于多数类,因为优化目标(如交叉熵损失)被多数类样本主导,导致对少数类的识别能力低下。
关键认知:不平衡问题的核心不是绝对数量差异,而是模型优化目标与业务需求的不匹配。我们需要的是在少数类上具有高召回率,同时控制多数类的误判率。
SMOTE(Synthetic Minority Over-sampling Technique)是最经典的过采样算法,其核心是通过线性插值生成新的少数类样本。具体实现时需要注意:
python复制from imblearn.over_sampling import SMOTE
# 关键参数调节
sm = SMOTE(
sampling_strategy=0.5, # 使少数类达到多数类的50%
k_neighbors=5, # 根据特征空间密度调整
random_state=42
)
X_res, y_res = sm.fit_resample(X_train, y_train)
实际应用中我发现,当特征维度很高时(>50维),SMOTE可能生成无意义的样本。这时可以先用PCA降维后再过采样,或者改用Borderline-SMOTE只对边界样本进行增强。
随机欠采样虽然简单,但会丢失大量信息。更聪明的做法是:
我在电商用户流失预测项目中对比发现,结合KMeans的聚类欠采样能使模型保持85%的召回率同时将训练时间缩短60%。
通过class_weight参数赋予不同类别不同的误分类代价。以逻辑回归为例:
python复制# 基于样本逆比例自动计算权重
model = LogisticRegression(
class_weight='balanced',
penalty='l1',
solver='liblinear'
)
# 或手动指定权重比
weights = {0:1, 1:10} # 少数类误判代价是多数类的10倍
经验提示:当类别极度不平衡时(如1:10000),建议先用log(ratio)平滑权重,避免梯度爆炸。
XGBoost提供了更精细的控制:
python复制import xgboost as xgb
# 设置scale_pos_weight参数
model = xgb.XGBClassifier(
scale_pos_weight=sum(negative)/sum(positive),
eval_metric='aucpr' # 对不平衡数据更友好的评估指标
)
在金融风控场景中,通过调整决策阈值(通常不是默认的0.5)可以实现在不同业务需求下的精准控制。
当少数类样本极少时,可以将其视为异常点处理:
python复制from sklearn.svm import OneClassSVM
ocsvm = OneClassSVM(
nu=0.01, # 预期异常点比例
kernel='rbf',
gamma='auto'
)
ocsvm.fit(X_majority) # 仅用多数类训练
隔离森林对高维数据表现良好,但需要注意:
我在服务器故障预测中的实践表明,结合自动编码器的重构误差作为新特征,能提升隔离森林30%的检测率。
必须采用更适合不平衡数据的指标:
| 指标 | 公式 | 适用场景 |
|---|---|---|
| F1-Score | 2*(P*R)/(P+R) | 类别相对平衡时 |
| MCC | (TPTN-FPFN)/sqrt((TP+FP)(TP+FN)(TN+FP)(TN+FN)) | 各类别都重要时 |
| AUC-PR | 精确率-召回率曲线下面积 | 极端不平衡时 |
在医疗场景中,我们可能需要:
通过自助采样使每个决策树看到平衡的数据:
python复制from imblearn.ensemble import BalancedRandomForestClassifier
brf = BalancedRandomForestClassifier(
n_estimators=500,
sampling_strategy='auto',
replacement=True, # 允许重复采样
oob_score=True
)
通过并行化实现高效训练:
python复制from imblearn.ensemble import EasyEnsembleClassifier
ee = EasyEnsembleClassifier(
n_subsets=50,
base_estimator=LogisticRegression(max_iter=1000),
n_jobs=-1 # 使用所有CPU核心
)
实际部署时,我通常会先用贝叶斯优化搜索最佳子集数量,再通过早停机制避免过拟合。
在某电信客户流失预测项目中,我采用的分阶段方案:
这套方案将流失用户的召回率从62%提升到89%,同时将误判率控制在可接受的15%以内。关键是要持续监控生产环境中的指标漂移,每季度重新校准一次模型。