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

测试集得分高于训练集是否方法有误?小样本不平衡数据集调参疑问

嘿,这个情况我之前在处理小样本不平衡分类任务时也碰到过,尤其是用随机森林的时候,先帮你拆解下可能的原因,再给你一些实操的建议:

为什么测试集得分会高于训练集?

这种反直觉的现象在小样本+多类别不平衡数据里其实挺常见的,主要有这几个可能:

  • 数据拆分的随机性与样本质量差异:3000个数据点拆12类,每类平均才250个样本,分层拆分后训练集每类大概175个、测试集75个。如果测试集里的样本刚好是该类别中更“典型”、噪声更少的样本,而训练集里混了更多难分类的边缘样本,就会导致测试集得分更高。
  • 随机森林的Bootstrap特性:随机森林每棵树训练时用的是Bootstrap采样(随机有放回选样本),训练集里大概36.8%的样本不会被某棵树选中(袋外样本OOB)。你计算训练集得分时用的是整个训练集,其中包含了每棵树没见过的OOB样本,这部分样本的预测效果本来就接近测试集;而如果训练集整体样本量太小,Bootstrap采样的方差会变大,进一步放大这种差异。
  • 不平衡数据集的评估偏差:虽然你用了cohen_kappa_score(比accuracy更适合不平衡数据),但如果少数类在测试集里的样本刚好是模型更容易识别的,而训练集里的少数类样本更复杂,也会拉低训练集的整体得分。

解决建议与实操步骤

针对你的情况,我建议从这几个方向入手排查和优化:

1. 先验证数据拆分的合理性

先确认训练集和测试集的类别分布、特征分布是否和原数据集一致,避免拆分带来的偏差:

import pandas as pd

# 对比类别分布比例
train_dist = pd.Series(y_train).value_counts(normalize=True).sort_index()
test_dist = pd.Series(y_test).value_counts(normalize=True).sort_index()
print("训练集类别分布:\n", train_dist)
print("\n测试集类别分布:\n", test_dist)

# 可以画特征分布箱线图对比,比如选几个重要特征
import seaborn as sns
import matplotlib.pyplot as plt
for col in X_Distances.columns[:3]:  # 选前3个特征示例
    plt.figure(figsize=(10,4))
    sns.boxplot(x='label', y=col, data=pd.concat([X_Distances, Y], axis=1))
    plt.title(f"Feature {col} Distribution by Class")
    plt.show()

2. 用OOB得分代替训练集整体得分

随机森林的OOB样本是模型训练时没见过的,用它来评估训练阶段的性能会比整个训练集更客观,更接近测试集的情况:

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import cohen_kappa_score, accuracy_score

# 初始化模型时开启OOB得分
rf = RandomForestClassifier(random_state=42, oob_score=True, class_weight='balanced')
rf.fit(x_train, y_train)

# 更准确的OOB预测方式
import numpy as np
oob_pred = np.argmax(rf.oob_decision_function_, axis=1)

# 计算OOB的kappa和accuracy
oob_kappa = cohen_kappa_score(y_train, oob_pred)
oob_acc = accuracy_score(y_train, oob_pred)
print(f"OOB Kappa: {oob_kappa:.4f}, OOB Accuracy: {oob_acc:.4f}")

# 对比测试集得分
test_pred = rf.predict(x_test)
test_kappa = cohen_kappa_score(y_test, test_pred)
test_acc = accuracy_score(y_test, test_pred)
print(f"Test Kappa: {test_kappa:.4f}, Test Accuracy: {test_acc:.4f}")

如果OOB得分和测试集得分接近,说明模型的性能是稳定的,之前的训练集整体得分参考性不强。

3. 用分层交叉验证代替单一拆分调参

单一的train_test_split随机性太强,用分层K折交叉验证(StratifiedKFold)来调参,能更稳定地评估模型性能,避免拆分带来的偶然情况:

from sklearn.model_selection import StratifiedKFold, GridSearchCV

# 定义参数网格,针对不平衡数据优先调class_weight
param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [None, 10, 20],
    'class_weight': ['balanced', 'balanced_subsample']
}

# 5折分层交叉验证
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 网格搜索,以cohen_kappa为主要评估指标
grid_search = GridSearchCV(
    estimator=RandomForestClassifier(random_state=42, oob_score=True),
    param_grid=param_grid,
    cv=skf,
    scoring=['cohen_kappa', 'accuracy'],
    refit='cohen_kappa',  # 按kappa得分选最佳模型
    verbose=1
)

grid_search.fit(x_train, y_train)

# 查看最佳参数和交叉验证得分
print("最佳参数组合:", grid_search.best_params_)
print("交叉验证最佳Kappa得分:", grid_search.best_score_)

# 用最佳模型评估测试集
best_rf = grid_search.best_estimator_
test_pred = best_rf.predict(x_test)
print(f"测试集Kappa: {cohen_kappa_score(y_test, test_pred):.4f}")
print(f"测试集Accuracy: {accuracy_score(y_test, test_pred):.4f}")

4. 针对性处理类别不平衡

除了设置class_weight,还可以考虑对训练集做轻微的过采样(比如SMOTE),但要注意小样本下过采样不要引入太多噪声:

from imblearn.over_sampling import SMOTE

# 对训练集做SMOTE过采样
smote = SMOTE(random_state=42)
x_train_smote, y_train_smote = smote.fit_resample(x_train, y_train)

# 用处理后的训练集训练模型
rf_smote = RandomForestClassifier(random_state=42, oob_score=True, class_weight='balanced')
rf_smote.fit(x_train_smote, y_train_smote)

# 评估测试集
test_pred_smote = rf_smote.predict(x_test)
print(f"SMOTE后测试集Kappa: {cohen_kappa_score(y_test, test_pred_smote):.4f}")

总结

这种测试集得分高于训练集的情况在小样本不平衡数据里并不罕见,大多是数据拆分随机性、训练集噪声或模型特性导致的。通过验证数据分布、使用OOB得分、交叉验证调参这些方法,你能更准确地评估模型的真实性能,找到更合适的参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:10:19