You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

随机森林训练测试准确率98%,新样本始终预测同一类求助

解决Random Forest对新样本全预测同一类的问题

首先,我完全理解你花30小时调试却毫无头绪的挫败感——这种“训练测试表现完美,但新数据彻底拉胯”的问题,往往出在数据一致性偏差不平衡数据集的模型倾斜上,而非单纯的参数调优。结合你的描述,我给你梳理几个关键排查方向和落地解决方案:

一、优先排查:新数据与训练集的特征一致性

哪怕你说“数据处理方式一致”,也极可能存在细节偏差,这是此类问题的头号元凶:

1. 核对特征列的完全匹配

Random Forest对输入特征的维度、列名是严格绑定的,哪怕差一列都会导致预测逻辑混乱。你可以运行以下代码快速对比:

# 提取训练集和新数据的特征列集合
train_cols = set(X_train.columns)
new_data_cols = set(df.columns)

print("训练集有但新数据缺失的列:", train_cols - new_data_cols)
print("新数据有但训练集没有的列:", new_data_cols - train_cols)
  • 如果有缺失的列:给新数据补上这些列并填充0(训练集独热编码时这些类别不存在,对应值本就是0);
  • 如果有新增的列:直接删除,模型训练时从未见过这些特征,无法做出有效判断。

2. 验证特征值的分布差异

训练集和新数据的特征分布漂移,会让模型彻底“看不懂”新数据。你可以:

  • 对连续特征:对比训练集和新数据的统计量,比如:
    print("训练集某连续特征统计:\n", X_train['feature_name'].describe())
    print("新数据对应特征统计:\n", df['feature_name'].describe())
    
  • 对类别特征:检查新数据的类别是否都在训练集的类别范围内(比如训练集某特征只有['a','b'],新数据却出现'c',独热编码后会新增列导致特征不匹配);
  • 量化差异:用PSI(群体稳定性指标)衡量特征分布变化,PSI>0.2就说明分布差异极大,需要重新处理数据。

3. 确认缺失值填充逻辑一致

你在新数据中用了df.fillna(-1),那训练集的缺失值是否也是用-1填充的?如果训练集是用均值、中位数或其他方式填充,新数据用-1会导致特征分布完全偏离,模型自然无法正确预测。

二、针对不平衡数据集的模型偏差调整

你的数据集class 0(15万)和class 1(60万)比例为1:4,属于中度不平衡,训练时的高准确率其实是被多数类(class 1)拉高的,模型可能对少数类的泛化能力极差——但你说新数据全预测为class 0,需要先明确真实概率情况:

1. 查看预测概率而非直接类别

别只用predict(),改用predict_proba()查看模型对新样本的真实概率输出:

probs = model.predict_proba(df)
# 查看前10个样本的class 0和class 1概率
print(probs[:10])
  • 如果所有样本的class 0概率显著高于0.5:要么新数据确实都是class 0(可以人工验证几个样本),要么新数据特征和训练集的class 0完全重合;
  • 如果概率接近0.5但被默认阈值(0.5)划到class 0:可以调整阈值,比如把class 1的预测阈值调低,提升对多数类的敏感度;
  • 如果概率极端偏向class 0:大概率是预处理导致新数据特征与训练集class 0特征完全一致。

2. 重新评估模型的真实性能

训练集和测试集的98%-99%准确率没有参考价值——哪怕模型全预测class 1,准确率也有80%。你需要查看混淆矩阵分类报告

from sklearn.metrics import classification_report, confusion_matrix

print("训练集分类报告:")
print(classification_report(y_train, model.predict(X_train)))
print("训练集混淆矩阵:")
print(confusion_matrix(y_train, model.predict(X_train)))

重点看class 0的召回率:如果召回率很低,说明模型根本没学会识别class 0;如果召回率很高,那问题还是出在新数据的预处理上。

3. 调整模型的类别权重

给少数类更高的权重,强迫模型关注少数类的特征:

model = RandomForestClassifier(
    n_estimators=N_ESTIMATORS,
    max_depth=MAX_DEPTH,
    random_state=RANDOM_STATE,
    oob_score=True,
    class_weight='balanced'  # 自动调整类别权重,反比于类别频率
)

也可以用class_weight='balanced_subsample',它会在每个决策树采样时调整权重,更适配随机森林的特性。

4. 尝试重采样方法

如果调整权重效果不好,可以对数据集进行重采样:

  • 过采样:对class 0的样本进行复制或用SMOTE合成新样本,让两类样本数量接近;
  • 欠采样:对class 1的样本随机删除,减少多数类样本数量;
  • 注意:重采样要在划分训练集和测试集之后进行,避免数据泄露。

三、其他排查点

  • 检查特征重要性:运行model.feature_importances_,看看哪些特征是模型最关注的。如果这些关键特征在新数据中的分布和训练集差异极大,会直接导致预测偏差;
  • 验证OOB分数的真实性:OOB分数同样会被多数类拉高,你可以手动计算OOB的混淆矩阵,看看模型对少数类的OOB表现;
  • 反向验证新数据:把少量新数据加入训练集重新训练,看模型是否能正确识别这些样本。如果还是不行,说明新数据的特征确实和训练集存在本质差异。

按照这个顺序排查,应该能快速定位问题。如果还是解决不了,可以提供新数据的特征统计量、模型的分类报告,或者几个新样本的特征值,方便进一步分析。

内容的提问来源于stack exchange,提问作者DL_Engineer

火山引擎 最新活动