如何处理分类问题中的不平衡数据?附Python实现与评估指南
不平衡二分类任务的解决方案与实践
必须关注的性能指标
不平衡数据下,准确率完全没用——模型只要全预测多数类(Class A)就能拿到80%的准确率,根本无法体现对少数类的识别能力。你需要重点关注这些指标:
- 混淆矩阵:直观查看少数类(Class B)的正确预测数(TP)、漏判数(FN),以及多数类的误判数(FP)
- 召回率(Recall/TPR):真正的Class B样本中被正确识别的比例,公式为
TP/(TP+FN)——这是衡量模型是否能有效捕捉少数类的核心指标 - 精确率(Precision):预测为Class B的样本中真正属于B的比例,公式为
TP/(TP+FP)——避免模型把太多A误判成B - F1分数:精确率和召回率的调和平均,公式为
2*(Precision*Recall)/(Precision+Recall)——平衡两者的综合表现 - PR-AUC(精确率-召回率曲线下面积):比ROC-AUC更适合不平衡场景,ROC-AUC对少数类的性能变化不敏感,而PR-AUC能直接反映模型在少数类上的表现
不平衡数据处理方案(Scikit-learn + imblearn实现)
以下是针对你的场景的具体代码,假设X是特征矩阵,y是标签(0=Class A,1=Class B):
1. 类别权重调整(最简单高效的首选方案)
大部分Scikit-learn分类器支持class_weight参数,自动给少数类分配更高权重,无需修改原始数据。
from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, confusion_matrix # 分层划分训练集/测试集,保证类别比例与原始数据一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # Logistic Regression 带类别权重 lr = LogisticRegression( class_weight='balanced', random_state=42, max_iter=1000 ) lr.fit(X_train, y_train) y_pred_lr = lr.predict(X_test) print("Logistic Regression(带类别权重)结果:") print(confusion_matrix(y_test, y_pred_lr)) print(classification_report(y_test, y_pred_lr)) # Random Forest 带类别权重 rf = RandomForestClassifier( class_weight='balanced', random_state=42, n_estimators=100 ) rf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) print("\nRandom Forest(带类别权重)结果:") print(confusion_matrix(y_test, y_pred_rf)) print(classification_report(y_test, y_pred_rf))
说明:class_weight='balanced'会根据样本比例自动计算权重,公式为 n_samples / (n_classes * np.bincount(y)),少数类的权重会更高。
2. 过采样(增加少数类样本)
如果类别权重效果不佳,可尝试过采样。SMOTE是生成合成少数类样本的方法,比直接复制少数类(随机过采样)更能避免过拟合。
from imblearn.over_sampling import SMOTE, RandomOverSampler # SMOTE 合成少数类样本 smote = SMOTE(random_state=42) X_resampled_smote, y_resampled_smote = smote.fit_resample(X_train, y_train) # 训练Random Forest rf_smote = RandomForestClassifier(random_state=42) rf_smote.fit(X_resampled_smote, y_resampled_smote) y_pred_smote = rf_smote.predict(X_test) print("SMOTE过采样后模型结果:") print(classification_report(y_test, y_pred_smote)) # 随机过采样(仅做对比,不推荐优先使用) ros = RandomOverSampler(random_state=42) X_resampled_ros, y_resampled_ros = ros.fit_resample(X_train, y_train) rf_ros = RandomForestClassifier(random_state=42) rf_ros.fit(X_resampled_ros, y_resampled_ros) y_pred_ros = rf_ros.predict(X_test) print("\n随机过采样后模型结果:") print(classification_report(y_test, y_pred_ros))
3. 欠采样(减少多数类样本)
仅在数据量极大(比如百万级样本)时考虑,因为会丢失多数类的关键信息。NearMiss是智能欠采样方法,保留与少数类更相似的多数类样本。
from imblearn.under_sampling import NearMiss, RandomUnderSampler # NearMiss 智能欠采样 nm = NearMiss(version=1) X_resampled_nm, y_resampled_nm = nm.fit_resample(X_train, y_train) rf_nm = RandomForestClassifier(random_state=42) rf_nm.fit(X_resampled_nm, y_resampled_nm) y_pred_nm = rf_nm.predict(X_test) print("NearMiss欠采样后模型结果:") print(classification_report(y_test, y_pred_nm)) # 随机欠采样(仅做对比) rus = RandomUnderSampler(random_state=42) X_resampled_rus, y_resampled_rus = rus.fit_resample(X_train, y_train) rf_rus = RandomForestClassifier(random_state=42) rf_rus.fit(X_resampled_rus, y_resampled_rus) y_pred_rus = rf_rus.predict(X_test) print("\n随机欠采样后模型结果:") print(classification_report(y_test, y_pred_rus))
4. 集成方法(采样+模型的最优组合)
集成方法通常比单一采样+模型的效果更好,推荐使用BalancedRandomForest或EasyEnsemble:
from imblearn.ensemble import BalancedRandomForestClassifier, EasyEnsembleClassifier # Balanced Random Forest:每个树训练时对多数类欠采样 brf = BalancedRandomForestClassifier(random_state=42, n_estimators=100) brf.fit(X_train, y_train) y_pred_brf = brf.predict(X_test) print("Balanced Random Forest结果:") print(classification_report(y_test, y_pred_brf)) # Easy Ensemble:对多数类多次欠采样,训练多个模型集成 ee = EasyEnsembleClassifier(random_state=42, n_estimators=100) ee.fit(X_train, y_train) y_pred_ee = ee.predict(X_test) print("\nEasy Ensemble结果:") print(classification_report(y_test, y_pred_ee))
最佳实践总结
- 优先尝试类别权重调整:实现简单,不修改原始数据,适合大多数场景,尤其是数据量中等的情况。
- 其次选择SMOTE过采样:比随机过采样更稳健,减少过拟合风险,适合特征空间连续的数据集。
- 欠采样仅在数据量极大时使用:避免丢失多数类的重要信息,优先用NearMiss而非随机欠采样。
- 集成方法是复杂数据集的最优选择:Balanced RF和Easy Ensemble能兼顾多数类和少数类的性能。
- 始终分层划分数据集:用
stratify=y保证训练集和测试集的类别比例与原始数据一致,避免评估偏差。 - 坚决抛弃准确率:重点关注少数类的召回率、F1分数、PR-AUC,这些指标才能真实反映模型性能。
内容的提问来源于stack exchange,提问作者Viper
相关产品推荐
相关产品推荐

