测试集得分高于训练集是否方法有误?小样本不平衡数据集调参疑问
嘿,这个情况我之前在处理小样本不平衡分类任务时也碰到过,尤其是用随机森林的时候,先帮你拆解下可能的原因,再给你一些实操的建议:
为什么测试集得分会高于训练集?
这种反直觉的现象在小样本+多类别不平衡数据里其实挺常见的,主要有这几个可能:
- 数据拆分的随机性与样本质量差异: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
相关产品推荐
相关产品推荐

