直接用 train_test_split 会放大类别不平衡问题,因其默认随机切分不保证各类别比例一致,导致训练集和测试集中正负样本分布失真;解决方法是传入 stratify=y 参数实现分层抽样。

为什么直接用 train_test_split 会放大类别不平衡问题?
默认的 train_test_split 按样本随机切分,不保证各类别在训练集/测试集中比例一致。比如原始数据中正样本只占 2%,切分后可能训练集正样本掉到 0.8%,测试集却跳到 3.5%——模型根本学不到稳定模式,评估结果也失真。
解决方法是强制按类别分层抽样:
- 必须传入
stratify=y参数(y是标签数组),否则分层失效 -
stratify仅支持一维标签;多标签或多输出场景需先转换为唯一组合编码 - 若某类别样本数 train_test_split 会直接报错
ValueError: The least populated class in y has only 1 member
过采样时为什么不能对整个数据集用 SMOTE?
SMOTE 通过合成少数类邻近样本点来扩充数据,但若在划分前就对全量数据运行,会导致训练集和测试集“信息泄露”:测试样本的合成邻居可能来自训练集,模型实际在作弊。
正确做法是只在训练集上 fit + sample:
- 先用
train_test_split(..., stratify=y)切分 - 再对
X_train和y_train调用SMOTE().fit_resample(X_train, y_train) - 注意:
fit_resample返回新数组,原X_train和y_train不变 - 如果使用
imblearn.pipeline.Pipeline,务必把SMOTE放在第一步,避免后续标准化等步骤污染合成逻辑
用 class_weight='balanced' 时权重怎么算?
不是简单按反比(如 98% vs 2% → 权重 1/0.98 vs 1/0.02),而是按 n_samples / (n_classes * n_samples_of_class) 计算。例如二分类中负样本占 90%,则负类权重为 1 / (2 * 0.9) ≈ 0.56,正类为 1 / (2 * 0.1) = 5.0。
这个策略只适用于支持该参数的模型(如 SVC、RandomForestClassifier、LogisticRegression):
- 树模型(如
RandomForestClassifier)内部用权重影响分割标准计算,而非简单加权损失 -
class_weight='balanced_subsample'仅对 bagging 类模型有效,每次 bootstrap 采样时动态重平衡 - 若手动传字典(如
{0: 1, 1: 5}),注意键必须严格匹配标签值类型(int/str),否则静默失效
验证阶段为什么不能只看准确率?
当负样本占 95%,模型全预测负类也能拿到 95% 准确率,但毫无价值。必须监控 classification_report 中的 recall(查全率)和 f1-score,尤其关注少数类指标。
实操建议:
- 用
sklearn.metrics.classification_report(y_true, y_pred, output_dict=True)提取各列数值,方便自动化判断 - 混淆矩阵中重点关注
tn(真负)、tp(真正)是否被严重低估,而不是总和 - 若使用交叉验证,必须用
StratifiedKFold,否则某折可能完全缺失少数类,导致f1-score计算失败或为 nan
最常被忽略的是:重采样或加权后,测试集仍必须保持原始分布——它代表真实场景,任何在测试集上做平衡的操作都会让评估失去意义。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











