You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何对整个数据集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

问题根源分析

  1. 训练集标签分布失衡
    未打乱的原始数据可能存在标签偏移:前18000行训练集中的阳性样本占比极低(甚至为0),单独对训练集采样只是打乱顺序,并没有改变样本分布。GaussianNB属于生成式模型,若训练集中没有足够的阳性样本,模型无法学习到阳性类别的特征分布,自然不会输出阳性预测。而先对整个数据集采样再拆分,训练集能覆盖到足够的阳性样本。

  2. 特征与标签的索引错位
    虽然两次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:15:49