为何对整个数据集DataFrame采样的预测模型效果优于仅采样训练集?
分类模型预测异常:仅采样训练集时无阳性预测结果
问题场景
我有一个20000行的DataFrame(original_df),按前18000行作为训练集、后2000行作为测试集拆分:
- 先对整个原始数据集用
sample打乱后再拆分训练集:模型预测结果正常,存在假阳性(fp)、假阴性(fn)、真阳性(tp)、真阴性(tn) - 先拆分未打乱的原始数据集,再对训练集单独采样:模型始终无法做出阳性预测,仅输出真阴性和假阴性;不做任何采样时,结果和这种情况完全一致
已排查索引匹配问题但未发现异常,且确认X_train和Y_train已被打乱、索引表面匹配,使用的分类器为sklearn.naive_bayes.GaussianNB()。
代码片段对比
先采样整个数据集再拆分(结果正常)
import math training_len = math.ceil(len(X) * 0.9) # 对特征和标签分别采样打乱 X.sample(frac=1, random_state=2) Y.sample(frac=1, random_state=2) # 拆分训练集和测试集 X_train = X.loc[:training_len] Y_train = Y.loc[:training_len] X_test = X.loc[training_len+1:] Y_test = Y.loc[training_len+1:] # 输出结果:fp, fn, tp, tn = 1314, 1703, 455, 8842
先拆分再采样训练集(结果异常)
import math training_len = math.ceil(len(X) * 0.9) # 直接拆分未打乱的数据集 X_train = X.loc[:training_len].sample(frac=1, random_state=2) Y_train = Y.loc[:training_len].sample(frac=1, random_state=2) X_test = X.loc[training_len+1:] Y_test = Y.loc[training_len+1:] # 输出结果:fp, fn, tp, tn = 0, 425, 0, 2518
问题根源分析
训练集标签分布失衡
未打乱的原始数据可能存在标签偏移:前18000行训练集中的阳性样本占比极低(甚至为0),单独对训练集采样只是打乱顺序,并没有改变样本分布。GaussianNB属于生成式模型,若训练集中没有足够的阳性样本,模型无法学习到阳性类别的特征分布,自然不会输出阳性预测。而先对整个数据集采样再拆分,训练集能覆盖到足够的阳性样本。特征与标签的索引错位
虽然两次sample用了相同的random_state,但对X.loc[:training_len]和Y.loc[:training_len]分别采样,本质是两个独立的打乱操作——即使随机种子相同,也无法保证打乱后的X_train和Y_train的索引完全对应(除非原始X和Y的索引完全一致且无重复)。这种错位会导致模型学习到错误的特征-标签关联,相当于没有真正学到阳性样本的规律。
修正方案
方案1:保证特征与标签同步打乱
拆分训练集后,通过共享随机索引的方式同步打乱特征和标签,避免错位:
import math from sklearn.naive_bayes import GaussianNB training_len = math.ceil(len(X) * 0.9) # 拆分未打乱的训练集原始数据 X_train_raw = X.loc[:training_len] Y_train_raw = Y.loc[:training_len] X_test = X.loc[training_len+1:] Y_test = Y.loc[training_len+1:] # 生成打乱后的索引,同步应用到特征和标签 shuffled_idx = X_train_raw.sample(frac=1, random_state=2).index X_train = X_train_raw.loc[shuffled_idx] Y_train = Y_train_raw.loc[shuffled_idx] # 训练并评估模型 model = GaussianNB() model.fit(X_train, Y_train)
方案2:检查训练集标签分布
先验证训练集的阳性样本占比:
# 查看未打乱的训练集中阳性样本数量(假设阳性标签为1) positive_count = Y.loc[:training_len].sum() print(f"训练集阳性样本数量:{positive_count}")
若阳性样本数量极少,可考虑:
- 使用分层拆分(
sklearn.model_selection.train_test_split的stratify参数),保证训练集和测试集的标签分布一致 - 对阳性样本进行过采样,或对阴性样本进行欠采样,平衡数据集分布
内容的提问来源于stack exchange,提问作者robxbob
相关产品推荐
相关产品推荐

